8 log "github.com/sirupsen/logrus"
10 "github.com/bytom/errors"
11 "github.com/bytom/protocol/bc"
12 "github.com/bytom/protocol/bc/legacy"
13 "github.com/bytom/protocol/state"
16 // maxCachedValidatedTxs is the max number of validated txs to cache.
17 const maxCachedValidatedTxs = 1000
20 // ErrTheDistantFuture is returned when waiting for a blockheight
21 // too far in excess of the tip of the blockchain.
22 ErrTheDistantFuture = errors.New("block height too far in future")
25 // Store provides storage for blockchain data: blocks and state tree
28 // Note, this is different from a state snapshot. A state snapshot
29 // provides access to the state at a given point in time -- outputs
30 // and issuance memory. The Chain type uses Store to load state
31 // from storage and persist validated data.
32 type Store interface {
34 GetBlock(uint64) (*legacy.Block, error)
35 LatestSnapshot(context.Context) (*state.Snapshot, uint64, error)
37 SaveBlock(*legacy.Block) error
38 FinalizeBlock(context.Context, uint64) error
39 SaveSnapshot(context.Context, uint64, *state.Snapshot) error
42 // Chain provides a complete, minimal blockchain database. It
43 // delegates the underlying storage to other objects, and uses
44 // validation logic from package validation to decide what
45 // objects can be safely stored.
47 InitialBlockHash bc.Hash
48 MaxIssuanceWindow time.Duration // only used by generators
51 cond sync.Cond // protects height, block, snapshot
54 snapshot *state.Snapshot
58 lastQueuedSnapshot time.Time
59 pendingSnapshots chan pendingSnapshot
64 type pendingSnapshot struct {
66 snapshot *state.Snapshot
69 // NewChain returns a new Chain using store as the underlying storage.
70 func NewChain(ctx context.Context, initialBlockHash bc.Hash, store Store, txPool *TxPool, heights <-chan uint64) (*Chain, error) {
72 InitialBlockHash: initialBlockHash,
74 pendingSnapshots: make(chan pendingSnapshot, 1),
77 c.state.cond.L = new(sync.Mutex)
79 log.WithField("current height", store.Height()).Info("Resume from the database")
80 c.state.height = store.Height()
82 if c.state.height < 1 {
83 c.state.snapshot = state.Empty()
85 c.state.block, _ = store.GetBlock(c.state.height)
86 c.state.snapshot, _, _ = store.LatestSnapshot(ctx)
89 // Note that c.height.n may still be zero here.
92 for h := range heights {
103 case ps := <-c.pendingSnapshots:
104 err := store.SaveSnapshot(ctx, ps.height, ps.snapshot)
106 log.WithField("error", err).Error("Error occurs when saving snapshot")
115 func (c *Chain) GetStore() *Store {
119 // Height returns the current height of the blockchain.
120 func (c *Chain) Height() uint64 {
121 c.state.cond.L.Lock()
122 defer c.state.cond.L.Unlock()
123 return c.state.height
126 // TimestampMS returns the latest known block timestamp.
127 func (c *Chain) TimestampMS() uint64 {
128 c.state.cond.L.Lock()
129 defer c.state.cond.L.Unlock()
130 if c.state.block == nil {
133 return c.state.block.TimestampMS
136 // State returns the most recent state available. It will not be current
137 // unless the current process is the leader. Callers should examine the
138 // returned block header's height if they need to verify the current state.
139 func (c *Chain) State() (*legacy.Block, *state.Snapshot) {
140 c.state.cond.L.Lock()
141 defer c.state.cond.L.Unlock()
142 return c.state.block, c.state.snapshot
145 func (c *Chain) setState(b *legacy.Block, s *state.Snapshot) {
146 c.state.cond.L.Lock()
147 defer c.state.cond.L.Unlock()
150 if b != nil && b.Height > c.state.height {
151 c.state.height = b.Height
152 c.state.cond.Broadcast()
156 // BlockSoonWaiter returns a channel that
157 // waits for the block at the given height,
158 // but it is an error to wait for a block far in the future.
159 // WaitForBlockSoon will timeout if the context times out.
160 // To wait unconditionally, the caller should use WaitForBlock.
161 func (c *Chain) BlockSoonWaiter(ctx context.Context, height uint64) <-chan error {
162 ch := make(chan error, 1)
166 if height > c.Height()+slop {
167 ch <- ErrTheDistantFuture
172 case <-c.BlockWaiter(height):
182 // BlockWaiter returns a channel that
183 // waits for the block at the given height.
184 func (c *Chain) BlockWaiter(height uint64) <-chan struct{} {
185 ch := make(chan struct{}, 1)
187 c.state.cond.L.Lock()
188 defer c.state.cond.L.Unlock()
189 for c.state.height < height {