OSDN Git Service

Refactor accountutxos DB rollback when orphan block rollback (#102)
[bytom/bytom.git] / protocol / block.go
1 package protocol
2
3 import (
4         log "github.com/sirupsen/logrus"
5
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"
11 )
12
13 var (
14         // ErrBadBlock is returned when a block is invalid.
15         ErrBadBlock = errors.New("invalid block")
16
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")
20 )
21
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)
25 }
26
27 // GetBlockByHash return a block by given hash
28 func (c *Chain) GetBlockByHash(hash *bc.Hash) (*legacy.Block, error) {
29         return c.store.GetBlock(hash)
30 }
31
32 // GetBlockByHeight return a block by given height
33 func (c *Chain) GetBlockByHeight(height uint64) (*legacy.Block, error) {
34         c.state.cond.L.Lock()
35         hash, ok := c.state.mainChain[height]
36         c.state.cond.L.Unlock()
37         if !ok {
38                 return nil, errors.New("can't find block in given hight")
39         }
40         return c.GetBlockByHash(hash)
41 }
42
43 // ValidateBlock validates an incoming block in advance of applying it
44 // to a snapshot (with ApplyValidBlock) and committing it to the
45 // blockchain (with CommitAppliedBlock).
46 func (c *Chain) ValidateBlock(block, prev *legacy.Block) error {
47         blockEnts := legacy.MapBlock(block)
48         prevEnts := legacy.MapBlock(prev)
49         if err := validation.ValidateBlock(blockEnts, prevEnts); err != nil {
50                 return errors.Sub(ErrBadBlock, err)
51         }
52         return nil
53 }
54
55 // ConnectBlock append block to end of chain
56 func (c *Chain) ConnectBlock(block *legacy.Block) error {
57         c.state.cond.L.Lock()
58         defer c.state.cond.L.Unlock()
59         return c.connectBlock(block)
60 }
61
62 func (c *Chain) connectBlock(block *legacy.Block) error {
63         newSnapshot := state.Copy(c.state.snapshot)
64         if err := newSnapshot.ApplyBlock(legacy.MapBlock(block)); err != nil {
65                 return err
66         }
67
68         blockHash := block.Hash()
69         if err := c.setState(block, newSnapshot, map[uint64]*bc.Hash{block.Height: &blockHash}); err != nil {
70                 return err
71         }
72
73         for _, tx := range block.Transactions {
74                 c.txPool.RemoveTransaction(&tx.Tx.ID)
75         }
76         return nil
77 }
78
79 func (c *Chain) getReorganizeBlocks(block *legacy.Block) ([]*legacy.Block, []*legacy.Block) {
80         attachBlocks := []*legacy.Block{}
81         detachBlocks := []*legacy.Block{}
82         ancestor := block
83
84         for !c.inMainchain(ancestor) {
85                 attachBlocks = append([]*legacy.Block{ancestor}, attachBlocks...)
86                 ancestor, _ = c.GetBlockByHash(&ancestor.PreviousBlockHash)
87         }
88
89         for d := c.state.block; d.Hash() != ancestor.Hash(); d, _ = c.GetBlockByHash(&d.PreviousBlockHash) {
90                 detachBlocks = append(detachBlocks, d)
91         }
92
93         return attachBlocks, detachBlocks
94 }
95
96 func (c *Chain) reorganizeChain(block *legacy.Block) error {
97         attachBlocks, detachBlocks := c.getReorganizeBlocks(block)
98         newSnapshot := state.Copy(c.state.snapshot)
99         chainChanges := map[uint64]*bc.Hash{}
100
101         for _, d := range detachBlocks {
102                 if err := newSnapshot.DetachBlock(legacy.MapBlock(d)); err != nil {
103                         return err
104                 }
105         }
106
107         for _, a := range attachBlocks {
108                 if err := newSnapshot.ApplyBlock(legacy.MapBlock(a)); err != nil {
109                         return err
110                 }
111                 aHash := a.Hash()
112                 chainChanges[a.Height] = &aHash
113         }
114
115         return c.setState(block, newSnapshot, chainChanges)
116 }
117
118 // SaveBlock will validate and save block into storage
119 func (c *Chain) SaveBlock(block *legacy.Block) error {
120         preBlock, _ := c.GetBlockByHash(&block.PreviousBlockHash)
121         if err := c.ValidateBlock(block, preBlock); err != nil {
122                 return err
123         }
124         if err := c.store.SaveBlock(block); err != nil {
125                 return err
126         }
127         blockHash := block.Hash()
128         log.WithFields(log.Fields{"height": block.Height, "hash": blockHash.String()}).Info("Block saved on disk")
129         return nil
130 }
131
132 func (c *Chain) findBestChainTail(block *legacy.Block) (bestBlock *legacy.Block) {
133         bestBlock = block
134         blockHash := block.Hash()
135         preorphans, ok := c.orphanManage.preOrphans[blockHash]
136         if !ok {
137                 return
138         }
139
140         for _, preorphan := range preorphans {
141                 orphanBlock, ok := c.orphanManage.Get(preorphan)
142                 if !ok {
143                         continue
144                 }
145
146                 if err := c.SaveBlock(orphanBlock); err != nil {
147                         log.WithFields(log.Fields{
148                                 "height": block.Height,
149                                 "hash":   blockHash.String(),
150                         }).Errorf("findBestChainTail fail on save block %v", err)
151                         continue
152                 }
153
154                 if subResult := c.findBestChainTail(orphanBlock); subResult.Height > bestBlock.Height {
155                         bestBlock = subResult
156                 }
157         }
158
159         c.orphanManage.Delete(&blockHash)
160         return
161 }
162
163 // ProcessBlock is the entry for handle block insert
164 func (c *Chain) ProcessBlock(block *legacy.Block) (bool, error) {
165         blockHash := block.Hash()
166         if c.BlockExist(&blockHash) {
167                 log.WithField("hash", blockHash.String()).Info("Skip process due to block already been handled")
168                 return false, nil
169         }
170         if !c.store.BlockExist(&block.PreviousBlockHash) {
171                 c.orphanManage.Add(block)
172                 return true, nil
173         }
174         if err := c.SaveBlock(block); err != nil {
175                 return false, err
176         }
177
178         bestBlock := c.findBestChainTail(block)
179         c.state.cond.L.Lock()
180         defer c.state.cond.L.Unlock()
181         if c.state.block.Hash() == bestBlock.PreviousBlockHash {
182                 return false, c.connectBlock(bestBlock)
183         }
184
185         if bestBlock.Height > c.state.block.Height && bestBlock.Bits >= c.state.block.Bits {
186                 return false, c.reorganizeChain(bestBlock)
187         }
188
189         return false, nil
190 }