OSDN Git Service

modify the chain structure for support orphan block
[bytom/bytom.git] / protocol / block.go
1 package protocol
2
3 import (
4         "time"
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 // maxBlockTxs limits the number of transactions
14 // included in each block.
15 const maxBlockTxs = 10000
16
17 // saveSnapshotFrequency stores how often to save a state
18 // snapshot to the Store.
19 const saveSnapshotFrequency = time.Hour
20
21 var (
22         // ErrBadBlock is returned when a block is invalid.
23         ErrBadBlock = errors.New("invalid block")
24
25         // ErrStaleState is returned when the Chain does not have a current
26         // blockchain state.
27         ErrStaleState = errors.New("stale blockchain state")
28
29         // ErrBadStateRoot is returned when the computed assets merkle root
30         // disagrees with the one declared in a block header.
31         ErrBadStateRoot = errors.New("invalid state merkle root")
32 )
33
34 // GetBlock returns the block at the given height, if there is one,
35 // otherwise it returns an error.
36 func (c *Chain) GetBlock(hash *bc.Hash) (*legacy.Block, error) {
37         return c.store.GetBlock(hash)
38 }
39
40 func (c *Chain) GetBlockByHeight(height uint64) (*legacy.Block, error) {
41         hash, ok := c.state.mainChain[height]
42         if !ok {
43                 return nil, nil
44         }
45         return c.GetBlock(hash)
46 }
47
48 // ValidateBlock validates an incoming block in advance of applying it
49 // to a snapshot (with ApplyValidBlock) and committing it to the
50 // blockchain (with CommitAppliedBlock).
51 func (c *Chain) ValidateBlock(block, prev *legacy.Block) error {
52         blockEnts := legacy.MapBlock(block)
53         prevEnts := legacy.MapBlock(prev)
54         err := validation.ValidateBlock(blockEnts, prevEnts)
55         if err != nil {
56                 return errors.Sub(ErrBadBlock, err)
57         }
58         return errors.Sub(ErrBadBlock, err)
59 }
60
61 // ApplyValidBlock creates an updated snapshot without validating the
62 // block.
63 func (c *Chain) ConnectBlock(block *legacy.Block) error {
64         newSnapshot := state.Copy(c.state.snapshot)
65         if err := newSnapshot.ApplyBlock(legacy.MapBlock(block)); err != nil {
66                 return err
67         }
68         if block.AssetsMerkleRoot != newSnapshot.Tree.RootHash() {
69                 return ErrBadStateRoot
70         }
71
72         blockHash := block.Hash()
73         if err := c.store.SaveSnapshot(newSnapshot, block.Height, &blockHash); err != nil {
74                 return err
75         }
76         c.state.mainChain[block.Height] = &blockHash
77         if err := c.store.SaveMainchain(c.state.mainChain, block.Height, &blockHash); err != nil {
78                 delete(c.state.mainChain, block.Height)
79                 return err
80         }
81         c.state.snapshot = newSnapshot
82         c.store.SaveStoreStatus(block.Height, &blockHash)
83
84         for _, tx := range block.Transactions {
85                 c.txPool.RemoveTransaction(&tx.Tx.ID)
86         }
87         return nil
88 }
89
90 func (c *Chain) getReorganizeBlocks(block *legacy.Block) ([]*legacy.Block, []*legacy.Block) {
91         attachBlocks := []*legacy.Block{}
92         detachBlocks := []*legacy.Block{}
93
94         ancestor := block
95         for ancestor, ok := c.orphanManage.Get(&ancestor.PreviousBlockHash); ok; {
96                 if c.InMainchain(ancestor) {
97                         break
98                 }
99                 attachBlocks = append([]*legacy.Block{ancestor}, attachBlocks...)
100         }
101
102         for n := c.state.block; n != nil; n, _ = c.GetBlock(&n.PreviousBlockHash) {
103                 if n.Hash() == ancestor.Hash() {
104                         break
105                 }
106                 detachBlocks = append(detachBlocks, n)
107         }
108
109         return attachBlocks, detachBlocks
110 }
111
112 func (c *Chain) AddOrphan(block *legacy.Block) error {
113         attachBlocks, detachBlocks := c.getReorganizeBlocks(block)
114         newSnapshot := state.Copy(c.state.snapshot)
115
116         for _, detachBlock := range detachBlocks {
117                 if err := newSnapshot.DetachBlock(legacy.MapBlock(detachBlock)); err != nil {
118                         return err
119                 }
120         }
121
122         for _, attachBlock := range attachBlocks {
123                 if err := newSnapshot.ApplyBlock(legacy.MapBlock(attachBlock)); err != nil {
124                         return err
125                 }
126         }
127
128         blockHash := block.Hash()
129         if err := c.store.SaveSnapshot(newSnapshot, block.Height, &blockHash); err != nil {
130                 return err
131         }
132         for _, attachBlock := range attachBlocks {
133                 attachBlockHash := attachBlock.Hash()
134                 c.state.mainChain[attachBlock.Height] = &attachBlockHash
135                 c.orphanManage.Delete(&attachBlockHash)
136         }
137         c.state.mainChain[block.Height] = &blockHash
138         if err := c.store.SaveMainchain(c.state.mainChain, block.Height, &blockHash); err != nil {
139                 delete(c.state.mainChain, block.Height)
140                 return err
141         }
142         c.state.snapshot = newSnapshot
143         c.store.SaveStoreStatus(block.Height, &blockHash)
144         return nil
145 }
146
147 func (c *Chain) AddBlock(block *legacy.Block) (bool, error) {
148         blockHash := block.Hash()
149         if c.orphanManage.BlockExist(&blockHash) || c.store.BlockExist(&blockHash) {
150                 return c.InMainchain(block), nil
151         }
152
153         if !c.store.BlockExist(&block.PreviousBlockHash) {
154                 c.orphanManage.Add(block)
155                 return true, nil
156         }
157
158         preBlock, err := c.GetBlock(&block.PreviousBlockHash)
159         if err != nil {
160                 return false, err
161         }
162
163         if err := c.ValidateBlock(block, preBlock); err != nil {
164                 return false, err
165         }
166         c.store.SaveBlock(block)
167
168         if *c.state.mainChain[preBlock.Height] == block.PreviousBlockHash {
169                 return false, c.ConnectBlock(block)
170         }
171
172         if block.Bits > c.state.block.Bits {
173                 return true, c.AddOrphan(block)
174         }
175         return true, nil
176 }
177
178 func (c *Chain) setHeight(h uint64) {
179         // We call setHeight from two places independently:
180         // CommitBlock and the Postgres LISTEN goroutine.
181         // This means we can get here twice for each block,
182         // and any of them might be arbitrarily delayed,
183         // which means h might be from the past.
184         // Detect and discard these duplicate calls.
185
186         c.state.cond.L.Lock()
187         defer c.state.cond.L.Unlock()
188
189         if h <= c.state.height {
190                 return
191         }
192         c.state.height = h
193         c.state.cond.Broadcast()
194 }