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 BeforeProposalBlock(nodeProgram []byte, gasLeft int64) ([]*types.Tx, int64, error)
25 ChainStatus() (uint64, *bc.Hash, error)
26 ValidateBlock(block *types.Block, verifyResults []*bc.TxVerifyResult) error
27 ValidateTxs(txs []*types.Tx, verifyResults []*bc.TxVerifyResult) error
28 ValidateTx(tx *types.Tx, verifyResult *bc.TxVerifyResult) error
29 ApplyBlock(block *types.Block) error
30 DetachBlock(block *types.Block) error
33 // Chain provides functions for working with the Bytom block chain.
35 orphanManage *OrphanManage
38 processBlockCh chan *processBlockMsg
39 subProtocols []Protocoler
41 signatureCache *common.Cache
42 eventDispatcher *event.Dispatcher
45 bestBlockHeader *types.BlockHeader // the last block on current main chain
46 lastIrrBlockHeader *types.BlockHeader // the last irreversible block
48 knownTxs *common.OrderedSet
51 // NewChain returns a new Chain using store as the underlying storage.
52 func NewChain(store Store, txPool *TxPool, subProtocols []Protocoler, eventDispatcher *event.Dispatcher) (*Chain, error) {
53 knownTxs, _ := common.NewOrderedSet(maxKnownTxs)
55 orphanManage: NewOrphanManage(),
58 subProtocols: subProtocols,
59 signatureCache: common.NewCache(maxSignatureCacheSize),
60 eventDispatcher: eventDispatcher,
61 processBlockCh: make(chan *processBlockMsg, maxProcessBlockChSize),
64 c.cond.L = new(sync.Mutex)
66 storeStatus := store.GetStoreStatus()
67 if storeStatus == nil {
68 if err := c.initChainStatus(); err != nil {
71 storeStatus = store.GetStoreStatus()
75 c.bestBlockHeader, err = c.store.GetBlockHeader(storeStatus.Hash)
80 c.lastIrrBlockHeader, err = c.store.GetBlockHeader(storeStatus.IrreversibleHash)
85 for _, p := range c.subProtocols {
86 if err := c.syncProtocolStatus(p); err != nil {
87 return nil, errors.Wrap(err, p.Name(), "sync sub protocol status")
95 func (c *Chain) initChainStatus() error {
96 genesisBlock := config.GenesisBlock()
97 txStatus := bc.NewTransactionStatus()
98 for i := range genesisBlock.Transactions {
99 if err := txStatus.SetStatus(i, false); err != nil {
104 if err := c.store.SaveBlock(genesisBlock, txStatus); err != nil {
108 utxoView := state.NewUtxoViewpoint()
109 bcBlock := types.MapBlock(genesisBlock)
110 if err := utxoView.ApplyBlock(bcBlock, txStatus); err != nil {
114 consensusResults := []*state.ConsensusResult{&state.ConsensusResult{
116 NumOfVote: make(map[string]uint64),
117 CoinbaseReward: make(map[string]uint64),
118 BlockHash: genesisBlock.Hash(),
122 genesisBlockHeader := &genesisBlock.BlockHeader
123 return c.store.SaveChainStatus(genesisBlockHeader, genesisBlockHeader, []*types.BlockHeader{genesisBlockHeader}, utxoView, consensusResults)
126 // BestBlockHeight returns the current height of the blockchain.
127 func (c *Chain) BestBlockHeight() uint64 {
129 defer c.cond.L.Unlock()
130 return c.bestBlockHeader.Height
133 // BestBlockHash return the hash of the main chain tail block
134 func (c *Chain) BestBlockHash() *bc.Hash {
136 defer c.cond.L.Unlock()
137 bestHash := c.bestBlockHeader.Hash()
141 // LastIrreversibleHeader returns the chain last irreversible block header
142 func (c *Chain) LastIrreversibleHeader() *types.BlockHeader {
144 defer c.cond.L.Unlock()
145 return c.lastIrrBlockHeader
148 // BestBlockHeader returns the chain best block header
149 func (c *Chain) BestBlockHeader() *types.BlockHeader {
151 defer c.cond.L.Unlock()
152 return c.bestBlockHeader
155 // InMainChain checks wheather a block is in the main chain
156 func (c *Chain) InMainChain(hash bc.Hash) bool {
157 blockHeader, err := c.store.GetBlockHeader(&hash)
162 blockHash, err := c.store.GetMainChainHash(blockHeader.Height)
164 log.WithFields(log.Fields{"module": logModule, "height": blockHeader.Height}).Debug("not contain block hash in main chain for specified height")
167 return *blockHash == hash
170 func (c *Chain) SubProtocols() []Protocoler {
171 return c.subProtocols
174 // trace back to the tail of the chain from the given block header
175 func (c *Chain) traceLongestChainTail(blockHeader *types.BlockHeader) (*types.BlockHeader, error) {
176 longestTail, workQueue := blockHeader, []*types.BlockHeader{blockHeader}
178 for ; len(workQueue) > 0; workQueue = workQueue[1:] {
179 currentHeader := workQueue[0]
180 currentHash := currentHeader.Hash()
181 hashes, err := c.store.GetBlockHashesByHeight(currentHeader.Height + 1)
186 for _, h := range hashes {
187 if header, err := c.store.GetBlockHeader(h); err != nil {
189 } else if header.PreviousBlockHash == currentHash {
190 if longestTail.Height < header.Height {
193 workQueue = append(workQueue, header)
197 return longestTail, nil
200 func (c *Chain) hasSeenTx(tx *types.Tx) bool {
201 return c.knownTxs.Has(tx.ID.String())
204 func (c *Chain) markTransactions(txs ...*types.Tx) {
205 for _, tx := range txs {
206 c.knownTxs.Add(tx.ID.String())
210 func (c *Chain) syncProtocolStatus(subProtocol Protocoler) error {
211 protocolHeight, protocolHash, err := subProtocol.ChainStatus()
213 return errors.Wrap(err, "failed on get sub protocol status")
216 if *protocolHash == c.bestBlockHeader.Hash() {
220 for !c.InMainChain(*protocolHash) {
221 block, err := c.GetBlockByHash(protocolHash)
223 return errors.Wrap(err, subProtocol.Name(), "can't get block by hash in chain")
226 if err := subProtocol.DetachBlock(block); err != nil {
227 return errors.Wrap(err, subProtocol.Name(), "sub protocol detach block err")
230 protocolHeight, protocolHash = block.Height -1, &block.PreviousBlockHash
233 for height := protocolHeight + 1; height <= c.BestBlockHeight(); height++ {
234 block, err := c.GetBlockByHeight(height)
236 return errors.Wrap(err, subProtocol.Name(), "can't get block by height in chain")
239 if err := subProtocol.ApplyBlock(block); err != nil {
240 return errors.Wrap(err, subProtocol.Name(), "sub protocol apply block err")
243 blockHash := block.Hash()
244 protocolHeight, protocolHash = block.Height, &blockHash
250 // This function must be called with mu lock in above level
251 func (c *Chain) setState(blockHeader, irrBlockHeader *types.BlockHeader, mainBlockHeaders []*types.BlockHeader, view *state.UtxoViewpoint, consensusResults []*state.ConsensusResult) error {
252 if err := c.store.SaveChainStatus(blockHeader, irrBlockHeader, mainBlockHeaders, view, consensusResults); err != nil {
256 c.bestBlockHeader = blockHeader
257 c.lastIrrBlockHeader = irrBlockHeader
259 blockHash := blockHeader.Hash()
260 log.WithFields(log.Fields{"module": logModule, "height": blockHeader.Height, "hash": blockHash.String()}).Debug("chain best status has been update")
265 // BlockWaiter returns a channel that waits for the block at the given height.
266 func (c *Chain) BlockWaiter(height uint64) <-chan struct{} {
267 ch := make(chan struct{}, 1)
270 defer c.cond.L.Unlock()
271 for c.bestBlockHeader.Height < height {
280 // GetTxPool return chain txpool.
281 func (c *Chain) GetTxPool() *TxPool {