OSDN Git Service

872ef4fb6dc3a951f36f0a1e4639303db6af7b68
[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/types"
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         // 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 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 func (c *Chain) calcReorganizeNodes(node *state.BlockNode) ([]*state.BlockNode, []*state.BlockNode) {
41         var attachNodes []*state.BlockNode
42         var detachNodes []*state.BlockNode
43
44         attachNode := node
45         for c.index.NodeByHeight(attachNode.Height) != attachNode {
46                 attachNodes = append([]*state.BlockNode{attachNode}, attachNodes...)
47                 attachNode = attachNode.Parent
48         }
49
50         detachNode := c.bestNode
51         for detachNode != attachNode {
52                 detachNodes = append(detachNodes, detachNode)
53                 detachNode = detachNode.Parent
54         }
55         return attachNodes, detachNodes
56 }
57
58 func (c *Chain) connectBlock(block *types.Block) (err error) {
59         bcBlock := types.MapBlock(block)
60         if bcBlock.TransactionStatus, err = c.store.GetTransactionStatus(&bcBlock.ID); err != nil {
61                 return err
62         }
63
64         utxoView := state.NewUtxoViewpoint()
65         if err := c.store.GetTransactionsUtxo(utxoView, bcBlock.Transactions); err != nil {
66                 return err
67         }
68         if err := utxoView.ApplyBlock(bcBlock, bcBlock.TransactionStatus); err != nil {
69                 return err
70         }
71
72         node := c.index.GetNode(&bcBlock.ID)
73         if err := c.setState(node, utxoView); 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) reorganizeChain(node *state.BlockNode) error {
84         attachNodes, detachNodes := c.calcReorganizeNodes(node)
85         utxoView := state.NewUtxoViewpoint()
86
87         for _, detachNode := range detachNodes {
88                 b, err := c.store.GetBlock(&detachNode.Hash)
89                 if err != nil {
90                         return err
91                 }
92
93                 detachBlock := types.MapBlock(b)
94                 if err := c.store.GetTransactionsUtxo(utxoView, detachBlock.Transactions); err != nil {
95                         return err
96                 }
97                 txStatus, err := c.GetTransactionStatus(&detachBlock.ID)
98                 if err != nil {
99                         return err
100                 }
101                 if err := utxoView.DetachBlock(detachBlock, txStatus); err != nil {
102                         return err
103                 }
104
105                 log.WithFields(log.Fields{"height": node.Height, "hash": node.Hash.String()}).Debug("detach from mainchain")
106         }
107
108         for _, attachNode := range attachNodes {
109                 b, err := c.store.GetBlock(&attachNode.Hash)
110                 if err != nil {
111                         return err
112                 }
113
114                 attachBlock := types.MapBlock(b)
115                 if err := c.store.GetTransactionsUtxo(utxoView, attachBlock.Transactions); err != nil {
116                         return err
117                 }
118                 txStatus, err := c.GetTransactionStatus(&attachBlock.ID)
119                 if err != nil {
120                         return err
121                 }
122                 if err := utxoView.ApplyBlock(attachBlock, txStatus); err != nil {
123                         return err
124                 }
125
126                 log.WithFields(log.Fields{"height": node.Height, "hash": node.Hash.String()}).Debug("attach from mainchain")
127         }
128
129         return c.setState(node, utxoView)
130 }
131
132 // SaveBlock will validate and save block into storage
133 func (c *Chain) saveBlock(block *types.Block) error {
134         bcBlock := types.MapBlock(block)
135         parent := c.index.GetNode(&block.PreviousBlockHash)
136
137         if err := validation.ValidateBlock(bcBlock, parent); err != nil {
138                 return errors.Sub(ErrBadBlock, err)
139         }
140         if err := c.store.SaveBlock(block, bcBlock.TransactionStatus); err != nil {
141                 return err
142         }
143
144         c.orphanManage.Delete(&bcBlock.ID)
145         node, err := state.NewBlockNode(&block.BlockHeader, parent)
146         if err != nil {
147                 return err
148         }
149
150         c.index.AddNode(node)
151         return nil
152 }
153
154 func (c *Chain) saveSubBlock(block *types.Block) *types.Block {
155         blockHash := block.Hash()
156         prevOrphans, ok := c.orphanManage.GetPrevOrphans(&blockHash)
157         if !ok {
158                 return block
159         }
160
161         bestBlock := block
162         for _, prevOrphan := range prevOrphans {
163                 orphanBlock, ok := c.orphanManage.Get(prevOrphan)
164                 if !ok {
165                         log.WithFields(log.Fields{"hash": prevOrphan.String()}).Warning("saveSubBlock fail to get block from orphanManage")
166                         continue
167                 }
168                 if err := c.saveBlock(orphanBlock); err != nil {
169                         log.WithFields(log.Fields{"hash": prevOrphan.String(), "height": orphanBlock.Height}).Warning("saveSubBlock fail to save block")
170                         continue
171                 }
172
173                 if subBestBlock := c.saveSubBlock(orphanBlock); subBestBlock.Height > bestBlock.Height {
174                         bestBlock = subBestBlock
175                 }
176         }
177         return bestBlock
178 }
179
180 type processBlockResponse struct {
181         isOrphan bool
182         err      error
183 }
184
185 type processBlockMsg struct {
186         block *types.Block
187         reply chan processBlockResponse
188 }
189
190 // ProcessBlock is the entry for chain update
191 func (c *Chain) ProcessBlock(block *types.Block) (bool, error) {
192         reply := make(chan processBlockResponse, 1)
193         c.processBlockCh <- &processBlockMsg{block: block, reply: reply}
194         response := <-reply
195         return response.isOrphan, response.err
196 }
197
198 func (c *Chain) blockProcesser() {
199         for msg := range c.processBlockCh {
200                 isOrphan, err := c.processBlock(msg.block)
201                 msg.reply <- processBlockResponse{isOrphan: isOrphan, err: err}
202         }
203 }
204
205 // ProcessBlock is the entry for handle block insert
206 func (c *Chain) processBlock(block *types.Block) (bool, error) {
207         blockHash := block.Hash()
208         if c.BlockExist(&blockHash) {
209                 log.WithFields(log.Fields{"hash": blockHash.String(), "height": block.Height}).Info("block has been processed")
210                 return c.orphanManage.BlockExist(&blockHash), nil
211         }
212
213         if parent := c.index.GetNode(&block.PreviousBlockHash); parent == nil {
214                 c.orphanManage.Add(block)
215                 return true, nil
216         }
217
218         if err := c.saveBlock(block); err != nil {
219                 return false, err
220         }
221
222         bestBlock := c.saveSubBlock(block)
223         bestBlockHash := bestBlock.Hash()
224         bestNode := c.index.GetNode(&bestBlockHash)
225
226         if bestNode.Parent == c.bestNode {
227                 log.Debug("append block to the end of mainchain")
228                 return false, c.connectBlock(bestBlock)
229         }
230
231         if bestNode.Height > c.bestNode.Height && bestNode.WorkSum.Cmp(c.bestNode.WorkSum) >= 0 {
232                 log.Debug("start to reorganize chain")
233                 return false, c.reorganizeChain(bestNode)
234         }
235         return false, nil
236 }