OSDN Git Service

2672acc03f058440e610bd99f6dc7cfa230c5e43
[bytom/vapor.git] / protocol / block.go
1 package protocol
2
3 import (
4         log "github.com/sirupsen/logrus"
5
6         "github.com/vapor/errors"
7         "github.com/vapor/protocol/bc"
8         "github.com/vapor/protocol/bc/types"
9         "github.com/vapor/protocol/state"
10         "github.com/vapor/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         node := c.index.GetNode(&bcBlock.ID)
90         if err := c.setState(node, utxoView); err != nil {
91                 return err
92         }
93         for _, tx := range block.Transactions {
94                 for key, value := range tx.Entries {
95                         switch value.(type) {
96                         case *bc.Claim:
97                                 c.store.SetWithdrawSpent(&key)
98                         default:
99                                 continue
100                         }
101                 }
102                 c.txPool.RemoveTransaction(&tx.Tx.ID)
103         }
104         return nil
105 }
106
107 func (c *Chain) reorganizeChain(node *state.BlockNode) error {
108         attachNodes, detachNodes := c.calcReorganizeNodes(node)
109         utxoView := state.NewUtxoViewpoint()
110
111         for _, detachNode := range detachNodes {
112                 b, err := c.store.GetBlock(&detachNode.Hash)
113                 if err != nil {
114                         return err
115                 }
116
117                 detachBlock := types.MapBlock(b)
118                 if err := c.store.GetTransactionsUtxo(utxoView, detachBlock.Transactions); err != nil {
119                         return err
120                 }
121                 txStatus, err := c.GetTransactionStatus(&detachBlock.ID)
122                 if err != nil {
123                         return err
124                 }
125                 if err := utxoView.DetachBlock(detachBlock, txStatus); err != nil {
126                         return err
127                 }
128
129                 log.WithFields(log.Fields{"height": node.Height, "hash": node.Hash.String()}).Debug("detach from mainchain")
130         }
131
132         for _, attachNode := range attachNodes {
133                 b, err := c.store.GetBlock(&attachNode.Hash)
134                 if err != nil {
135                         return err
136                 }
137
138                 attachBlock := types.MapBlock(b)
139                 if err := c.store.GetTransactionsUtxo(utxoView, attachBlock.Transactions); err != nil {
140                         return err
141                 }
142                 txStatus, err := c.GetTransactionStatus(&attachBlock.ID)
143                 if err != nil {
144                         return err
145                 }
146                 if err := utxoView.ApplyBlock(attachBlock, txStatus); err != nil {
147                         return err
148                 }
149
150                 log.WithFields(log.Fields{"height": node.Height, "hash": node.Hash.String()}).Debug("attach from mainchain")
151         }
152
153         return c.setState(node, utxoView)
154 }
155
156 // SaveBlock will validate and save block into storage
157 func (c *Chain) saveBlock(block *types.Block) error {
158         bcBlock := types.MapBlock(block)
159         parent := c.index.GetNode(&block.PreviousBlockHash)
160
161         if err := validation.ValidateBlock(bcBlock, parent, block, c.Authoritys, c.position); err != nil {
162                 return errors.Sub(ErrBadBlock, err)
163         }
164         if err := c.store.SaveBlock(block, bcBlock.TransactionStatus); err != nil {
165                 return err
166         }
167
168         c.orphanManage.Delete(&bcBlock.ID)
169         node, err := state.NewBlockNode(&block.BlockHeader, parent)
170         if err != nil {
171                 return err
172         }
173
174         c.index.AddNode(node)
175         return nil
176 }
177
178 func (c *Chain) saveSubBlock(block *types.Block) *types.Block {
179         blockHash := block.Hash()
180         prevOrphans, ok := c.orphanManage.GetPrevOrphans(&blockHash)
181         if !ok {
182                 return block
183         }
184
185         bestBlock := block
186         for _, prevOrphan := range prevOrphans {
187                 orphanBlock, ok := c.orphanManage.Get(prevOrphan)
188                 if !ok {
189                         log.WithFields(log.Fields{"hash": prevOrphan.String()}).Warning("saveSubBlock fail to get block from orphanManage")
190                         continue
191                 }
192                 if err := c.saveBlock(orphanBlock); err != nil {
193                         log.WithFields(log.Fields{"hash": prevOrphan.String(), "height": orphanBlock.Height}).Warning("saveSubBlock fail to save block")
194                         continue
195                 }
196
197                 if subBestBlock := c.saveSubBlock(orphanBlock); subBestBlock.Height > bestBlock.Height {
198                         bestBlock = subBestBlock
199                 }
200         }
201         return bestBlock
202 }
203
204 type processBlockResponse struct {
205         isOrphan bool
206         err      error
207 }
208
209 type processBlockMsg struct {
210         block *types.Block
211         reply chan processBlockResponse
212 }
213
214 // ProcessBlock is the entry for chain update
215 func (c *Chain) ProcessBlock(block *types.Block) (bool, error) {
216         reply := make(chan processBlockResponse, 1)
217         c.processBlockCh <- &processBlockMsg{block: block, reply: reply}
218         response := <-reply
219         return response.isOrphan, response.err
220 }
221
222 func (c *Chain) blockProcesser() {
223         for msg := range c.processBlockCh {
224                 isOrphan, err := c.processBlock(msg.block)
225                 msg.reply <- processBlockResponse{isOrphan: isOrphan, err: err}
226         }
227 }
228
229 // ProcessBlock is the entry for handle block insert
230 func (c *Chain) processBlock(block *types.Block) (bool, error) {
231         blockHash := block.Hash()
232         if c.BlockExist(&blockHash) {
233                 log.WithFields(log.Fields{"hash": blockHash.String(), "height": block.Height}).Info("block has been processed")
234                 return c.orphanManage.BlockExist(&blockHash), nil
235         }
236
237         if parent := c.index.GetNode(&block.PreviousBlockHash); parent == nil {
238                 c.orphanManage.Add(block)
239                 return true, nil
240         }
241
242         if err := c.saveBlock(block); err != nil {
243                 return false, err
244         }
245
246         bestBlock := c.saveSubBlock(block)
247         bestBlockHash := bestBlock.Hash()
248         bestNode := c.index.GetNode(&bestBlockHash)
249
250         if bestNode.Parent == c.bestNode {
251                 log.Debug("append block to the end of mainchain")
252                 return false, c.connectBlock(bestBlock)
253         }
254
255         if bestNode.Height > c.bestNode.Height && bestNode.WorkSum.Cmp(c.bestNode.WorkSum) >= 0 {
256                 log.Debug("start to reorganize chain")
257                 return false, c.reorganizeChain(bestNode)
258         }
259         return false, nil
260 }