6 log "github.com/sirupsen/logrus"
8 "github.com/vapor/common"
9 "github.com/vapor/config"
10 "github.com/vapor/errors"
11 "github.com/vapor/event"
12 "github.com/vapor/protocol/bc"
13 "github.com/vapor/protocol/bc/types"
14 "github.com/vapor/protocol/state"
18 maxProcessBlockChSize = 1024
19 maxKnownTxs = 32768 // Maximum transactions hashes to keep in the known list (prevent DOS)
22 type Protocoler interface {
24 ChainStatus() (uint64, *bc.Hash, error)
25 ValidateBlock(block *types.Block) error
26 ValidateTxs(txs []*types.Tx) error
27 ApplyBlock(block *types.Block) error
28 DetachBlock(block *types.Block) error
31 // Chain provides functions for working with the Bytom block chain.
33 orphanManage *OrphanManage
36 processBlockCh chan *processBlockMsg
37 subProtocols []Protocoler
39 signatureCache *common.Cache
40 eventDispatcher *event.Dispatcher
43 bestBlockHeader *types.BlockHeader // the last block on current main chain
44 lastIrrBlockHeader *types.BlockHeader // the last irreversible block
46 knownTxs *common.OrderedSet
49 // NewChain returns a new Chain using store as the underlying storage.
50 func NewChain(store Store, txPool *TxPool, subProtocols []Protocoler, eventDispatcher *event.Dispatcher) (*Chain, error) {
51 knownTxs, _ := common.NewOrderedSet(maxKnownTxs)
53 orphanManage: NewOrphanManage(),
56 subProtocols: subProtocols,
57 signatureCache: common.NewCache(maxSignatureCacheSize),
58 eventDispatcher: eventDispatcher,
59 processBlockCh: make(chan *processBlockMsg, maxProcessBlockChSize),
62 c.cond.L = new(sync.Mutex)
64 storeStatus := store.GetStoreStatus()
65 if storeStatus == nil {
66 if err := c.initChainStatus(); err != nil {
69 storeStatus = store.GetStoreStatus()
73 c.bestBlockHeader, err = c.store.GetBlockHeader(storeStatus.Hash)
78 c.lastIrrBlockHeader, err = c.store.GetBlockHeader(storeStatus.IrreversibleHash)
83 for _, p := range c.subProtocols {
84 if err := c.syncProtocolStatus(p); err != nil {
85 return nil, errors.Wrap(err, p.Name(), "sync sub protocol status")
93 func (c *Chain) initChainStatus() error {
94 genesisBlock := config.GenesisBlock()
95 txStatus := bc.NewTransactionStatus()
96 for i := range genesisBlock.Transactions {
97 if err := txStatus.SetStatus(i, false); err != nil {
102 if err := c.store.SaveBlock(genesisBlock, txStatus); err != nil {
106 utxoView := state.NewUtxoViewpoint()
107 bcBlock := types.MapBlock(genesisBlock)
108 if err := utxoView.ApplyBlock(bcBlock, txStatus); err != nil {
112 consensusResults := []*state.ConsensusResult{&state.ConsensusResult{
114 NumOfVote: make(map[string]uint64),
115 CoinbaseReward: make(map[string]uint64),
116 BlockHash: genesisBlock.Hash(),
120 genesisBlockHeader := &genesisBlock.BlockHeader
121 return c.store.SaveChainStatus(genesisBlockHeader, genesisBlockHeader, []*types.BlockHeader{genesisBlockHeader}, utxoView, consensusResults)
124 // BestBlockHeight returns the current height of the blockchain.
125 func (c *Chain) BestBlockHeight() uint64 {
127 defer c.cond.L.Unlock()
128 return c.bestBlockHeader.Height
131 // BestBlockHash return the hash of the main chain tail block
132 func (c *Chain) BestBlockHash() *bc.Hash {
134 defer c.cond.L.Unlock()
135 bestHash := c.bestBlockHeader.Hash()
139 // LastIrreversibleHeader returns the chain last irreversible block header
140 func (c *Chain) LastIrreversibleHeader() *types.BlockHeader {
142 defer c.cond.L.Unlock()
143 return c.lastIrrBlockHeader
146 // BestBlockHeader returns the chain best block header
147 func (c *Chain) BestBlockHeader() *types.BlockHeader {
149 defer c.cond.L.Unlock()
150 return c.bestBlockHeader
153 // InMainChain checks wheather a block is in the main chain
154 func (c *Chain) InMainChain(hash bc.Hash) bool {
155 blockHeader, err := c.store.GetBlockHeader(&hash)
160 blockHash, err := c.store.GetMainChainHash(blockHeader.Height)
162 log.WithFields(log.Fields{"module": logModule, "height": blockHeader.Height}).Debug("not contain block hash in main chain for specified height")
165 return *blockHash == hash
168 // trace back to the tail of the chain from the given block header
169 func (c *Chain) traceLongestChainTail(blockHeader *types.BlockHeader) (*types.BlockHeader, error) {
170 longestTail, workQueue := blockHeader, []*types.BlockHeader{blockHeader}
172 for ; len(workQueue) > 0; workQueue = workQueue[1:] {
173 currentHeader := workQueue[0]
174 currentHash := currentHeader.Hash()
175 hashes, err := c.store.GetBlockHashesByHeight(currentHeader.Height + 1)
180 for _, h := range hashes {
181 if header, err := c.store.GetBlockHeader(h); err != nil {
183 } else if header.PreviousBlockHash == currentHash {
184 if longestTail.Height < header.Height {
187 workQueue = append(workQueue, header)
191 return longestTail, nil
194 func (c *Chain) hasSeenTx(tx *types.Tx) bool {
195 return c.knownTxs.Has(tx.ID.String())
198 func (c *Chain) markTransactions(txs ...*types.Tx) {
199 for _, tx := range txs {
200 c.knownTxs.Add(tx.ID.String())
204 func (c *Chain) syncProtocolStatus(subProtocol Protocoler) error {
205 protocolHeight, protocolHash, err := subProtocol.ChainStatus()
207 return errors.Wrap(err, "failed on get sub protocol status")
210 if protocolHeight == c.BestBlockHeight() && protocolHash == c.BestBlockHash() {
214 for !c.InMainChain(*protocolHash) {
215 block, err := c.GetBlockByHash(protocolHash)
217 return errors.Wrap(err, subProtocol.Name(), "can't get block by hash in chain")
220 if err := subProtocol.DetachBlock(block); err != nil {
221 return errors.Wrap(err, subProtocol.Name(), "sub protocol detach block err")
224 protocolHeight, protocolHash, err = subProtocol.ChainStatus()
226 return errors.Wrap(err, "failed on get sub protocol status")
230 for height := protocolHeight + 1; height <= c.BestBlockHeight(); height++ {
231 block, err := c.GetBlockByHeight(height)
233 return errors.Wrap(err, subProtocol.Name(), "can't get block by height in chain")
236 if err := subProtocol.ApplyBlock(block); err != nil {
237 return errors.Wrap(err, subProtocol.Name(), "sub protocol apply block err")
240 protocolHeight, protocolHash, err = subProtocol.ChainStatus()
242 return errors.Wrap(err, "failed on get sub protocol status")
245 if *protocolHash != block.Hash() {
246 return errors.Wrap(errors.New("sub protocol status sync err"), subProtocol.Name())
253 // This function must be called with mu lock in above level
254 func (c *Chain) setState(blockHeader, irrBlockHeader *types.BlockHeader, mainBlockHeaders []*types.BlockHeader, view *state.UtxoViewpoint, consensusResults []*state.ConsensusResult) error {
255 if err := c.store.SaveChainStatus(blockHeader, irrBlockHeader, mainBlockHeaders, view, consensusResults); err != nil {
259 c.bestBlockHeader = blockHeader
260 c.lastIrrBlockHeader = irrBlockHeader
262 blockHash := blockHeader.Hash()
263 log.WithFields(log.Fields{"module": logModule, "height": blockHeader.Height, "hash": blockHash.String()}).Debug("chain best status has been update")
268 // BlockWaiter returns a channel that waits for the block at the given height.
269 func (c *Chain) BlockWaiter(height uint64) <-chan struct{} {
270 ch := make(chan struct{}, 1)
273 defer c.cond.L.Unlock()
274 for c.bestBlockHeader.Height < height {
283 // GetTxPool return chain txpool.
284 func (c *Chain) GetTxPool() *TxPool {