OSDN Git Service

merge bvm branch with master
[bytom/bytom.git] / protocol / block.go
1 package protocol
2
3 import (
4         "context"
5         "fmt"
6         "time"
7
8         "github.com/bytom/errors"
9         "github.com/bytom/log"
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"
14 )
15
16 // maxBlockTxs limits the number of transactions
17 // included in each block.
18 const maxBlockTxs = 10000
19
20 // saveSnapshotFrequency stores how often to save a state
21 // snapshot to the Store.
22 const saveSnapshotFrequency = time.Hour
23
24 var (
25         // ErrBadBlock is returned when a block is invalid.
26         ErrBadBlock = errors.New("invalid block")
27
28         // ErrStaleState is returned when the Chain does not have a current
29         // blockchain state.
30         ErrStaleState = errors.New("stale blockchain state")
31
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")
35 )
36
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)
41 }
42
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.
46 //
47 // After generating the block, the pending transaction pool will be
48 // empty.
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.
53
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)
57         }
58
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)
62
63         b := &legacy.Block{
64                 BlockHeader: legacy.BlockHeader{
65                         Version:           1,
66                         Height:            prev.Height + 1,
67                         PreviousBlockHash: prev.Hash(),
68                         TimestampMS:       timestampMS,
69                         BlockCommitment:   legacy.BlockCommitment{},
70                 },
71         }
72
73         var txEntries []*bc.Tx
74
75         for _, tx := range txs {
76                 if len(b.Transactions) >= maxBlockTxs {
77                         break
78                 }
79
80                 // Filter out transactions that are not well-formed.
81                 err := c.ValidateTx(tx.Tx)
82                 if err != nil {
83                         // TODO(bobg): log this?
84                         continue
85                 }
86
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?
91                         continue
92                 }
93                 if tx.Tx.MaxTimeMs > 0 && tx.Tx.MaxTimeMs < b.TimestampMS {
94                         // TODO(bobg): log this?
95                         continue
96                 }
97
98                 // Filter out double-spends etc.
99                 err = newSnapshot.ApplyTx(tx.Tx)
100                 if err != nil {
101                         // TODO(bobg): log this?
102                         continue
103                 }
104
105                 b.Transactions = append(b.Transactions, tx)
106                 txEntries = append(txEntries, tx.Tx)
107         }
108
109         var err error
110
111         b.TransactionsMerkleRoot, err = bc.MerkleRoot(txEntries)
112         if err != nil {
113                 return nil, nil, errors.Wrap(err, "calculating tx merkle root")
114         }
115
116         b.AssetsMerkleRoot = newSnapshot.Tree.RootHash()
117
118         return b, newSnapshot, nil
119 }
120
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)
128         if err != nil {
129                 return errors.Sub(ErrBadBlock, err)
130         }
131         return errors.Sub(ErrBadBlock, err)
132 }
133
134 // ApplyValidBlock creates an updated snapshot without validating the
135 // block.
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))
139         if err != nil {
140                 return nil, err
141         }
142         if block.AssetsMerkleRoot != newSnapshot.Tree.RootHash() {
143                 return nil, ErrBadStateRoot
144         }
145         return newSnapshot, nil
146 }
147
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
151 // required here.
152 //
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)
156 // are triggered.
157 //
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)
164         if err != nil {
165                 return errors.Wrap(err, "storing block")
166         }*/
167         if block.Time().After(c.lastQueuedSnapshot.Add(saveSnapshotFrequency)) {
168                 c.queueSnapshot(ctx, block.Height, block.Time(), snapshot)
169         }
170
171         err := c.store.FinalizeBlock(ctx, block.Height)
172         if err != nil {
173                 return errors.Wrap(err, "finalizing block")
174         }
175
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)
185         return nil
186 }
187
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}
191         select {
192         case c.pendingSnapshots <- ps:
193                 c.lastQueuedSnapshot = timestamp
194         default:
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)
198         }
199 }
200
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.
208
209         c.state.cond.L.Lock()
210         defer c.state.cond.L.Unlock()
211
212         if h <= c.state.height {
213                 return
214         }
215         c.state.height = h
216         c.state.cond.Broadcast()
217 }
218
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
224         if err != nil {
225                 return nil, errors.Wrap(err, "calculating zero value of tx merkle root")
226         }
227
228         b := &legacy.Block{
229                 BlockHeader: legacy.BlockHeader{
230                         Version:     1,
231                         Height:      1,
232                         TimestampMS: bc.Millis(timestamp),
233                         BlockCommitment: legacy.BlockCommitment{
234                                 TransactionsMerkleRoot: root,
235                         },
236                 },
237         }
238         return b, nil
239 }