OSDN Git Service

fix pos reward coinbase output[0] bug (#1962)
authorwyjDoraemon <46176410+wyjDoraemon@users.noreply.github.com>
Tue, 15 Jun 2021 06:34:33 +0000 (14:34 +0800)
committerGitHub <noreply@github.com>
Tue, 15 Jun 2021 06:34:33 +0000 (14:34 +0800)
* fix pos reward coinbase output[0] bug

* opt code

* add return

* fix

Co-authored-by: Paladz <yzhu101@uottawa.ca>
blockchain/txbuilder/builder.go
proposal/proposal.go
protocol/apply_block.go
protocol/validation/block.go
protocol/validation/block_test.go

index 6d2499d..47ff5f1 100644 (file)
@@ -45,6 +45,11 @@ func (b *TemplateBuilder) AddOutput(o *types.TxOutput) error {
        return nil
 }
 
+// Outputs return outputs of transactions
+func (b *TemplateBuilder) Outputs() []*types.TxOutput {
+       return b.outputs
+}
+
 // InputCount return number of input in the template builder
 func (b *TemplateBuilder) InputCount() int {
        return len(b.inputs)
index 86e3ebf..f4799ae 100644 (file)
@@ -161,8 +161,17 @@ func (b *blockBuilder) createCoinbaseTx() (tx *types.Tx, err error) {
                return nil, err
        }
 
+       if err = builder.AddOutput(types.NewOriginalTxOutput(*consensus.BTMAssetID, 0, script, [][]byte{})); err != nil {
+               return nil, err
+       }
+
        if b.block.Height%state.BlocksOfEpoch == 1 && b.block.Height != 1 {
                for controlProgram, amount := range checkpoint.Rewards {
+                       if controlProgram == hex.EncodeToString(script) {
+                               builder.Outputs()[0].Amount = amount
+                               continue
+                       }
+
                        controlProgramBytes, err := hex.DecodeString(controlProgram)
                        if err != nil {
                                return nil, err
@@ -172,10 +181,6 @@ func (b *blockBuilder) createCoinbaseTx() (tx *types.Tx, err error) {
                                return nil, err
                        }
                }
-       } else {
-               if err = builder.AddOutput(types.NewOriginalTxOutput(*consensus.BTMAssetID, 0, script, [][]byte{})); err != nil {
-                       return nil, err
-               }
        }
 
        _, txData, err := builder.Build()
index 30ee256..3f53293 100644 (file)
@@ -142,6 +142,7 @@ func (c *Casper) replayCheckpoint(hash bc.Hash) (*treeNode, error) {
                        ParentHash: parent.checkpoint.Hash,
                        Parent:     parent.checkpoint,
                        Status:     state.Growing,
+                       Rewards:    make(map[string]uint64),
                        Votes:      make(map[string]uint64),
                        Guaranties: make(map[string]uint64),
                },
@@ -153,6 +154,10 @@ func (c *Casper) replayCheckpoint(hash bc.Hash) (*treeNode, error) {
                        return nil, err
                }
 
+               if err := node.checkpoint.ApplyValidatorReward(attachBlock); err != nil {
+                       return nil, err
+               }
+
                node.checkpoint.Hash = attachBlock.Hash()
                node.checkpoint.Height = attachBlock.Height
                node.checkpoint.Timestamp = attachBlock.Timestamp
index 32b9dc4..10a498f 100644 (file)
@@ -60,22 +60,43 @@ func checkCoinbaseAmount(b *bc.Block, checkpoint *state.Checkpoint) error {
                if len(tx.TxHeader.ResultIds) != 1 {
                        return errors.Wrap(ErrWrongCoinbaseTransaction, "have more than 1 output")
                }
-       } else {
-               if len(tx.TxHeader.ResultIds) != len(checkpoint.Rewards) {
-                       return errors.Wrap(ErrWrongCoinbaseTransaction)
+
+               return nil
+       }
+
+       return checkoutRewardCoinbase(tx, checkpoint)
+}
+
+func checkoutRewardCoinbase(tx *bc.Tx, checkpoint *state.Checkpoint) error {
+       resultIdLen := len(tx.TxHeader.ResultIds)
+       if resultIdLen != len(checkpoint.Rewards) && resultIdLen != len(checkpoint.Rewards)+1 {
+               return errors.Wrap(ErrWrongCoinbaseTransaction)
+       }
+
+       var startIndex int
+       if resultIdLen == len(checkpoint.Rewards)+1 {
+               output, err := tx.Output(*tx.TxHeader.ResultIds[0])
+               if err != nil {
+                       return err
                }
 
-               rewards := checkpoint.Rewards
-               for i := 0; i < len(tx.TxHeader.ResultIds); i++ {
-                       output := tx.TxHeader.ResultIds[i]
-                       out, err := tx.Output(*output)
-                       if err != nil {
-                               return err
-                       }
-
-                       if rewards[hex.EncodeToString(out.ControlProgram.Code)] != out.Source.Value.Amount {
-                               return errors.Wrap(ErrWrongCoinbaseTransaction)
-                       }
+               if output.Source.Value.Amount != 0 {
+                       return errors.Wrap(ErrWrongCoinbaseTransaction, "dismatch output amount")
+               }
+
+               startIndex = 1
+       }
+
+       rewards := checkpoint.Rewards
+       for i := startIndex; i < resultIdLen; i++ {
+               output := tx.TxHeader.ResultIds[i]
+               out, err := tx.Output(*output)
+               if err != nil {
+                       return err
+               }
+
+               if rewards[hex.EncodeToString(out.ControlProgram.Code)] != out.Source.Value.Amount {
+                       return errors.Wrap(ErrWrongCoinbaseTransaction)
                }
        }
 
index a63df39..37997d3 100644 (file)
@@ -113,6 +113,42 @@ func TestCheckCoinbaseAmount(t *testing.T) {
                                        types.NewTx(types.TxData{
                                                Inputs: []*types.TxInput{types.NewCoinbaseInput(nil)},
                                                Outputs: []*types.TxOutput{
+                                                       types.NewOriginalTxOutput(*consensus.BTMAssetID, 0, []byte("controlProgram"), nil),
+                                                       types.NewOriginalTxOutput(*consensus.BTMAssetID, 5000, []byte("controlProgram"), nil),
+                                               },
+                                       }),
+                               },
+                       },
+                       checkpoint: &state.Checkpoint{
+                               Rewards: map[string]uint64{hex.EncodeToString([]byte("controlProgram")): 5000},
+                       },
+                       err: nil,
+               },
+               {
+                       block: &types.Block{
+                               BlockHeader: types.BlockHeader{Height: state.BlocksOfEpoch + 1},
+                               Transactions: []*types.Tx{
+                                       types.NewTx(types.TxData{
+                                               Inputs: []*types.TxInput{types.NewCoinbaseInput(nil)},
+                                               Outputs: []*types.TxOutput{
+                                                       types.NewOriginalTxOutput(*consensus.BTMAssetID, 1000, []byte("controlProgram"), nil),
+                                                       types.NewOriginalTxOutput(*consensus.BTMAssetID, 5000, []byte("controlProgram"), nil),
+                                               },
+                                       }),
+                               },
+                       },
+                       checkpoint: &state.Checkpoint{
+                               Rewards: map[string]uint64{hex.EncodeToString([]byte("controlProgram")): 5000},
+                       },
+                       err: ErrWrongCoinbaseTransaction,
+               },
+               {
+                       block: &types.Block{
+                               BlockHeader: types.BlockHeader{Height: state.BlocksOfEpoch + 1},
+                               Transactions: []*types.Tx{
+                                       types.NewTx(types.TxData{
+                                               Inputs: []*types.TxInput{types.NewCoinbaseInput(nil)},
+                                               Outputs: []*types.TxOutput{
                                                        types.NewOriginalTxOutput(*consensus.BTMAssetID, 5000, []byte("controlProgram1"), nil),
                                                        types.NewOriginalTxOutput(*consensus.BTMAssetID, 5000, []byte("controlProgram2"), nil),
                                                },