OSDN Git Service

get BlockNode from database (#188)
authoroysheng <33340252+oysheng@users.noreply.github.com>
Tue, 25 Jun 2019 11:58:37 +0000 (19:58 +0800)
committerPaladz <yzhu101@uottawa.ca>
Tue, 25 Jun 2019 11:58:37 +0000 (19:58 +0800)
* 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

17 files changed:
database/cache.go
database/cache_test.go
database/store.go
database/store_test.go
protocol/bbft.go
protocol/block.go
protocol/block_test.go
protocol/consensus_node_manager.go
protocol/protocol.go
protocol/state/blockindex.go [deleted file]
protocol/state/blockindex_test.go [deleted file]
protocol/store.go
protocol/txpool_test.go
protocol/validation/block.go
protocol/validation/block_test.go
test/utxo_view/utxo_view_test.go
test/utxo_view/utxo_view_test_util.go

index 89ef3a4..444abee 100644 (file)
@@ -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)
 }
index 1419377..a15f3b3 100644 (file)
@@ -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")
                }
        }
 
index 6de274b..ea947a1 100644 (file)
@@ -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
 }
index ea009eb..53b158b 100644 (file)
@@ -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
+}
index 6c61949..0d00737 100644 (file)
@@ -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
 }
index d5ef5cf..a1d5830 100644 (file)
@@ -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
 }
index 6fcece8..cde4188 100644 (file)
@@ -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)
        }
 }
index 66d1826..12146f1 100644 (file)
@@ -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
                }
index cb31fe3..39ef664 100644 (file)
@@ -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 (file)
index e2e1354..0000000
+++ /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 (file)
index 360c049..0000000
+++ /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]))
-               }
-       }
-}
index b3909ff..2da3ed1 100644 (file)
@@ -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
index 338d57f..80ff1d8 100644 (file)
@@ -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
 }
 
index 630a87d..b226a57 100644 (file)
@@ -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
index 6f126c0..61e6033 100644 (file)
@@ -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},
                },
index bddeb54..aeb7ec6 100644 (file)
@@ -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)
                }
 
index 3948f7e..1e021bc 100644 (file)
@@ -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 {