OSDN Git Service

chmod -x for source files (#557)
[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 )
11
12 var (
13         // ErrBadBlock is returned when a block is invalid.
14         ErrBadBlock = errors.New("invalid block")
15
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.orphanManage.BlockExist(hash) || c.index.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 hight")
36         }
37         return c.store.GetBlock(&node.Hash)
38 }
39
40 // ConnectBlock append block to end of chain
41 func (c *Chain) ConnectBlock(block *types.Block) error {
42         c.state.cond.L.Lock()
43         defer c.state.cond.L.Unlock()
44         return c.connectBlock(block)
45 }
46
47 func (c *Chain) connectBlock(block *types.Block) (err error) {
48         bcBlock := types.MapBlock(block)
49         utxoView := state.NewUtxoViewpoint()
50         bcBlock.TransactionStatus, err = c.store.GetTransactionStatus(&bcBlock.ID)
51         if err != nil {
52                 return err
53         }
54
55         if err := c.store.GetTransactionsUtxo(utxoView, bcBlock.Transactions); err != nil {
56                 return err
57         }
58         if err := utxoView.ApplyBlock(bcBlock, bcBlock.TransactionStatus); err != nil {
59                 return err
60         }
61
62         if err := c.setState(block, utxoView); err != nil {
63                 return err
64         }
65
66         for _, tx := range block.Transactions {
67                 c.txPool.RemoveTransaction(&tx.Tx.ID)
68         }
69         return nil
70 }
71
72 func (c *Chain) getReorganizeBlocks(block *types.Block) ([]*types.Block, []*types.Block) {
73         attachBlocks := []*types.Block{}
74         detachBlocks := []*types.Block{}
75         ancestor := block
76
77         for !c.index.InMainchain(ancestor.Hash()) {
78                 attachBlocks = append([]*types.Block{ancestor}, attachBlocks...)
79                 ancestor, _ = c.GetBlockByHash(&ancestor.PreviousBlockHash)
80         }
81
82         for d, _ := c.store.GetBlock(c.state.hash); d.Hash() != ancestor.Hash(); d, _ = c.store.GetBlock(&d.PreviousBlockHash) {
83                 detachBlocks = append(detachBlocks, d)
84         }
85
86         return attachBlocks, detachBlocks
87 }
88
89 func (c *Chain) reorganizeChain(block *types.Block) error {
90         attachBlocks, detachBlocks := c.getReorganizeBlocks(block)
91         utxoView := state.NewUtxoViewpoint()
92
93         for _, d := range detachBlocks {
94                 detachBlock := types.MapBlock(d)
95                 if err := c.store.GetTransactionsUtxo(utxoView, detachBlock.Transactions); err != nil {
96                         return err
97                 }
98                 txStatus, err := c.GetTransactionStatus(&detachBlock.ID)
99                 if err != nil {
100                         return err
101                 }
102                 if err := utxoView.DetachBlock(detachBlock, txStatus); err != nil {
103                         return err
104                 }
105                 log.WithFields(log.Fields{"height": detachBlock.Height, "hash": detachBlock.ID.String()}).Debug("Detach from mainchain")
106         }
107
108         for _, a := range attachBlocks {
109                 attachBlock := types.MapBlock(a)
110                 if err := c.store.GetTransactionsUtxo(utxoView, attachBlock.Transactions); err != nil {
111                         return err
112                 }
113                 txStatus, err := c.GetTransactionStatus(&attachBlock.ID)
114                 if err != nil {
115                         return err
116                 }
117
118                 if err := utxoView.ApplyBlock(attachBlock, txStatus); err != nil {
119                         return err
120                 }
121                 log.WithFields(log.Fields{"height": attachBlock.Height, "hash": attachBlock.ID.String()}).Debug("Attach from mainchain")
122         }
123
124         return c.setState(block, utxoView)
125 }
126
127 // SaveBlock will validate and save block into storage
128 func (c *Chain) SaveBlock(block *types.Block) error {
129         blockEnts := types.MapBlock(block)
130         if err := c.validateBlock(blockEnts); err != nil {
131                 return errors.Sub(ErrBadBlock, err)
132         }
133
134         if err := c.store.SaveBlock(block, blockEnts.TransactionStatus); err != nil {
135                 return err
136         }
137         log.WithFields(log.Fields{"height": block.Height, "hash": blockEnts.ID.String()}).Info("Block saved on disk")
138
139         c.orphanManage.Delete(&blockEnts.ID)
140         parent := c.index.GetNode(&block.PreviousBlockHash)
141         node, err := NewBlockNode(&block.BlockHeader, parent)
142         if err != nil {
143                 return err
144         }
145
146         c.index.AddNode(node)
147         return nil
148 }
149
150 func (c *Chain) findBestChainTail(block *types.Block) (bestBlock *types.Block) {
151         bestBlock = block
152         blockHash := block.Hash()
153         preorphans, ok := c.orphanManage.preOrphans[blockHash]
154         if !ok {
155                 return
156         }
157
158         for _, preorphan := range preorphans {
159                 orphanBlock, ok := c.orphanManage.Get(preorphan)
160                 if !ok {
161                         continue
162                 }
163
164                 if err := c.SaveBlock(orphanBlock); err != nil {
165                         log.WithFields(log.Fields{
166                                 "height": block.Height,
167                                 "hash":   blockHash.String(),
168                         }).Errorf("findBestChainTail fail on save block %v", err)
169                         continue
170                 }
171
172                 if subResult := c.findBestChainTail(orphanBlock); subResult.Height > bestBlock.Height {
173                         bestBlock = subResult
174                 }
175         }
176
177         return
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 func (c *Chain) ProcessBlock(block *types.Block) (bool, error) {
191         reply := make(chan processBlockResponse, 1)
192         c.processBlockCh <- &processBlockMsg{block: block, reply: reply}
193         response := <-reply
194         return response.isOrphan, response.err
195 }
196
197 func (c *Chain) blockProcesser() {
198         for msg := range c.processBlockCh {
199                 isOrphan, err := c.processBlock(msg.block)
200                 msg.reply <- processBlockResponse{isOrphan: isOrphan, err: err}
201         }
202 }
203
204 // ProcessBlock is the entry for handle block insert
205 func (c *Chain) processBlock(block *types.Block) (bool, error) {
206         blockHash := block.Hash()
207         if c.BlockExist(&blockHash) {
208                 log.WithField("hash", blockHash.String()).Debug("Skip process due to block already been handled")
209                 return c.orphanManage.BlockExist(&blockHash), nil
210         }
211         if !c.store.BlockExist(&block.PreviousBlockHash) {
212                 log.WithField("hash", blockHash.String()).Debug("Add block to orphan manage")
213                 c.orphanManage.Add(block)
214                 return true, nil
215         }
216         if err := c.SaveBlock(block); err != nil {
217                 return false, err
218         }
219
220         bestBlock := c.findBestChainTail(block)
221         bestMainChain := c.index.BestNode()
222         bestBlockHash := bestBlock.Hash()
223         bestNode := c.index.GetNode(&bestBlockHash)
224
225         if bestNode.parent == bestMainChain {
226                 log.WithField("hash", blockHash.String()).Debug("Start to append block to the tail of mainchain")
227                 return false, c.connectBlock(bestBlock)
228         }
229
230         if bestNode.height > bestMainChain.height && bestNode.workSum.Cmp(bestMainChain.workSum) >= 0 {
231                 log.WithField("hash", blockHash.String()).Debug("Start to reorganize mainchain")
232                 return false, c.reorganizeChain(bestBlock)
233         }
234
235         return false, nil
236 }