OSDN Git Service

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