OSDN Git Service

Txpool upgrade (#327)
[bytom/bytom.git] / protocol / protocol.go
1 package protocol
2
3 import (
4         "context"
5         "sync"
6         "time"
7
8         "github.com/bytom/blockchain/txdb"
9         "github.com/bytom/blockchain/txdb/storage"
10         "github.com/bytom/errors"
11         "github.com/bytom/protocol/bc"
12         "github.com/bytom/protocol/bc/legacy"
13         "github.com/bytom/protocol/seed"
14         "github.com/bytom/protocol/state"
15 )
16
17 // maxCachedValidatedTxs is the max number of validated txs to cache.
18 const maxCachedValidatedTxs = 1000
19
20 var (
21         // ErrTheDistantFuture is returned when waiting for a blockheight
22         // too far in excess of the tip of the blockchain.
23         ErrTheDistantFuture = errors.New("block height too far in future")
24 )
25
26 // Store provides storage for blockchain data: blocks and state tree
27 // snapshots.
28 //
29 // Note, this is different from a state snapshot. A state snapshot
30 // provides access to the state at a given point in time -- outputs
31 // and issuance memory. The Chain type uses Store to load state
32 // from storage and persist validated data.
33 type Store interface {
34         BlockExist(*bc.Hash) bool
35
36         GetBlock(*bc.Hash) (*legacy.Block, error)
37         GetMainchain(*bc.Hash) (map[uint64]*bc.Hash, error)
38         GetStoreStatus() txdb.BlockStoreStateJSON
39         GetTransactionsUtxo(*state.UtxoViewpoint, []*bc.Tx) error
40         GetUtxo(*bc.Hash) (*storage.UtxoEntry, error)
41
42         SaveBlock(*legacy.Block) error
43         SaveChainStatus(*legacy.Block, *state.UtxoViewpoint, map[uint64]*bc.Hash) error
44 }
45
46 // OrphanManage is use to handle all the orphan block
47 type OrphanManage struct {
48         //TODO: add orphan cached block limit
49         orphan     map[bc.Hash]*legacy.Block
50         preOrphans map[bc.Hash][]*bc.Hash
51         mtx        sync.RWMutex
52 }
53
54 // NewOrphanManage return a new orphan block
55 func NewOrphanManage() *OrphanManage {
56         return &OrphanManage{
57                 orphan:     make(map[bc.Hash]*legacy.Block),
58                 preOrphans: make(map[bc.Hash][]*bc.Hash),
59         }
60 }
61
62 // BlockExist check is the block in OrphanManage
63 func (o *OrphanManage) BlockExist(hash *bc.Hash) bool {
64         o.mtx.RLock()
65         _, ok := o.orphan[*hash]
66         o.mtx.RUnlock()
67         return ok
68 }
69
70 // Add will add the block to OrphanManage
71 func (o *OrphanManage) Add(block *legacy.Block) {
72         blockHash := block.Hash()
73         o.mtx.Lock()
74         defer o.mtx.Unlock()
75
76         if _, ok := o.orphan[blockHash]; ok {
77                 return
78         }
79
80         o.orphan[blockHash] = block
81         o.preOrphans[block.PreviousBlockHash] = append(o.preOrphans[block.PreviousBlockHash], &blockHash)
82 }
83
84 // Delete will delelte the block from OrphanManage
85 func (o *OrphanManage) Delete(hash *bc.Hash) {
86         o.mtx.Lock()
87         defer o.mtx.Unlock()
88         block, ok := o.orphan[*hash]
89         if !ok {
90                 return
91         }
92         delete(o.orphan, *hash)
93
94         preOrphans, ok := o.preOrphans[block.PreviousBlockHash]
95         if !ok || len(preOrphans) == 1 {
96                 delete(o.preOrphans, block.PreviousBlockHash)
97                 return
98         }
99
100         for i, preOrphan := range preOrphans {
101                 if preOrphan == hash {
102                         o.preOrphans[block.PreviousBlockHash] = append(preOrphans[:i], preOrphans[i+1:]...)
103                         return
104                 }
105         }
106 }
107
108 // Get return the orphan block by hash
109 func (o *OrphanManage) Get(hash *bc.Hash) (*legacy.Block, bool) {
110         o.mtx.RLock()
111         block, ok := o.orphan[*hash]
112         o.mtx.RUnlock()
113         return block, ok
114 }
115
116 // Chain provides a complete, minimal blockchain database. It
117 // delegates the underlying storage to other objects, and uses
118 // validation logic from package validation to decide what
119 // objects can be safely stored.
120 type Chain struct {
121         InitialBlockHash  bc.Hash
122         MaxIssuanceWindow time.Duration // only used by generators
123
124         orphanManage *OrphanManage
125         txPool       *TxPool
126
127         state struct {
128                 cond      sync.Cond
129                 block     *legacy.Block
130                 hash      *bc.Hash
131                 mainChain map[uint64]*bc.Hash
132         }
133         store      Store
134         seedCaches *seed.SeedCaches
135 }
136
137 // NewChain returns a new Chain using store as the underlying storage.
138 func NewChain(initialBlockHash bc.Hash, store Store, txPool *TxPool) (*Chain, error) {
139         c := &Chain{
140                 InitialBlockHash: initialBlockHash,
141                 orphanManage:     NewOrphanManage(),
142                 store:            store,
143                 txPool:           txPool,
144                 seedCaches:       seed.NewSeedCaches(),
145         }
146         c.state.cond.L = new(sync.Mutex)
147         storeStatus := store.GetStoreStatus()
148
149         if storeStatus.Hash == nil {
150                 c.state.mainChain = make(map[uint64]*bc.Hash)
151                 return c, nil
152         }
153
154         c.state.hash = storeStatus.Hash
155         var err error
156         if c.state.block, err = store.GetBlock(storeStatus.Hash); err != nil {
157                 return nil, err
158         }
159         if c.state.mainChain, err = store.GetMainchain(storeStatus.Hash); err != nil {
160                 return nil, err
161         }
162         return c, nil
163 }
164
165 // Height returns the current height of the blockchain.
166 func (c *Chain) Height() uint64 {
167         c.state.cond.L.Lock()
168         defer c.state.cond.L.Unlock()
169         if c.state.block == nil {
170                 return 0
171         }
172         return c.state.block.Height
173 }
174
175 // BestBlockHash return the hash of the chain tail block
176 func (c *Chain) BestBlockHash() *bc.Hash {
177         c.state.cond.L.Lock()
178         defer c.state.cond.L.Unlock()
179         return c.state.hash
180 }
181
182 func (c *Chain) inMainchain(block *legacy.Block) bool {
183         hash, ok := c.state.mainChain[block.Height]
184         if !ok {
185                 return false
186         }
187         return *hash == block.Hash()
188 }
189
190 // InMainChain checks wheather a block is in the main chain
191 func (c *Chain) InMainChain(height uint64, hash bc.Hash) bool {
192         c.state.cond.L.Lock()
193         h, ok := c.state.mainChain[height]
194         c.state.cond.L.Unlock()
195         if !ok {
196                 return false
197         }
198
199         return *h == hash
200 }
201
202 // TimestampMS returns the latest known block timestamp.
203 func (c *Chain) Timestamp() uint64 {
204         c.state.cond.L.Lock()
205         defer c.state.cond.L.Unlock()
206         if c.state.block == nil {
207                 return 0
208         }
209         return c.state.block.Timestamp
210 }
211
212 // BestBlock returns the chain tail block
213 func (c *Chain) BestBlock() *legacy.Block {
214         c.state.cond.L.Lock()
215         defer c.state.cond.L.Unlock()
216         return c.state.block
217 }
218
219 // SeedCaches return the seedCached manager
220 func (c *Chain) SeedCaches() *seed.SeedCaches {
221         return c.seedCaches
222 }
223
224 // GetUtxo try to find the utxo status in db
225 func (c *Chain) GetUtxo(hash *bc.Hash) (*storage.UtxoEntry, error) {
226         return c.store.GetUtxo(hash)
227 }
228
229 // GetTransactionsUtxo return all the utxos that related to the txs' inputs
230 func (c *Chain) GetTransactionsUtxo(view *state.UtxoViewpoint, txs []*bc.Tx) error {
231         return c.store.GetTransactionsUtxo(view, txs)
232 }
233
234 // This function must be called with mu lock in above level
235 func (c *Chain) setState(block *legacy.Block, view *state.UtxoViewpoint, m map[uint64]*bc.Hash) error {
236         blockHash := block.Hash()
237         c.state.block = block
238         c.state.hash = &blockHash
239         for k, v := range m {
240                 c.state.mainChain[k] = v
241         }
242
243         if err := c.store.SaveChainStatus(block, view, c.state.mainChain); err != nil {
244                 return err
245         }
246
247         c.state.cond.Broadcast()
248         return nil
249 }
250
251 // BlockSoonWaiter returns a channel that
252 // waits for the block at the given height,
253 // but it is an error to wait for a block far in the future.
254 // WaitForBlockSoon will timeout if the context times out.
255 // To wait unconditionally, the caller should use WaitForBlock.
256 func (c *Chain) BlockSoonWaiter(ctx context.Context, height uint64) <-chan error {
257         ch := make(chan error, 1)
258
259         go func() {
260                 const slop = 3
261                 if height > c.Height()+slop {
262                         ch <- ErrTheDistantFuture
263                         return
264                 }
265
266                 select {
267                 case <-c.BlockWaiter(height):
268                         ch <- nil
269                 case <-ctx.Done():
270                         ch <- ctx.Err()
271                 }
272         }()
273
274         return ch
275 }
276
277 // BlockWaiter returns a channel that
278 // waits for the block at the given height.
279 func (c *Chain) BlockWaiter(height uint64) <-chan struct{} {
280         ch := make(chan struct{}, 1)
281         go func() {
282                 c.state.cond.L.Lock()
283                 defer c.state.cond.L.Unlock()
284                 for c.state.block.Height < height {
285                         c.state.cond.Wait()
286                 }
287                 ch <- struct{}{}
288         }()
289
290         return ch
291 }