8 "github.com/bytom/errors"
10 "github.com/bytom/protocol/bc"
11 "github.com/bytom/protocol/bc/legacy"
12 "github.com/bytom/protocol/state"
15 // maxCachedValidatedTxs is the max number of validated txs to cache.
16 const maxCachedValidatedTxs = 1000
19 // ErrTheDistantFuture is returned when waiting for a blockheight
20 // too far in excess of the tip of the blockchain.
21 ErrTheDistantFuture = errors.New("block height too far in future")
24 // Store provides storage for blockchain data: blocks and state tree
27 // Note, this is different from a state snapshot. A state snapshot
28 // provides access to the state at a given point in time -- outputs
29 // and issuance memory. The Chain type uses Store to load state
30 // from storage and persist validated data.
31 type Store interface {
33 GetBlock(uint64) (*legacy.Block, error)
34 LatestSnapshot(context.Context) (*state.Snapshot, uint64, error)
36 SaveBlock(*legacy.Block) error
37 FinalizeBlock(context.Context, uint64) error
38 SaveSnapshot(context.Context, uint64, *state.Snapshot) error
41 // Chain provides a complete, minimal blockchain database. It
42 // delegates the underlying storage to other objects, and uses
43 // validation logic from package validation to decide what
44 // objects can be safely stored.
46 InitialBlockHash bc.Hash
47 MaxIssuanceWindow time.Duration // only used by generators
50 cond sync.Cond // protects height, block, snapshot
53 snapshot *state.Snapshot
57 lastQueuedSnapshot time.Time
58 pendingSnapshots chan pendingSnapshot
63 type pendingSnapshot struct {
65 snapshot *state.Snapshot
68 // NewChain returns a new Chain using store as the underlying storage.
69 func NewChain(ctx context.Context, initialBlockHash bc.Hash, store Store, txPool *TxPool, heights <-chan uint64) (*Chain, error) {
71 InitialBlockHash: initialBlockHash,
73 pendingSnapshots: make(chan pendingSnapshot, 1),
76 c.state.cond.L = new(sync.Mutex)
78 log.Printf(ctx, "bytom's Height:%v.", store.Height())
79 c.state.height = store.Height()
80 c.state.block, _ = store.GetBlock(c.state.height)
81 c.state.snapshot, _, _ = store.LatestSnapshot(ctx)
83 // Note that c.height.n may still be zero here.
86 for h := range heights {
97 case ps := <-c.pendingSnapshots:
98 err := store.SaveSnapshot(ctx, ps.height, ps.snapshot)
100 log.Error(ctx, err, "at", "saving snapshot")
109 func (c *Chain) GetStore() *Store {
113 // Height returns the current height of the blockchain.
114 func (c *Chain) Height() uint64 {
115 c.state.cond.L.Lock()
116 defer c.state.cond.L.Unlock()
117 return c.state.height
120 // TimestampMS returns the latest known block timestamp.
121 func (c *Chain) TimestampMS() uint64 {
122 c.state.cond.L.Lock()
123 defer c.state.cond.L.Unlock()
124 if c.state.block == nil {
127 return c.state.block.TimestampMS
130 // State returns the most recent state available. It will not be current
131 // unless the current process is the leader. Callers should examine the
132 // returned block header's height if they need to verify the current state.
133 func (c *Chain) State() (*legacy.Block, *state.Snapshot) {
134 c.state.cond.L.Lock()
135 defer c.state.cond.L.Unlock()
136 return c.state.block, c.state.snapshot
139 func (c *Chain) setState(b *legacy.Block, s *state.Snapshot) {
140 c.state.cond.L.Lock()
141 defer c.state.cond.L.Unlock()
144 if b != nil && b.Height > c.state.height {
145 c.state.height = b.Height
146 c.state.cond.Broadcast()
150 // BlockSoonWaiter returns a channel that
151 // waits for the block at the given height,
152 // but it is an error to wait for a block far in the future.
153 // WaitForBlockSoon will timeout if the context times out.
154 // To wait unconditionally, the caller should use WaitForBlock.
155 func (c *Chain) BlockSoonWaiter(ctx context.Context, height uint64) <-chan error {
156 ch := make(chan error, 1)
160 if height > c.Height()+slop {
161 ch <- ErrTheDistantFuture
166 case <-c.BlockWaiter(height):
176 // BlockWaiter returns a channel that
177 // waits for the block at the given height.
178 func (c *Chain) BlockWaiter(height uint64) <-chan struct{} {
179 ch := make(chan struct{}, 1)
181 c.state.cond.L.Lock()
182 defer c.state.cond.L.Unlock()
183 for c.state.height < height {