4 log "github.com/sirupsen/logrus"
6 "github.com/bytom/errors"
7 "github.com/bytom/protocol/bc"
8 "github.com/bytom/protocol/bc/legacy"
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 // ErrStaleState is returned when the Chain does not have a current
19 ErrStaleState = errors.New("stale blockchain state")
21 // ErrBadStateRoot is returned when the computed assets merkle root
22 // disagrees with the one declared in a block header.
23 ErrBadStateRoot = errors.New("invalid state merkle root")
26 // BlockExist check is a block in chain or orphan
27 func (c *Chain) BlockExist(hash *bc.Hash) bool {
28 return c.orphanManage.BlockExist(hash) || c.store.BlockExist(hash)
31 // GetBlockByHash return a block by given hash
32 func (c *Chain) GetBlockByHash(hash *bc.Hash) (*legacy.Block, error) {
33 return c.store.GetBlock(hash)
36 // GetBlockByHeight return a block by given height
37 func (c *Chain) GetBlockByHeight(height uint64) (*legacy.Block, error) {
39 hash, ok := c.state.mainChain[height]
40 c.state.cond.L.Unlock()
42 return nil, errors.New("can't find block in given hight")
44 return c.GetBlockByHash(hash)
47 // ValidateBlock validates an incoming block in advance of applying it
48 // to a snapshot (with ApplyValidBlock) and committing it to the
49 // blockchain (with CommitAppliedBlock).
50 func (c *Chain) ValidateBlock(block, prev *legacy.Block) error {
51 blockEnts := legacy.MapBlock(block)
52 prevEnts := legacy.MapBlock(prev)
53 if err := validation.ValidateBlock(blockEnts, prevEnts); err != nil {
54 return errors.Sub(ErrBadBlock, err)
59 // ConnectBlock append block to end of chain
60 func (c *Chain) ConnectBlock(block *legacy.Block) error {
62 defer c.state.cond.L.Unlock()
63 return c.connectBlock(block)
66 func (c *Chain) connectBlock(block *legacy.Block) error {
67 newSnapshot := state.Copy(c.state.snapshot)
68 if err := newSnapshot.ApplyBlock(legacy.MapBlock(block)); err != nil {
72 blockHash := block.Hash()
73 if err := c.setState(block, newSnapshot, map[uint64]*bc.Hash{block.Height: &blockHash}); err != nil {
77 for _, tx := range block.Transactions {
78 c.txPool.RemoveTransaction(&tx.Tx.ID)
83 func (c *Chain) getReorganizeBlocks(block *legacy.Block) ([]*legacy.Block, []*legacy.Block) {
84 attachBlocks := []*legacy.Block{}
85 detachBlocks := []*legacy.Block{}
88 for !c.inMainchain(ancestor) {
89 attachBlocks = append([]*legacy.Block{ancestor}, attachBlocks...)
90 ancestor, _ = c.GetBlockByHash(&ancestor.PreviousBlockHash)
93 for d := c.state.block; d.Hash() != ancestor.Hash(); d, _ = c.GetBlockByHash(&d.PreviousBlockHash) {
94 detachBlocks = append(detachBlocks, d)
97 return attachBlocks, detachBlocks
100 func (c *Chain) reorganizeChain(block *legacy.Block) error {
101 attachBlocks, detachBlocks := c.getReorganizeBlocks(block)
102 newSnapshot := state.Copy(c.state.snapshot)
103 chainChanges := map[uint64]*bc.Hash{}
105 for _, d := range detachBlocks {
106 if err := newSnapshot.DetachBlock(legacy.MapBlock(d)); err != nil {
111 for _, a := range attachBlocks {
112 if err := newSnapshot.ApplyBlock(legacy.MapBlock(a)); err != nil {
116 chainChanges[a.Height] = &aHash
119 return c.setState(block, newSnapshot, chainChanges)
122 // SaveBlock will validate and save block into storage
123 func (c *Chain) SaveBlock(block *legacy.Block) error {
124 preBlock, _ := c.GetBlockByHash(&block.PreviousBlockHash)
125 if err := c.ValidateBlock(block, preBlock); err != nil {
128 if err := c.store.SaveBlock(block); err != nil {
131 blockHash := block.Hash()
132 log.WithFields(log.Fields{"height": block.Height, "hash": blockHash.String()}).Info("Block saved on disk")
136 func (c *Chain) findBestChainTail(block *legacy.Block) (bestBlock *legacy.Block) {
138 blockHash := block.Hash()
139 preorphans, ok := c.orphanManage.preOrphans[blockHash]
144 for _, preorphan := range preorphans {
145 orphanBlock, ok := c.orphanManage.Get(preorphan)
150 if err := c.SaveBlock(orphanBlock); err != nil {
151 log.WithFields(log.Fields{
152 "height": block.Height,
153 "hash": blockHash.String(),
154 }).Errorf("findBestChainTail fail on save block %v", err)
158 if subResult := c.findBestChainTail(orphanBlock); subResult.Height > bestBlock.Height {
159 bestBlock = subResult
163 c.orphanManage.Delete(&blockHash)
167 // ProcessBlock is the entry for handle block insert
168 func (c *Chain) ProcessBlock(block *legacy.Block) (bool, error) {
169 blockHash := block.Hash()
170 if c.BlockExist(&blockHash) {
171 log.WithField("hash", blockHash.String()).Info("Skip process due to block already been handled")
174 if !c.store.BlockExist(&block.PreviousBlockHash) {
175 c.orphanManage.Add(block)
178 if err := c.SaveBlock(block); err != nil {
182 bestBlock := c.findBestChainTail(block)
183 c.state.cond.L.Lock()
184 if c.state.block.Hash() == bestBlock.PreviousBlockHash {
185 defer c.state.cond.L.Unlock()
186 return false, c.connectBlock(bestBlock)
189 if bestBlock.Height > c.state.block.Height && bestBlock.Bits >= c.state.block.Bits {
190 defer c.state.cond.L.Unlock()
191 return false, c.reorganizeChain(bestBlock)
193 c.state.cond.L.Unlock()