6 log "github.com/sirupsen/logrus"
8 "github.com/bytom/vapor/common"
9 "github.com/bytom/vapor/config"
10 "github.com/bytom/vapor/consensus"
11 "github.com/bytom/vapor/errors"
12 "github.com/bytom/vapor/event"
13 "github.com/bytom/vapor/protocol/bc"
14 "github.com/bytom/vapor/protocol/bc/types"
15 "github.com/bytom/vapor/protocol/state"
19 maxProcessBlockChSize = 1024
20 maxKnownTxs = 32768 // Maximum transactions hashes to keep in the known list (prevent DOS)
21 maxPrevRoundVoteBlockHashCacheSize = 32768
24 // ErrNotInitSubProtocolChainStatus represent the node state of sub protocol has not been initialized
25 var ErrNotInitSubProtocolChainStatus = errors.New("node state of sub protocol has not been initialized")
27 // SubProtocol is interface for layer 2 consensus protocol
28 type SubProtocol interface {
31 BeforeProposalBlock(block *types.Block, gasLeft int64, isTimeout func() bool) ([]*types.Tx, error)
33 // ChainStatus return the the current block height and block hash of sub protocol.
34 // it will return ErrNotInitSubProtocolChainStatus if not initialized.
35 ChainStatus() (uint64, *bc.Hash, error)
36 InitChainStatus(*bc.Hash) error
37 ValidateBlock(block *types.Block, verifyResults []*bc.TxVerifyResult) error
38 ValidateTx(tx *types.Tx, verifyResult *bc.TxVerifyResult, blockHeight uint64) error
39 ApplyBlock(block *types.Block) error
40 DetachBlock(block *types.Block) error
43 // Chain provides functions for working with the Bytom block chain.
45 orphanManage *OrphanManage
48 processBlockCh chan *processBlockMsg
49 subProtocols []SubProtocol
51 signatureCache *common.Cache
52 prevRoundVoteBlockHashCache *common.Cache
53 eventDispatcher *event.Dispatcher
56 bestBlockHeader *types.BlockHeader // the last block on current main chain
57 lastIrrBlockHeader *types.BlockHeader // the last irreversible block
59 knownTxs *common.OrderedSet
62 // NewChain returns a new Chain using store as the underlying storage.
63 func NewChain(store Store, txPool *TxPool, subProtocols []SubProtocol, eventDispatcher *event.Dispatcher) (*Chain, error) {
64 knownTxs, _ := common.NewOrderedSet(maxKnownTxs)
66 orphanManage: NewOrphanManage(),
69 subProtocols: subProtocols,
70 signatureCache: common.NewCache(maxSignatureCacheSize),
71 prevRoundVoteBlockHashCache: common.NewCache(maxPrevRoundVoteBlockHashCacheSize),
72 eventDispatcher: eventDispatcher,
73 processBlockCh: make(chan *processBlockMsg, maxProcessBlockChSize),
76 c.cond.L = new(sync.Mutex)
78 storeStatus := store.GetStoreStatus()
79 if storeStatus == nil {
80 if err := c.initChainStatus(); err != nil {
83 storeStatus = store.GetStoreStatus()
87 c.bestBlockHeader, err = c.store.GetBlockHeader(storeStatus.Hash)
92 c.lastIrrBlockHeader, err = c.store.GetBlockHeader(storeStatus.IrreversibleHash)
97 for _, p := range c.subProtocols {
98 if err := c.syncProtocolStatus(p); err != nil {
99 return nil, errors.Wrap(err, p.Name(), "sync sub protocol status")
103 go c.blockProcesser()
107 func (c *Chain) initChainStatus() error {
108 genesisBlock := config.GenesisBlock()
109 txStatus := bc.NewTransactionStatus()
110 for i := range genesisBlock.Transactions {
111 if err := txStatus.SetStatus(i, false); err != nil {
116 if err := c.store.SaveBlock(genesisBlock, txStatus); err != nil {
120 utxoView := state.NewUtxoViewpoint()
121 bcBlock := types.MapBlock(genesisBlock)
122 if err := utxoView.ApplyBlock(bcBlock, txStatus); err != nil {
126 for _, subProtocol := range c.subProtocols {
127 if err := subProtocol.ApplyBlock(genesisBlock); err != nil {
132 consensusResults := []*state.ConsensusResult{{
134 NumOfVote: make(map[string]uint64),
135 CoinbaseReward: make(map[string]uint64),
136 BlockHash: genesisBlock.Hash(),
140 genesisBlockHeader := &genesisBlock.BlockHeader
141 return c.store.SaveChainStatus(genesisBlockHeader, genesisBlockHeader, []*types.BlockHeader{genesisBlockHeader}, utxoView, consensusResults)
144 // getPrevRoundVoteBlockHash return the previous round block hash by the given block header
145 func (c *Chain) getPrevRoundVoteBlockHash(hash *bc.Hash) (*bc.Hash, error) {
146 if data, ok := c.prevRoundVoteBlockHashCache.Get(*hash); ok {
147 return data.(*bc.Hash), nil
150 header, err := c.store.GetBlockHeader(hash)
152 return nil, errNotFoundBlockNode
155 if header.Height%consensus.ActiveNetParams.RoundVoteBlockNums == 0 {
156 c.prevRoundVoteBlockHashCache.Add(*hash, hash)
160 if data, ok := c.prevRoundVoteBlockHashCache.Get(header.PreviousBlockHash); ok {
161 c.prevRoundVoteBlockHashCache.Add(*hash, data.(*bc.Hash))
162 return data.(*bc.Hash), nil
165 // loop find the prev round vote block hash
166 for header.Height%consensus.ActiveNetParams.RoundVoteBlockNums != 0 {
167 header, err = c.store.GetBlockHeader(&header.PreviousBlockHash)
172 preRoundVoteBlockHash := header.Hash()
173 c.prevRoundVoteBlockHashCache.Add(*hash, &preRoundVoteBlockHash)
174 return &preRoundVoteBlockHash, nil
177 // BestBlockHeight returns the current height of the blockchain.
178 func (c *Chain) BestBlockHeight() uint64 {
180 defer c.cond.L.Unlock()
181 return c.bestBlockHeader.Height
184 // BestBlockHash return the hash of the main chain tail block
185 func (c *Chain) BestBlockHash() *bc.Hash {
187 defer c.cond.L.Unlock()
188 bestHash := c.bestBlockHeader.Hash()
192 // LastIrreversibleHeader returns the chain last irreversible block header
193 func (c *Chain) LastIrreversibleHeader() *types.BlockHeader {
195 defer c.cond.L.Unlock()
196 return c.lastIrrBlockHeader
199 // BestBlockHeader returns the chain best block header
200 func (c *Chain) BestBlockHeader() *types.BlockHeader {
202 defer c.cond.L.Unlock()
203 return c.bestBlockHeader
206 // InMainChain checks wheather a block is in the main chain
207 func (c *Chain) InMainChain(hash bc.Hash) bool {
208 blockHeader, err := c.store.GetBlockHeader(&hash)
213 blockHash, err := c.store.GetMainChainHash(blockHeader.Height)
215 log.WithFields(log.Fields{"module": logModule, "height": blockHeader.Height}).Debug("not contain block hash in main chain for specified height")
218 return *blockHash == hash
221 // SubProtocols return list of layer 2 consensus protocol
222 func (c *Chain) SubProtocols() []SubProtocol {
223 return c.subProtocols
226 // trace back to the tail of the chain from the given block header
227 func (c *Chain) traceLongestChainTail(blockHeader *types.BlockHeader) (*types.BlockHeader, error) {
228 longestTail, workQueue := blockHeader, []*types.BlockHeader{blockHeader}
230 for ; len(workQueue) > 0; workQueue = workQueue[1:] {
231 currentHeader := workQueue[0]
232 currentHash := currentHeader.Hash()
233 hashes, err := c.store.GetBlockHashesByHeight(currentHeader.Height + 1)
238 for _, h := range hashes {
239 if header, err := c.store.GetBlockHeader(h); err != nil {
241 } else if header.PreviousBlockHash == currentHash {
242 if longestTail.Height < header.Height {
245 workQueue = append(workQueue, header)
249 return longestTail, nil
252 func (c *Chain) hasSeenTx(tx *types.Tx) bool {
253 return c.knownTxs.Has(tx.ID.String())
256 func (c *Chain) markTransactions(txs ...*types.Tx) {
257 for _, tx := range txs {
258 c.knownTxs.Add(tx.ID.String())
262 func (c *Chain) syncProtocolStatus(subProtocol SubProtocol) error {
263 if c.bestBlockHeader.Height < subProtocol.StartHeight() {
267 protocolHeight, protocolHash, err := subProtocol.ChainStatus()
268 if err == ErrNotInitSubProtocolChainStatus {
269 startHash, err := c.store.GetMainChainHash(subProtocol.StartHeight())
271 return errors.Wrap(err, subProtocol.Name(), "can't get block hash by height")
274 if err := subProtocol.InitChainStatus(startHash); err != nil {
275 return errors.Wrap(err, subProtocol.Name(), "fail init chain status")
278 protocolHeight, protocolHash = subProtocol.StartHeight(), startHash
279 } else if err != nil {
280 return errors.Wrap(err, subProtocol.Name(), "can't get chain status")
283 if *protocolHash == c.bestBlockHeader.Hash() {
287 for !c.InMainChain(*protocolHash) {
288 block, err := c.GetBlockByHash(protocolHash)
290 return errors.Wrap(err, subProtocol.Name(), "can't get block by hash in chain")
293 if err := subProtocol.DetachBlock(block); err != nil {
294 return errors.Wrap(err, subProtocol.Name(), "sub protocol detach block err")
297 protocolHeight, protocolHash = block.Height-1, &block.PreviousBlockHash
300 for height := protocolHeight + 1; height <= c.bestBlockHeader.Height; height++ {
301 block, err := c.GetBlockByHeight(height)
303 return errors.Wrap(err, subProtocol.Name(), "can't get block by height in chain")
306 if err := subProtocol.ApplyBlock(block); err != nil {
307 return errors.Wrap(err, subProtocol.Name(), "sub protocol apply block err")
310 blockHash := block.Hash()
311 protocolHeight, protocolHash = block.Height, &blockHash
317 // This function must be called with mu lock in above level
318 func (c *Chain) setState(blockHeader, irrBlockHeader *types.BlockHeader, mainBlockHeaders []*types.BlockHeader, view *state.UtxoViewpoint, consensusResults []*state.ConsensusResult) error {
319 if err := c.store.SaveChainStatus(blockHeader, irrBlockHeader, mainBlockHeaders, view, consensusResults); err != nil {
323 c.bestBlockHeader = blockHeader
324 c.lastIrrBlockHeader = irrBlockHeader
326 blockHash := blockHeader.Hash()
327 log.WithFields(log.Fields{"module": logModule, "height": blockHeader.Height, "hash": blockHash.String()}).Debug("chain best status has been update")
332 // BlockWaiter returns a channel that waits for the block at the given height.
333 func (c *Chain) BlockWaiter(height uint64) <-chan struct{} {
334 ch := make(chan struct{}, 1)
337 defer c.cond.L.Unlock()
338 for c.bestBlockHeader.Height < height {
347 // GetTxPool return chain txpool.
348 func (c *Chain) GetTxPool() *TxPool {