OSDN Git Service

Merge branch 'master' into demo
[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(height uint64) (*legacy.Block, error) {
40         return c.store.GetBlock(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         //TODO replace with a pre-defined init blo
138         var newSnapshot *state.Snapshot
139         if c.state.snapshot == nil {
140                 newSnapshot = state.Empty()
141         } else {
142                 newSnapshot = state.Copy(c.state.snapshot)
143         }
144
145         err := newSnapshot.ApplyBlock(legacy.MapBlock(block))
146         if err != nil {
147                 return nil, err
148         }
149         //fmt.Printf("want %v, ger %v \n", block.BlockHeader.AssetsMerkleRoot, newSnapshot.Tree.RootHash())
150         if block.AssetsMerkleRoot != newSnapshot.Tree.RootHash() {
151                 return nil, ErrBadStateRoot
152         }
153         return newSnapshot, nil
154 }
155
156 // CommitBlock commits a block to the blockchain. The block
157 // must already have been applied with ApplyValidBlock or
158 // ApplyNewBlock, which will have produced the new snapshot that's
159 // required here.
160 //
161 // This function saves the block to the store and sometimes (not more
162 // often than saveSnapshotFrequency) saves the state tree to the
163 // store. New-block callbacks (via asynchronous block-processor pins)
164 // are triggered.
165 //
166 // TODO(bobg): rename to CommitAppliedBlock for clarity (deferred from https://github.com/chain/chain/pull/788)
167 func (c *Chain) CommitAppliedBlock(ctx context.Context, block *legacy.Block, snapshot *state.Snapshot) error {
168         // SaveBlock is the linearization point. Once the block is committed
169         // to persistent storage, the block has been applied and everything
170         // else can be derived from that block.
171         err := c.store.SaveBlock(block)
172         if err != nil {
173                 return errors.Wrap(err, "storing block")
174         }
175         if block.Time().After(c.lastQueuedSnapshot.Add(saveSnapshotFrequency)) {
176                 c.queueSnapshot(ctx, block.Height, block.Time(), snapshot)
177         }
178
179         err = c.store.FinalizeBlock(ctx, block.Height)
180         if err != nil {
181                 return errors.Wrap(err, "finalizing block")
182         }
183
184         // c.setState will update the local blockchain state and height.
185         // When c.store is a txdb.Store, and c has been initialized with a
186         // channel from txdb.ListenBlocks, then the above call to
187         // c.store.FinalizeBlock will have done a postgresql NOTIFY and
188         // that will wake up the goroutine in NewChain, which also calls
189         // setHeight.  But duplicate calls with the same blockheight are
190         // harmless; and the following call is required in the cases where
191         // it's not redundant.
192         c.setState(block, snapshot)
193         return nil
194 }
195
196 func (c *Chain) queueSnapshot(ctx context.Context, height uint64, timestamp time.Time, s *state.Snapshot) {
197         // Non-blockingly queue the snapshot for storage.
198         ps := pendingSnapshot{height: height, snapshot: s}
199         select {
200         case c.pendingSnapshots <- ps:
201                 c.lastQueuedSnapshot = timestamp
202         default:
203                 // Skip it; saving snapshots is taking longer than the snapshotting period.
204                 log.Printf(ctx, "snapshot storage is taking too long; last queued at %s",
205                         c.lastQueuedSnapshot)
206         }
207 }
208
209 func (c *Chain) setHeight(h uint64) {
210         // We call setHeight from two places independently:
211         // CommitBlock and the Postgres LISTEN goroutine.
212         // This means we can get here twice for each block,
213         // and any of them might be arbitrarily delayed,
214         // which means h might be from the past.
215         // Detect and discard these duplicate calls.
216
217         c.state.cond.L.Lock()
218         defer c.state.cond.L.Unlock()
219
220         if h <= c.state.height {
221                 return
222         }
223         c.state.height = h
224         c.state.cond.Broadcast()
225 }
226
227 func NewInitialBlock(timestamp time.Time) (*legacy.Block, error) {
228         // TODO(kr): move this into a lower-level package (e.g. chain/protocol/bc)
229         // so that other packages (e.g. chain/protocol/validation) unit tests can
230         // call this function.
231         root, err := bc.MerkleRoot(nil) // calculate the zero value of the tx merkle root
232         if err != nil {
233                 return nil, errors.Wrap(err, "calculating zero value of tx merkle root")
234         }
235
236         b := &legacy.Block{
237                 BlockHeader: legacy.BlockHeader{
238                         Version:     1,
239                         Height:      1,
240                         TimestampMS: bc.Millis(timestamp),
241                         BlockCommitment: legacy.BlockCommitment{
242                                 TransactionsMerkleRoot: root,
243                         },
244                 },
245                 Transactions: []*legacy.Tx{},
246         }
247         return b, nil
248 }