OSDN Git Service

Utxo storage (#196)
[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/legacy"
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) (*legacy.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) (*legacy.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 // ValidateBlock validates an incoming block in advance of applying it
44 // to a snapshot (with ApplyValidBlock) and committing it to the
45 // blockchain (with CommitAppliedBlock).
46 func (c *Chain) ValidateBlock(block, prev *legacy.Block) error {
47         blockEnts := legacy.MapBlock(block)
48         prevEnts := legacy.MapBlock(prev)
49         if err := validation.ValidateBlock(blockEnts, prevEnts, c.seedCaches); err != nil {
50                 return errors.Sub(ErrBadBlock, err)
51         }
52         return nil
53 }
54
55 // ConnectBlock append block to end of chain
56 func (c *Chain) ConnectBlock(block *legacy.Block) error {
57         c.state.cond.L.Lock()
58         defer c.state.cond.L.Unlock()
59         return c.connectBlock(block)
60 }
61
62 func (c *Chain) connectBlock(block *legacy.Block) error {
63         bcBlock := legacy.MapBlock(block)
64         utxoView := state.NewUtxoViewpoint()
65
66         if err := c.store.GetTransactionsUtxo(utxoView, bcBlock.Transactions); err != nil {
67                 return err
68         }
69         if err := utxoView.ApplyBlock(bcBlock); err != nil {
70                 return err
71         }
72
73         blockHash := block.Hash()
74         if err := c.setState(block, utxoView, map[uint64]*bc.Hash{block.Height: &blockHash}); err != nil {
75                 return err
76         }
77
78         for _, tx := range block.Transactions {
79                 c.txPool.RemoveTransaction(&tx.Tx.ID)
80         }
81         return nil
82 }
83
84 func (c *Chain) getReorganizeBlocks(block *legacy.Block) ([]*legacy.Block, []*legacy.Block) {
85         attachBlocks := []*legacy.Block{}
86         detachBlocks := []*legacy.Block{}
87         ancestor := block
88
89         for !c.inMainchain(ancestor) {
90                 attachBlocks = append([]*legacy.Block{ancestor}, attachBlocks...)
91                 ancestor, _ = c.GetBlockByHash(&ancestor.PreviousBlockHash)
92         }
93
94         for d := c.state.block; d.Hash() != ancestor.Hash(); d, _ = c.GetBlockByHash(&d.PreviousBlockHash) {
95                 detachBlocks = append(detachBlocks, d)
96         }
97
98         return attachBlocks, detachBlocks
99 }
100
101 func (c *Chain) reorganizeChain(block *legacy.Block) error {
102         attachBlocks, detachBlocks := c.getReorganizeBlocks(block)
103         utxoView := state.NewUtxoViewpoint()
104         chainChanges := map[uint64]*bc.Hash{}
105
106         for _, d := range detachBlocks {
107                 detachBlock := legacy.MapBlock(d)
108                 if err := c.store.GetTransactionsUtxo(utxoView, detachBlock.Transactions); err != nil {
109                         return err
110                 }
111                 if err := utxoView.DetachBlock(detachBlock); err != nil {
112                         return err
113                 }
114         }
115
116         for _, a := range attachBlocks {
117                 attachBlock := legacy.MapBlock(a)
118                 if err := c.store.GetTransactionsUtxo(utxoView, attachBlock.Transactions); err != nil {
119                         return err
120                 }
121                 if err := utxoView.ApplyBlock(attachBlock); err != nil {
122                         return err
123                 }
124                 aHash := a.Hash()
125                 chainChanges[a.Height] = &aHash
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 *legacy.Block) error {
133         preBlock, _ := c.GetBlockByHash(&block.PreviousBlockHash)
134         if err := c.ValidateBlock(block, preBlock); err != nil {
135                 return err
136         }
137         if err := c.store.SaveBlock(block); err != nil {
138                 return err
139         }
140         blockHash := block.Hash()
141         log.WithFields(log.Fields{"height": block.Height, "hash": blockHash.String()}).Info("Block saved on disk")
142         return nil
143 }
144
145 func (c *Chain) findBestChainTail(block *legacy.Block) (bestBlock *legacy.Block) {
146         bestBlock = block
147         blockHash := block.Hash()
148         preorphans, ok := c.orphanManage.preOrphans[blockHash]
149         if !ok {
150                 return
151         }
152
153         for _, preorphan := range preorphans {
154                 orphanBlock, ok := c.orphanManage.Get(preorphan)
155                 if !ok {
156                         continue
157                 }
158
159                 if err := c.SaveBlock(orphanBlock); err != nil {
160                         log.WithFields(log.Fields{
161                                 "height": block.Height,
162                                 "hash":   blockHash.String(),
163                         }).Errorf("findBestChainTail fail on save block %v", err)
164                         continue
165                 }
166
167                 if subResult := c.findBestChainTail(orphanBlock); subResult.Height > bestBlock.Height {
168                         bestBlock = subResult
169                 }
170         }
171
172         c.orphanManage.Delete(&blockHash)
173         return
174 }
175
176 // ProcessBlock is the entry for handle block insert
177 func (c *Chain) ProcessBlock(block *legacy.Block) (bool, error) {
178         blockHash := block.Hash()
179         if c.BlockExist(&blockHash) {
180                 log.WithField("hash", blockHash.String()).Info("Skip process due to block already been handled")
181                 return false, nil
182         }
183         if !c.store.BlockExist(&block.PreviousBlockHash) {
184                 c.orphanManage.Add(block)
185                 return true, nil
186         }
187         if err := c.SaveBlock(block); err != nil {
188                 return false, err
189         }
190
191         bestBlock := c.findBestChainTail(block)
192         c.state.cond.L.Lock()
193         defer c.state.cond.L.Unlock()
194         if c.state.block.Hash() == bestBlock.PreviousBlockHash {
195                 return false, c.connectBlock(bestBlock)
196         }
197
198         if bestBlock.Height > c.state.block.Height && bestBlock.Bits >= c.state.block.Bits {
199                 return false, c.reorganizeChain(bestBlock)
200         }
201
202         return false, nil
203 }