OSDN Git Service

coinbase reward (#1948)
authorwyjDoraemon <46176410+wyjDoraemon@users.noreply.github.com>
Thu, 10 Jun 2021 06:41:28 +0000 (14:41 +0800)
committerGitHub <noreply@github.com>
Thu, 10 Jun 2021 06:41:28 +0000 (14:41 +0800)
* coinbase reward

* fix test case

* fix transaction test

* delete unuseful code

* delete debug code

* fix ci

Co-authored-by: Paladz <yzhu101@uottawa.ca>
18 files changed:
account/utxo_keeper_test.go
blockchain/txbuilder/actions.go
database/store.go
database/store_test.go
netsync/chainmgr/fast_sync_test.go
netsync/peers/peer.go
proposal/proposal.go
protocol/apply_block.go
protocol/bc/types/map.go
protocol/bc/types/transaction_test.go
protocol/block.go
protocol/protocol.go
protocol/state/checkpoint.go
protocol/state/reward.go
protocol/txpool_test.go
protocol/validation/block.go
protocol/validation/block_test.go
test/utxo_view/utxo_view_test.go

index fc337b6..3b0d706 100644 (file)
@@ -6,9 +6,9 @@ import (
        "testing"
        "time"
 
+       dbm "github.com/bytom/bytom/database/leveldb"
        "github.com/bytom/bytom/protocol/bc"
        "github.com/bytom/bytom/testutil"
-       dbm "github.com/bytom/bytom/database/leveldb"
 )
 
 func TestAddUnconfirmedUtxo(t *testing.T) {
index 0a03cc4..d144df3 100644 (file)
@@ -241,4 +241,3 @@ func (a *voteOutputAction) Build(ctx context.Context, b *TemplateBuilder) error
 func (a *voteOutputAction) ActionType() string {
        return "vote_output"
 }
-
index 1a4594b..a2d46e7 100644 (file)
@@ -359,10 +359,10 @@ func (s *Store) saveCheckpoints(batch dbm.Batch, checkpoints []*state.Checkpoint
 
                batch.Set(calcCheckpointKey(checkpoint.Height, &checkpoint.Hash), data)
                log.WithFields(log.Fields{
-                       "module": logModule,
-                       "height": checkpoint.Height,
-                       "hash":   checkpoint.Hash.String(),
-                       "status": checkpoint.Status,
+                       "module":   logModule,
+                       "height":   checkpoint.Height,
+                       "hash":     checkpoint.Hash.String(),
+                       "status":   checkpoint.Status,
                        "duration": time.Since(startTime),
                }).Info("checkpoint saved on disk")
        }
index ba91edc..8295aea 100644 (file)
@@ -151,7 +151,7 @@ func TestSaveChainStatus(t *testing.T) {
        }
 
        contractView := state.NewContractViewpoint()
-       if err := store.SaveChainStatus(node, view, contractView,0, &bc.Hash{}); err != nil {
+       if err := store.SaveChainStatus(node, view, contractView, 0, &bc.Hash{}); err != nil {
                t.Fatal(err)
        }
 
@@ -211,4 +211,3 @@ func TestSaveBlock(t *testing.T) {
                t.Errorf("got block header:%v, expect block header:%v", gotBlockHeader, block.BlockHeader)
        }
 }
-
index 3f5bc6a..17a163d 100644 (file)
@@ -8,7 +8,6 @@ import (
        "testing"
        "time"
 
-       "github.com/bytom/bytom/testcontrol"
        "github.com/bytom/bytom/consensus"
        dbm "github.com/bytom/bytom/database/leveldb"
        "github.com/bytom/bytom/errors"
@@ -16,6 +15,7 @@ import (
        "github.com/bytom/bytom/protocol/bc"
        "github.com/bytom/bytom/protocol/bc/types"
        "github.com/bytom/bytom/test/mock"
+       "github.com/bytom/bytom/testcontrol"
        "github.com/bytom/bytom/testutil"
 )
 
index c343538..401c392 100644 (file)
@@ -185,7 +185,7 @@ func (p *Peer) GetPeerInfo() *PeerInfo {
        }
 }
 
-func (p *Peer) getRelatedTxs(txs []*types.Tx) ([]*types.Tx) {
+func (p *Peer) getRelatedTxs(txs []*types.Tx) []*types.Tx {
        var relatedTxs []*types.Tx
        for _, tx := range txs {
                if p.isRelatedTx(tx) {
index f320c25..86e3ebf 100644 (file)
@@ -1,6 +1,7 @@
 package proposal
 
 import (
+       "encoding/hex"
        "sort"
        "strconv"
        "time"
@@ -77,12 +78,11 @@ func newBlockBuilder(chain *protocol.Chain, validator *state.Validator, accountM
 
 func (b *blockBuilder) build() (*types.Block, error) {
        b.block.Transactions = []*types.Tx{nil}
-       feeAmount, err := b.applyTransactionFromPool()
-       if err != nil {
+       if err := b.applyTransactionFromPool(); err != nil {
                return nil, err
        }
 
-       if err := b.applyCoinbaseTransaction(feeAmount); err != nil {
+       if err := b.applyCoinbaseTransaction(); err != nil {
                return nil, err
        }
 
@@ -95,8 +95,8 @@ func (b *blockBuilder) build() (*types.Block, error) {
        return b.block, nil
 }
 
-func (b *blockBuilder) applyCoinbaseTransaction(feeAmount uint64) error {
-       coinbaseTx, err := b.createCoinbaseTx(feeAmount)
+func (b *blockBuilder) applyCoinbaseTransaction() error {
+       coinbaseTx, err := b.createCoinbaseTx()
        if err != nil {
                return errors.Wrap(err, "fail on create coinbase tx")
        }
@@ -111,7 +111,7 @@ func (b *blockBuilder) applyCoinbaseTransaction(feeAmount uint64) error {
        return nil
 }
 
-func (b *blockBuilder) applyTransactionFromPool() (uint64, error) {
+func (b *blockBuilder) applyTransactionFromPool() error {
        txDescList := b.chain.GetTxPool().GetTransactions()
        sort.Sort(byTime(txDescList))
        return b.applyTransactions(txDescList, timeoutWarn)
@@ -134,7 +134,7 @@ func (b *blockBuilder) calculateBlockCommitment() (err error) {
 // createCoinbaseTx returns a coinbase transaction paying an appropriate subsidy
 // based on the passed block height to the provided address.  When the address
 // is nil, the coinbase transaction will instead be redeemable by anyone.
-func (b *blockBuilder) createCoinbaseTx(feeAmount uint64) (tx *types.Tx, err error) {
+func (b *blockBuilder) createCoinbaseTx() (tx *types.Tx, err error) {
        arbitrary := append([]byte{0x00}, []byte(strconv.FormatUint(b.block.Height, 10))...)
        var script []byte
        if b.accountManager == nil {
@@ -156,11 +156,27 @@ func (b *blockBuilder) createCoinbaseTx(feeAmount uint64) (tx *types.Tx, err err
                return nil, err
        }
 
-       coinbaseAmount := consensus.BlockSubsidy(b.block.Height)
-       if err = builder.AddOutput(types.NewOriginalTxOutput(*consensus.BTMAssetID, coinbaseAmount + feeAmount, script, [][]byte{})); err != nil {
+       checkpoint, err := b.getPrevCheckpoint()
+       if err != nil {
                return nil, err
        }
-       //TODO: calculate reward to proposer
+
+       if b.block.Height%state.BlocksOfEpoch == 1 && b.block.Height != 1 {
+               for controlProgram, amount := range checkpoint.Rewards {
+                       controlProgramBytes, err := hex.DecodeString(controlProgram)
+                       if err != nil {
+                               return nil, err
+                       }
+
+                       if err := builder.AddOutput(types.NewOriginalTxOutput(*consensus.BTMAssetID, amount, controlProgramBytes, [][]byte{})); err != nil {
+                               return nil, err
+                       }
+               }
+       } else {
+               if err = builder.AddOutput(types.NewOriginalTxOutput(*consensus.BTMAssetID, 0, script, [][]byte{})); err != nil {
+                       return nil, err
+               }
+       }
 
        _, txData, err := builder.Build()
        if err != nil {
@@ -180,8 +196,7 @@ func (b *blockBuilder) createCoinbaseTx(feeAmount uint64) (tx *types.Tx, err err
        return tx, nil
 }
 
-func (b *blockBuilder) applyTransactions(txs []*protocol.TxDesc, timeoutStatus uint8) (uint64, error) {
-       var feeAmount uint64
+func (b *blockBuilder) applyTransactions(txs []*protocol.TxDesc, timeoutStatus uint8) error {
        batchTxs := []*protocol.TxDesc{}
        for i := 0; i < len(txs); i++ {
                if batchTxs = append(batchTxs, txs[i]); len(batchTxs) < batchApplyNum && i != len(txs)-1 {
@@ -189,7 +204,7 @@ func (b *blockBuilder) applyTransactions(txs []*protocol.TxDesc, timeoutStatus u
                }
 
                results, gasLeft := b.preValidateTxs(batchTxs, b.chain, b.utxoView, b.gasLeft)
-               for j, result := range results {
+               for _, result := range results {
                        if result.err != nil {
                                log.WithFields(log.Fields{"module": logModule, "error": result.err}).Error("propose block generation: skip tx due to")
                                b.chain.GetTxPool().RemoveTransaction(&result.tx.ID)
@@ -197,7 +212,6 @@ func (b *blockBuilder) applyTransactions(txs []*protocol.TxDesc, timeoutStatus u
                        }
 
                        b.block.Transactions = append(b.block.Transactions, result.tx)
-                       feeAmount += batchTxs[j].Fee
                }
 
                b.gasLeft = gasLeft
@@ -206,7 +220,7 @@ func (b *blockBuilder) applyTransactions(txs []*protocol.TxDesc, timeoutStatus u
                        break
                }
        }
-       return feeAmount, nil
+       return nil
 }
 
 type validateTxResult struct {
@@ -266,3 +280,11 @@ func (b *blockBuilder) getTimeoutStatus() uint8 {
 
        return b.timeoutStatus
 }
+
+func (b *blockBuilder) prevBlockHash() *bc.Hash {
+       return &b.block.PreviousBlockHash
+}
+
+func (b *blockBuilder) getPrevCheckpoint() (*state.Checkpoint, error) {
+       return b.chain.PrevCheckpointByPrevHash(b.prevBlockHash())
+}
index 1380497..30ee256 100644 (file)
@@ -75,6 +75,7 @@ func (c *Casper) applyBlockToCheckpoint(block *types.Block) (*state.Checkpoint,
                        ParentHash: parent.Hash,
                        Parent:     parent,
                        Status:     state.Growing,
+                       Rewards:    make(map[string]uint64),
                        Votes:      make(map[string]uint64),
                        Guaranties: make(map[string]uint64),
                }
@@ -88,6 +89,13 @@ func (c *Casper) applyBlockToCheckpoint(block *types.Block) (*state.Checkpoint,
                node.addChild(&treeNode{checkpoint: checkpoint})
        } else if mod == 0 {
                checkpoint.Status = state.Unjustified
+               if err := checkpoint.ApplyFederationReward(); err != nil {
+                       return nil, err
+               }
+       }
+
+       if err := checkpoint.ApplyValidatorReward(block); err != nil {
+               return nil, err
        }
 
        checkpoint.Height = block.Height
@@ -152,7 +160,8 @@ func (c *Casper) replayCheckpoint(hash bc.Hash) (*treeNode, error) {
        return node, nil
 }
 
-func applyTransactions(target *state.Checkpoint, transactions []*types.Tx) error {for _, tx := range transactions {
+func applyTransactions(target *state.Checkpoint, transactions []*types.Tx) error {
+       for _, tx := range transactions {
                for _, input := range tx.Inputs {
                        if vetoInput, ok := input.TypedInput.(*types.VetoInput); ok {
                                if err := processVeto(vetoInput, target); err != nil {
index dc848ab..a73ca1d 100644 (file)
@@ -1,6 +1,7 @@
 package types
 
 import (
+       "github.com/bytom/bytom/consensus"
        "github.com/bytom/bytom/protocol/bc"
        "github.com/bytom/bytom/protocol/vm"
        "github.com/bytom/bytom/protocol/vm/vmutil"
@@ -58,9 +59,13 @@ func (mh *mapHelper) generateTx() *bc.Tx {
 func (mh *mapHelper) mapCoinbaseInput(i int, input *CoinbaseInput) {
        mh.coinbase = bc.NewCoinbase(input.Arbitrary)
        mh.inputIDs[i] = mh.addEntry(mh.coinbase)
+       var totalAmount uint64
+       for _, output := range mh.txData.Outputs {
+               totalAmount += output.Amount
+       }
        mh.muxSources[i] = &bc.ValueSource{
                Ref:   &mh.inputIDs[i],
-               Value: &mh.txData.Outputs[0].AssetAmount,
+               Value: &bc.AssetAmount{AssetId: consensus.BTMAssetID, Amount: totalAmount},
        }
 }
 
index 26c434b..3689c97 100644 (file)
@@ -155,7 +155,7 @@ func TestTransaction(t *testing.T) {
                                "737461746544617461", // output 0: state data
                                "00",                 // output 1: witness length
                        }, ""),
-                       hash: testutil.MustDecodeHash("9f0ca64c282b7069c67af78e989a14ed8d208aa99222370d941dad4bbf69cb2c"),
+                       hash: testutil.MustDecodeHash("27d7fae74355ee9de2014bc51c30de27afa922ac4171eca1f38a05965c899c79"),
                },
        }
        for i, test := range cases {
index e07e03b..a0dc301 100644 (file)
@@ -225,7 +225,12 @@ func (c *Chain) saveBlock(block *types.Block) error {
        bcBlock := types.MapBlock(block)
        parent := c.index.GetNode(&block.PreviousBlockHash)
 
-       if err := validation.ValidateBlock(bcBlock, parent, c.ProgramConverter); err != nil {
+       checkpoint, err := c.PrevCheckpointByPrevHash(&block.PreviousBlockHash)
+       if err != nil {
+               return err
+       }
+
+       if err := validation.ValidateBlock(bcBlock, parent, checkpoint, c.ProgramConverter); err != nil {
                return errors.Sub(ErrBadBlock, err)
        }
 
index 44ca69f..4ba9975 100644 (file)
@@ -242,3 +242,8 @@ func (c *Chain) BlockWaiter(height uint64) <-chan struct{} {
 func (c *Chain) GetTxPool() *TxPool {
        return c.txPool
 }
+
+// PrevCheckpointByPrevHash get previous checkpoint by previous block hash
+func (c *Chain) PrevCheckpointByPrevHash(preBlockHash *bc.Hash) (*state.Checkpoint, error) {
+       return c.casper.parentCheckpointByPrevHash(preBlockHash)
+}
index 1da4df2..a55700f 100644 (file)
@@ -65,7 +65,7 @@ type Checkpoint struct {
        Status    CheckpointStatus
 
        Rewards    map[string]uint64 // controlProgram -> num of reward
-       Votes      map[string]uint64 // putKey -> num of vote
+       Votes      map[string]uint64 // pubKey -> num of vote
        Guaranties map[string]uint64 // pubKey -> num of guaranty
 }
 
index 03ecd89..3280e45 100644 (file)
@@ -12,7 +12,7 @@ import (
 
 const (
        totalSupply       = 15.66 * 1e16
-       singleBlockReward = uint64(570776255) //AnnualSupply(0.3 * 1e16) / AnnualBlock(365 * 24 * 60 * 10)
+       singleBlockReward = uint64(570776255) // AnnualSupply(0.3 * 1e16) / AnnualBlock(365 * 24 * 60 * 10)
        rewardThreshold   = 0.5
 )
 
@@ -88,6 +88,10 @@ func (c *Checkpoint) ApplyValidatorReward(block *types.Block) error {
                }
        }
 
+       if c.Parent == nil {
+               return errors.New("the checkpoint parent is nil")
+       }
+
        validatorReward, err := validatorRewardPerBlock(c.Parent)
        if err != nil {
                return err
@@ -100,6 +104,10 @@ func (c *Checkpoint) ApplyValidatorReward(block *types.Block) error {
 
 // ApplyFederationReward  federation gain the reward in an epoch
 func (c *Checkpoint) ApplyFederationReward() error {
+       if c.Parent == nil {
+               return errors.New("the checkpoint parent is nil")
+       }
+
        federationReward, err := federationBlockReward(c.Parent)
        if err != nil {
                return err
index 189697a..4dda751 100644 (file)
@@ -100,17 +100,19 @@ type mockStore struct{}
 func (s *mockStore) GetBlockHeader(hash *bc.Hash) (*types.BlockHeader, error)     { return nil, nil }
 func (s *mockStore) GetCheckpoint(hash *bc.Hash) (*state.Checkpoint, error)       { return nil, nil }
 func (s *mockStore) GetCheckpointsByHeight(u uint64) ([]*state.Checkpoint, error) { return nil, nil }
-func (s *mockStore) SaveCheckpoints([]*state.Checkpoint) error                   { return nil }
-func (s *mockStore) CheckpointsFromNode(height uint64, hash *bc.Hash) ([]*state.Checkpoint, error)      { return nil, nil }
-func (s *mockStore) BlockExist(hash *bc.Hash) bool                                { return false }
-func (s *mockStore) GetBlock(*bc.Hash) (*types.Block, error)                      { return nil, nil }
-func (s *mockStore) GetStoreStatus() *BlockStoreState                             { return nil }
-func (s *mockStore) GetTransactionsUtxo(*state.UtxoViewpoint, []*bc.Tx) error     { return nil }
-func (s *mockStore) GetUtxo(*bc.Hash) (*storage.UtxoEntry, error)                 { return nil, nil }
-func (s *mockStore) GetContract(hash [32]byte) ([]byte, error)                    { return nil, nil }
-func (s *mockStore) LoadBlockIndex(uint64) (*state.BlockIndex, error)             { return nil, nil }
-func (s *mockStore) SaveBlock(*types.Block) error                                 { return nil }
-func (s *mockStore) SaveBlockHeader(*types.BlockHeader) error                     { return nil }
+func (s *mockStore) SaveCheckpoints([]*state.Checkpoint) error                    { return nil }
+func (s *mockStore) CheckpointsFromNode(height uint64, hash *bc.Hash) ([]*state.Checkpoint, error) {
+       return nil, nil
+}
+func (s *mockStore) BlockExist(hash *bc.Hash) bool                            { return false }
+func (s *mockStore) GetBlock(*bc.Hash) (*types.Block, error)                  { return nil, nil }
+func (s *mockStore) GetStoreStatus() *BlockStoreState                         { return nil }
+func (s *mockStore) GetTransactionsUtxo(*state.UtxoViewpoint, []*bc.Tx) error { return nil }
+func (s *mockStore) GetUtxo(*bc.Hash) (*storage.UtxoEntry, error)             { return nil, nil }
+func (s *mockStore) GetContract(hash [32]byte) ([]byte, error)                { return nil, nil }
+func (s *mockStore) LoadBlockIndex(uint64) (*state.BlockIndex, error)         { return nil, nil }
+func (s *mockStore) SaveBlock(*types.Block) error                             { return nil }
+func (s *mockStore) SaveBlockHeader(*types.BlockHeader) error                 { return nil }
 func (s *mockStore) SaveChainStatus(*state.BlockNode, *state.UtxoViewpoint, *state.ContractViewpoint, uint64, *bc.Hash) error {
        return nil
 }
@@ -597,8 +599,10 @@ type mockStore1 struct{}
 func (s *mockStore1) GetBlockHeader(hash *bc.Hash) (*types.BlockHeader, error)     { return nil, nil }
 func (s *mockStore1) GetCheckpoint(hash *bc.Hash) (*state.Checkpoint, error)       { return nil, nil }
 func (s *mockStore1) GetCheckpointsByHeight(u uint64) ([]*state.Checkpoint, error) { return nil, nil }
-func (s *mockStore1) SaveCheckpoints([]*state.Checkpoint) error                   { return nil }
-func (s *mockStore1) CheckpointsFromNode(height uint64, hash *bc.Hash) ([]*state.Checkpoint, error)      { return nil, nil }
+func (s *mockStore1) SaveCheckpoints([]*state.Checkpoint) error                    { return nil }
+func (s *mockStore1) CheckpointsFromNode(height uint64, hash *bc.Hash) ([]*state.Checkpoint, error) {
+       return nil, nil
+}
 func (s *mockStore1) BlockExist(hash *bc.Hash) bool                                { return false }
 func (s *mockStore1) GetBlock(*bc.Hash) (*types.Block, error)                      { return nil, nil }
 func (s *mockStore1) GetStoreStatus() *BlockStoreState                             { return nil }
@@ -609,12 +613,14 @@ func (s *mockStore1) GetTransactionsUtxo(utxoView *state.UtxoViewpoint, tx []*bc
        }
        return nil
 }
-func (s *mockStore1) GetUtxo(*bc.Hash) (*storage.UtxoEntry, error)        { return nil, nil }
-func (s *mockStore1) GetContract(hash [32]byte) ([]byte, error)           { return nil, nil }
-func (s *mockStore1) LoadBlockIndex(uint64) (*state.BlockIndex, error)    { return nil, nil }
-func (s *mockStore1) SaveBlock(*types.Block) error                        { return nil }
-func (s *mockStore1) SaveBlockHeader(*types.BlockHeader) error            { return nil }
-func (s *mockStore1) SaveChainStatus(*state.BlockNode, *state.UtxoViewpoint, *state.ContractViewpoint, uint64, *bc.Hash) error { return nil}
+func (s *mockStore1) GetUtxo(*bc.Hash) (*storage.UtxoEntry, error)     { return nil, nil }
+func (s *mockStore1) GetContract(hash [32]byte) ([]byte, error)        { return nil, nil }
+func (s *mockStore1) LoadBlockIndex(uint64) (*state.BlockIndex, error) { return nil, nil }
+func (s *mockStore1) SaveBlock(*types.Block) error                     { return nil }
+func (s *mockStore1) SaveBlockHeader(*types.BlockHeader) error         { return nil }
+func (s *mockStore1) SaveChainStatus(*state.BlockNode, *state.UtxoViewpoint, *state.ContractViewpoint, uint64, *bc.Hash) error {
+       return nil
+}
 
 func TestProcessTransaction(t *testing.T) {
        txPool := &TxPool{
index 5d9bc10..32b9dc4 100644 (file)
@@ -1,6 +1,7 @@
 package validation
 
 import (
+       "encoding/hex"
        "time"
 
        log "github.com/sirupsen/logrus"
@@ -36,24 +37,48 @@ func checkBlockTime(b *bc.Block, parent *types.BlockHeader) error {
        return nil
 }
 
-func checkCoinbaseAmount(b *bc.Block, amount uint64) error {
+func checkCoinbaseAmount(b *bc.Block, checkpoint *state.Checkpoint) error {
        if len(b.Transactions) == 0 {
                return errors.Wrap(ErrWrongCoinbaseTransaction, "block is empty")
        }
 
        tx := b.Transactions[0]
-       if len(tx.TxHeader.ResultIds) != 1 {
-               return errors.Wrap(ErrWrongCoinbaseTransaction, "have more than 1 output")
+       if len(tx.TxHeader.ResultIds) == 0 {
+               return errors.Wrap(ErrWrongCoinbaseTransaction, "tx header resultIds is empty")
        }
 
-       output, err := tx.Output(*tx.TxHeader.ResultIds[0])
-       if err != nil {
-               return err
-       }
+       if b.Height%state.BlocksOfEpoch != 1 || b.Height == 1 {
+               output, err := tx.Output(*tx.TxHeader.ResultIds[0])
+               if err != nil {
+                       return err
+               }
+
+               if output.Source.Value.Amount != 0 {
+                       return errors.Wrap(ErrWrongCoinbaseTransaction, "dismatch output amount")
+               }
 
-       if output.Source.Value.Amount != amount {
-               return errors.Wrap(ErrWrongCoinbaseTransaction, "dismatch output amount")
+               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)
+               }
+
+               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)
+                       }
+               }
        }
+
        return nil
 }
 
@@ -77,27 +102,26 @@ func ValidateBlockHeader(b *bc.Block, parent *state.BlockNode) error {
 }
 
 // ValidateBlock validates a block and the transactions within.
-func ValidateBlock(b *bc.Block, parent *state.BlockNode, converter ProgramConverterFunc) error {
+func ValidateBlock(b *bc.Block, parent *state.BlockNode, checkpoint *state.Checkpoint, converter ProgramConverterFunc) error {
        startTime := time.Now()
        if err := ValidateBlockHeader(b, parent); err != nil {
                return err
        }
 
        blockGasSum := uint64(0)
-       coinbaseAmount := consensus.BlockSubsidy(b.BlockHeader.Height)
+
        validateResults := ValidateTxs(b.Transactions, b, converter)
        for i, validateResult := range validateResults {
                if validateResult.err != nil {
                        return errors.Wrapf(validateResult.err, "validate of transaction %d of %d", i, len(b.Transactions))
                }
 
-               coinbaseAmount += validateResult.gasStatus.BTMValue
                if blockGasSum += uint64(validateResult.gasStatus.GasUsed); blockGasSum > consensus.MaxBlockGas {
                        return errOverBlockLimit
                }
        }
 
-       if err := checkCoinbaseAmount(b, coinbaseAmount); err != nil {
+       if err := checkCoinbaseAmount(b, checkpoint); err != nil {
                return err
        }
 
index 0c86f4c..a63df39 100644 (file)
@@ -1,6 +1,7 @@
 package validation
 
 import (
+       "encoding/hex"
        "math"
        "testing"
        "time"
@@ -67,42 +68,79 @@ func TestCheckBlockTime(t *testing.T) {
 
 func TestCheckCoinbaseAmount(t *testing.T) {
        cases := []struct {
-               txs    []*types.Tx
-               amount uint64
-               err    error
+               block      *types.Block
+               checkpoint *state.Checkpoint
+               err        error
        }{
                {
-                       txs: []*types.Tx{
-                               types.NewTx(types.TxData{
-                                       Inputs:  []*types.TxInput{types.NewCoinbaseInput(nil)},
-                                       Outputs: []*types.TxOutput{types.NewOriginalTxOutput(*consensus.BTMAssetID, 5000, nil, nil)},
-                               }),
+                       block: &types.Block{
+                               BlockHeader: types.BlockHeader{Height: 0},
+                               Transactions: []*types.Tx{
+                                       types.NewTx(types.TxData{
+                                               Inputs: []*types.TxInput{types.NewCoinbaseInput(nil)},
+                                               Outputs: []*types.TxOutput{
+                                                       types.NewOriginalTxOutput(*consensus.BTMAssetID, 0, []byte("controlProgram"), nil),
+                                               },
+                                       }),
+                               },
                        },
-                       amount: 5000,
-                       err:    nil,
+                       checkpoint: &state.Checkpoint{
+                               Rewards: map[string]uint64{hex.EncodeToString([]byte("controlProgram")): 5000},
+                       },
+                       err: nil,
                },
                {
-                       txs: []*types.Tx{
-                               types.NewTx(types.TxData{
-                                       Inputs:  []*types.TxInput{types.NewCoinbaseInput(nil)},
-                                       Outputs: []*types.TxOutput{types.NewOriginalTxOutput(*consensus.BTMAssetID, 5000, nil, 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, 5000, []byte("controlProgram"), nil),
+                                               },
+                                       }),
+                               },
                        },
-                       amount: 6000,
-                       err:    ErrWrongCoinbaseTransaction,
+                       checkpoint: &state.Checkpoint{
+                               Rewards: map[string]uint64{hex.EncodeToString([]byte("controlProgram")): 5000},
+                       },
+                       err: nil,
                },
                {
-                       txs:    []*types.Tx{},
-                       amount: 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),
+                                               },
+                                       }),
+                               },
+                       },
+                       checkpoint: &state.Checkpoint{
+                               Rewards: map[string]uint64{
+                                       hex.EncodeToString([]byte("controlProgram1")): 5000,
+                                       hex.EncodeToString([]byte("controlProgram2")): 5000},
+                       },
+                       err: nil,
+               },
+               {
+                       block: &types.Block{
+                               BlockHeader:  types.BlockHeader{},
+                               Transactions: []*types.Tx{},
+                       },
+                       checkpoint: &state.Checkpoint{
+                               Rewards: map[string]uint64{"controlProgram": 5000},
+                       },
+                       err: ErrWrongCoinbaseTransaction,
                },
        }
 
-       block := new(types.Block)
        for i, c := range cases {
-               block.Transactions = c.txs
-               if err := checkCoinbaseAmount(types.MapBlock(block), c.amount); rootErr(err) != c.err {
-                       t.Errorf("case %d got error %s, want %s", i, err, c.err)
+               if err := checkCoinbaseAmount(types.MapBlock(c.block), c.checkpoint); rootErr(err) != c.err {
+                       t.Errorf("case %d got error %v, want %v", i, err, c.err)
                }
        }
 }
@@ -219,10 +257,11 @@ func TestValidateBlock(t *testing.T) {
        cp, _ := vmutil.DefaultCoinbaseProgram()
        converter := func(prog []byte) ([]byte, error) { return nil, nil }
        cases := []struct {
-               desc   string
-               block  *bc.Block
-               parent *state.BlockNode
-               err    error
+               desc       string
+               block      *bc.Block
+               parent     *state.BlockNode
+               checkpoint *state.Checkpoint
+               err        error
        }{
                {
                        desc: "The calculated transaction merkel root hash is not equals to the hash of the block header (blocktest#1009)",
@@ -230,7 +269,7 @@ func TestValidateBlock(t *testing.T) {
                                ID: bc.Hash{V0: 1},
                                BlockHeader: &bc.BlockHeader{
                                        Version:          1,
-                                       Height:           1,
+                                       Height:           state.BlocksOfEpoch + 1,
                                        Timestamp:        1523358600,
                                        PreviousBlockId:  &bc.Hash{V0: 0},
                                        TransactionsRoot: &bc.Hash{V0: 1},
@@ -240,16 +279,20 @@ func TestValidateBlock(t *testing.T) {
                                                Version:        1,
                                                SerializedSize: 1,
                                                Inputs:         []*types.TxInput{types.NewCoinbaseInput(nil)},
-                                               Outputs:        []*types.TxOutput{types.NewOriginalTxOutput(*consensus.BTMAssetID, 41250000000, cp, nil)},
+                                               Outputs: []*types.TxOutput{
+                                                       types.NewOriginalTxOutput(*consensus.BTMAssetID, 41250000000, cp, nil),
+                                               },
                                        }),
                                },
                        },
                        parent: &state.BlockNode{
                                Version:   1,
-                               Height:    0,
+                               Height:    state.BlocksOfEpoch,
                                Timestamp: 1523352600,
                                Hash:      bc.Hash{V0: 0},
                        },
+                       checkpoint: &state.Checkpoint{
+                               Rewards: map[string]uint64{hex.EncodeToString(cp): 41250000000}},
                        err: errMismatchedMerkleRoot,
                },
                {
@@ -258,7 +301,7 @@ func TestValidateBlock(t *testing.T) {
                                ID: bc.Hash{V0: 1},
                                BlockHeader: &bc.BlockHeader{
                                        Version:          1,
-                                       Height:           1,
+                                       Height:           state.BlocksOfEpoch + 1,
                                        Timestamp:        1523358600,
                                        PreviousBlockId:  &bc.Hash{V0: 0},
                                        TransactionsRoot: &bc.Hash{V0: 6294987741126419124, V1: 12520373106916389157, V2: 5040806596198303681, V3: 1151748423853876189},
@@ -268,55 +311,27 @@ func TestValidateBlock(t *testing.T) {
                                                Version:        1,
                                                SerializedSize: 1,
                                                Inputs:         []*types.TxInput{types.NewCoinbaseInput(nil)},
-                                               Outputs:        []*types.TxOutput{types.NewOriginalTxOutput(*consensus.BTMAssetID, 41250000000, cp, nil)},
+                                               Outputs: []*types.TxOutput{
+                                                       types.NewOriginalTxOutput(*consensus.BTMAssetID, 41250000000, cp, nil),
+                                               },
                                        }),
                                },
                        },
                        parent: &state.BlockNode{
                                Version:   1,
-                               Height:    0,
+                               Height:    state.BlocksOfEpoch,
                                Timestamp: 1523352600,
                                Hash:      bc.Hash{V0: 0},
                        },
-                       err: errMismatchedMerkleRoot,
-               },
-               {
-                       desc: "the coinbase amount is less than the real coinbase amount (txtest#1014)",
-                       block: &bc.Block{
-                               ID: bc.Hash{V0: 1},
-                               BlockHeader: &bc.BlockHeader{
-                                       Version:         1,
-                                       Height:          1,
-                                       Timestamp:       1523358600,
-                                       PreviousBlockId: &bc.Hash{V0: 0},
-                               },
-                               Transactions: []*bc.Tx{
-                                       types.MapTx(&types.TxData{
-                                               Version:        1,
-                                               SerializedSize: 1,
-                                               Inputs:         []*types.TxInput{types.NewCoinbaseInput(nil)},
-                                               Outputs:        []*types.TxOutput{types.NewOriginalTxOutput(*consensus.BTMAssetID, 41250000000, cp, nil)},
-                                       }),
-                                       types.MapTx(&types.TxData{
-                                               Version:        1,
-                                               SerializedSize: 1,
-                                               Inputs:         []*types.TxInput{types.NewSpendInput([][]byte{}, *newHash(8), *consensus.BTMAssetID, 100000000, 0, cp, nil)},
-                                               Outputs:        []*types.TxOutput{types.NewOriginalTxOutput(*consensus.BTMAssetID, 90000000, cp, nil)},
-                                       }),
-                               },
+                       checkpoint: &state.Checkpoint{
+                               Rewards: map[string]uint64{hex.EncodeToString(cp): 41250000000},
                        },
-                       parent: &state.BlockNode{
-                               Version:   1,
-                               Height:    0,
-                               Timestamp: 1523352600,
-                               Hash:      bc.Hash{V0: 0},
-                       },
-                       err: ErrWrongCoinbaseTransaction,
+                       err: errMismatchedMerkleRoot,
                },
        }
 
        for i, c := range cases {
-               err := ValidateBlock(c.block, c.parent, converter)
+               err := ValidateBlock(c.block, c.parent, c.checkpoint, converter)
                if rootErr(err) != c.err {
                        t.Errorf("case #%d (%s) got error %s, want %s", i, c.desc, err, c.err)
                }
@@ -352,6 +367,10 @@ func TestGasOverBlockLimit(t *testing.T) {
                },
        }
 
+       checkpoint := &state.Checkpoint{
+               Rewards: nil,
+       }
+
        for i := 0; i < 100; i++ {
                block.Transactions = append(block.Transactions, types.MapTx(&types.TxData{
                        Version:        1,
@@ -365,7 +384,7 @@ func TestGasOverBlockLimit(t *testing.T) {
                }))
        }
 
-       if err := ValidateBlock(block, parent, converter); err != errOverBlockLimit {
+       if err := ValidateBlock(block, parent, checkpoint, converter); err != errOverBlockLimit {
                t.Errorf("got error %s, want %s", err, errOverBlockLimit)
        }
 }
index df7744c..b70dd15 100644 (file)
@@ -315,7 +315,7 @@ func TestAttachOrDetachBlocks(t *testing.T) {
                                t.Error(err)
                        }
                }
-               if err := store.SaveChainStatus(node, utxoViewpoint, contractView,0, &bc.Hash{}); err != nil {
+               if err := store.SaveChainStatus(node, utxoViewpoint, contractView, 0, &bc.Hash{}); err != nil {
                        t.Error(err)
                }