OSDN Git Service

chain.state using block.height instead of height (#82)
[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         // ErrStaleState is returned when the Chain does not have a current
18         // blockchain state.
19         ErrStaleState = errors.New("stale blockchain state")
20
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")
24 )
25
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)
29 }
30
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)
34 }
35
36 // GetBlockByHeight return a block by given height
37 func (c *Chain) GetBlockByHeight(height uint64) (*legacy.Block, error) {
38         c.state.cond.L.Lock()
39         hash, ok := c.state.mainChain[height]
40         c.state.cond.L.Unlock()
41         if !ok {
42                 return nil, errors.New("can't find block in given hight")
43         }
44         return c.GetBlockByHash(hash)
45 }
46
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)
55         }
56         return nil
57 }
58
59 // ConnectBlock append block to end of chain
60 func (c *Chain) ConnectBlock(block *legacy.Block) error {
61         c.state.cond.L.Lock()
62         defer c.state.cond.L.Unlock()
63         return c.connectBlock(block)
64 }
65
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 {
69                 return err
70         }
71
72         blockHash := block.Hash()
73         if err := c.setState(block, newSnapshot, map[uint64]*bc.Hash{block.Height: &blockHash}); err != nil {
74                 return err
75         }
76
77         for _, tx := range block.Transactions {
78                 c.txPool.RemoveTransaction(&tx.Tx.ID)
79         }
80         return nil
81 }
82
83 func (c *Chain) getReorganizeBlocks(block *legacy.Block) ([]*legacy.Block, []*legacy.Block) {
84         attachBlocks := []*legacy.Block{}
85         detachBlocks := []*legacy.Block{}
86         ancestor := block
87
88         for !c.inMainchain(ancestor) {
89                 attachBlocks = append([]*legacy.Block{ancestor}, attachBlocks...)
90                 ancestor, _ = c.GetBlockByHash(&ancestor.PreviousBlockHash)
91         }
92
93         for d := c.state.block; d.Hash() != ancestor.Hash(); d, _ = c.GetBlockByHash(&d.PreviousBlockHash) {
94                 detachBlocks = append(detachBlocks, d)
95         }
96
97         return attachBlocks, detachBlocks
98 }
99
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{}
104
105         for _, d := range detachBlocks {
106                 if err := newSnapshot.DetachBlock(legacy.MapBlock(d)); err != nil {
107                         return err
108                 }
109         }
110
111         for _, a := range attachBlocks {
112                 if err := newSnapshot.ApplyBlock(legacy.MapBlock(a)); err != nil {
113                         return err
114                 }
115                 aHash := a.Hash()
116                 chainChanges[a.Height] = &aHash
117         }
118
119         return c.setState(block, newSnapshot, chainChanges)
120 }
121
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 {
126                 return err
127         }
128         if err := c.store.SaveBlock(block); err != nil {
129                 return err
130         }
131         blockHash := block.Hash()
132         log.WithFields(log.Fields{"height": block.Height, "hash": blockHash.String()}).Info("Block saved on disk")
133         return nil
134 }
135
136 func (c *Chain) findBestChainTail(block *legacy.Block) (bestBlock *legacy.Block) {
137         bestBlock = block
138         blockHash := block.Hash()
139         preorphans, ok := c.orphanManage.preOrphans[blockHash]
140         if !ok {
141                 return
142         }
143
144         for _, preorphan := range preorphans {
145                 orphanBlock, ok := c.orphanManage.Get(preorphan)
146                 if !ok {
147                         continue
148                 }
149
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)
155                         continue
156                 }
157
158                 if subResult := c.findBestChainTail(orphanBlock); subResult.Height > bestBlock.Height {
159                         bestBlock = subResult
160                 }
161         }
162
163         c.orphanManage.Delete(&blockHash)
164         return
165 }
166
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")
172                 return false, nil
173         }
174         if !c.store.BlockExist(&block.PreviousBlockHash) {
175                 c.orphanManage.Add(block)
176                 return true, nil
177         }
178         if err := c.SaveBlock(block); err != nil {
179                 return false, err
180         }
181
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)
187         }
188
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)
192         }
193         c.state.cond.L.Unlock()
194         return false, nil
195 }