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"
17 // maxCachedValidatedTxs is the max number of validated txs to cache.
18 const maxCachedValidatedTxs = 1000
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")
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
34 // NewOrphanManage return a new orphan block
35 func NewOrphanManage() *OrphanManage {
37 orphan: make(map[bc.Hash]*types.Block),
38 preOrphans: make(map[bc.Hash][]*bc.Hash),
42 // BlockExist check is the block in OrphanManage
43 func (o *OrphanManage) BlockExist(hash *bc.Hash) bool {
45 _, ok := o.orphan[*hash]
50 // Add will add the block to OrphanManage
51 func (o *OrphanManage) Add(block *types.Block) {
52 blockHash := block.Hash()
56 if _, ok := o.orphan[blockHash]; ok {
60 o.orphan[blockHash] = block
61 o.preOrphans[block.PreviousBlockHash] = append(o.preOrphans[block.PreviousBlockHash], &blockHash)
64 // Delete will delelte the block from OrphanManage
65 func (o *OrphanManage) Delete(hash *bc.Hash) {
68 block, ok := o.orphan[*hash]
72 delete(o.orphan, *hash)
74 preOrphans, ok := o.preOrphans[block.PreviousBlockHash]
75 if !ok || len(preOrphans) == 1 {
76 delete(o.preOrphans, block.PreviousBlockHash)
80 for i, preOrphan := range preOrphans {
81 if preOrphan == hash {
82 o.preOrphans[block.PreviousBlockHash] = append(preOrphans[:i], preOrphans[i+1:]...)
88 // Get return the orphan block by hash
89 func (o *OrphanManage) Get(hash *bc.Hash) (*types.Block, bool) {
91 block, ok := o.orphan[*hash]
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.
101 InitialBlockHash bc.Hash
102 MaxIssuanceWindow time.Duration // only used by generators
104 orphanManage *OrphanManage
111 mainChain map[uint64]*bc.Hash
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) {
119 InitialBlockHash: initialBlockHash,
120 orphanManage: NewOrphanManage(),
124 c.state.cond.L = new(sync.Mutex)
125 storeStatus := store.GetStoreStatus()
127 if storeStatus.Hash == nil {
128 c.state.mainChain = make(map[uint64]*bc.Hash)
132 c.state.hash = storeStatus.Hash
134 if c.state.block, err = store.GetBlock(storeStatus.Hash); err != nil {
137 if c.state.mainChain, err = store.GetMainchain(storeStatus.Hash); err != nil {
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 {
150 return c.state.block.Height
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()
160 func (c *Chain) inMainchain(block *types.Block) bool {
161 hash, ok := c.state.mainChain[block.Height]
165 return *hash == block.Hash()
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()
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 {
187 return c.state.block.Timestamp
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()
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)
202 // GetSeed return the seed for the given block
203 func (c *Chain) GetSeed(height uint64, preBlock *bc.Hash) (*bc.Hash, error) {
205 return consensus.InitialSeed, nil
206 } else if height%consensus.SeedPerRetarget == 0 {
209 return c.store.GetSeed(preBlock)
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)
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)
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
231 if err := c.store.SaveChainStatus(block, view, c.state.mainChain); err != nil {
235 c.state.cond.Broadcast()
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)
249 if height > c.Height()+slop {
250 ch <- ErrTheDistantFuture
255 case <-c.BlockWaiter(height):
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)
270 c.state.cond.L.Lock()
271 defer c.state.cond.L.Unlock()
272 for c.state.block.Height < height {