OSDN Git Service

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