4 log "github.com/sirupsen/logrus"
6 "github.com/bytom/errors"
7 "github.com/bytom/protocol/bc"
8 "github.com/bytom/protocol/bc/types"
9 "github.com/bytom/protocol/state"
13 // ErrBadBlock is returned when a block is invalid.
14 ErrBadBlock = errors.New("invalid block")
16 // ErrBadStateRoot is returned when the computed assets merkle root
17 // disagrees with the one declared in a block header.
18 ErrBadStateRoot = errors.New("invalid state merkle root")
21 // BlockExist check is a block in chain or orphan
22 func (c *Chain) BlockExist(hash *bc.Hash) bool {
23 return c.orphanManage.BlockExist(hash) || c.index.BlockExist(hash)
26 // GetBlockByHash return a block by given hash
27 func (c *Chain) GetBlockByHash(hash *bc.Hash) (*types.Block, error) {
28 return c.store.GetBlock(hash)
31 // GetBlockByHeight return a block by given height
32 func (c *Chain) GetBlockByHeight(height uint64) (*types.Block, error) {
33 node := c.index.NodeByHeight(height)
35 return nil, errors.New("can't find block in given hight")
37 return c.store.GetBlock(&node.Hash)
40 // ConnectBlock append block to end of chain
41 func (c *Chain) ConnectBlock(block *types.Block) error {
43 defer c.state.cond.L.Unlock()
44 return c.connectBlock(block)
47 func (c *Chain) connectBlock(block *types.Block) (err error) {
48 bcBlock := types.MapBlock(block)
49 utxoView := state.NewUtxoViewpoint()
50 bcBlock.TransactionStatus, err = c.store.GetTransactionStatus(&bcBlock.ID)
55 if err := c.store.GetTransactionsUtxo(utxoView, bcBlock.Transactions); err != nil {
58 if err := utxoView.ApplyBlock(bcBlock, bcBlock.TransactionStatus); err != nil {
62 if err := c.setState(block, utxoView); err != nil {
66 for _, tx := range block.Transactions {
67 c.txPool.RemoveTransaction(&tx.Tx.ID)
72 func (c *Chain) getReorganizeBlocks(block *types.Block) ([]*types.Block, []*types.Block) {
73 attachBlocks := []*types.Block{}
74 detachBlocks := []*types.Block{}
77 for !c.index.InMainchain(ancestor.Hash()) {
78 attachBlocks = append([]*types.Block{ancestor}, attachBlocks...)
79 ancestor, _ = c.GetBlockByHash(&ancestor.PreviousBlockHash)
82 for d, _ := c.store.GetBlock(c.state.hash); d.Hash() != ancestor.Hash(); d, _ = c.store.GetBlock(&d.PreviousBlockHash) {
83 detachBlocks = append(detachBlocks, d)
86 return attachBlocks, detachBlocks
89 func (c *Chain) reorganizeChain(block *types.Block) error {
90 attachBlocks, detachBlocks := c.getReorganizeBlocks(block)
91 utxoView := state.NewUtxoViewpoint()
93 for _, d := range detachBlocks {
94 detachBlock := types.MapBlock(d)
95 if err := c.store.GetTransactionsUtxo(utxoView, detachBlock.Transactions); err != nil {
98 txStatus, err := c.GetTransactionStatus(&detachBlock.ID)
102 if err := utxoView.DetachBlock(detachBlock, txStatus); err != nil {
105 log.WithFields(log.Fields{"height": detachBlock.Height, "hash": detachBlock.ID.String()}).Debug("Detach from mainchain")
108 for _, a := range attachBlocks {
109 attachBlock := types.MapBlock(a)
110 if err := c.store.GetTransactionsUtxo(utxoView, attachBlock.Transactions); err != nil {
113 txStatus, err := c.GetTransactionStatus(&attachBlock.ID)
118 if err := utxoView.ApplyBlock(attachBlock, txStatus); err != nil {
121 log.WithFields(log.Fields{"height": attachBlock.Height, "hash": attachBlock.ID.String()}).Debug("Attach from mainchain")
124 return c.setState(block, utxoView)
127 // SaveBlock will validate and save block into storage
128 func (c *Chain) SaveBlock(block *types.Block) error {
129 blockEnts := types.MapBlock(block)
130 if err := c.validateBlock(blockEnts); err != nil {
131 return errors.Sub(ErrBadBlock, err)
134 if err := c.store.SaveBlock(block, blockEnts.TransactionStatus); err != nil {
137 log.WithFields(log.Fields{"height": block.Height, "hash": blockEnts.ID.String()}).Info("Block saved on disk")
139 c.orphanManage.Delete(&blockEnts.ID)
140 parent := c.index.GetNode(&block.PreviousBlockHash)
141 node, err := NewBlockNode(&block.BlockHeader, parent)
146 c.index.AddNode(node)
150 func (c *Chain) findBestChainTail(block *types.Block) (bestBlock *types.Block) {
152 blockHash := block.Hash()
153 preorphans, ok := c.orphanManage.preOrphans[blockHash]
158 for _, preorphan := range preorphans {
159 orphanBlock, ok := c.orphanManage.Get(preorphan)
164 if err := c.SaveBlock(orphanBlock); err != nil {
165 log.WithFields(log.Fields{
166 "height": block.Height,
167 "hash": blockHash.String(),
168 }).Errorf("findBestChainTail fail on save block %v", err)
172 if subResult := c.findBestChainTail(orphanBlock); subResult.Height > bestBlock.Height {
173 bestBlock = subResult
180 type processBlockResponse struct {
185 type processBlockMsg struct {
187 reply chan processBlockResponse
190 func (c *Chain) ProcessBlock(block *types.Block) (bool, error) {
191 reply := make(chan processBlockResponse, 1)
192 c.processBlockCh <- &processBlockMsg{block: block, reply: reply}
194 return response.isOrphan, response.err
197 func (c *Chain) blockProcesser() {
198 for msg := range c.processBlockCh {
199 isOrphan, err := c.processBlock(msg.block)
200 msg.reply <- processBlockResponse{isOrphan: isOrphan, err: err}
204 // ProcessBlock is the entry for handle block insert
205 func (c *Chain) processBlock(block *types.Block) (bool, error) {
206 blockHash := block.Hash()
207 if c.BlockExist(&blockHash) {
208 log.WithField("hash", blockHash.String()).Debug("Skip process due to block already been handled")
209 return c.orphanManage.BlockExist(&blockHash), nil
211 if !c.store.BlockExist(&block.PreviousBlockHash) {
212 log.WithField("hash", blockHash.String()).Debug("Add block to orphan manage")
213 c.orphanManage.Add(block)
216 if err := c.SaveBlock(block); err != nil {
220 bestBlock := c.findBestChainTail(block)
221 bestMainChain := c.index.BestNode()
222 bestBlockHash := bestBlock.Hash()
223 bestNode := c.index.GetNode(&bestBlockHash)
225 if bestNode.parent == bestMainChain {
226 log.WithField("hash", blockHash.String()).Debug("Start to append block to the tail of mainchain")
227 return false, c.connectBlock(bestBlock)
230 if bestNode.height > bestMainChain.height && bestNode.workSum.Cmp(bestMainChain.workSum) >= 0 {
231 log.WithField("hash", blockHash.String()).Debug("Start to reorganize mainchain")
232 return false, c.reorganizeChain(bestBlock)