7 "github.com/bytom/errors"
9 "github.com/bytom/protocol/bc"
10 "github.com/bytom/protocol/bc/legacy"
11 "github.com/bytom/protocol/state"
12 "github.com/bytom/protocol/validation"
15 // maxBlockTxs limits the number of transactions
16 // included in each block.
17 const maxBlockTxs = 10000
19 // saveSnapshotFrequency stores how often to save a state
20 // snapshot to the Store.
21 const saveSnapshotFrequency = time.Hour
24 // ErrBadBlock is returned when a block is invalid.
25 ErrBadBlock = errors.New("invalid block")
27 // ErrStaleState is returned when the Chain does not have a current
29 ErrStaleState = errors.New("stale blockchain state")
31 // ErrBadStateRoot is returned when the computed assets merkle root
32 // disagrees with the one declared in a block header.
33 ErrBadStateRoot = errors.New("invalid state merkle root")
36 // GetBlock returns the block at the given height, if there is one,
37 // otherwise it returns an error.
38 func (c *Chain) GetBlock(height uint64) (*legacy.Block, error) {
39 return c.store.GetBlock(height)
42 // ValidateBlock validates an incoming block in advance of applying it
43 // to a snapshot (with ApplyValidBlock) and committing it to the
44 // blockchain (with CommitAppliedBlock).
45 func (c *Chain) ValidateBlock(block, prev *legacy.Block) error {
46 blockEnts := legacy.MapBlock(block)
47 prevEnts := legacy.MapBlock(prev)
48 err := validation.ValidateBlock(blockEnts, prevEnts)
50 return errors.Sub(ErrBadBlock, err)
52 return errors.Sub(ErrBadBlock, err)
55 // ApplyValidBlock creates an updated snapshot without validating the
57 func (c *Chain) ApplyValidBlock(block *legacy.Block) (*state.Snapshot, error) {
58 //TODO replace with a pre-defined init blo
59 var newSnapshot *state.Snapshot
60 if c.state.snapshot == nil {
61 newSnapshot = state.Empty()
63 newSnapshot = state.Copy(c.state.snapshot)
66 err := newSnapshot.ApplyBlock(legacy.MapBlock(block))
70 //fmt.Printf("want %v, ger %v \n", block.BlockHeader.AssetsMerkleRoot, newSnapshot.Tree.RootHash())
71 if block.AssetsMerkleRoot != newSnapshot.Tree.RootHash() {
72 return nil, ErrBadStateRoot
74 return newSnapshot, nil
77 // CommitBlock commits a block to the blockchain. The block
78 // must already have been applied with ApplyValidBlock or
79 // ApplyNewBlock, which will have produced the new snapshot that's
82 // This function saves the block to the store and sometimes (not more
83 // often than saveSnapshotFrequency) saves the state tree to the
84 // store. New-block callbacks (via asynchronous block-processor pins)
87 // TODO(bobg): rename to CommitAppliedBlock for clarity (deferred from https://github.com/chain/chain/pull/788)
88 func (c *Chain) CommitAppliedBlock(ctx context.Context, block *legacy.Block, snapshot *state.Snapshot) error {
89 // SaveBlock is the linearization point. Once the block is committed
90 // to persistent storage, the block has been applied and everything
91 // else can be derived from that block.
92 err := c.store.SaveBlock(block)
94 return errors.Wrap(err, "storing block")
96 if block.Time().After(c.lastQueuedSnapshot.Add(saveSnapshotFrequency)) {
97 c.queueSnapshot(ctx, block.Height, block.Time(), snapshot)
100 err = c.store.FinalizeBlock(ctx, block.Height)
102 return errors.Wrap(err, "finalizing block")
105 // c.setState will update the local blockchain state and height.
106 // When c.store is a txdb.Store, and c has been initialized with a
107 // channel from txdb.ListenBlocks, then the above call to
108 // c.store.FinalizeBlock will have done a postgresql NOTIFY and
109 // that will wake up the goroutine in NewChain, which also calls
110 // setHeight. But duplicate calls with the same blockheight are
111 // harmless; and the following call is required in the cases where
112 // it's not redundant.
113 c.setState(block, snapshot)
117 func (c *Chain) queueSnapshot(ctx context.Context, height uint64, timestamp time.Time, s *state.Snapshot) {
118 // Non-blockingly queue the snapshot for storage.
119 ps := pendingSnapshot{height: height, snapshot: s}
121 case c.pendingSnapshots <- ps:
122 c.lastQueuedSnapshot = timestamp
124 // Skip it; saving snapshots is taking longer than the snapshotting period.
125 log.Printf(ctx, "snapshot storage is taking too long; last queued at %s",
126 c.lastQueuedSnapshot)
130 func (c *Chain) setHeight(h uint64) {
131 // We call setHeight from two places independently:
132 // CommitBlock and the Postgres LISTEN goroutine.
133 // This means we can get here twice for each block,
134 // and any of them might be arbitrarily delayed,
135 // which means h might be from the past.
136 // Detect and discard these duplicate calls.
138 c.state.cond.L.Lock()
139 defer c.state.cond.L.Unlock()
141 if h <= c.state.height {
145 c.state.cond.Broadcast()
148 func NewInitialBlock(timestamp time.Time) (*legacy.Block, error) {
149 // TODO(kr): move this into a lower-level package (e.g. chain/protocol/bc)
150 // so that other packages (e.g. chain/protocol/validation) unit tests can
151 // call this function.
152 root, err := bc.MerkleRoot(nil) // calculate the zero value of the tx merkle root
154 return nil, errors.Wrap(err, "calculating zero value of tx merkle root")
158 BlockHeader: legacy.BlockHeader{
161 TimestampMS: bc.Millis(timestamp),
162 BlockCommitment: legacy.BlockCommitment{
163 TransactionsMerkleRoot: root,
166 Transactions: []*legacy.Tx{},