6 log "github.com/sirupsen/logrus"
8 "github.com/bytom/vapor/common"
9 "github.com/bytom/vapor/config"
10 "github.com/bytom/vapor/errors"
11 "github.com/bytom/vapor/event"
12 "github.com/bytom/vapor/protocol/bc"
13 "github.com/bytom/vapor/protocol/bc/types"
14 "github.com/bytom/vapor/protocol/state"
18 maxProcessBlockChSize = 1024
19 maxKnownTxs = 32768 // Maximum transactions hashes to keep in the known list (prevent DOS)
22 // Protocoler is interface for layer 2 consensus protocol
23 type Protocoler interface {
26 BeforeProposalBlock(txs []*types.Tx, blockHeight uint64, gasLeft int64, isTimeout func() bool) ([]*types.Tx, error)
27 ChainStatus() (uint64, *bc.Hash, error)
28 ValidateBlock(block *types.Block, verifyResults []*bc.TxVerifyResult) error
29 ValidateTx(tx *types.Tx, verifyResult *bc.TxVerifyResult, blockHeight uint64) error
30 ApplyBlock(block *types.Block) error
31 DetachBlock(block *types.Block) error
34 // Chain provides functions for working with the Bytom block chain.
36 orphanManage *OrphanManage
39 processBlockCh chan *processBlockMsg
40 subProtocols []Protocoler
42 signatureCache *common.Cache
43 eventDispatcher *event.Dispatcher
46 bestBlockHeader *types.BlockHeader // the last block on current main chain
47 lastIrrBlockHeader *types.BlockHeader // the last irreversible block
49 knownTxs *common.OrderedSet
52 // NewChain returns a new Chain using store as the underlying storage.
53 func NewChain(store Store, txPool *TxPool, subProtocols []Protocoler, eventDispatcher *event.Dispatcher) (*Chain, error) {
54 knownTxs, _ := common.NewOrderedSet(maxKnownTxs)
56 orphanManage: NewOrphanManage(),
59 subProtocols: subProtocols,
60 signatureCache: common.NewCache(maxSignatureCacheSize),
61 eventDispatcher: eventDispatcher,
62 processBlockCh: make(chan *processBlockMsg, maxProcessBlockChSize),
65 c.cond.L = new(sync.Mutex)
67 storeStatus := store.GetStoreStatus()
68 if storeStatus == nil {
69 if err := c.initChainStatus(); err != nil {
72 storeStatus = store.GetStoreStatus()
76 c.bestBlockHeader, err = c.store.GetBlockHeader(storeStatus.Hash)
81 c.lastIrrBlockHeader, err = c.store.GetBlockHeader(storeStatus.IrreversibleHash)
86 for _, p := range c.subProtocols {
87 if err := c.syncProtocolStatus(p); err != nil {
88 return nil, errors.Wrap(err, p.Name(), "sync sub protocol status")
96 func (c *Chain) initChainStatus() error {
97 genesisBlock := config.GenesisBlock()
98 txStatus := bc.NewTransactionStatus()
99 for i := range genesisBlock.Transactions {
100 if err := txStatus.SetStatus(i, false); err != nil {
105 if err := c.store.SaveBlock(genesisBlock, txStatus); err != nil {
109 utxoView := state.NewUtxoViewpoint()
110 bcBlock := types.MapBlock(genesisBlock)
111 if err := utxoView.ApplyBlock(bcBlock, txStatus); err != nil {
115 for _, subProtocol := range c.subProtocols {
116 if err := subProtocol.ApplyBlock(genesisBlock); err != nil {
121 consensusResults := []*state.ConsensusResult{{
123 NumOfVote: make(map[string]uint64),
124 CoinbaseReward: make(map[string]uint64),
125 BlockHash: genesisBlock.Hash(),
129 genesisBlockHeader := &genesisBlock.BlockHeader
130 return c.store.SaveChainStatus(genesisBlockHeader, genesisBlockHeader, []*types.BlockHeader{genesisBlockHeader}, utxoView, consensusResults)
133 // BestBlockHeight returns the current height of the blockchain.
134 func (c *Chain) BestBlockHeight() uint64 {
136 defer c.cond.L.Unlock()
137 return c.bestBlockHeader.Height
140 // BestBlockHash return the hash of the main chain tail block
141 func (c *Chain) BestBlockHash() *bc.Hash {
143 defer c.cond.L.Unlock()
144 bestHash := c.bestBlockHeader.Hash()
148 // LastIrreversibleHeader returns the chain last irreversible block header
149 func (c *Chain) LastIrreversibleHeader() *types.BlockHeader {
151 defer c.cond.L.Unlock()
152 return c.lastIrrBlockHeader
155 // BestBlockHeader returns the chain best block header
156 func (c *Chain) BestBlockHeader() *types.BlockHeader {
158 defer c.cond.L.Unlock()
159 return c.bestBlockHeader
162 // InMainChain checks wheather a block is in the main chain
163 func (c *Chain) InMainChain(hash bc.Hash) bool {
164 blockHeader, err := c.store.GetBlockHeader(&hash)
169 blockHash, err := c.store.GetMainChainHash(blockHeader.Height)
171 log.WithFields(log.Fields{"module": logModule, "height": blockHeader.Height}).Debug("not contain block hash in main chain for specified height")
174 return *blockHash == hash
177 // SubProtocols return list of layer 2 consensus protocol
178 func (c *Chain) SubProtocols() []Protocoler {
179 return c.subProtocols
182 // trace back to the tail of the chain from the given block header
183 func (c *Chain) traceLongestChainTail(blockHeader *types.BlockHeader) (*types.BlockHeader, error) {
184 longestTail, workQueue := blockHeader, []*types.BlockHeader{blockHeader}
186 for ; len(workQueue) > 0; workQueue = workQueue[1:] {
187 currentHeader := workQueue[0]
188 currentHash := currentHeader.Hash()
189 hashes, err := c.store.GetBlockHashesByHeight(currentHeader.Height + 1)
194 for _, h := range hashes {
195 if header, err := c.store.GetBlockHeader(h); err != nil {
197 } else if header.PreviousBlockHash == currentHash {
198 if longestTail.Height < header.Height {
201 workQueue = append(workQueue, header)
205 return longestTail, nil
208 func (c *Chain) hasSeenTx(tx *types.Tx) bool {
209 return c.knownTxs.Has(tx.ID.String())
212 func (c *Chain) markTransactions(txs ...*types.Tx) {
213 for _, tx := range txs {
214 c.knownTxs.Add(tx.ID.String())
218 func (c *Chain) syncProtocolStatus(subProtocol Protocoler) error {
219 if c.bestBlockHeader.Height < subProtocol.StartHeight() {
223 protocolHeight, protocolHash, err := subProtocol.ChainStatus()
225 return errors.Wrap(err, "failed on get sub protocol status")
228 if *protocolHash == c.bestBlockHeader.Hash() {
232 for !c.InMainChain(*protocolHash) {
233 block, err := c.GetBlockByHash(protocolHash)
235 return errors.Wrap(err, subProtocol.Name(), "can't get block by hash in chain")
238 if err := subProtocol.DetachBlock(block); err != nil {
239 return errors.Wrap(err, subProtocol.Name(), "sub protocol detach block err")
242 protocolHeight, protocolHash = block.Height-1, &block.PreviousBlockHash
245 for height := protocolHeight + 1; height <= c.bestBlockHeader.Height; height++ {
246 block, err := c.GetBlockByHeight(height)
248 return errors.Wrap(err, subProtocol.Name(), "can't get block by height in chain")
251 if err := subProtocol.ApplyBlock(block); err != nil {
252 return errors.Wrap(err, subProtocol.Name(), "sub protocol apply block err")
255 blockHash := block.Hash()
256 protocolHeight, protocolHash = block.Height, &blockHash
262 // This function must be called with mu lock in above level
263 func (c *Chain) setState(blockHeader, irrBlockHeader *types.BlockHeader, mainBlockHeaders []*types.BlockHeader, view *state.UtxoViewpoint, consensusResults []*state.ConsensusResult) error {
264 if err := c.store.SaveChainStatus(blockHeader, irrBlockHeader, mainBlockHeaders, view, consensusResults); err != nil {
268 c.bestBlockHeader = blockHeader
269 c.lastIrrBlockHeader = irrBlockHeader
271 blockHash := blockHeader.Hash()
272 log.WithFields(log.Fields{"module": logModule, "height": blockHeader.Height, "hash": blockHash.String()}).Debug("chain best status has been update")
277 // BlockWaiter returns a channel that waits for the block at the given height.
278 func (c *Chain) BlockWaiter(height uint64) <-chan struct{} {
279 ch := make(chan struct{}, 1)
282 defer c.cond.L.Unlock()
283 for c.bestBlockHeader.Height < height {
292 // GetTxPool return chain txpool.
293 func (c *Chain) GetTxPool() *TxPool {