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"
10 "github.com/bytom/protocol/validation"
14 // ErrBadBlock is returned when a block is invalid.
15 ErrBadBlock = errors.New("invalid block")
17 // ErrBadStateRoot is returned when the computed assets merkle root
18 // disagrees with the one declared in a block header.
19 ErrBadStateRoot = errors.New("invalid state merkle root")
22 // BlockExist check is a block in chain or orphan
23 func (c *Chain) BlockExist(hash *bc.Hash) bool {
24 return c.orphanManage.BlockExist(hash) || c.store.BlockExist(hash)
27 // GetBlockByHash return a block by given hash
28 func (c *Chain) GetBlockByHash(hash *bc.Hash) (*types.Block, error) {
29 return c.store.GetBlock(hash)
32 // GetBlockByHeight return a block by given height
33 func (c *Chain) GetBlockByHeight(height uint64) (*types.Block, error) {
35 hash, ok := c.state.mainChain[height]
36 c.state.cond.L.Unlock()
38 return nil, errors.New("can't find block in given hight")
40 return c.GetBlockByHash(hash)
43 // ConnectBlock append block to end of chain
44 func (c *Chain) ConnectBlock(block *types.Block) error {
46 defer c.state.cond.L.Unlock()
47 return c.connectBlock(block)
50 func (c *Chain) connectBlock(block *types.Block) (err error) {
51 bcBlock := types.MapBlock(block)
52 utxoView := state.NewUtxoViewpoint()
53 bcBlock.TransactionStatus, err = c.store.GetTransactionStatus(&bcBlock.ID)
58 if err := c.store.GetTransactionsUtxo(utxoView, bcBlock.Transactions); err != nil {
61 if err := utxoView.ApplyBlock(bcBlock, bcBlock.TransactionStatus); err != nil {
65 blockHash := block.Hash()
66 if err := c.setState(block, utxoView, map[uint64]*bc.Hash{block.Height: &blockHash}); err != nil {
70 for _, tx := range block.Transactions {
71 c.txPool.RemoveTransaction(&tx.Tx.ID)
76 func (c *Chain) getReorganizeBlocks(block *types.Block) ([]*types.Block, []*types.Block) {
77 attachBlocks := []*types.Block{}
78 detachBlocks := []*types.Block{}
81 for !c.inMainchain(ancestor) {
82 attachBlocks = append([]*types.Block{ancestor}, attachBlocks...)
83 ancestor, _ = c.GetBlockByHash(&ancestor.PreviousBlockHash)
86 for d := c.state.block; d.Hash() != ancestor.Hash(); d, _ = c.GetBlockByHash(&d.PreviousBlockHash) {
87 detachBlocks = append(detachBlocks, d)
90 return attachBlocks, detachBlocks
93 func (c *Chain) reorganizeChain(block *types.Block) error {
94 attachBlocks, detachBlocks := c.getReorganizeBlocks(block)
95 utxoView := state.NewUtxoViewpoint()
96 chainChanges := map[uint64]*bc.Hash{}
98 for _, d := range detachBlocks {
99 detachBlock := types.MapBlock(d)
100 if err := c.store.GetTransactionsUtxo(utxoView, detachBlock.Transactions); err != nil {
103 txStatus, err := c.GetTransactionStatus(&detachBlock.ID)
107 if err := utxoView.DetachBlock(detachBlock, txStatus); err != nil {
112 for _, a := range attachBlocks {
113 attachBlock := types.MapBlock(a)
114 if err := c.store.GetTransactionsUtxo(utxoView, attachBlock.Transactions); err != nil {
117 txStatus, err := c.GetTransactionStatus(&attachBlock.ID)
122 if err := utxoView.ApplyBlock(attachBlock, txStatus); err != nil {
125 chainChanges[a.Height] = &attachBlock.ID
128 return c.setState(block, utxoView, chainChanges)
131 // SaveBlock will validate and save block into storage
132 func (c *Chain) SaveBlock(block *types.Block) error {
133 preBlock, err := c.GetBlockByHash(&block.PreviousBlockHash)
138 blockEnts := types.MapBlock(block)
139 prevEnts := types.MapBlock(preBlock)
141 seed, err := c.GetSeed(block.Height, &block.PreviousBlockHash)
146 if err := validation.ValidateBlock(blockEnts, prevEnts, seed, c.store); err != nil {
147 return errors.Sub(ErrBadBlock, err)
150 if err := c.store.SaveBlock(block, blockEnts.TransactionStatus, seed); err != nil {
154 log.WithFields(log.Fields{"height": block.Height, "hash": block.Hash().String()}).Info("Block saved on disk")
158 func (c *Chain) findBestChainTail(block *types.Block) (bestBlock *types.Block) {
160 blockHash := block.Hash()
161 preorphans, ok := c.orphanManage.preOrphans[blockHash]
166 for _, preorphan := range preorphans {
167 orphanBlock, ok := c.orphanManage.Get(preorphan)
172 if err := c.SaveBlock(orphanBlock); err != nil {
173 log.WithFields(log.Fields{
174 "height": block.Height,
175 "hash": blockHash.String(),
176 }).Errorf("findBestChainTail fail on save block %v", err)
180 if subResult := c.findBestChainTail(orphanBlock); subResult.Height > bestBlock.Height {
181 bestBlock = subResult
185 c.orphanManage.Delete(&blockHash)
189 // ProcessBlock is the entry for handle block insert
190 func (c *Chain) ProcessBlock(block *types.Block) (bool, error) {
191 blockHash := block.Hash()
192 if c.BlockExist(&blockHash) {
193 log.WithField("hash", blockHash.String()).Info("Skip process due to block already been handled")
196 if !c.store.BlockExist(&block.PreviousBlockHash) {
197 c.orphanManage.Add(block)
200 if err := c.SaveBlock(block); err != nil {
204 bestBlock := c.findBestChainTail(block)
205 c.state.cond.L.Lock()
206 defer c.state.cond.L.Unlock()
207 if c.state.block.Hash() == bestBlock.PreviousBlockHash {
208 return false, c.connectBlock(bestBlock)
211 if bestBlock.Height > c.state.block.Height && bestBlock.Bits >= c.state.block.Bits {
212 return false, c.reorganizeChain(bestBlock)