OSDN Git Service

Utxo storage (#196)
[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.Height == 0 {
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         if height == 0 {
193                 return true
194         }
195
196         c.state.cond.L.Lock()
197         h, ok := c.state.mainChain[height]
198         c.state.cond.L.Unlock()
199         if !ok {
200                 return false
201         }
202
203         return *h == hash
204 }
205
206 // TimestampMS returns the latest known block timestamp.
207 func (c *Chain) TimestampMS() uint64 {
208         c.state.cond.L.Lock()
209         defer c.state.cond.L.Unlock()
210         if c.state.block == nil {
211                 return 0
212         }
213         return c.state.block.TimestampMS
214 }
215
216 // BestBlock returns the chain tail block
217 func (c *Chain) BestBlock() *legacy.Block {
218         c.state.cond.L.Lock()
219         defer c.state.cond.L.Unlock()
220         return c.state.block
221 }
222
223 // SeedCaches return the seedCached manager
224 func (c *Chain) SeedCaches() *seed.SeedCaches {
225         return c.seedCaches
226 }
227
228 // GetUtxo try to find the utxo status in db
229 func (c *Chain) GetUtxo(hash *bc.Hash) (*storage.UtxoEntry, error) {
230         return c.store.GetUtxo(hash)
231 }
232
233 // GetTransactionsUtxo return all the utxos that related to the txs' inputs
234 func (c *Chain) GetTransactionsUtxo(view *state.UtxoViewpoint, txs []*bc.Tx) error {
235         return c.store.GetTransactionsUtxo(view, txs)
236 }
237
238 // This function must be called with mu lock in above level
239 func (c *Chain) setState(block *legacy.Block, view *state.UtxoViewpoint, m map[uint64]*bc.Hash) error {
240         blockHash := block.Hash()
241         c.state.block = block
242         c.state.hash = &blockHash
243         for k, v := range m {
244                 c.state.mainChain[k] = v
245         }
246
247         if err := c.store.SaveChainStatus(block, view, c.state.mainChain); err != nil {
248                 return err
249         }
250
251         c.state.cond.Broadcast()
252         return nil
253 }
254
255 // BlockSoonWaiter returns a channel that
256 // waits for the block at the given height,
257 // but it is an error to wait for a block far in the future.
258 // WaitForBlockSoon will timeout if the context times out.
259 // To wait unconditionally, the caller should use WaitForBlock.
260 func (c *Chain) BlockSoonWaiter(ctx context.Context, height uint64) <-chan error {
261         ch := make(chan error, 1)
262
263         go func() {
264                 const slop = 3
265                 if height > c.Height()+slop {
266                         ch <- ErrTheDistantFuture
267                         return
268                 }
269
270                 select {
271                 case <-c.BlockWaiter(height):
272                         ch <- nil
273                 case <-ctx.Done():
274                         ch <- ctx.Err()
275                 }
276         }()
277
278         return ch
279 }
280
281 // BlockWaiter returns a channel that
282 // waits for the block at the given height.
283 func (c *Chain) BlockWaiter(height uint64) <-chan struct{} {
284         ch := make(chan struct{}, 1)
285         go func() {
286                 c.state.cond.L.Lock()
287                 defer c.state.cond.L.Unlock()
288                 for c.state.block.Height < height {
289                         c.state.cond.Wait()
290                 }
291                 ch <- struct{}{}
292         }()
293
294         return ch
295 }