8 "github.com/bytom/errors"
10 "github.com/bytom/protocol/bc"
11 "github.com/bytom/protocol/bc/legacy"
12 "github.com/bytom/protocol/state"
13 "github.com/bytom/protocol/validation"
16 // maxBlockTxs limits the number of transactions
17 // included in each block.
18 const maxBlockTxs = 10000
20 // saveSnapshotFrequency stores how often to save a state
21 // snapshot to the Store.
22 const saveSnapshotFrequency = time.Hour
25 // ErrBadBlock is returned when a block is invalid.
26 ErrBadBlock = errors.New("invalid block")
28 // ErrStaleState is returned when the Chain does not have a current
30 ErrStaleState = errors.New("stale blockchain state")
32 // ErrBadStateRoot is returned when the computed assets merkle root
33 // disagrees with the one declared in a block header.
34 ErrBadStateRoot = errors.New("invalid state merkle root")
37 // GetBlock returns the block at the given height, if there is one,
38 // otherwise it returns an error.
39 func (c *Chain) GetBlock(ctx context.Context, height uint64) (*legacy.Block, error) {
40 return c.store.GetBlock(ctx, height)
43 // GenerateBlock generates a valid, but unsigned, candidate block from
44 // the current pending transaction pool. It returns the new block and
45 // a snapshot of what the state snapshot is if the block is applied.
47 // After generating the block, the pending transaction pool will be
49 func (c *Chain) GenerateBlock(ctx context.Context, prev *legacy.Block, snapshot *state.Snapshot, now time.Time, txs []*legacy.Tx) (*legacy.Block, *state.Snapshot, error) {
50 // TODO(kr): move this into a lower-level package (e.g. chain/protocol/bc)
51 // so that other packages (e.g. chain/protocol/validation) unit tests can
52 // call this function.
54 timestampMS := bc.Millis(now)
55 if timestampMS < prev.TimestampMS {
56 return nil, nil, fmt.Errorf("timestamp %d is earlier than prevblock timestamp %d", timestampMS, prev.TimestampMS)
59 // Make a copy of the snapshot that we can apply our changes to.
60 newSnapshot := state.Copy(c.state.snapshot)
61 newSnapshot.PruneNonces(timestampMS)
64 BlockHeader: legacy.BlockHeader{
66 Height: prev.Height + 1,
67 PreviousBlockHash: prev.Hash(),
68 TimestampMS: timestampMS,
69 BlockCommitment: legacy.BlockCommitment{},
73 var txEntries []*bc.Tx
75 for _, tx := range txs {
76 if len(b.Transactions) >= maxBlockTxs {
80 // Filter out transactions that are not well-formed.
81 err := c.ValidateTx(tx.Tx)
83 // TODO(bobg): log this?
87 // Filter out transactions that are not yet valid, or no longer
88 // valid, per the block's timestamp.
89 if tx.Tx.MinTimeMs > 0 && tx.Tx.MinTimeMs > b.TimestampMS {
90 // TODO(bobg): log this?
93 if tx.Tx.MaxTimeMs > 0 && tx.Tx.MaxTimeMs < b.TimestampMS {
94 // TODO(bobg): log this?
98 // Filter out double-spends etc.
99 err = newSnapshot.ApplyTx(tx.Tx)
101 // TODO(bobg): log this?
105 b.Transactions = append(b.Transactions, tx)
106 txEntries = append(txEntries, tx.Tx)
111 b.TransactionsMerkleRoot, err = bc.MerkleRoot(txEntries)
113 return nil, nil, errors.Wrap(err, "calculating tx merkle root")
116 b.AssetsMerkleRoot = newSnapshot.Tree.RootHash()
118 return b, newSnapshot, nil
121 // ValidateBlock validates an incoming block in advance of applying it
122 // to a snapshot (with ApplyValidBlock) and committing it to the
123 // blockchain (with CommitAppliedBlock).
124 func (c *Chain) ValidateBlock(block, prev *legacy.Block) error {
125 blockEnts := legacy.MapBlock(block)
126 prevEnts := legacy.MapBlock(prev)
127 err := validation.ValidateBlock(blockEnts, prevEnts)
129 return errors.Sub(ErrBadBlock, err)
131 return errors.Sub(ErrBadBlock, err)
134 // ApplyValidBlock creates an updated snapshot without validating the
136 func (c *Chain) ApplyValidBlock(block *legacy.Block) (*state.Snapshot, error) {
137 newSnapshot := state.Copy(c.state.snapshot)
138 err := newSnapshot.ApplyBlock(legacy.MapBlock(block))
142 if block.AssetsMerkleRoot != newSnapshot.Tree.RootHash() {
143 return nil, ErrBadStateRoot
145 return newSnapshot, nil
148 // CommitBlock commits a block to the blockchain. The block
149 // must already have been applied with ApplyValidBlock or
150 // ApplyNewBlock, which will have produced the new snapshot that's
153 // This function saves the block to the store and sometimes (not more
154 // often than saveSnapshotFrequency) saves the state tree to the
155 // store. New-block callbacks (via asynchronous block-processor pins)
158 // TODO(bobg): rename to CommitAppliedBlock for clarity (deferred from https://github.com/chain/chain/pull/788)
159 func (c *Chain) CommitAppliedBlock(ctx context.Context, block *legacy.Block, snapshot *state.Snapshot) error {
160 // SaveBlock is the linearization point. Once the block is committed
161 // to persistent storage, the block has been applied and everything
162 // else can be derived from that block.
163 /*err := c.store.SaveBlock(ctx, block)
165 return errors.Wrap(err, "storing block")
167 if block.Time().After(c.lastQueuedSnapshot.Add(saveSnapshotFrequency)) {
168 c.queueSnapshot(ctx, block.Height, block.Time(), snapshot)
171 err := c.store.FinalizeBlock(ctx, block.Height)
173 return errors.Wrap(err, "finalizing block")
176 // c.setState will update the local blockchain state and height.
177 // When c.store is a txdb.Store, and c has been initialized with a
178 // channel from txdb.ListenBlocks, then the above call to
179 // c.store.FinalizeBlock will have done a postgresql NOTIFY and
180 // that will wake up the goroutine in NewChain, which also calls
181 // setHeight. But duplicate calls with the same blockheight are
182 // harmless; and the following call is required in the cases where
183 // it's not redundant.
184 c.setState(block, snapshot)
188 func (c *Chain) queueSnapshot(ctx context.Context, height uint64, timestamp time.Time, s *state.Snapshot) {
189 // Non-blockingly queue the snapshot for storage.
190 ps := pendingSnapshot{height: height, snapshot: s}
192 case c.pendingSnapshots <- ps:
193 c.lastQueuedSnapshot = timestamp
195 // Skip it; saving snapshots is taking longer than the snapshotting period.
196 log.Printf(ctx, "snapshot storage is taking too long; last queued at %s",
197 c.lastQueuedSnapshot)
201 func (c *Chain) setHeight(h uint64) {
202 // We call setHeight from two places independently:
203 // CommitBlock and the Postgres LISTEN goroutine.
204 // This means we can get here twice for each block,
205 // and any of them might be arbitrarily delayed,
206 // which means h might be from the past.
207 // Detect and discard these duplicate calls.
209 c.state.cond.L.Lock()
210 defer c.state.cond.L.Unlock()
212 if h <= c.state.height {
216 c.state.cond.Broadcast()
219 func NewInitialBlock(timestamp time.Time) (*legacy.Block, error) {
220 // TODO(kr): move this into a lower-level package (e.g. chain/protocol/bc)
221 // so that other packages (e.g. chain/protocol/validation) unit tests can
222 // call this function.
223 root, err := bc.MerkleRoot(nil) // calculate the zero value of the tx merkle root
225 return nil, errors.Wrap(err, "calculating zero value of tx merkle root")
229 BlockHeader: legacy.BlockHeader{
232 TimestampMS: bc.Millis(timestamp),
233 BlockCommitment: legacy.BlockCommitment{
234 TransactionsMerkleRoot: root,