OSDN Git Service

improve some code (#482)
[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
17         // ErrBadStateRoot is returned when the computed assets merkle root
18         // disagrees with the one declared in a block header.
19         ErrBadStateRoot = errors.New("invalid state merkle root")
20 )
21
22 // BlockExist check is a block in chain or orphan
23 func (c *Chain) BlockExist(hash *bc.Hash) bool {
24         return c.orphanManage.BlockExist(hash) || c.store.BlockExist(hash)
25 }
26
27 // GetBlockByHash return a block by given hash
28 func (c *Chain) GetBlockByHash(hash *bc.Hash) (*types.Block, error) {
29         return c.store.GetBlock(hash)
30 }
31
32 // GetBlockByHeight return a block by given height
33 func (c *Chain) GetBlockByHeight(height uint64) (*types.Block, error) {
34         c.state.cond.L.Lock()
35         hash, ok := c.state.mainChain[height]
36         c.state.cond.L.Unlock()
37         if !ok {
38                 return nil, errors.New("can't find block in given hight")
39         }
40         return c.GetBlockByHash(hash)
41 }
42
43 // ConnectBlock append block to end of chain
44 func (c *Chain) ConnectBlock(block *types.Block) error {
45         c.state.cond.L.Lock()
46         defer c.state.cond.L.Unlock()
47         return c.connectBlock(block)
48 }
49
50 func (c *Chain) connectBlock(block *types.Block) (err error) {
51         bcBlock := types.MapBlock(block)
52         utxoView := state.NewUtxoViewpoint()
53         bcBlock.TransactionStatus, err = c.store.GetTransactionStatus(&bcBlock.ID)
54         if err != nil {
55                 return err
56         }
57
58         if err := c.store.GetTransactionsUtxo(utxoView, bcBlock.Transactions); err != nil {
59                 return err
60         }
61         if err := utxoView.ApplyBlock(bcBlock, bcBlock.TransactionStatus); err != nil {
62                 return err
63         }
64
65         blockHash := block.Hash()
66         if err := c.setState(block, utxoView, map[uint64]*bc.Hash{block.Height: &blockHash}); err != nil {
67                 return err
68         }
69
70         for _, tx := range block.Transactions {
71                 c.txPool.RemoveTransaction(&tx.Tx.ID)
72         }
73         return nil
74 }
75
76 func (c *Chain) getReorganizeBlocks(block *types.Block) ([]*types.Block, []*types.Block) {
77         attachBlocks := []*types.Block{}
78         detachBlocks := []*types.Block{}
79         ancestor := block
80
81         for !c.inMainchain(ancestor) {
82                 attachBlocks = append([]*types.Block{ancestor}, attachBlocks...)
83                 ancestor, _ = c.GetBlockByHash(&ancestor.PreviousBlockHash)
84         }
85
86         for d := c.state.block; d.Hash() != ancestor.Hash(); d, _ = c.GetBlockByHash(&d.PreviousBlockHash) {
87                 detachBlocks = append(detachBlocks, d)
88         }
89
90         return attachBlocks, detachBlocks
91 }
92
93 func (c *Chain) reorganizeChain(block *types.Block) error {
94         attachBlocks, detachBlocks := c.getReorganizeBlocks(block)
95         utxoView := state.NewUtxoViewpoint()
96         chainChanges := map[uint64]*bc.Hash{}
97
98         for _, d := range detachBlocks {
99                 detachBlock := types.MapBlock(d)
100                 if err := c.store.GetTransactionsUtxo(utxoView, detachBlock.Transactions); err != nil {
101                         return err
102                 }
103                 txStatus, err := c.GetTransactionStatus(&detachBlock.ID)
104                 if err != nil {
105                         return err
106                 }
107                 if err := utxoView.DetachBlock(detachBlock, txStatus); err != nil {
108                         return err
109                 }
110         }
111
112         for _, a := range attachBlocks {
113                 attachBlock := types.MapBlock(a)
114                 if err := c.store.GetTransactionsUtxo(utxoView, attachBlock.Transactions); err != nil {
115                         return err
116                 }
117                 txStatus, err := c.GetTransactionStatus(&attachBlock.ID)
118                 if err != nil {
119                         return err
120                 }
121
122                 if err := utxoView.ApplyBlock(attachBlock, txStatus); err != nil {
123                         return err
124                 }
125                 chainChanges[a.Height] = &attachBlock.ID
126         }
127
128         return c.setState(block, utxoView, chainChanges)
129 }
130
131 // SaveBlock will validate and save block into storage
132 func (c *Chain) SaveBlock(block *types.Block) error {
133         preBlock, err := c.GetBlockByHash(&block.PreviousBlockHash)
134         if err != nil {
135                 return err
136         }
137
138         blockEnts := types.MapBlock(block)
139         prevEnts := types.MapBlock(preBlock)
140
141         seed, err := c.GetSeed(block.Height, &block.PreviousBlockHash)
142         if err != nil {
143                 return err
144         }
145
146         if err := validation.ValidateBlock(blockEnts, prevEnts, seed, c.store); err != nil {
147                 return errors.Sub(ErrBadBlock, err)
148         }
149
150         if err := c.store.SaveBlock(block, blockEnts.TransactionStatus, seed); err != nil {
151                 return err
152         }
153
154         log.WithFields(log.Fields{"height": block.Height, "hash": block.Hash().String()}).Info("Block saved on disk")
155         return nil
156 }
157
158 func (c *Chain) findBestChainTail(block *types.Block) (bestBlock *types.Block) {
159         bestBlock = block
160         blockHash := block.Hash()
161         preorphans, ok := c.orphanManage.preOrphans[blockHash]
162         if !ok {
163                 return
164         }
165
166         for _, preorphan := range preorphans {
167                 orphanBlock, ok := c.orphanManage.Get(preorphan)
168                 if !ok {
169                         continue
170                 }
171
172                 if err := c.SaveBlock(orphanBlock); err != nil {
173                         log.WithFields(log.Fields{
174                                 "height": block.Height,
175                                 "hash":   blockHash.String(),
176                         }).Errorf("findBestChainTail fail on save block %v", err)
177                         continue
178                 }
179
180                 if subResult := c.findBestChainTail(orphanBlock); subResult.Height > bestBlock.Height {
181                         bestBlock = subResult
182                 }
183         }
184
185         c.orphanManage.Delete(&blockHash)
186         return
187 }
188
189 // ProcessBlock is the entry for handle block insert
190 func (c *Chain) ProcessBlock(block *types.Block) (bool, error) {
191         blockHash := block.Hash()
192         if c.BlockExist(&blockHash) {
193                 log.WithField("hash", blockHash.String()).Info("Skip process due to block already been handled")
194                 return false, nil
195         }
196         if !c.store.BlockExist(&block.PreviousBlockHash) {
197                 c.orphanManage.Add(block)
198                 return true, nil
199         }
200         if err := c.SaveBlock(block); err != nil {
201                 return false, err
202         }
203
204         bestBlock := c.findBestChainTail(block)
205         c.state.cond.L.Lock()
206         defer c.state.cond.L.Unlock()
207         if c.state.block.Hash() == bestBlock.PreviousBlockHash {
208                 return false, c.connectBlock(bestBlock)
209         }
210
211         if bestBlock.Height > c.state.block.Height && bestBlock.Bits >= c.state.block.Bits {
212                 return false, c.reorganizeChain(bestBlock)
213         }
214
215         return false, nil
216 }