From 42c5ab23c13e7f43d0055221ec38752555bd987e Mon Sep 17 00:00:00 2001 From: oysheng <33340252+oysheng@users.noreply.github.com> Date: Tue, 25 Jun 2019 19:58:37 +0800 Subject: [PATCH] get BlockNode from database (#188) * modify BlockNode to database * modify newBlockNode * optimise LoadBlockIndex * optimise hash * restruct blockIndex * delete redundancy function * remove blockIndex BlockExist function and add Remove cached blockNode * modify function name * modify blockwitness index * optimise * optimise chain best node * modify dir * optimise * delete blockindex * optimise style * remove argument * modify name * modify get blockwitness func * add getBestNode * optimise blockHashes * remove BlockNode * optimise struct * optimise * optimise protocol * optimise saveChainStatus * optimise * optimise * remove consensusNodeManager * modify node to blockheader * modify node name * remove redundancy getBlocker * modify node to blockHeader * add lock * add deep copy * fix * modify function name * modify order * optimise init getBlockHashes * optimise * modify database test * fix validation test * fix txpool test * fix utxo_view test * delete protocol block test * optimise name and position * optimise * delete cond * modify Name * abstract common function * optimise * add unit test * optimise unit test * delete code * save main chain hash by height * save main chain hashes * fix error * optimise * optimise * optimsie * optimise * optimise --- database/cache.go | 86 ++++++++++++-- database/cache_test.go | 89 ++++++++++++-- database/store.go | 212 ++++++++++++++++++++++------------ database/store_test.go | 183 ++++++----------------------- protocol/bbft.go | 98 ++++++++-------- protocol/block.go | 149 +++++++++++++----------- protocol/block_test.go | 178 +++++++++------------------- protocol/consensus_node_manager.go | 99 +++++++--------- protocol/protocol.go | 73 +++++++----- protocol/state/blockindex.go | 190 ------------------------------ protocol/state/blockindex_test.go | 181 ----------------------------- protocol/store.go | 11 +- protocol/txpool_test.go | 22 ++-- protocol/validation/block.go | 11 +- protocol/validation/block_test.go | 119 ++++++++++--------- test/utxo_view/utxo_view_test.go | 7 +- test/utxo_view/utxo_view_test_util.go | 17 --- 17 files changed, 684 insertions(+), 1041 deletions(-) delete mode 100644 protocol/state/blockindex.go delete mode 100644 protocol/state/blockindex_test.go diff --git a/database/cache.go b/database/cache.go index 89ef3a45..444abeec 100644 --- a/database/cache.go +++ b/database/cache.go @@ -14,44 +14,56 @@ import ( const ( maxCachedBlockHeaders = 4096 maxCachedBlockTransactions = 1024 + maxCachedBlockHashes = 8192 + maxCachedMainChainHashes = 8192 maxCachedVoteResults = 128 ) -type fillBlockHeaderFn func(hash *bc.Hash, height uint64) (*types.BlockHeader, error) +type fillBlockHeaderFn func(hash *bc.Hash) (*types.BlockHeader, error) type fillBlockTransactionsFn func(hash *bc.Hash) ([]*types.Tx, error) +type fillBlockHashesFn func(height uint64) ([]*bc.Hash, error) +type fillMainChainHashFn func(height uint64) (*bc.Hash, error) type fillVoteResultFn func(seq uint64) (*state.VoteResult, error) -func newCache(fillBlockHeader fillBlockHeaderFn, fillBlockTxs fillBlockTransactionsFn, fillVoteResult fillVoteResultFn) cache { +func newCache(fillBlockHeader fillBlockHeaderFn, fillBlockTxs fillBlockTransactionsFn, fillBlockHashes fillBlockHashesFn, fillMainChainHash fillMainChainHashFn, fillVoteResult fillVoteResultFn) cache { return cache{ - lruBlockHeaders: common.NewCache(maxCachedBlockHeaders), - lruBlockTxs: common.NewCache(maxCachedBlockTransactions), - lruVoteResults: common.NewCache(maxCachedVoteResults), + lruBlockHeaders: common.NewCache(maxCachedBlockHeaders), + lruBlockTxs: common.NewCache(maxCachedBlockTransactions), + lruBlockHashes: common.NewCache(maxCachedBlockHashes), + lruMainChainHashes: common.NewCache(maxCachedMainChainHashes), + lruVoteResults: common.NewCache(maxCachedVoteResults), fillBlockHeaderFn: fillBlockHeader, fillBlockTransactionFn: fillBlockTxs, + fillBlockHashesFn: fillBlockHashes, + fillMainChainHashFn: fillMainChainHash, fillVoteResultFn: fillVoteResult, } } type cache struct { - lruBlockHeaders *common.Cache - lruBlockTxs *common.Cache - lruVoteResults *common.Cache + lruBlockHeaders *common.Cache + lruBlockTxs *common.Cache + lruBlockHashes *common.Cache + lruMainChainHashes *common.Cache + lruVoteResults *common.Cache - fillBlockHeaderFn func(hash *bc.Hash, height uint64) (*types.BlockHeader, error) + fillBlockHeaderFn func(hash *bc.Hash) (*types.BlockHeader, error) fillBlockTransactionFn func(hash *bc.Hash) ([]*types.Tx, error) + fillBlockHashesFn func(uint64) ([]*bc.Hash, error) + fillMainChainHashFn func(uint64) (*bc.Hash, error) fillVoteResultFn func(seq uint64) (*state.VoteResult, error) sf singleflight.Group } -func (c *cache) lookupBlockHeader(hash *bc.Hash, height uint64) (*types.BlockHeader, error) { +func (c *cache) lookupBlockHeader(hash *bc.Hash) (*types.BlockHeader, error) { if data, ok := c.lruBlockHeaders.Get(*hash); ok { return data.(*types.BlockHeader), nil } blockHeader, err := c.sf.Do("BlockHeader:"+hash.String(), func() (interface{}, error) { - blockHeader, err := c.fillBlockHeaderFn(hash, height) + blockHeader, err := c.fillBlockHeaderFn(hash) if err != nil { return nil, err } @@ -76,7 +88,7 @@ func (c *cache) lookupBlockTxs(hash *bc.Hash) ([]*types.Tx, error) { return nil, err } - c.lruBlockTxs.Add(hash, blockTxs) + c.lruBlockTxs.Add(*hash, blockTxs) return blockTxs, nil }) if err != nil { @@ -106,10 +118,60 @@ func (c *cache) lookupVoteResult(seq uint64) (*state.VoteResult, error) { return voteResult.(*state.VoteResult).Fork(), nil } +func (c *cache) lookupMainChainHash(height uint64) (*bc.Hash, error) { + if hash, ok := c.lruMainChainHashes.Get(height); ok { + return hash.(*bc.Hash), nil + } + + heightStr := strconv.FormatUint(height, 10) + hash, err := c.sf.Do("BlockHashByHeight:"+heightStr, func() (interface{}, error) { + hash, err := c.fillMainChainHashFn(height) + if err != nil { + return nil, err + } + + c.lruMainChainHashes.Add(height, hash) + return hash, nil + }) + if err != nil { + return nil, err + } + return hash.(*bc.Hash), nil +} + +func (c *cache) lookupBlockHashesByHeight(height uint64) ([]*bc.Hash, error) { + if hashes, ok := c.lruBlockHashes.Get(height); ok { + return hashes.([]*bc.Hash), nil + } + + heightStr := strconv.FormatUint(height, 10) + hashes, err := c.sf.Do("BlockHashesByHeight:"+heightStr, func() (interface{}, error) { + hashes, err := c.fillBlockHashesFn(height) + if err != nil { + return nil, err + } + + c.lruBlockHashes.Add(height, hashes) + return hashes, nil + }) + if err != nil { + return nil, err + } + return hashes.([]*bc.Hash), nil +} + func (c *cache) removeBlockHeader(blockHeader *types.BlockHeader) { c.lruBlockHeaders.Remove(blockHeader.Hash()) } +func (c *cache) removeBlockHashes(height uint64) { + c.lruBlockHashes.Remove(height) +} + +func (c *cache) removeMainChainHash(height uint64) { + c.lruMainChainHashes.Remove(height) +} + func (c *cache) removeVoteResult(voteResult *state.VoteResult) { c.lruVoteResults.Remove(voteResult.Seq) } diff --git a/database/cache_test.go b/database/cache_test.go index 14193778..a15f3b39 100644 --- a/database/cache_test.go +++ b/database/cache_test.go @@ -16,23 +16,31 @@ func TestBlockCache(t *testing.T) { }, } } + newVoteResult := func(seq uint64) *state.VoteResult { return &state.VoteResult{ Seq: seq, } } + blocks := make(map[bc.Hash]*types.Block) + blockHashes := make(map[uint64]*bc.Hash) + blockIndexHashes := make(map[uint64][]*bc.Hash) for i := 0; i < maxCachedBlockHeaders+10; i++ { block := newBlock(uint64(i)) - blocks[block.Hash()] = block + hash := block.Hash() + blocks[hash] = block + blockHashes[block.Height] = &hash + blockIndexHashes[block.Height] = append(blockIndexHashes[block.Height], &hash) } + voteResults := make(map[uint64]*state.VoteResult) for i := 0; i < maxCachedVoteResults+10; i++ { voteResult := newVoteResult(uint64(i)) voteResults[voteResult.Seq] = voteResult } - fillBlockHeaderFn := func(hash *bc.Hash, height uint64) (*types.BlockHeader, error) { + fillBlockHeaderFn := func(hash *bc.Hash) (*types.BlockHeader, error) { return &blocks[*hash].BlockHeader, nil } @@ -40,23 +48,30 @@ func TestBlockCache(t *testing.T) { return blocks[*hash].Transactions, nil } + fillBlockHashesFn := func(height uint64) ([]*bc.Hash, error) { + return blockIndexHashes[height], nil + } + + fillMainChainHashFn := func(height uint64) (*bc.Hash, error) { + return blockHashes[height], nil + } + fillVoteResultFn := func(seq uint64) (*state.VoteResult, error) { return voteResults[seq], nil } - cache := newCache(fillBlockHeaderFn, fillBlockTxsFn, fillVoteResultFn) - + cache := newCache(fillBlockHeaderFn, fillBlockTxsFn, fillBlockHashesFn, fillMainChainHashFn, fillVoteResultFn) for i := 0; i < maxCachedBlockHeaders+10; i++ { block := newBlock(uint64(i)) hash := block.Hash() - cache.lookupBlockHeader(&hash, block.Height) + cache.lookupBlockHeader(&hash) } for i := 0; i < 10; i++ { block := newBlock(uint64(i)) hash := block.Hash() if _, ok := cache.lruBlockHeaders.Get(hash); ok { - t.Fatalf("find old block") + t.Fatalf("find old block header") } } @@ -64,7 +79,67 @@ func TestBlockCache(t *testing.T) { block := newBlock(uint64(i)) hash := block.Hash() if _, ok := cache.lruBlockHeaders.Get(hash); !ok { - t.Fatalf("can't find new block") + t.Fatalf("can't find new block header") + } + } + + for i := 0; i < maxCachedBlockTransactions+10; i++ { + block := newBlock(uint64(i)) + hash := block.Hash() + cache.lookupBlockTxs(&hash) + } + + for i := 0; i < 10; i++ { + block := newBlock(uint64(i)) + hash := block.Hash() + if _, ok := cache.lruBlockTxs.Get(hash); ok { + t.Fatalf("find old block transactions") + } + } + + for i := 10; i < maxCachedBlockTransactions+10; i++ { + block := newBlock(uint64(i)) + hash := block.Hash() + if _, ok := cache.lruBlockTxs.Get(hash); !ok { + t.Fatalf("can't find new block transactions") + } + } + + for i := 0; i < maxCachedBlockHashes+10; i++ { + block := newBlock(uint64(i)) + cache.lookupBlockHashesByHeight(block.Height) + } + + for i := 0; i < 10; i++ { + block := newBlock(uint64(i)) + if _, ok := cache.lruBlockHashes.Get(block.Height); ok { + t.Fatalf("find old block Hashes for specified height") + } + } + + for i := 10; i < maxCachedBlockHashes+10; i++ { + block := newBlock(uint64(i)) + if _, ok := cache.lruBlockHashes.Get(block.Height); !ok { + t.Fatalf("can't find new block Hashes for specified height") + } + } + + for i := 0; i < maxCachedMainChainHashes+10; i++ { + block := newBlock(uint64(i)) + cache.lookupMainChainHash(block.Height) + } + + for i := 0; i < 10; i++ { + block := newBlock(uint64(i)) + if _, ok := cache.lruMainChainHashes.Get(block.Height); ok { + t.Fatalf("find old main chain block Hash for specified height") + } + } + + for i := 10; i < maxCachedMainChainHashes+10; i++ { + block := newBlock(uint64(i)) + if _, ok := cache.lruMainChainHashes.Get(block.Height); !ok { + t.Fatalf("can't find new main chain block Hash for specified height") } } diff --git a/database/store.go b/database/store.go index 6de274be..ea947a1d 100644 --- a/database/store.go +++ b/database/store.go @@ -18,14 +18,31 @@ import ( "github.com/vapor/protocol/state" ) -const logModule = "leveldb" +const ( + // log module + logModule = "leveldb" + // the byte of colon(:) + colon = byte(0x3a) +) + +const ( + blockStore byte = iota + blockHashes + blockHeader + blockTransactons + mainChainIndex + txStatus + voteResult +) var ( - blockStoreKey = []byte("blockStore") - blockHeaderPrefix = []byte("BH:") - blockTransactonsPrefix = []byte("BTXS:") - txStatusPrefix = []byte("BTS:") - voteResultPrefix = []byte("VR:") + blockStoreKey = []byte{blockStore} + blockHashesPrefix = []byte{blockHashes, colon} + blockHeaderPrefix = []byte{blockHeader, colon} + blockTransactonsPrefix = []byte{blockTransactons, colon} + mainChainIndexPrefix = []byte{mainChainIndex, colon} + txStatusPrefix = []byte{txStatus, colon} + voteResultPrefix = []byte{voteResult, colon} ) func loadBlockStoreStateJSON(db dbm.DB) *protocol.BlockStoreState { @@ -49,11 +66,20 @@ type Store struct { cache cache } -func calcBlockHeaderKey(height uint64, hash *bc.Hash) []byte { +func calcMainChainIndexPrefix(height uint64) []byte { + buf := [8]byte{} + binary.BigEndian.PutUint64(buf[:], height) + return append(mainChainIndexPrefix, buf[:]...) +} + +func calcBlockHashesPrefix(height uint64) []byte { buf := [8]byte{} binary.BigEndian.PutUint64(buf[:], height) - key := append(blockHeaderPrefix, buf[:]...) - return append(key, hash.Bytes()...) + return append(blockHashesPrefix, buf[:]...) +} + +func calcBlockHeaderKey(hash *bc.Hash) []byte { + return append(blockHeaderPrefix, hash.Bytes()...) } func calcBlockTransactionsKey(hash *bc.Hash) []byte { @@ -70,18 +96,18 @@ func calcVoteResultKey(seq uint64) []byte { return append(voteResultPrefix, buf[:]...) } -// GetBlockHeader return the block header by given hash and height -func GetBlockHeader(db dbm.DB, hash *bc.Hash, height uint64) (*types.BlockHeader, error) { - binaryBlockHeader := db.Get(calcBlockHeaderKey(height, hash)) +// GetBlockHeader return the block header by given hash +func GetBlockHeader(db dbm.DB, hash *bc.Hash) (*types.BlockHeader, error) { + binaryBlockHeader := db.Get(calcBlockHeaderKey(hash)) if binaryBlockHeader == nil { return nil, fmt.Errorf("There are no blockHeader with given hash %s", hash.String()) } - block := &types.Block{} - if err := block.UnmarshalText(binaryBlockHeader); err != nil { + blockHeader := &types.BlockHeader{} + if err := blockHeader.UnmarshalText(binaryBlockHeader); err != nil { return nil, err } - return &block.BlockHeader, nil + return blockHeader, nil } // GetBlockTransactions return the block transactions by given hash @@ -98,6 +124,34 @@ func GetBlockTransactions(db dbm.DB, hash *bc.Hash) ([]*types.Tx, error) { return block.Transactions, nil } +// GetBlockHashesByHeight return block hashes by given height +func GetBlockHashesByHeight(db dbm.DB, height uint64) ([]*bc.Hash, error) { + binaryHashes := db.Get(calcBlockHashesPrefix(height)) + if binaryHashes == nil { + return []*bc.Hash{}, nil + } + + hashes := []*bc.Hash{} + if err := json.Unmarshal(binaryHashes, &hashes); err != nil { + return nil, err + } + return hashes, nil +} + +// GetMainChainHash return BlockHash by given height +func GetMainChainHash(db dbm.DB, height uint64) (*bc.Hash, error) { + binaryHash := db.Get(calcMainChainIndexPrefix(height)) + if binaryHash == nil { + return nil, fmt.Errorf("There are no BlockHash with given height %d", height) + } + + hash := &bc.Hash{} + if err := hash.UnmarshalText(binaryHash); err != nil { + return nil, err + } + return hash, nil +} + // GetVoteResult return the vote result by given sequence func GetVoteResult(db dbm.DB, seq uint64) (*state.VoteResult, error) { data := db.Get(calcVoteResultKey(seq)) @@ -114,31 +168,41 @@ func GetVoteResult(db dbm.DB, seq uint64) (*state.VoteResult, error) { // NewStore creates and returns a new Store object. func NewStore(db dbm.DB) *Store { - fillBlockHeaderFn := func(hash *bc.Hash, height uint64) (*types.BlockHeader, error) { - return GetBlockHeader(db, hash, height) + fillBlockHeaderFn := func(hash *bc.Hash) (*types.BlockHeader, error) { + return GetBlockHeader(db, hash) } fillBlockTxsFn := func(hash *bc.Hash) ([]*types.Tx, error) { return GetBlockTransactions(db, hash) } + + fillBlockHashesFn := func(height uint64) ([]*bc.Hash, error) { + return GetBlockHashesByHeight(db, height) + } + + fillMainChainHashFn := func(height uint64) (*bc.Hash, error) { + return GetMainChainHash(db, height) + } + fillVoteResultFn := func(seq uint64) (*state.VoteResult, error) { return GetVoteResult(db, seq) } - bc := newCache(fillBlockHeaderFn, fillBlockTxsFn, fillVoteResultFn) + + cache := newCache(fillBlockHeaderFn, fillBlockTxsFn, fillBlockHashesFn, fillMainChainHashFn, fillVoteResultFn) return &Store{ db: db, - cache: bc, + cache: cache, } } // BlockExist check if the block is stored in disk -func (s *Store) BlockExist(hash *bc.Hash, height uint64) bool { - _, err := s.cache.lookupBlockHeader(hash, height) +func (s *Store) BlockExist(hash *bc.Hash) bool { + _, err := s.cache.lookupBlockHeader(hash) return err == nil } // GetBlock return the block by given hash -func (s *Store) GetBlock(hash *bc.Hash, height uint64) (*types.Block, error) { - blockHeader, err := s.GetBlockHeader(hash, height) +func (s *Store) GetBlock(hash *bc.Hash) (*types.Block, error) { + blockHeader, err := s.GetBlockHeader(hash) if err != nil { return nil, err } @@ -155,8 +219,8 @@ func (s *Store) GetBlock(hash *bc.Hash, height uint64) (*types.Block, error) { } // GetBlockHeader return the BlockHeader by given hash -func (s *Store) GetBlockHeader(hash *bc.Hash, height uint64) (*types.BlockHeader, error) { - return s.cache.lookupBlockHeader(hash, height) +func (s *Store) GetBlockHeader(hash *bc.Hash) (*types.BlockHeader, error) { + return s.cache.lookupBlockHeader(hash) } // GetBlockTransactions return the Block transactions by given hash @@ -164,6 +228,16 @@ func (s *Store) GetBlockTransactions(hash *bc.Hash) ([]*types.Tx, error) { return s.cache.lookupBlockTxs(hash) } +// GetBlockHashesByHeight return the block hash by the specified height +func (s *Store) GetBlockHashesByHeight(height uint64) ([]*bc.Hash, error) { + return s.cache.lookupBlockHashesByHeight(height) +} + +// GetMainChainHash return the block hash by the specified height +func (s *Store) GetMainChainHash(height uint64) (*bc.Hash, error) { + return s.cache.lookupMainChainHash(height) +} + // GetStoreStatus return the BlockStoreStateJSON func (s *Store) GetStoreStatus() *protocol.BlockStoreState { return loadBlockStoreStateJSON(s.db) @@ -198,49 +272,6 @@ func (s *Store) GetVoteResult(seq uint64) (*state.VoteResult, error) { return s.cache.lookupVoteResult(seq) } -func (s *Store) LoadBlockIndex(stateBestHeight uint64) (*state.BlockIndex, error) { - startTime := time.Now() - blockIndex := state.NewBlockIndex() - bhIter := s.db.IteratorPrefix(blockHeaderPrefix) - defer bhIter.Release() - - var lastNode *state.BlockNode - for bhIter.Next() { - bh := &types.BlockHeader{} - if err := bh.UnmarshalText(bhIter.Value()); err != nil { - return nil, err - } - - // If a block with a height greater than the best height of state is added to the index, - // It may cause a bug that the new block cant not be process properly. - if bh.Height > stateBestHeight { - break - } - - var parent *state.BlockNode - if lastNode == nil || lastNode.Hash == bh.PreviousBlockHash { - parent = lastNode - } else { - parent = blockIndex.GetNode(&bh.PreviousBlockHash) - } - - node, err := state.NewBlockNode(bh, parent) - if err != nil { - return nil, err - } - - blockIndex.AddNode(node) - lastNode = node - } - - log.WithFields(log.Fields{ - "module": logModule, - "height": stateBestHeight, - "duration": time.Since(startTime), - }).Debug("initialize load history block index from database") - return blockIndex, nil -} - // SaveBlock persists a new block in the protocol. func (s *Store) SaveBlock(block *types.Block, ts *bc.TransactionStatus) error { startTime := time.Now() @@ -256,16 +287,30 @@ func (s *Store) SaveBlock(block *types.Block, ts *bc.TransactionStatus) error { binaryTxStatus, err := proto.Marshal(ts) if err != nil { - return errors.Wrap(err, "marshal block transaction status") + return errors.Wrap(err, "Marshal block transaction status") } + blockHashes := []*bc.Hash{} + hashes, err := s.GetBlockHashesByHeight(block.Height) + if err != nil { + return err + } + blockHashes = append(blockHashes, hashes...) blockHash := block.Hash() + blockHashes = append(blockHashes, &blockHash) + binaryBlockHashes, err := json.Marshal(blockHashes) + if err != nil { + return errors.Wrap(err, "Marshal block hashes") + } + batch := s.db.NewBatch() - batch.Set(calcBlockHeaderKey(block.Height, &blockHash), binaryBlockHeader) + batch.Set(calcBlockHashesPrefix(block.Height), binaryBlockHashes) + batch.Set(calcBlockHeaderKey(&blockHash), binaryBlockHeader) batch.Set(calcBlockTransactionsKey(&blockHash), binaryBlockTxs) batch.Set(calcTxStatusKey(&blockHash), binaryTxStatus) batch.Write() + s.cache.removeBlockHashes(block.Height) log.WithFields(log.Fields{ "module": logModule, "height": block.Height, @@ -283,13 +328,13 @@ func (s *Store) SaveBlockHeader(blockHeader *types.BlockHeader) error { } blockHash := blockHeader.Hash() - s.db.Set(calcBlockHeaderKey(blockHeader.Height, &blockHash), binaryBlockHeader) + s.db.Set(calcBlockHeaderKey(&blockHash), binaryBlockHeader) s.cache.removeBlockHeader(blockHeader) return nil } // SaveChainStatus save the core's newest status && delete old status -func (s *Store) SaveChainStatus(node, irreversibleNode *state.BlockNode, view *state.UtxoViewpoint, voteResults []*state.VoteResult) error { +func (s *Store) SaveChainStatus(blockHeader, irrBlockHeader *types.BlockHeader, mainBlockHeaders []*types.BlockHeader, view *state.UtxoViewpoint, voteResults []*state.VoteResult) error { batch := s.db.NewBatch() if err := saveUtxoView(batch, view); err != nil { return err @@ -305,17 +350,30 @@ func (s *Store) SaveChainStatus(node, irreversibleNode *state.BlockNode, view *s s.cache.removeVoteResult(vote) } + blockHash := blockHeader.Hash() + irrBlockHash := irrBlockHeader.Hash() bytes, err := json.Marshal(protocol.BlockStoreState{ - Height: node.Height, - Hash: &node.Hash, - IrreversibleHeight: irreversibleNode.Height, - IrreversibleHash: &irreversibleNode.Hash, + Height: blockHeader.Height, + Hash: &blockHash, + IrreversibleHeight: irrBlockHeader.Height, + IrreversibleHash: &irrBlockHash, }) if err != nil { return err } - batch.Set(blockStoreKey, bytes) + + // save main chain blockHeaders + for _, bh := range mainBlockHeaders { + blockHash := bh.Hash() + binaryBlockHash, err := blockHash.MarshalText() + if err != nil { + return errors.Wrap(err, "Marshal block hash") + } + + batch.Set(calcMainChainIndexPrefix(bh.Height), binaryBlockHash) + s.cache.removeMainChainHash(bh.Height) + } batch.Write() return nil } diff --git a/database/store_test.go b/database/store_test.go index ea009eb6..53b158b0 100644 --- a/database/store_test.go +++ b/database/store_test.go @@ -4,7 +4,6 @@ import ( "os" "testing" - "github.com/vapor/config" dbm "github.com/vapor/database/leveldb" "github.com/vapor/database/storage" "github.com/vapor/protocol" @@ -14,144 +13,6 @@ import ( "github.com/vapor/testutil" ) -func TestLoadBlockIndex(t *testing.T) { - config.CommonConfig = config.DefaultConfig() - testDB := dbm.NewDB("testdb", "leveldb", "temp") - store := NewStore(testDB) - defer func() { - testDB.Close() - os.RemoveAll("temp") - }() - - block := config.GenesisBlock() - txStatus := bc.NewTransactionStatus() - - if err := store.SaveBlock(block, txStatus); err != nil { - t.Fatal(err) - } - - for block.Height <= 128 { - preHash := block.Hash() - block.PreviousBlockHash = preHash - block.Height++ - if err := store.SaveBlock(block, txStatus); err != nil { - t.Fatal(err) - } - - if block.Height%32 != 0 { - continue - } - - for i := uint64(0); i < block.Height/32; i++ { - block.Version++ - if err := store.SaveBlock(block, txStatus); err != nil { - t.Fatal(err) - } - } - } - - if _, err := store.LoadBlockIndex(128); err != nil { - t.Fatal(err) - } -} - -func TestLoadBlockIndexBestHeight(t *testing.T) { - cases := []struct { - blockBestHeight uint64 - stateBestHeight uint64 - }{ - { - blockBestHeight: 100, - stateBestHeight: 90, - }, - { - blockBestHeight: 100, - stateBestHeight: 0, - }, - { - blockBestHeight: 100, - stateBestHeight: 100, - }, - } - - testDB := dbm.NewDB("testdb", "leveldb", "temp") - defer func() { - testDB.Close() - os.RemoveAll("temp") - }() - store := NewStore(testDB) - var savedBlocks []types.Block - - for _, c := range cases { - block := config.GenesisBlock() - txStatus := bc.NewTransactionStatus() - - for i := uint64(0); i < c.blockBestHeight; i++ { - if err := store.SaveBlock(block, txStatus); err != nil { - t.Fatal(err) - } - - savedBlocks = append(savedBlocks, *block) - block.PreviousBlockHash = block.Hash() - block.Height++ - } - - index, err := store.LoadBlockIndex(c.stateBestHeight) - if err != nil { - t.Fatal(err) - } - - for _, block := range savedBlocks { - blockHash := block.Hash() - if block.Height <= c.stateBestHeight != index.BlockExist(&blockHash) { - t.Errorf("Error in load block index") - } - } - } -} - -func TestLoadBlockIndexEquals(t *testing.T) { - testDB := dbm.NewDB("testdb", "leveldb", "temp") - store := NewStore(testDB) - defer func() { - testDB.Close() - os.RemoveAll("temp") - }() - - block := config.GenesisBlock() - txStatus := bc.NewTransactionStatus() - expectBlockIndex := state.NewBlockIndex() - var parent *state.BlockNode - - for block.Height <= 100 { - if err := store.SaveBlock(block, txStatus); err != nil { - t.Fatal(err) - } - - if block.Height != 0 { - parent = expectBlockIndex.GetNode(&block.PreviousBlockHash) - } - - node, err := state.NewBlockNode(&block.BlockHeader, parent) - if err != nil { - t.Fatal(err) - } - - expectBlockIndex.AddNode(node) - block.PreviousBlockHash = block.Hash() - block.Height++ - } - - index, err := store.LoadBlockIndex(100) - if err != nil { - t.Fatal(err) - } - - if !testutil.DeepEqual(expectBlockIndex, index) { - t.Errorf("got block index:%v, expect block index:%v", index, expectBlockIndex) - } -} - func TestSaveChainStatus(t *testing.T) { testDB := dbm.NewDB("testdb", "leveldb", "temp") defer func() { @@ -161,7 +22,8 @@ func TestSaveChainStatus(t *testing.T) { store := NewStore(testDB) - node := &state.BlockNode{Height: 100, Hash: bc.Hash{V0: 0, V1: 1, V2: 2, V3: 3}} + blockHeader := &types.BlockHeader{Height: 100} + blockHash := blockHeader.Hash() //Hash: bc.Hash{V0: 0, V1: 1, V2: 2, V3: 3} view := &state.UtxoViewpoint{ Entries: map[bc.Hash]*storage.UtxoEntry{ bc.Hash{V0: 1, V1: 2, V2: 3, V3: 4}: &storage.UtxoEntry{Type: storage.NormalUTXOType, BlockHeight: 100, Spent: false}, @@ -174,11 +36,11 @@ func TestSaveChainStatus(t *testing.T) { }, } - if err := store.SaveChainStatus(node, node, view, []*state.VoteResult{}); err != nil { + if err := store.SaveChainStatus(blockHeader, blockHeader, []*types.BlockHeader{blockHeader}, view, []*state.VoteResult{}); err != nil { t.Fatal(err) } - expectStatus := &protocol.BlockStoreState{Height: node.Height, Hash: &node.Hash, IrreversibleHeight: node.Height, IrreversibleHash: &node.Hash} + expectStatus := &protocol.BlockStoreState{Height: blockHeader.Height, Hash: &blockHash, IrreversibleHeight: blockHeader.Height, IrreversibleHash: &blockHash} if !testutil.DeepEqual(store.GetStoreStatus(), expectStatus) { t.Errorf("got block status:%v, expect block status:%v", store.GetStoreStatus(), expectStatus) } @@ -213,15 +75,14 @@ func TestSaveBlock(t *testing.T) { }() store := NewStore(testDB) - - block := config.GenesisBlock() + block := mockGenesisBlock() status := &bc.TransactionStatus{VerifyStatus: []*bc.TxVerifyResult{{StatusFail: true}}} if err := store.SaveBlock(block, status); err != nil { t.Fatal(err) } blockHash := block.Hash() - gotBlock, err := store.GetBlock(&blockHash, block.Height) + gotBlock, err := store.GetBlock(&blockHash) if err != nil { t.Fatal(err) } @@ -241,7 +102,7 @@ func TestSaveBlock(t *testing.T) { t.Errorf("got status:%v, expect status:%v", gotStatus, status) } - data := store.db.Get(calcBlockHeaderKey(block.Height, &blockHash)) + data := store.db.Get(calcBlockHeaderKey(&blockHash)) gotBlockHeader := types.BlockHeader{} if err := gotBlockHeader.UnmarshalText(data); err != nil { t.Fatal(err) @@ -251,3 +112,33 @@ func TestSaveBlock(t *testing.T) { t.Errorf("got block header:%v, expect block header:%v", gotBlockHeader, block.BlockHeader) } } + +func mockGenesisBlock() *types.Block { + txData := types.TxData{ + Version: 1, + Inputs: []*types.TxInput{ + types.NewCoinbaseInput([]byte("Information is power. -- Jan/11/2013. Computing is power. -- Apr/24/2018.")), + }, + Outputs: []*types.TxOutput{ + types.NewVoteOutput(bc.AssetID{V0: 1}, uint64(10000), []byte{0x51}, []byte{0x51}), + }, + } + tx := types.NewTx(txData) + txStatus := bc.NewTransactionStatus() + txStatus.SetStatus(0, false) + txStatusHash, _ := types.TxStatusMerkleRoot(txStatus.VerifyStatus) + merkleRoot, _ := types.TxMerkleRoot([]*bc.Tx{tx.Tx}) + block := &types.Block{ + BlockHeader: types.BlockHeader{ + Version: 1, + Height: 0, + Timestamp: 1528945000, + BlockCommitment: types.BlockCommitment{ + TransactionsMerkleRoot: merkleRoot, + TransactionStatusHash: txStatusHash, + }, + }, + Transactions: []*types.Tx{tx}, + } + return block +} diff --git a/protocol/bbft.go b/protocol/bbft.go index 6c619495..0d007377 100644 --- a/protocol/bbft.go +++ b/protocol/bbft.go @@ -29,15 +29,15 @@ func signCacheKey(blockHash, pubkey string) string { return fmt.Sprintf("%s:%s", blockHash, pubkey) } -func (c *Chain) isIrreversible(blockNode *state.BlockNode) bool { - consensusNodes, err := c.consensusNodeManager.getConsensusNodes(&blockNode.Parent.Hash) +func (c *Chain) isIrreversible(blockHeader *types.BlockHeader) bool { + consensusNodes, err := c.getConsensusNodes(&blockHeader.PreviousBlockHash) if err != nil { return false } signCount := 0 for i := 0; i < len(consensusNodes); i++ { - if ok, _ := blockNode.BlockWitness.Test(uint32(i)); ok { + if blockHeader.BlockWitness.Get(uint64(i)) != nil { signCount++ } } @@ -47,52 +47,51 @@ func (c *Chain) isIrreversible(blockNode *state.BlockNode) bool { // GetVoteResultByHash return vote result by block hash func (c *Chain) GetVoteResultByHash(blockHash *bc.Hash) (*state.VoteResult, error) { - blockNode := c.index.GetNode(blockHash) - return c.consensusNodeManager.getVoteResult(state.CalcVoteSeq(blockNode.Height), blockNode) + blockHeader, err := c.store.GetBlockHeader(blockHash) + if err != nil { + return nil, err + } + return c.getVoteResult(state.CalcVoteSeq(blockHeader.Height), blockHeader) } // IsBlocker returns whether the consensus node is a blocker at the specified time func (c *Chain) IsBlocker(prevBlockHash *bc.Hash, pubKey string, timeStamp uint64) (bool, error) { - xPub, err := c.consensusNodeManager.getBlocker(prevBlockHash, timeStamp) + xPub, err := c.GetBlocker(prevBlockHash, timeStamp) if err != nil { return false, err } return xPub == pubKey, nil } -// GetBlock return blocker by specified timestamp -func (c *Chain) GetBlocker(prevBlockHash *bc.Hash, timestamp uint64) (string, error) { - return c.consensusNodeManager.getBlocker(prevBlockHash, timestamp) -} - // ProcessBlockSignature process the received block signature messages // return whether a block become irreversible, if so, the chain module must update status func (c *Chain) ProcessBlockSignature(signature, xPub []byte, blockHash *bc.Hash) error { xpubStr := hex.EncodeToString(xPub[:]) - blockNode := c.index.GetNode(blockHash) + blockHeader, _ := c.store.GetBlockHeader(blockHash) + // save the signature if the block is not exist - if blockNode == nil { + if blockHeader == nil { cacheKey := signCacheKey(blockHash.String(), xpubStr) c.signatureCache.Add(cacheKey, signature) return nil } - consensusNode, err := c.consensusNodeManager.getConsensusNode(&blockNode.Parent.Hash, xpubStr) + consensusNode, err := c.getConsensusNode(&blockHeader.PreviousBlockHash, xpubStr) if err != nil { return err } - if exist, _ := blockNode.BlockWitness.Test(uint32(consensusNode.Order)); exist { + if blockHeader.BlockWitness.Get(consensusNode.Order) != nil { return nil } c.cond.L.Lock() defer c.cond.L.Unlock() - if err := c.checkNodeSign(blockNode.BlockHeader(), consensusNode, signature); err != nil { + if err := c.checkNodeSign(blockHeader, consensusNode, signature); err != nil { return err } - if err := c.updateBlockSignature(blockNode, consensusNode.Order, signature); err != nil { + if err := c.updateBlockSignature(blockHeader, consensusNode.Order, signature); err != nil { return err } return c.eventDispatcher.Post(event.BlockSignatureEvent{BlockHash: *blockHash, Signature: signature, XPub: xPub}) @@ -102,7 +101,7 @@ func (c *Chain) ProcessBlockSignature(signature, xPub []byte, blockHash *bc.Hash // if some signature is invalid, they will be reset to nil // if the block has not the signature of blocker, it will return error func (c *Chain) validateSign(block *types.Block) error { - consensusNodeMap, err := c.consensusNodeManager.getConsensusNodes(&block.PreviousBlockHash) + consensusNodeMap, err := c.getConsensusNodes(&block.PreviousBlockHash) if err != nil { return err } @@ -153,13 +152,22 @@ func (c *Chain) checkNodeSign(bh *types.BlockHeader, consensusNode *state.Consen return errInvalidSignature } - blockNodes := c.consensusNodeManager.blockIndex.NodesByHeight(bh.Height) - for _, blockNode := range blockNodes { - if blockNode.Hash == bh.Hash() { + blockHashes, err := c.store.GetBlockHashesByHeight(bh.Height) + if err != nil { + return err + } + + for _, blockHash := range blockHashes { + if *blockHash == bh.Hash() { continue } - consensusNode, err := c.consensusNodeManager.getConsensusNode(&blockNode.Parent.Hash, consensusNode.XPub.String()) + blockHeader, err := c.store.GetBlockHeader(blockHash) + if err != nil { + return err + } + + consensusNode, err := c.getConsensusNode(&blockHeader.PreviousBlockHash, consensusNode.XPub.String()) if err != nil && err != errNotFoundConsensusNode { return err } @@ -168,7 +176,7 @@ func (c *Chain) checkNodeSign(bh *types.BlockHeader, consensusNode *state.Consen continue } - if ok, err := blockNode.BlockWitness.Test(uint32(consensusNode.Order)); err == nil && ok { + if blockHeader.BlockWitness.Get(consensusNode.Order) != nil { return errDoubleSignBlock } } @@ -179,27 +187,28 @@ func (c *Chain) checkNodeSign(bh *types.BlockHeader, consensusNode *state.Consen func (c *Chain) SignBlock(block *types.Block) ([]byte, error) { xprv := config.CommonConfig.PrivateKey() xpubStr := xprv.XPub().String() - node, err := c.consensusNodeManager.getConsensusNode(&block.PreviousBlockHash, xpubStr) + node, err := c.getConsensusNode(&block.PreviousBlockHash, xpubStr) if err == errNotFoundConsensusNode { return nil, nil } else if err != nil { return nil, err } - c.cond.L.Lock() - defer c.cond.L.Unlock() //check double sign in same block height - blockNodes := c.consensusNodeManager.blockIndex.NodesByHeight(block.Height) - for _, blockNode := range blockNodes { - // Has already signed the same height block - if ok, err := blockNode.BlockWitness.Test(uint32(node.Order)); err == nil && ok { - return nil, nil - } + blockHashes, err := c.store.GetBlockHashesByHeight(block.Height) + if err != nil { + return nil, err } - for blockNode := c.index.GetNode(&block.PreviousBlockHash); !c.index.InMainchain(blockNode.Hash); blockNode = blockNode.Parent { - if blockNode.Height <= c.bestIrreversibleNode.Height { - return nil, errSignForkChain + for _, hash := range blockHashes { + blockHeader, err := c.store.GetBlockHeader(hash) + if err != nil { + return nil, err + } + + // Has already signed the same height block + if blockHeader.BlockWitness.Get(node.Order) != nil { + return nil, nil } } @@ -211,28 +220,17 @@ func (c *Chain) SignBlock(block *types.Block) ([]byte, error) { return signature, nil } -func (c *Chain) updateBlockSignature(blockNode *state.BlockNode, nodeOrder uint64, signature []byte) error { - if err := blockNode.BlockWitness.Set(uint32(nodeOrder)); err != nil { - return err - } - - blockHeader, err := c.store.GetBlockHeader(&blockNode.Hash, blockNode.Height) - if err != nil { - return err - } - +func (c *Chain) updateBlockSignature(blockHeader *types.BlockHeader, nodeOrder uint64, signature []byte) error { blockHeader.Set(nodeOrder, signature) - if err := c.store.SaveBlockHeader(blockHeader); err != nil { return err } - if c.isIrreversible(blockNode) && blockNode.Height > c.bestIrreversibleNode.Height { - if err := c.store.SaveChainStatus(c.bestNode, blockNode, state.NewUtxoViewpoint(), []*state.VoteResult{}); err != nil { + if c.isIrreversible(blockHeader) && blockHeader.Height > c.bestIrrBlockHeader.Height { + if err := c.store.SaveChainStatus(c.bestBlockHeader, blockHeader, []*types.BlockHeader{}, state.NewUtxoViewpoint(), []*state.VoteResult{}); err != nil { return err } - - c.bestIrreversibleNode = blockNode + c.bestIrrBlockHeader = blockHeader } return nil } diff --git a/protocol/block.go b/protocol/block.go index d5ef5cf7..a1d5830d 100644 --- a/protocol/block.go +++ b/protocol/block.go @@ -17,71 +17,78 @@ var ( ErrBadBlock = errors.New("invalid block") // ErrBadStateRoot is returned when the computed assets merkle root // disagrees with the one declared in a block header. - ErrBadStateRoot = errors.New("invalid state merkle root") - errBelowIrreversibleBlock = errors.New("the height of block below the height of irreversible block") + ErrBadStateRoot = errors.New("invalid state merkle root") ) // BlockExist check is a block in chain or orphan func (c *Chain) BlockExist(hash *bc.Hash) bool { - return c.index.BlockExist(hash) || c.orphanManage.BlockExist(hash) + if _, err := c.store.GetBlockHeader(hash); err == nil { + return true + } + return c.orphanManage.BlockExist(hash) } // GetBlockByHash return a block by given hash func (c *Chain) GetBlockByHash(hash *bc.Hash) (*types.Block, error) { - node := c.index.GetNode(hash) - if node == nil { - return nil, errors.New("can't find block in given hash") - } - return c.store.GetBlock(hash, node.Height) + return c.store.GetBlock(hash) } -// GetBlockByHeight return a block header by given height +// GetBlockByHeight return a block by given height func (c *Chain) GetBlockByHeight(height uint64) (*types.Block, error) { - node := c.index.NodeByHeight(height) - if node == nil { - return nil, errors.New("can't find block in given height") + hash, err := c.store.GetMainChainHash(height) + if err != nil { + return nil, errors.Wrap(err, "can't find block in given height") } - return c.store.GetBlock(&node.Hash, height) + return c.store.GetBlock(hash) } // GetHeaderByHash return a block header by given hash func (c *Chain) GetHeaderByHash(hash *bc.Hash) (*types.BlockHeader, error) { - node := c.index.GetNode(hash) - if node == nil { - return nil, errors.New("can't find block header in given hash") - } - return node.BlockHeader(), nil + return c.store.GetBlockHeader(hash) } // GetHeaderByHeight return a block header by given height func (c *Chain) GetHeaderByHeight(height uint64) (*types.BlockHeader, error) { - node := c.index.NodeByHeight(height) - if node == nil { - return nil, errors.New("can't find block header in given height") + hash, err := c.store.GetMainChainHash(height) + if err != nil { + return nil, errors.Wrap(err, "can't find block header in given height") } - return node.BlockHeader(), nil + return c.store.GetBlockHeader(hash) } -func (c *Chain) calcReorganizeNodes(node *state.BlockNode) ([]*state.BlockNode, []*state.BlockNode) { - var attachNodes []*state.BlockNode - var detachNodes []*state.BlockNode +func (c *Chain) calcReorganizeChain(beginAttach *types.BlockHeader, beginDetach *types.BlockHeader) ([]*types.BlockHeader, []*types.BlockHeader, error) { + var err error + var attachBlockHeaders []*types.BlockHeader + var detachBlockHeaders []*types.BlockHeader - attachNode := node - for c.index.NodeByHeight(attachNode.Height) != attachNode { - attachNodes = append([]*state.BlockNode{attachNode}, attachNodes...) - attachNode = attachNode.Parent - } + for attachBlockHeader, detachBlockHeader := beginAttach, beginDetach; detachBlockHeader.Hash() != attachBlockHeader.Hash(); { + var attachRollback, detachRollBack bool + if attachRollback = attachBlockHeader.Height >= detachBlockHeader.Height; attachRollback { + attachBlockHeaders = append([]*types.BlockHeader{attachBlockHeader}, attachBlockHeaders...) + } - detachNode := c.bestNode - for detachNode != attachNode { - detachNodes = append(detachNodes, detachNode) - detachNode = detachNode.Parent + if detachRollBack = attachBlockHeader.Height <= detachBlockHeader.Height; detachRollBack { + detachBlockHeaders = append(detachBlockHeaders, detachBlockHeader) + } + + if attachRollback { + attachBlockHeader, err = c.store.GetBlockHeader(&attachBlockHeader.PreviousBlockHash) + if err != nil { + return nil, nil, err + } + } + + if detachRollBack { + detachBlockHeader, err = c.store.GetBlockHeader(&detachBlockHeader.PreviousBlockHash) + if err != nil { + return nil, nil, err + } + } } - return attachNodes, detachNodes + return attachBlockHeaders, detachBlockHeaders, nil } func (c *Chain) connectBlock(block *types.Block) (err error) { - irreversibleNode := c.bestIrreversibleNode bcBlock := types.MapBlock(block) if bcBlock.TransactionStatus, err = c.store.GetTransactionStatus(&bcBlock.ID); err != nil { return err @@ -95,7 +102,7 @@ func (c *Chain) connectBlock(block *types.Block) (err error) { return err } - voteResult, err := c.consensusNodeManager.getBestVoteResult() + voteResult, err := c.getBestVoteResult() if err != nil { return err } @@ -103,12 +110,12 @@ func (c *Chain) connectBlock(block *types.Block) (err error) { return err } - node := c.index.GetNode(&bcBlock.ID) - if c.isIrreversible(node) && block.Height > irreversibleNode.Height { - irreversibleNode = node + irrBlockHeader := c.bestIrrBlockHeader + if c.isIrreversible(&block.BlockHeader) && block.Height > irrBlockHeader.Height { + irrBlockHeader = &block.BlockHeader } - if err := c.setState(node, irreversibleNode, utxoView, []*state.VoteResult{voteResult}); err != nil { + if err := c.setState(&block.BlockHeader, irrBlockHeader, []*types.BlockHeader{&block.BlockHeader}, utxoView, []*state.VoteResult{voteResult}); err != nil { return err } @@ -118,18 +125,22 @@ func (c *Chain) connectBlock(block *types.Block) (err error) { return nil } -func (c *Chain) reorganizeChain(node *state.BlockNode) error { - attachNodes, detachNodes := c.calcReorganizeNodes(node) +func (c *Chain) reorganizeChain(blockHeader *types.BlockHeader) error { + attachBlockHeaders, detachBlockHeaders, err := c.calcReorganizeChain(blockHeader, c.bestBlockHeader) + if err != nil { + return err + } + utxoView := state.NewUtxoViewpoint() voteResults := []*state.VoteResult{} - irreversibleNode := c.bestIrreversibleNode - voteResult, err := c.consensusNodeManager.getBestVoteResult() + voteResult, err := c.getBestVoteResult() if err != nil { return err } - for _, detachNode := range detachNodes { - b, err := c.store.GetBlock(&detachNode.Hash, detachNode.Height) + for _, detachBlockHeader := range detachBlockHeaders { + detachHash := detachBlockHeader.Hash() + b, err := c.store.GetBlock(&detachHash) if err != nil { return err } @@ -152,11 +163,14 @@ func (c *Chain) reorganizeChain(node *state.BlockNode) error { return err } - log.WithFields(log.Fields{"module": logModule, "height": node.Height, "hash": node.Hash.String()}).Debug("detach from mainchain") + blockHash := blockHeader.Hash() + log.WithFields(log.Fields{"module": logModule, "height": blockHeader.Height, "hash": blockHash.String()}).Debug("detach from mainchain") } - for _, attachNode := range attachNodes { - b, err := c.store.GetBlock(&attachNode.Hash, attachNode.Height) + irrBlockHeader := c.bestIrrBlockHeader + for _, attachBlockHeader := range attachBlockHeaders { + attachHash := attachBlockHeader.Hash() + b, err := c.store.GetBlock(&attachHash) if err != nil { return err } @@ -183,18 +197,19 @@ func (c *Chain) reorganizeChain(node *state.BlockNode) error { voteResults = append(voteResults, voteResult.Fork()) } - if c.isIrreversible(attachNode) && attachNode.Height > irreversibleNode.Height { - irreversibleNode = attachNode + if c.isIrreversible(attachBlockHeader) && attachBlockHeader.Height > irrBlockHeader.Height { + irrBlockHeader = attachBlockHeader } - log.WithFields(log.Fields{"module": logModule, "height": node.Height, "hash": node.Hash.String()}).Debug("attach from mainchain") + blockHash := blockHeader.Hash() + log.WithFields(log.Fields{"module": logModule, "height": blockHeader.Height, "hash": blockHash.String()}).Debug("attach from mainchain") } - if detachNodes[len(detachNodes)-1].Height <= c.bestIrreversibleNode.Height && irreversibleNode.Height <= c.bestIrreversibleNode.Height { + if detachBlockHeaders[len(detachBlockHeaders)-1].Height <= c.bestIrrBlockHeader.Height && irrBlockHeader.Height <= c.bestIrrBlockHeader.Height { return errors.New("rollback block below the height of irreversible block") } voteResults = append(voteResults, voteResult.Fork()) - return c.setState(node, irreversibleNode, utxoView, voteResults) + return c.setState(blockHeader, irrBlockHeader, attachBlockHeaders, utxoView, voteResults) } // SaveBlock will validate and save block into storage @@ -203,7 +218,11 @@ func (c *Chain) saveBlock(block *types.Block) error { return errors.Sub(ErrBadBlock, err) } - parent := c.index.GetNode(&block.PreviousBlockHash) + parent, err := c.store.GetBlockHeader(&block.PreviousBlockHash) + if err != nil { + return err + } + bcBlock := types.MapBlock(block) if err := validation.ValidateBlock(bcBlock, parent); err != nil { return errors.Sub(ErrBadBlock, err) @@ -217,14 +236,7 @@ func (c *Chain) saveBlock(block *types.Block) error { if err := c.store.SaveBlock(block, bcBlock.TransactionStatus); err != nil { return err } - c.orphanManage.Delete(&bcBlock.ID) - node, err := state.NewBlockNode(&block.BlockHeader, parent) - if err != nil { - return err - } - - c.index.AddNode(node) if len(signature) != 0 { xPub := config.CommonConfig.PrivateKey().XPub() @@ -294,7 +306,7 @@ func (c *Chain) processBlock(block *types.Block) (bool, error) { return c.orphanManage.BlockExist(&blockHash), nil } - if parent := c.index.GetNode(&block.PreviousBlockHash); parent == nil { + if _, err := c.store.GetBlockHeader(&block.PreviousBlockHash); err != nil { c.orphanManage.Add(block) return true, nil } @@ -304,19 +316,18 @@ func (c *Chain) processBlock(block *types.Block) (bool, error) { } bestBlock := c.saveSubBlock(block) - bestBlockHash := bestBlock.Hash() - bestNode := c.index.GetNode(&bestBlockHash) + bestBlockHeader := &bestBlock.BlockHeader c.cond.L.Lock() defer c.cond.L.Unlock() - if bestNode.Parent == c.bestNode { + if bestBlockHeader.PreviousBlockHash == c.bestBlockHeader.Hash() { log.WithFields(log.Fields{"module": logModule}).Debug("append block to the end of mainchain") return false, c.connectBlock(bestBlock) } - if bestNode.Height > c.bestNode.Height { + if bestBlockHeader.Height > c.bestBlockHeader.Height { log.WithFields(log.Fields{"module": logModule}).Debug("start to reorganize chain") - return false, c.reorganizeChain(bestNode) + return false, c.reorganizeChain(bestBlockHeader) } return false, nil } diff --git a/protocol/block_test.go b/protocol/block_test.go index 6fcece86..cde41880 100644 --- a/protocol/block_test.go +++ b/protocol/block_test.go @@ -3,144 +3,80 @@ package protocol import ( "testing" - "github.com/vapor/config" + "github.com/vapor/database/storage" "github.com/vapor/protocol/bc" + "github.com/vapor/protocol/bc/types" "github.com/vapor/protocol/state" "github.com/vapor/testutil" ) -func TestCalcReorganizeNodes(t *testing.T) { - config.CommonConfig = config.DefaultConfig() - c := &Chain{index: state.NewBlockIndex()} - header := config.GenesisBlock().BlockHeader - initNode, err := state.NewBlockNode(&header, nil) - if err != nil { - t.Fatal(err) - } - - c.index.AddNode(initNode) - var wantAttachNodes []*state.BlockNode - var wantDetachNodes []*state.BlockNode - - mainChainNode := initNode - for i := 1; i <= 7; i++ { - header.Height = uint64(i) - mainChainNode, err = state.NewBlockNode(&header, mainChainNode) - if err != nil { - t.Fatal(err) - } - wantDetachNodes = append([]*state.BlockNode{mainChainNode}, wantDetachNodes...) - c.index.AddNode(mainChainNode) - } - c.bestNode = mainChainNode - c.index.SetMainChain(mainChainNode) +type mStore struct { + blockHeaders map[bc.Hash]*types.BlockHeader +} - sideChainNode := initNode - for i := 1; i <= 13; i++ { - header.Height = uint64(i) - sideChainNode, err = state.NewBlockNode(&header, sideChainNode) - if err != nil { - t.Fatal(err) - } - wantAttachNodes = append(wantAttachNodes, sideChainNode) - c.index.AddNode(sideChainNode) - } +func (s *mStore) BlockExist(hash *bc.Hash) bool { return false } +func (s *mStore) GetBlock(*bc.Hash) (*types.Block, error) { return nil, nil } +func (s *mStore) GetBlockHeader(hash *bc.Hash) (*types.BlockHeader, error) { + return s.blockHeaders[*hash], nil +} +func (s *mStore) GetStoreStatus() *BlockStoreState { return nil } +func (s *mStore) GetTransactionStatus(*bc.Hash) (*bc.TransactionStatus, error) { return nil, nil } +func (s *mStore) GetTransactionsUtxo(*state.UtxoViewpoint, []*bc.Tx) error { return nil } +func (s *mStore) GetUtxo(*bc.Hash) (*storage.UtxoEntry, error) { return nil, nil } +func (s *mStore) GetVoteResult(uint64) (*state.VoteResult, error) { return nil, nil } +func (s *mStore) GetMainChainHash(uint64) (*bc.Hash, error) { return nil, nil } +func (s *mStore) GetBlockHashesByHeight(uint64) ([]*bc.Hash, error) { return nil, nil } +func (s *mStore) SaveBlock(*types.Block, *bc.TransactionStatus) error { return nil } +func (s *mStore) SaveBlockHeader(blockHeader *types.BlockHeader) error { + s.blockHeaders[blockHeader.Hash()] = blockHeader + return nil +} +func (s *mStore) SaveChainStatus(*types.BlockHeader, *types.BlockHeader, []*types.BlockHeader, *state.UtxoViewpoint, []*state.VoteResult) error { + return nil +} - getAttachNodes, getDetachNodes := c.calcReorganizeNodes(sideChainNode) - if !testutil.DeepEqual(wantAttachNodes, getAttachNodes) { - t.Errorf("attach nodes want %v but get %v", wantAttachNodes, getAttachNodes) - } - if !testutil.DeepEqual(wantDetachNodes, getDetachNodes) { - t.Errorf("detach nodes want %v but get %v", wantDetachNodes, getDetachNodes) +func TestCalcReorganizeChain(t *testing.T) { + c := &Chain{ + store: &mStore{ + blockHeaders: make(map[bc.Hash]*types.BlockHeader), + }, } -} -func TestEdgeCalcReorganizeNodes(t *testing.T) { - config.CommonConfig = config.DefaultConfig() - header := config.GenesisBlock().BlockHeader - initNode, err := state.NewBlockNode(&header, nil) - if err != nil { - t.Fatal(err) + initBlockHeader := &types.BlockHeader{ + Height: 0, + Version: 1, } + c.store.SaveBlockHeader(initBlockHeader) - testNodes := []*state.BlockNode{initNode} - testNewNodes := []*state.BlockNode{initNode} - for i := uint64(1); i <= 5; i++ { - node := &state.BlockNode{ - Height: i, - Hash: bc.Hash{V0: uint64(i)}, - Parent: testNodes[i-1], + var wantAttachBlockHeaders []*types.BlockHeader + var wantDetachBlockHeaders []*types.BlockHeader + mainChainBlockHeader := initBlockHeader + newChainBlockHeader := initBlockHeader + for i := 1; i <= 7; i++ { + mainChainBlockHeader = &types.BlockHeader{ + PreviousBlockHash: mainChainBlockHeader.Hash(), + Height: uint64(i), } - testNodes = append(testNodes, node) + wantDetachBlockHeaders = append([]*types.BlockHeader{mainChainBlockHeader}, wantDetachBlockHeaders...) + c.store.SaveBlockHeader(mainChainBlockHeader) + } - newNode := &state.BlockNode{ - Height: i, - Hash: bc.Hash{V1: uint64(i)}, - Parent: testNewNodes[i-1], + for i := 1; i <= 13; i++ { + newChainBlockHeader = &types.BlockHeader{ + PreviousBlockHash: newChainBlockHeader.Hash(), + Height: uint64(i), + Version: 1, } - testNewNodes = append(testNewNodes, newNode) + wantAttachBlockHeaders = append(wantAttachBlockHeaders, newChainBlockHeader) + c.store.SaveBlockHeader(newChainBlockHeader) } - cases := []struct { - mainChainNode *state.BlockNode - newNode *state.BlockNode - wantAttachNodes []*state.BlockNode - wantDetachNodes []*state.BlockNode - }{ - { - mainChainNode: testNodes[1], - newNode: testNodes[5], - wantAttachNodes: testNodes[2:], - wantDetachNodes: []*state.BlockNode{}, - }, - { - mainChainNode: testNodes[5], - newNode: testNodes[2], - wantAttachNodes: []*state.BlockNode{}, - wantDetachNodes: []*state.BlockNode{testNodes[5], testNodes[4], testNodes[3]}, - }, - { - mainChainNode: testNodes[2], - newNode: testNodes[2], - wantAttachNodes: []*state.BlockNode{}, - wantDetachNodes: []*state.BlockNode{}, - }, - { - mainChainNode: testNewNodes[3], - newNode: testNodes[2], - wantAttachNodes: testNodes[1:3], - wantDetachNodes: []*state.BlockNode{testNewNodes[3], testNewNodes[2], testNewNodes[1]}, - }, - { - mainChainNode: testNewNodes[2], - newNode: testNodes[3], - wantAttachNodes: testNodes[1:4], - wantDetachNodes: []*state.BlockNode{testNewNodes[2], testNewNodes[1]}, - }, - { - mainChainNode: testNodes[5], - newNode: testNewNodes[3], - wantAttachNodes: testNewNodes[1:4], - wantDetachNodes: []*state.BlockNode{testNodes[5], testNodes[4], testNodes[3], testNodes[2], testNodes[1]}, - }, + getAttachBlockHeaders, getDetachBlockHeaders, _ := c.calcReorganizeChain(newChainBlockHeader, mainChainBlockHeader) + if !testutil.DeepEqual(wantAttachBlockHeaders, getAttachBlockHeaders) { + t.Errorf("attach headers want %v but get %v", wantAttachBlockHeaders, getAttachBlockHeaders) } - for i, c := range cases { - chain := &Chain{index: state.NewBlockIndex()} - chain.index.AddNode(initNode) - for i := uint64(1); i <= c.mainChainNode.Height; i++ { - chain.index.AddNode(testNodes[i]) - } - chain.bestNode = c.mainChainNode - chain.index.SetMainChain(c.mainChainNode) - getAttachNodes, getDetachNodes := chain.calcReorganizeNodes(c.newNode) - - if !testutil.DeepEqual(c.wantAttachNodes, getAttachNodes) { - t.Errorf("test case %d, attach nodes want %v but get %v", i, c.wantAttachNodes, getAttachNodes) - } - - if !testutil.DeepEqual(c.wantDetachNodes, getDetachNodes) { - t.Errorf("test case %d, detach nodes want %v but get %v", i, c.wantDetachNodes, getDetachNodes) - } + if !testutil.DeepEqual(wantDetachBlockHeaders, getDetachBlockHeaders) { + t.Errorf("detach headers want %v but get %v", wantDetachBlockHeaders, getDetachBlockHeaders) } } diff --git a/protocol/consensus_node_manager.go b/protocol/consensus_node_manager.go index 66d18263..12146f11 100644 --- a/protocol/consensus_node_manager.go +++ b/protocol/consensus_node_manager.go @@ -4,6 +4,7 @@ import ( "github.com/vapor/consensus" "github.com/vapor/errors" "github.com/vapor/protocol/bc" + "github.com/vapor/protocol/bc/types" "github.com/vapor/protocol/state" ) @@ -12,19 +13,7 @@ var ( errNotFoundBlockNode = errors.New("can not find block node") ) -type consensusNodeManager struct { - store Store - blockIndex *state.BlockIndex -} - -func newConsensusNodeManager(store Store, blockIndex *state.BlockIndex) *consensusNodeManager { - return &consensusNodeManager{ - store: store, - blockIndex: blockIndex, - } -} - -func (c *consensusNodeManager) getConsensusNode(prevBlockHash *bc.Hash, pubkey string) (*state.ConsensusNode, error) { +func (c *Chain) getConsensusNode(prevBlockHash *bc.Hash, pubkey string) (*state.ConsensusNode, error) { consensusNodeMap, err := c.getConsensusNodes(prevBlockHash) if err != nil { return nil, err @@ -37,7 +26,8 @@ func (c *consensusNodeManager) getConsensusNode(prevBlockHash *bc.Hash, pubkey s return node, nil } -func (c *consensusNodeManager) getBlocker(prevBlockHash *bc.Hash, timeStamp uint64) (string, error) { +// GetBlocker return blocker by specified timestamp +func (c *Chain) GetBlocker(prevBlockHash *bc.Hash, timeStamp uint64) (string, error) { consensusNodeMap, err := c.getConsensusNodes(prevBlockHash) if err != nil { return "", err @@ -69,35 +59,39 @@ func getBlockerOrder(startTimestamp, blockTimestamp, numOfConsensusNode uint64) return (blockTimestamp - lastRoundStartTime) / (consensus.BlockNumEachNode * consensus.BlockTimeInterval) } -func (c *consensusNodeManager) getPrevRoundLastBlock(prevBlockHash *bc.Hash) (*state.BlockNode, error) { - node := c.blockIndex.GetNode(prevBlockHash) - if node == nil { +func (c *Chain) getPrevRoundLastBlock(prevBlockHash *bc.Hash) (*types.BlockHeader, error) { + blockHeader, err := c.store.GetBlockHeader(prevBlockHash) + if err != nil { return nil, errNotFoundBlockNode } - for node.Height%consensus.RoundVoteBlockNums != 0 { - node = node.Parent + for blockHeader.Height%consensus.RoundVoteBlockNums != 0 { + blockHeader, err = c.store.GetBlockHeader(&blockHeader.PreviousBlockHash) + if err != nil { + return nil, err + } } - return node, nil + return blockHeader, nil } -func (c *consensusNodeManager) getConsensusNodes(prevBlockHash *bc.Hash) (map[string]*state.ConsensusNode, error) { - prevBlockNode := c.blockIndex.GetNode(prevBlockHash) - if prevBlockNode == nil { +func (c *Chain) getConsensusNodes(prevBlockHash *bc.Hash) (map[string]*state.ConsensusNode, error) { + prevBlockHeader, err := c.store.GetBlockHeader(prevBlockHash) + if err != nil { return nil, errNotFoundBlockNode } - preSeq := state.CalcVoteSeq(prevBlockNode.Height+1) - 1 - if bestSeq := state.CalcVoteSeq(c.blockIndex.BestNode().Height); preSeq > bestSeq { + bestBlockHeader := c.bestBlockHeader + preSeq := state.CalcVoteSeq(prevBlockHeader.Height+1) - 1 + if bestSeq := state.CalcVoteSeq(bestBlockHeader.Height); preSeq > bestSeq { preSeq = bestSeq } - lastBlockNode, err := c.getPrevRoundLastBlock(prevBlockHash) + lastBlockHeader, err := c.getPrevRoundLastBlock(prevBlockHash) if err != nil { return nil, err } - voteResult, err := c.getVoteResult(preSeq, lastBlockNode) + voteResult, err := c.getVoteResult(preSeq, lastBlockHeader) if err != nil { return nil, err } @@ -105,51 +99,43 @@ func (c *consensusNodeManager) getConsensusNodes(prevBlockHash *bc.Hash) (map[st return voteResult.ConsensusNodes() } -func (c *consensusNodeManager) getBestVoteResult() (*state.VoteResult, error) { - blockNode := c.blockIndex.BestNode() - seq := state.CalcVoteSeq(blockNode.Height) - return c.getVoteResult(seq, blockNode) +func (c *Chain) getBestVoteResult() (*state.VoteResult, error) { + bestBlockHeader := c.bestBlockHeader + seq := state.CalcVoteSeq(bestBlockHeader.Height) + return c.getVoteResult(seq, bestBlockHeader) } // getVoteResult return the vote result // seq represent the sequence of vote -// blockNode represent the chain in which the result of the vote is located +// blockHeader represent the chain in which the result of the vote is located // Voting results need to be adjusted according to the chain -func (c *consensusNodeManager) getVoteResult(seq uint64, blockNode *state.BlockNode) (*state.VoteResult, error) { +func (c *Chain) getVoteResult(seq uint64, blockHeader *types.BlockHeader) (*state.VoteResult, error) { voteResult, err := c.store.GetVoteResult(seq) if err != nil { return nil, err } - if err := c.reorganizeVoteResult(voteResult, blockNode); err != nil { + if err := c.reorganizeVoteResult(voteResult, blockHeader); err != nil { return nil, err } return voteResult, nil } -func (c *consensusNodeManager) reorganizeVoteResult(voteResult *state.VoteResult, node *state.BlockNode) error { - mainChainNode := c.blockIndex.GetNode(&voteResult.BlockHash) - var attachNodes []*state.BlockNode - var detachNodes []*state.BlockNode - for forkChainNode := node; mainChainNode != forkChainNode; { - var forChainRollback, mainChainRollBack bool - if forChainRollback = forkChainNode.Height >= mainChainNode.Height; forChainRollback { - attachNodes = append([]*state.BlockNode{forkChainNode}, attachNodes...) - } - if mainChainRollBack = forkChainNode.Height <= mainChainNode.Height; mainChainRollBack { - detachNodes = append(detachNodes, mainChainNode) - } - if forChainRollback { - forkChainNode = forkChainNode.Parent - } - if mainChainRollBack { - mainChainNode = mainChainNode.Parent - } +func (c *Chain) reorganizeVoteResult(voteResult *state.VoteResult, blockHeader *types.BlockHeader) error { + mainChainBlockHeader, err := c.store.GetBlockHeader(&voteResult.BlockHash) + if err != nil { + return err + } + + attachBlockHeaders, detachBlockHeaders, err := c.calcReorganizeChain(blockHeader, mainChainBlockHeader) + if err != nil { + return err } - for _, node := range detachNodes { - block, err := c.store.GetBlock(&node.Hash, node.Height) + for _, bh := range detachBlockHeaders { + blockHash := bh.Hash() + block, err := c.store.GetBlock(&blockHash) if err != nil { return err } @@ -159,8 +145,9 @@ func (c *consensusNodeManager) reorganizeVoteResult(voteResult *state.VoteResult } } - for _, node := range attachNodes { - block, err := c.store.GetBlock(&node.Hash, node.Height) + for _, bh := range attachBlockHeaders { + blockHash := bh.Hash() + block, err := c.store.GetBlock(&blockHash) if err != nil { return err } diff --git a/protocol/protocol.go b/protocol/protocol.go index cb31fe39..39ef664f 100644 --- a/protocol/protocol.go +++ b/protocol/protocol.go @@ -17,19 +17,17 @@ const maxProcessBlockChSize = 1024 // Chain provides functions for working with the Bytom block chain. type Chain struct { - index *state.BlockIndex orphanManage *OrphanManage txPool *TxPool store Store processBlockCh chan *processBlockMsg - consensusNodeManager *consensusNodeManager - signatureCache *common.Cache - eventDispatcher *event.Dispatcher + signatureCache *common.Cache + eventDispatcher *event.Dispatcher - cond sync.Cond - bestNode *state.BlockNode - bestIrreversibleNode *state.BlockNode + cond sync.Cond + bestBlockHeader *types.BlockHeader + bestIrrBlockHeader *types.BlockHeader } // NewChain returns a new Chain using store as the underlying storage. @@ -53,14 +51,15 @@ func NewChain(store Store, txPool *TxPool, eventDispatcher *event.Dispatcher) (* } var err error - if c.index, err = store.LoadBlockIndex(storeStatus.Height); err != nil { + c.bestBlockHeader, err = c.store.GetBlockHeader(storeStatus.Hash) + if err != nil { return nil, err } - c.bestNode = c.index.GetNode(storeStatus.Hash) - c.bestIrreversibleNode = c.index.GetNode(storeStatus.IrreversibleHash) - c.consensusNodeManager = newConsensusNodeManager(store, c.index) - c.index.SetMainChain(c.bestNode) + c.bestIrrBlockHeader, err = c.store.GetBlockHeader(storeStatus.IrreversibleHash) + if err != nil { + return nil, err + } go c.blockProcesser() return c, nil } @@ -90,54 +89,66 @@ func (c *Chain) initChainStatus() error { BlockHash: genesisBlock.Hash(), BlockHeight: 0, }} - node, err := state.NewBlockNode(&genesisBlock.BlockHeader, nil) - if err != nil { - return err - } - return c.store.SaveChainStatus(node, node, utxoView, voteResults) + genesisBlockHeader := &genesisBlock.BlockHeader + return c.store.SaveChainStatus(genesisBlockHeader, genesisBlockHeader, []*types.BlockHeader{genesisBlockHeader}, utxoView, voteResults) } // BestBlockHeight returns the current height of the blockchain. func (c *Chain) BestBlockHeight() uint64 { c.cond.L.Lock() defer c.cond.L.Unlock() - return c.bestNode.Height + return c.bestBlockHeader.Height } // BestBlockHash return the hash of the chain tail block func (c *Chain) BestBlockHash() *bc.Hash { c.cond.L.Lock() defer c.cond.L.Unlock() - return &c.bestNode.Hash + bestHash := c.bestBlockHeader.Hash() + return &bestHash } -// BestIrreversibleHeader returns the chain best irreversible block +// BestIrreversibleHeader returns the chain best irreversible block header func (c *Chain) BestIrreversibleHeader() *types.BlockHeader { - return c.bestIrreversibleNode.BlockHeader() + c.cond.L.Lock() + defer c.cond.L.Unlock() + return c.bestIrrBlockHeader } +// BestBlockHeader returns the chain best block header func (c *Chain) BestBlockHeader() *types.BlockHeader { - node := c.index.BestNode() - return node.BlockHeader() + c.cond.L.Lock() + defer c.cond.L.Unlock() + return c.bestBlockHeader } // InMainChain checks wheather a block is in the main chain func (c *Chain) InMainChain(hash bc.Hash) bool { - return c.index.InMainchain(hash) + blockHeader, err := c.store.GetBlockHeader(&hash) + if err != nil { + return false + } + + blockHash, err := c.store.GetMainChainHash(blockHeader.Height) + if err != nil { + log.WithFields(log.Fields{"module": logModule, "height": blockHeader.Height}).Debug("not contain block hash in main chain for specified height") + return false + } + return *blockHash == hash } // This function must be called with mu lock in above level -func (c *Chain) setState(node, irreversibleNode *state.BlockNode, view *state.UtxoViewpoint, voteResults []*state.VoteResult) error { - if err := c.store.SaveChainStatus(node, irreversibleNode, view, voteResults); err != nil { +func (c *Chain) setState(blockHeader, irrBlockHeader *types.BlockHeader, mainBlockHeaders []*types.BlockHeader, view *state.UtxoViewpoint, voteResults []*state.VoteResult) error { + if err := c.store.SaveChainStatus(blockHeader, irrBlockHeader, mainBlockHeaders, view, voteResults); err != nil { return err } - c.index.SetMainChain(node) - c.bestNode = node - c.bestIrreversibleNode = irreversibleNode + c.bestBlockHeader = blockHeader + c.bestIrrBlockHeader = irrBlockHeader - log.WithFields(log.Fields{"module": logModule, "height": c.bestNode.Height, "hash": c.bestNode.Hash.String()}).Debug("chain best status has been update") + blockHash := blockHeader.Hash() + log.WithFields(log.Fields{"module": logModule, "height": blockHeader.Height, "hash": blockHash.String()}).Debug("chain best status has been update") c.cond.Broadcast() return nil } @@ -148,7 +159,7 @@ func (c *Chain) BlockWaiter(height uint64) <-chan struct{} { go func() { c.cond.L.Lock() defer c.cond.L.Unlock() - for c.bestNode.Height < height { + for c.bestBlockHeader.Height < height { c.cond.Wait() } ch <- struct{}{} diff --git a/protocol/state/blockindex.go b/protocol/state/blockindex.go deleted file mode 100644 index e2e13548..00000000 --- a/protocol/state/blockindex.go +++ /dev/null @@ -1,190 +0,0 @@ -package state - -import ( - "errors" - "sort" - "sync" - - "github.com/vapor/common" - "github.com/vapor/consensus" - "github.com/vapor/protocol/bc" - "github.com/vapor/protocol/bc/types" -) - -// approxNodesPerDay is an approximation of the number of new blocks there are -// in a day on average. -const approxNodesPerDay = 24 * 24 - -// BlockNode represents a block within the block chain and is primarily used to -// aid in selecting the best chain to be the main chain. -type BlockNode struct { - Parent *BlockNode // parent is the parent block for this node. - Hash bc.Hash // hash of the block. - - Version uint64 - Height uint64 - Timestamp uint64 - BlockWitness *common.BitMap - TransactionsMerkleRoot bc.Hash - TransactionStatusHash bc.Hash -} - -func NewBlockNode(bh *types.BlockHeader, parent *BlockNode) (*BlockNode, error) { - if bh.Height != 0 && parent == nil { - return nil, errors.New("parent node can not be nil") - } - - node := &BlockNode{ - Parent: parent, - Hash: bh.Hash(), - Version: bh.Version, - Height: bh.Height, - Timestamp: bh.Timestamp, - TransactionsMerkleRoot: bh.TransactionsMerkleRoot, - TransactionStatusHash: bh.TransactionStatusHash, - } - - node.BlockWitness = common.NewBitMap(uint32(len(bh.Witness))) - for i, witness := range bh.Witness { - if len(witness) != 0 { - if err := node.BlockWitness.Set(uint32(i)); err != nil { - return nil, err - } - } - } - return node, nil -} - -// blockHeader convert a node to the header struct -func (node *BlockNode) BlockHeader() *types.BlockHeader { - previousBlockHash := bc.Hash{} - if node.Parent != nil { - previousBlockHash = node.Parent.Hash - } - return &types.BlockHeader{ - Version: node.Version, - Height: node.Height, - PreviousBlockHash: previousBlockHash, - Timestamp: node.Timestamp, - BlockCommitment: types.BlockCommitment{ - TransactionsMerkleRoot: node.TransactionsMerkleRoot, - TransactionStatusHash: node.TransactionStatusHash, - }, - } -} - -func (node *BlockNode) CalcPastMedianTime() uint64 { - timestamps := []uint64{} - iterNode := node - for i := 0; i < consensus.MedianTimeBlocks && iterNode != nil; i++ { - timestamps = append(timestamps, iterNode.Timestamp) - iterNode = iterNode.Parent - } - - sort.Sort(common.TimeSorter(timestamps)) - return timestamps[len(timestamps)/2] -} - -// BlockIndex is the struct for help chain trace block chain as tree -type BlockIndex struct { - sync.RWMutex - - index map[bc.Hash]*BlockNode - heightIndex map[uint64][]*BlockNode - mainChain []*BlockNode -} - -// NewBlockIndex will create a empty BlockIndex -func NewBlockIndex() *BlockIndex { - return &BlockIndex{ - index: make(map[bc.Hash]*BlockNode), - heightIndex: make(map[uint64][]*BlockNode), - mainChain: make([]*BlockNode, 0, approxNodesPerDay), - } -} - -// AddNode will add node to the index map -func (bi *BlockIndex) AddNode(node *BlockNode) { - bi.Lock() - bi.index[node.Hash] = node - bi.heightIndex[node.Height] = append(bi.heightIndex[node.Height], node) - bi.Unlock() -} - -// GetNode will search node from the index map -func (bi *BlockIndex) GetNode(hash *bc.Hash) *BlockNode { - bi.RLock() - defer bi.RUnlock() - return bi.index[*hash] -} - -func (bi *BlockIndex) BestNode() *BlockNode { - bi.RLock() - defer bi.RUnlock() - return bi.mainChain[len(bi.mainChain)-1] -} - -// BlockExist check does the block existed in blockIndex -func (bi *BlockIndex) BlockExist(hash *bc.Hash) bool { - bi.RLock() - _, ok := bi.index[*hash] - bi.RUnlock() - return ok -} - -// TODO: THIS FUNCTION MIGHT BE DELETED -func (bi *BlockIndex) InMainchain(hash bc.Hash) bool { - bi.RLock() - defer bi.RUnlock() - - node, ok := bi.index[hash] - if !ok { - return false - } - return bi.nodeByHeight(node.Height) == node -} - -func (bi *BlockIndex) nodeByHeight(height uint64) *BlockNode { - if height >= uint64(len(bi.mainChain)) { - return nil - } - return bi.mainChain[height] -} - -// NodeByHeight returns the block node at the specified height. -func (bi *BlockIndex) NodeByHeight(height uint64) *BlockNode { - bi.RLock() - defer bi.RUnlock() - return bi.nodeByHeight(height) -} - -// NodesByHeight return all block nodes at the specified height. -func (bi *BlockIndex) NodesByHeight(height uint64) []*BlockNode { - bi.RLock() - defer bi.RUnlock() - return bi.heightIndex[height] -} - -// SetMainChain will set the the mainChain array -func (bi *BlockIndex) SetMainChain(node *BlockNode) { - bi.Lock() - defer bi.Unlock() - - needed := node.Height + 1 - if uint64(cap(bi.mainChain)) < needed { - nodes := make([]*BlockNode, needed, needed+approxNodesPerDay) - copy(nodes, bi.mainChain) - bi.mainChain = nodes - } else { - i := uint64(len(bi.mainChain)) - bi.mainChain = bi.mainChain[0:needed] - for ; i < needed; i++ { - bi.mainChain[i] = nil - } - } - - for node != nil && bi.mainChain[node.Height] != node { - bi.mainChain[node.Height] = node - node = node.Parent - } -} diff --git a/protocol/state/blockindex_test.go b/protocol/state/blockindex_test.go deleted file mode 100644 index 360c0496..00000000 --- a/protocol/state/blockindex_test.go +++ /dev/null @@ -1,181 +0,0 @@ -package state - -import ( - "testing" - - "github.com/davecgh/go-spew/spew" - - "github.com/vapor/protocol/bc" - "github.com/vapor/protocol/bc/types" -) - -func TestCalcPastMedianTime(t *testing.T) { - cases := []struct { - Timestamps []uint64 - MedianTime uint64 - }{ - { - Timestamps: []uint64{1}, - MedianTime: 1, - }, - { - Timestamps: []uint64{1, 2}, - MedianTime: 2, - }, - { - Timestamps: []uint64{1, 3, 2}, - MedianTime: 2, - }, - { - Timestamps: []uint64{1, 3, 2, 3}, - MedianTime: 3, - }, - { - Timestamps: []uint64{1, 2, 3, 4, 5, 6, 7, 8, 11, 10, 9}, - MedianTime: 6, - }, - { - Timestamps: []uint64{1, 2, 3, 4, 5, 6, 7, 8, 11, 10, 9, 11, 11, 11, 14}, - MedianTime: 10, - }, - } - - for idx, c := range cases { - var parentNode *BlockNode - for i := range c.Timestamps { - blockHeader := &types.BlockHeader{ - Height: uint64(i), - Timestamp: c.Timestamps[i], - } - - blockNode, err := NewBlockNode(blockHeader, parentNode) - if err != nil { - t.Fatal(err) - } - parentNode = blockNode - } - - medianTime := parentNode.CalcPastMedianTime() - if medianTime != c.MedianTime { - t.Fatalf("calc median timestamp failed, index: %d, expected: %d, have: %d", idx, c.MedianTime, medianTime) - } - } -} - -func TestSetMainChain(t *testing.T) { - blockIndex := NewBlockIndex() - var lastNode *BlockNode - for i := uint64(0); i < 4; i++ { - node := &BlockNode{ - Height: i, - Hash: bc.Hash{V0: i}, - Parent: lastNode, - } - blockIndex.AddNode(node) - lastNode = node - } - - tailNode := lastNode - blockIndex.SetMainChain(lastNode) - for lastNode.Parent != nil { - if !blockIndex.InMainchain(lastNode.Hash) { - t.Fatalf("block %d, hash %v is not in main chain", lastNode.Height, lastNode.Hash) - } - lastNode = lastNode.Parent - } - - // fork and set main chain - forkHeight := uint64(1) - lastNode = blockIndex.nodeByHeight(forkHeight) - for i := uint64(1); i <= 3; i++ { - node := &BlockNode{ - Height: lastNode.Height + 1, - Hash: bc.Hash{V1: uint64(i)}, - Parent: lastNode, - } - blockIndex.AddNode(node) - lastNode = node - } - - bestNode := lastNode - blockIndex.SetMainChain(lastNode) - for lastNode.Parent != nil { - if !blockIndex.InMainchain(lastNode.Hash) { - t.Fatalf("after fork, block %d, hash %v is not in main chain", lastNode.Height, lastNode.Hash) - } - lastNode = lastNode.Parent - } - - if bestNode != blockIndex.BestNode() { - t.Fatalf("check best node failed") - } - - for tailNode.Parent != nil && tailNode.Height > forkHeight { - if blockIndex.InMainchain(tailNode.Hash) { - t.Fatalf("old chain block %d, hash %v still in main chain", tailNode.Height, tailNode.Hash) - } - tailNode = tailNode.Parent - } -} - -// MockBlockIndex will mock a empty BlockIndex -func MockBlockIndex() *BlockIndex { - return &BlockIndex{ - index: make(map[bc.Hash]*BlockNode), - heightIndex: make(map[uint64][]*BlockNode), - mainChain: make([]*BlockNode, 0, 2), - } -} - -func TestSetMainChainExtendCap(t *testing.T) { - blockIndex := MockBlockIndex() - var lastNode *BlockNode - - cases := []struct { - start uint64 - stop uint64 - wantLen int - wantCap int - }{ - { - start: 0, - stop: 500, - wantLen: 500, - wantCap: 500 + approxNodesPerDay, - }, - { - start: 500, - stop: 1000, - wantLen: 1000, - wantCap: 500 + approxNodesPerDay, - }, - { - start: 1000, - stop: 2000, - wantLen: 2000, - wantCap: 2000 + approxNodesPerDay, - }, - } - - for num, c := range cases { - for i := c.start; i < c.stop; i++ { - node := &BlockNode{ - Height: i, - Hash: bc.Hash{V0: i}, - Parent: lastNode, - } - blockIndex.AddNode(node) - lastNode = node - } - blockIndex.SetMainChain(lastNode) - if c.wantLen != len(blockIndex.mainChain) || c.wantCap != cap(blockIndex.mainChain) { - t.Fatalf("SetMainChain extended capacity error, index: %d, got len: %d, got cap: %d, want len: %d, want cap: %d", num, len(blockIndex.mainChain), cap(blockIndex.mainChain), c.wantLen, c.wantCap) - } - } - - for i := 0; i < len(blockIndex.mainChain); i++ { - if blockIndex.mainChain[i] != blockIndex.index[blockIndex.mainChain[i].Hash] { - t.Fatal("SetMainChain extended capacity error, index:", i, "want:", spew.Sdump(blockIndex.mainChain[i]), "got:", spew.Sdump(blockIndex.index[blockIndex.mainChain[i].Hash])) - } - } -} diff --git a/protocol/store.go b/protocol/store.go index b3909ff8..2da3ed1c 100644 --- a/protocol/store.go +++ b/protocol/store.go @@ -15,20 +15,21 @@ var ( // Store provides storage interface for blockchain data type Store interface { - BlockExist(*bc.Hash, uint64) bool + BlockExist(*bc.Hash) bool - GetBlock(*bc.Hash, uint64) (*types.Block, error) - GetBlockHeader(*bc.Hash, uint64) (*types.BlockHeader, error) + GetBlock(*bc.Hash) (*types.Block, error) + GetBlockHeader(*bc.Hash) (*types.BlockHeader, error) GetStoreStatus() *BlockStoreState GetTransactionStatus(*bc.Hash) (*bc.TransactionStatus, error) GetTransactionsUtxo(*state.UtxoViewpoint, []*bc.Tx) error GetUtxo(*bc.Hash) (*storage.UtxoEntry, error) GetVoteResult(uint64) (*state.VoteResult, error) + GetMainChainHash(uint64) (*bc.Hash, error) + GetBlockHashesByHeight(uint64) ([]*bc.Hash, error) - LoadBlockIndex(uint64) (*state.BlockIndex, error) SaveBlock(*types.Block, *bc.TransactionStatus) error SaveBlockHeader(*types.BlockHeader) error - SaveChainStatus(*state.BlockNode, *state.BlockNode, *state.UtxoViewpoint, []*state.VoteResult) error + SaveChainStatus(*types.BlockHeader, *types.BlockHeader, []*types.BlockHeader, *state.UtxoViewpoint, []*state.VoteResult) error } // BlockStoreState represents the core's db status diff --git a/protocol/txpool_test.go b/protocol/txpool_test.go index 338d57fb..80ff1d82 100644 --- a/protocol/txpool_test.go +++ b/protocol/txpool_test.go @@ -111,18 +111,19 @@ var testTxs = []*types.Tx{ type mockStore struct{} -func (s *mockStore) BlockExist(hash *bc.Hash, height uint64) bool { return false } -func (s *mockStore) GetBlock(*bc.Hash, uint64) (*types.Block, error) { return nil, nil } -func (s *mockStore) GetBlockHeader(*bc.Hash, uint64) (*types.BlockHeader, 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) GetBlockHeader(*bc.Hash) (*types.BlockHeader, error) { return nil, nil } func (s *mockStore) GetStoreStatus() *BlockStoreState { return nil } func (s *mockStore) GetTransactionStatus(*bc.Hash) (*bc.TransactionStatus, error) { return nil, 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) GetVoteResult(uint64) (*state.VoteResult, error) { return nil, nil } -func (s *mockStore) LoadBlockIndex(uint64) (*state.BlockIndex, error) { return nil, nil } +func (s *mockStore) GetMainChainHash(uint64) (*bc.Hash, error) { return nil, nil } +func (s *mockStore) GetBlockHashesByHeight(uint64) ([]*bc.Hash, error) { return nil, nil } func (s *mockStore) SaveBlock(*types.Block, *bc.TransactionStatus) error { return nil } func (s *mockStore) SaveBlockHeader(*types.BlockHeader) error { return nil } -func (s *mockStore) SaveChainStatus(*state.BlockNode, *state.BlockNode, *state.UtxoViewpoint, []*state.VoteResult) error { +func (s *mockStore) SaveChainStatus(*types.BlockHeader, *types.BlockHeader, []*types.BlockHeader, *state.UtxoViewpoint, []*state.VoteResult) error { return nil } @@ -655,9 +656,9 @@ func TestRemoveOrphan(t *testing.T) { type mockStore1 struct{} -func (s *mockStore1) BlockExist(hash *bc.Hash, height uint64) bool { return false } -func (s *mockStore1) GetBlock(*bc.Hash, uint64) (*types.Block, error) { return nil, nil } -func (s *mockStore1) GetBlockHeader(*bc.Hash, uint64) (*types.BlockHeader, 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) GetBlockHeader(*bc.Hash) (*types.BlockHeader, error) { return nil, nil } func (s *mockStore1) GetStoreStatus() *BlockStoreState { return nil } func (s *mockStore1) GetTransactionStatus(*bc.Hash) (*bc.TransactionStatus, error) { return nil, nil } func (s *mockStore1) GetTransactionsUtxo(utxoView *state.UtxoViewpoint, tx []*bc.Tx) error { @@ -669,10 +670,11 @@ func (s *mockStore1) GetTransactionsUtxo(utxoView *state.UtxoViewpoint, tx []*bc } func (s *mockStore1) GetUtxo(*bc.Hash) (*storage.UtxoEntry, error) { return nil, nil } func (s *mockStore1) GetVoteResult(uint64) (*state.VoteResult, error) { return nil, nil } -func (s *mockStore1) LoadBlockIndex(uint64) (*state.BlockIndex, error) { return nil, nil } +func (s *mockStore1) GetMainChainHash(uint64) (*bc.Hash, error) { return nil, nil } +func (s *mockStore1) GetBlockHashesByHeight(uint64) ([]*bc.Hash, error) { return nil, nil } func (s *mockStore1) SaveBlock(*types.Block, *bc.TransactionStatus) error { return nil } func (s *mockStore1) SaveBlockHeader(*types.BlockHeader) error { return nil } -func (s *mockStore1) SaveChainStatus(*state.BlockNode, *state.BlockNode, *state.UtxoViewpoint, []*state.VoteResult) error { +func (s *mockStore1) SaveChainStatus(*types.BlockHeader, *types.BlockHeader, []*types.BlockHeader, *state.UtxoViewpoint, []*state.VoteResult) error { return nil } diff --git a/protocol/validation/block.go b/protocol/validation/block.go index 630a87d4..b226a574 100644 --- a/protocol/validation/block.go +++ b/protocol/validation/block.go @@ -9,7 +9,6 @@ import ( "github.com/vapor/errors" "github.com/vapor/protocol/bc" "github.com/vapor/protocol/bc/types" - "github.com/vapor/protocol/state" ) const logModule = "leveldb" @@ -25,7 +24,7 @@ var ( errVersionRegression = errors.New("version regression") ) -func checkBlockTime(b *bc.Block, parent *state.BlockNode) error { +func checkBlockTime(b *bc.Block, parent *types.BlockHeader) error { now := uint64(time.Now().UnixNano() / 1e6) if b.Timestamp < (parent.Timestamp + consensus.BlockTimeInterval) { return errBadTimestamp @@ -64,22 +63,22 @@ func checkCoinbaseAmount(b *bc.Block, amount uint64) error { } // ValidateBlockHeader check the block's header -func ValidateBlockHeader(b *bc.Block, parent *state.BlockNode) error { +func ValidateBlockHeader(b *bc.Block, parent *types.BlockHeader) error { if b.Version != 1 { return errors.WithDetailf(errVersionRegression, "previous block verson %d, current block version %d", parent.Version, b.Version) } if b.Height != parent.Height+1 { return errors.WithDetailf(errMisorderedBlockHeight, "previous block height %d, current block height %d", parent.Height, b.Height) } - if parent.Hash != *b.PreviousBlockId { - return errors.WithDetailf(errMismatchedBlock, "previous block ID %x, current block wants %x", parent.Hash.Bytes(), b.PreviousBlockId.Bytes()) + if parent.Hash() != *b.PreviousBlockId { + return errors.WithDetailf(errMismatchedBlock, "previous block ID %x, current block wants %x", parent.Hash().Bytes(), b.PreviousBlockId.Bytes()) } return checkBlockTime(b, parent) } // ValidateBlock validates a block and the transactions within. -func ValidateBlock(b *bc.Block, parent *state.BlockNode) error { +func ValidateBlock(b *bc.Block, parent *types.BlockHeader) error { startTime := time.Now() if err := ValidateBlockHeader(b, parent); err != nil { return err diff --git a/protocol/validation/block_test.go b/protocol/validation/block_test.go index 6f126c0d..61e60337 100644 --- a/protocol/validation/block_test.go +++ b/protocol/validation/block_test.go @@ -8,7 +8,6 @@ import ( "github.com/vapor/consensus" "github.com/vapor/protocol/bc" "github.com/vapor/protocol/bc/types" - "github.com/vapor/protocol/state" "github.com/vapor/protocol/vm" "github.com/vapor/protocol/vm/vmutil" ) @@ -45,7 +44,7 @@ func TestCheckBlockTime(t *testing.T) { }, } - parent := &state.BlockNode{Version: 1} + parent := &types.BlockHeader{Version: 1} block := &bc.Block{ BlockHeader: &bc.BlockHeader{Version: 1}, } @@ -54,8 +53,9 @@ func TestCheckBlockTime(t *testing.T) { parent.Timestamp = c.parentTime[0] parentSuccessor := parent for i := 1; i < len(c.parentTime); i++ { - parentSuccessor.Parent = &state.BlockNode{Version: 1, Timestamp: c.parentTime[i]} - parentSuccessor = parentSuccessor.Parent + Previous := &types.BlockHeader{Version: 1, Timestamp: c.parentTime[i]} + parentSuccessor.PreviousBlockHash = Previous.Hash() + parentSuccessor = Previous } block.Timestamp = c.blockTime @@ -108,17 +108,24 @@ func TestCheckCoinbaseAmount(t *testing.T) { } func TestValidateBlockHeader(t *testing.T) { + parent := &types.BlockHeader{ + Version: 1, + Height: 0, + Timestamp: 1523352600000, + } + parentHash := parent.Hash() + cases := []struct { desc string block *bc.Block - parent *state.BlockNode + parent *types.BlockHeader err error }{ { block: &bc.Block{BlockHeader: &bc.BlockHeader{ Version: 2, }}, - parent: &state.BlockNode{ + parent: &types.BlockHeader{ Version: 1, }, err: errVersionRegression, @@ -128,7 +135,7 @@ func TestValidateBlockHeader(t *testing.T) { Version: 1, Height: 20, }}, - parent: &state.BlockNode{ + parent: &types.BlockHeader{ Version: 1, Height: 18, }, @@ -139,12 +146,12 @@ func TestValidateBlockHeader(t *testing.T) { block: &bc.Block{BlockHeader: &bc.BlockHeader{ Version: 1, Height: 20, - PreviousBlockId: &bc.Hash{V0: 18}, + PreviousBlockId: &bc.Hash{V0: 20}, }}, - parent: &state.BlockNode{ - Version: 1, - Height: 19, - Hash: bc.Hash{V0: 19}, + parent: &types.BlockHeader{ + Version: 1, + Height: 19, + PreviousBlockHash: bc.Hash{V0: 19}, }, err: errMismatchedBlock, }, @@ -155,16 +162,11 @@ func TestValidateBlockHeader(t *testing.T) { Version: 1, Height: 1, Timestamp: 1523352601000, - PreviousBlockId: &bc.Hash{V0: 0}, + PreviousBlockId: &parentHash, }, }, - parent: &state.BlockNode{ - Version: 1, - Height: 0, - Timestamp: 1523352600000, - Hash: bc.Hash{V0: 0}, - }, - err: nil, + parent: parent, + err: nil, }, { desc: "version greater than 1 (blocktest#1001)", @@ -174,7 +176,7 @@ func TestValidateBlockHeader(t *testing.T) { Version: 2, }, }, - parent: &state.BlockNode{ + parent: &types.BlockHeader{ Version: 1, }, err: errVersionRegression, @@ -187,7 +189,7 @@ func TestValidateBlockHeader(t *testing.T) { Version: 0, }, }, - parent: &state.BlockNode{ + parent: &types.BlockHeader{ Version: 1, }, err: errVersionRegression, @@ -200,7 +202,7 @@ func TestValidateBlockHeader(t *testing.T) { Version: math.MaxUint64, }, }, - parent: &state.BlockNode{ + parent: &types.BlockHeader{ Version: 1, }, err: errVersionRegression, @@ -217,10 +219,18 @@ func TestValidateBlockHeader(t *testing.T) { // TestValidateBlock test the ValidateBlock function func TestValidateBlock(t *testing.T) { cp, _ := vmutil.DefaultCoinbaseProgram() + parent := &types.BlockHeader{ + Version: 1, + Height: 0, + Timestamp: 1523352600000, + PreviousBlockHash: bc.Hash{V0: 0}, + } + parentHash := parent.Hash() + cases := []struct { desc string block *bc.Block - parent *state.BlockNode + parent *types.BlockHeader err error }{ { @@ -231,7 +241,7 @@ func TestValidateBlock(t *testing.T) { Version: 1, Height: 1, Timestamp: 1523352601000, - PreviousBlockId: &bc.Hash{V0: 0}, + PreviousBlockId: &parentHash, TransactionsRoot: &bc.Hash{V0: 1}, }, Transactions: []*bc.Tx{ @@ -243,13 +253,8 @@ func TestValidateBlock(t *testing.T) { }), }, }, - parent: &state.BlockNode{ - Version: 1, - Height: 0, - Timestamp: 1523352600000, - Hash: bc.Hash{V0: 0}, - }, - err: errMismatchedMerkleRoot, + parent: parent, + err: errMismatchedMerkleRoot, }, { desc: "The calculated transaction status merkel root hash is not equals to the hash of the block header (blocktest#1009)", @@ -259,7 +264,7 @@ func TestValidateBlock(t *testing.T) { Version: 1, Height: 1, Timestamp: 1523352601000, - PreviousBlockId: &bc.Hash{V0: 0}, + PreviousBlockId: &parentHash, TransactionsRoot: &bc.Hash{V0: 6294987741126419124, V1: 12520373106916389157, V2: 5040806596198303681, V3: 1151748423853876189}, TransactionStatusHash: &bc.Hash{V0: 1}, }, @@ -272,13 +277,8 @@ func TestValidateBlock(t *testing.T) { }), }, }, - parent: &state.BlockNode{ - Version: 1, - Height: 0, - Timestamp: 1523352600000, - Hash: bc.Hash{V0: 0}, - }, - err: errMismatchedMerkleRoot, + parent: parent, + err: errMismatchedMerkleRoot, }, { desc: "the coinbase amount is less than the real coinbase amount (txtest#1014)", @@ -288,7 +288,7 @@ func TestValidateBlock(t *testing.T) { Version: 1, Height: 1, Timestamp: 1523352601000, - PreviousBlockId: &bc.Hash{V0: 0}, + PreviousBlockId: &parentHash, }, Transactions: []*bc.Tx{ types.MapTx(&types.TxData{ @@ -305,13 +305,8 @@ func TestValidateBlock(t *testing.T) { }), }, }, - parent: &state.BlockNode{ - Version: 1, - Height: 0, - Timestamp: 1523352600000, - Hash: bc.Hash{V0: 0}, - }, - err: ErrWrongCoinbaseTransaction, + parent: parent, + err: ErrWrongCoinbaseTransaction, }, } @@ -326,19 +321,21 @@ func TestValidateBlock(t *testing.T) { // TestGasOverBlockLimit check if the gas of the block has the max limit (blocktest#1012) func TestGasOverBlockLimit(t *testing.T) { cp, _ := vmutil.DefaultCoinbaseProgram() - parent := &state.BlockNode{ - Version: 1, - Height: 0, - Timestamp: 1523352600000, - Hash: bc.Hash{V0: 0}, + parent := &types.BlockHeader{ + Version: 1, + Height: 0, + Timestamp: 1523352600000, + PreviousBlockHash: bc.Hash{V0: 0}, } + parentHash := parent.Hash() + block := &bc.Block{ ID: bc.Hash{V0: 1}, BlockHeader: &bc.BlockHeader{ Version: 1, Height: 1, Timestamp: 1523352601000, - PreviousBlockId: &bc.Hash{V0: 0}, + PreviousBlockId: &parentHash, TransactionsRoot: &bc.Hash{V0: 1}, }, Transactions: []*bc.Tx{ @@ -372,19 +369,21 @@ func TestGasOverBlockLimit(t *testing.T) { // TestSetTransactionStatus verify the transaction status is set correctly (blocktest#1010) func TestSetTransactionStatus(t *testing.T) { cp, _ := vmutil.DefaultCoinbaseProgram() - parent := &state.BlockNode{ - Version: 1, - Height: 0, - Timestamp: 1523352600000, - Hash: bc.Hash{V0: 0}, + parent := &types.BlockHeader{ + Version: 1, + Height: 0, + Timestamp: 1523352600000, + PreviousBlockHash: bc.Hash{V0: 0}, } + parentHash := parent.Hash() + block := &bc.Block{ ID: bc.Hash{V0: 1}, BlockHeader: &bc.BlockHeader{ Version: 1, Height: 1, Timestamp: 1523352601000, - PreviousBlockId: &bc.Hash{V0: 0}, + PreviousBlockId: &parentHash, TransactionsRoot: &bc.Hash{V0: 12212572290317752069, V1: 8979003395977198825, V2: 3978010681554327084, V3: 12322462500143540195}, TransactionStatusHash: &bc.Hash{V0: 8682965660674182538, V1: 8424137560837623409, V2: 6979974817894224946, V3: 4673809519342015041}, }, diff --git a/test/utxo_view/utxo_view_test.go b/test/utxo_view/utxo_view_test.go index bddeb543..aeb7ec6d 100644 --- a/test/utxo_view/utxo_view_test.go +++ b/test/utxo_view/utxo_view_test.go @@ -404,7 +404,8 @@ func TestAttachOrDetachBlocks(t *testing.T) { }, }, } - node := blockNode(types.MapBlock(&mockBlocks[0].Block).BlockHeader) + + mockBlockHeader := &mockBlocks[0].Block.BlockHeader defer os.RemoveAll("temp") for index, c := range cases { testDB := dbm.NewDB("testdb", "leveldb", "temp") @@ -414,7 +415,7 @@ func TestAttachOrDetachBlocks(t *testing.T) { for k, v := range c.before { utxoViewpoint.Entries[k] = v } - if err := store.SaveChainStatus(node, node, utxoViewpoint, []*state.VoteResult{}); err != nil { + if err := store.SaveChainStatus(mockBlockHeader, mockBlockHeader, []*types.BlockHeader{mockBlockHeader}, utxoViewpoint, []*state.VoteResult{}); err != nil { t.Error(err) } @@ -436,7 +437,7 @@ func TestAttachOrDetachBlocks(t *testing.T) { t.Error(err) } } - if err := store.SaveChainStatus(node, node, utxoViewpoint, []*state.VoteResult{}); err != nil { + if err := store.SaveChainStatus(mockBlockHeader, mockBlockHeader, []*types.BlockHeader{mockBlockHeader}, utxoViewpoint, []*state.VoteResult{}); err != nil { t.Error(err) } diff --git a/test/utxo_view/utxo_view_test_util.go b/test/utxo_view/utxo_view_test_util.go index 3948f7e2..1e021bc4 100644 --- a/test/utxo_view/utxo_view_test_util.go +++ b/test/utxo_view/utxo_view_test_util.go @@ -6,7 +6,6 @@ import ( "github.com/vapor/consensus" "github.com/vapor/protocol/bc" "github.com/vapor/protocol/bc/types" - "github.com/vapor/protocol/state" "github.com/vapor/testutil" ) @@ -49,22 +48,6 @@ func (t *tx) OutputHash(outIndex int) *bc.Hash { return t.Tx.ResultIds[outIndex] } -func blockNode(header *bc.BlockHeader) *state.BlockNode { - h := types.BlockHeader{ - Version: header.Version, - Height: header.Height, - PreviousBlockHash: *header.PreviousBlockId, - Timestamp: header.Timestamp, - } - return &state.BlockNode{ - Parent: nil, - Hash: h.Hash(), - Version: h.Version, - Height: h.Height, - Timestamp: h.Timestamp, - } -} - func mustDecodeHex(str string) []byte { data, err := hex.DecodeString(str) if err != nil { -- 2.11.0