OSDN Git Service

modify import path (#1805)
[bytom/bytom.git] / protocol / block.go
1 package protocol
2
3 import (
4         log "github.com/sirupsen/logrus"
5
6         "github.com/bytom/bytom/errors"
7         "github.com/bytom/bytom/protocol/bc"
8         "github.com/bytom/bytom/protocol/bc/types"
9         "github.com/bytom/bytom/protocol/state"
10         "github.com/bytom/bytom/protocol/validation"
11 )
12
13 var (
14         // ErrBadBlock is returned when a block is invalid.
15         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")
19 )
20
21 // BlockExist check is a block in chain or orphan
22 func (c *Chain) BlockExist(hash *bc.Hash) bool {
23         return c.index.BlockExist(hash) || c.orphanManage.BlockExist(hash)
24 }
25
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)
29 }
30
31 // GetBlockByHeight return a block header by given height
32 func (c *Chain) GetBlockByHeight(height uint64) (*types.Block, error) {
33         node := c.index.NodeByHeight(height)
34         if node == nil {
35                 return nil, errors.New("can't find block in given height")
36         }
37         return c.store.GetBlock(&node.Hash)
38 }
39
40 // GetHeaderByHash return a block header by given hash
41 func (c *Chain) GetHeaderByHash(hash *bc.Hash) (*types.BlockHeader, error) {
42         node := c.index.GetNode(hash)
43         if node == nil {
44                 return nil, errors.New("can't find block header in given hash")
45         }
46         return node.BlockHeader(), nil
47 }
48
49 // GetHeaderByHeight return a block header by given height
50 func (c *Chain) GetHeaderByHeight(height uint64) (*types.BlockHeader, error) {
51         node := c.index.NodeByHeight(height)
52         if node == nil {
53                 return nil, errors.New("can't find block header in given height")
54         }
55         return node.BlockHeader(), nil
56 }
57
58 func (c *Chain) calcReorganizeNodes(node *state.BlockNode) ([]*state.BlockNode, []*state.BlockNode) {
59         var attachNodes []*state.BlockNode
60         var detachNodes []*state.BlockNode
61
62         attachNode := node
63         for c.index.NodeByHeight(attachNode.Height) != attachNode {
64                 attachNodes = append([]*state.BlockNode{attachNode}, attachNodes...)
65                 attachNode = attachNode.Parent
66         }
67
68         detachNode := c.bestNode
69         for detachNode != attachNode {
70                 detachNodes = append(detachNodes, detachNode)
71                 detachNode = detachNode.Parent
72         }
73         return attachNodes, detachNodes
74 }
75
76 func (c *Chain) connectBlock(block *types.Block) (err error) {
77         bcBlock := types.MapBlock(block)
78         if bcBlock.TransactionStatus, err = c.store.GetTransactionStatus(&bcBlock.ID); err != nil {
79                 return err
80         }
81
82         utxoView := state.NewUtxoViewpoint()
83         if err := c.store.GetTransactionsUtxo(utxoView, bcBlock.Transactions); err != nil {
84                 return err
85         }
86         if err := utxoView.ApplyBlock(bcBlock, bcBlock.TransactionStatus); err != nil {
87                 return err
88         }
89
90         node := c.index.GetNode(&bcBlock.ID)
91         if err := c.setState(node, utxoView); err != nil {
92                 return err
93         }
94
95         for _, tx := range block.Transactions {
96                 c.txPool.RemoveTransaction(&tx.Tx.ID)
97         }
98         return nil
99 }
100
101 func (c *Chain) reorganizeChain(node *state.BlockNode) error {
102         attachNodes, detachNodes := c.calcReorganizeNodes(node)
103         utxoView := state.NewUtxoViewpoint()
104
105         txsToRestore := map[bc.Hash]*types.Tx{}
106         for _, detachNode := range detachNodes {
107                 b, err := c.store.GetBlock(&detachNode.Hash)
108                 if err != nil {
109                         return err
110                 }
111
112                 detachBlock := types.MapBlock(b)
113                 if err := c.store.GetTransactionsUtxo(utxoView, detachBlock.Transactions); err != nil {
114                         return err
115                 }
116                 txStatus, err := c.GetTransactionStatus(&detachBlock.ID)
117                 if err != nil {
118                         return err
119                 }
120                 if err := utxoView.DetachBlock(detachBlock, txStatus); err != nil {
121                         return err
122                 }
123
124                 for _, tx := range b.Transactions {
125                         txsToRestore[tx.ID] = tx
126                 }
127                 log.WithFields(log.Fields{"module": logModule, "height": node.Height, "hash": node.Hash.String()}).Debug("detach from mainchain")
128         }
129
130         txsToRemove := map[bc.Hash]*types.Tx{}
131         for _, attachNode := range attachNodes {
132                 b, err := c.store.GetBlock(&attachNode.Hash)
133                 if err != nil {
134                         return err
135                 }
136
137                 attachBlock := types.MapBlock(b)
138                 if err := c.store.GetTransactionsUtxo(utxoView, attachBlock.Transactions); err != nil {
139                         return err
140                 }
141                 txStatus, err := c.GetTransactionStatus(&attachBlock.ID)
142                 if err != nil {
143                         return err
144                 }
145                 if err := utxoView.ApplyBlock(attachBlock, txStatus); err != nil {
146                         return err
147                 }
148
149                 for _, tx := range b.Transactions {
150                         if _, ok := txsToRestore[tx.ID]; !ok {
151                                 txsToRemove[tx.ID] = tx
152                         } else {
153                                 delete(txsToRestore, tx.ID)
154                         }
155                 }
156
157                 log.WithFields(log.Fields{"module": logModule, "height": node.Height, "hash": node.Hash.String()}).Debug("attach from mainchain")
158         }
159
160         if err := c.setState(node, utxoView); err != nil {
161                 return err
162         }
163
164         for txHash := range txsToRemove {
165                 c.txPool.RemoveTransaction(&txHash)
166         }
167
168         for _, tx := range txsToRestore {
169                 // the number of restored Tx should be very small or most of time ZERO
170                 // Error returned from validation is ignored, tx could still be lost if validation fails.
171                 // TODO: adjust tx timestamp so that it won't starve in pool.
172                 if _, err := c.ValidateTx(tx); err != nil {
173                         log.WithFields(log.Fields{"module": logModule, "tx_id": tx.Tx.ID.String(), "error": err}).Info("restore tx fail")
174                 }
175         }
176
177         if len(txsToRestore) > 0 {
178                 log.WithFields(log.Fields{"module": logModule, "num": len(txsToRestore)}).Debug("restore txs back to pool")
179         }
180
181         return nil
182 }
183
184 // SaveBlock will validate and save block into storage
185 func (c *Chain) saveBlock(block *types.Block) error {
186         bcBlock := types.MapBlock(block)
187         parent := c.index.GetNode(&block.PreviousBlockHash)
188
189         if err := validation.ValidateBlock(bcBlock, parent); err != nil {
190                 return errors.Sub(ErrBadBlock, err)
191         }
192         if err := c.store.SaveBlock(block, bcBlock.TransactionStatus); err != nil {
193                 return err
194         }
195
196         c.orphanManage.Delete(&bcBlock.ID)
197         node, err := state.NewBlockNode(&block.BlockHeader, parent)
198         if err != nil {
199                 return err
200         }
201
202         c.index.AddNode(node)
203         return nil
204 }
205
206 func (c *Chain) saveSubBlock(block *types.Block) *types.Block {
207         blockHash := block.Hash()
208         prevOrphans, ok := c.orphanManage.GetPrevOrphans(&blockHash)
209         if !ok {
210                 return block
211         }
212
213         bestBlock := block
214         for _, prevOrphan := range prevOrphans {
215                 orphanBlock, ok := c.orphanManage.Get(prevOrphan)
216                 if !ok {
217                         log.WithFields(log.Fields{"module": logModule, "hash": prevOrphan.String()}).Warning("saveSubBlock fail to get block from orphanManage")
218                         continue
219                 }
220                 if err := c.saveBlock(orphanBlock); err != nil {
221                         log.WithFields(log.Fields{"module": logModule, "hash": prevOrphan.String(), "height": orphanBlock.Height}).Warning("saveSubBlock fail to save block")
222                         continue
223                 }
224
225                 if subBestBlock := c.saveSubBlock(orphanBlock); subBestBlock.Height > bestBlock.Height {
226                         bestBlock = subBestBlock
227                 }
228         }
229         return bestBlock
230 }
231
232 type processBlockResponse struct {
233         isOrphan bool
234         err      error
235 }
236
237 type processBlockMsg struct {
238         block *types.Block
239         reply chan processBlockResponse
240 }
241
242 // ProcessBlock is the entry for chain update
243 func (c *Chain) ProcessBlock(block *types.Block) (bool, error) {
244         reply := make(chan processBlockResponse, 1)
245         c.processBlockCh <- &processBlockMsg{block: block, reply: reply}
246         response := <-reply
247         return response.isOrphan, response.err
248 }
249
250 func (c *Chain) blockProcesser() {
251         for msg := range c.processBlockCh {
252                 isOrphan, err := c.processBlock(msg.block)
253                 msg.reply <- processBlockResponse{isOrphan: isOrphan, err: err}
254         }
255 }
256
257 // ProcessBlock is the entry for handle block insert
258 func (c *Chain) processBlock(block *types.Block) (bool, error) {
259         blockHash := block.Hash()
260         if c.BlockExist(&blockHash) {
261                 log.WithFields(log.Fields{"module": logModule, "hash": blockHash.String(), "height": block.Height}).Info("block has been processed")
262                 return c.orphanManage.BlockExist(&blockHash), nil
263         }
264
265         if parent := c.index.GetNode(&block.PreviousBlockHash); parent == nil {
266                 c.orphanManage.Add(block)
267                 return true, nil
268         }
269
270         if err := c.saveBlock(block); err != nil {
271                 return false, err
272         }
273
274         bestBlock := c.saveSubBlock(block)
275         bestBlockHash := bestBlock.Hash()
276         bestNode := c.index.GetNode(&bestBlockHash)
277
278         if bestNode.Parent == c.bestNode {
279                 log.WithFields(log.Fields{"module": logModule}).Debug("append block to the end of mainchain")
280                 return false, c.connectBlock(bestBlock)
281         }
282
283         if bestNode.Height > c.bestNode.Height && bestNode.WorkSum.Cmp(c.bestNode.WorkSum) >= 0 {
284                 log.WithFields(log.Fields{"module": logModule}).Debug("start to reorganize chain")
285                 return false, c.reorganizeChain(bestNode)
286         }
287         return false, nil
288 }