OSDN Git Service

Merge pull request #3 from Bytom/develop
[bytom/bytom.git] / protocol / protocol.go
1 /*
2 Package protocol provides the logic to tie together
3 storage and validation for a Chain Protocol blockchain.
4
5 This comprises all behavior that's common to every full
6 node, as well as other functions that need to operate on the
7 blockchain state.
8
9 Here are a few examples of typical full node types.
10
11 Generator
12
13 A generator has two basic jobs: collecting transactions from
14 other nodes and putting them into blocks.
15
16 To add a new block to the blockchain, call GenerateBlock,
17 sign the block (possibly collecting signatures from other
18 parties), and call CommitBlock.
19
20 Signer
21
22 A signer validates blocks generated by the Generator and signs
23 at most one block at each height.
24
25 Participant
26
27 A participant node in a network may select outputs for spending
28 and compose transactions.
29
30 To publish a new transaction, prepare your transaction
31 (select outputs, and compose and sign the tx) and send the
32 transaction to the network's generator. To wait for
33 confirmation, call BlockWaiter on successive block heights
34 and inspect the blockchain state until you find that the
35 transaction has been either confirmed or rejected. Note
36 that transactions may be malleable if there's no commitment
37 to TXSIGHASH.
38
39 To ingest a block, call ValidateBlock and CommitBlock.
40 */
41 package protocol
42
43 import (
44         "context"
45         "sync"
46         "time"
47
48         "github.com/golang/groupcache/lru"
49
50         "github.com/blockchain/errors"
51         //"github.com/blockchain/log"
52         "github.com/blockchain/protocol/bc"
53         "github.com/blockchain/protocol/bc/legacy"
54         "github.com/blockchain/protocol/state"
55 )
56
57 // maxCachedValidatedTxs is the max number of validated txs to cache.
58 const maxCachedValidatedTxs = 1000
59
60 var (
61         // ErrTheDistantFuture is returned when waiting for a blockheight
62         // too far in excess of the tip of the blockchain.
63         ErrTheDistantFuture = errors.New("block height too far in future")
64 )
65
66 // Store provides storage for blockchain data: blocks and state tree
67 // snapshots.
68 //
69 // Note, this is different from a state snapshot. A state snapshot
70 // provides access to the state at a given point in time -- outputs
71 // and issuance memory. The Chain type uses Store to load state
72 // from storage and persist validated data.
73 type Store interface {
74         Height(context.Context) (uint64, error)
75         GetBlock(context.Context, uint64) (*legacy.Block, error)
76 //      LatestSnapshot(context.Context) (*state.Snapshot, uint64, error)
77
78 //      SaveBlock(context.Context, *legacy.Block) error
79         FinalizeBlock(context.Context, uint64) error
80 //      SaveSnapshot(context.Context, uint64, *state.Snapshot) error
81 }
82
83 // Chain provides a complete, minimal blockchain database. It
84 // delegates the underlying storage to other objects, and uses
85 // validation logic from package validation to decide what
86 // objects can be safely stored.
87 type Chain struct {
88         InitialBlockHash  bc.Hash
89         MaxIssuanceWindow time.Duration // only used by generators
90
91         state struct {
92                 cond     sync.Cond // protects height, block, snapshot
93                 height   uint64
94                 block    *legacy.Block   // current only if leader
95                 snapshot *state.Snapshot // current only if leader
96         }
97         store Store
98
99         lastQueuedSnapshot time.Time
100         pendingSnapshots   chan pendingSnapshot
101
102         prevalidated prevalidatedTxsCache
103 }
104
105 type pendingSnapshot struct {
106         height   uint64
107         snapshot *state.Snapshot
108 }
109
110 // NewChain returns a new Chain using store as the underlying storage.
111 func NewChain(ctx context.Context, initialBlockHash bc.Hash, store Store, heights <-chan uint64) (*Chain, error) {
112         c := &Chain{
113                 InitialBlockHash: initialBlockHash,
114                 store:            store,
115                 pendingSnapshots: make(chan pendingSnapshot, 1),
116                 prevalidated: prevalidatedTxsCache{
117                         lru: lru.New(maxCachedValidatedTxs),
118                 },
119         }
120         c.state.cond.L = new(sync.Mutex)
121
122         var err error
123         c.state.height, err = store.Height(ctx)
124         if err != nil {
125                 return nil, errors.Wrap(err, "looking up blockchain height")
126         }
127
128         // Note that c.height.n may still be zero here.
129         if heights != nil {
130                 go func() {
131                         for h := range heights {
132                                 c.setHeight(h)
133                         }
134                 }()
135         }
136
137         go func() {
138                 for {
139                         select {
140                         case <-ctx.Done():
141                                 return
142                         //case ps := <-c.pendingSnapshots:
143                                 /*err = store.SaveSnapshot(ctx, ps.height, ps.snapshot)
144                                 if err != nil {
145                                         log.Error(ctx, err, "at", "saving snapshot")
146                                 }
147                 */
148                         }
149                 }
150         }()
151
152         return c, nil
153 }
154
155 // Height returns the current height of the blockchain.
156 func (c *Chain) Height() uint64 {
157         c.state.cond.L.Lock()
158         defer c.state.cond.L.Unlock()
159         return c.state.height
160 }
161
162 // TimestampMS returns the latest known block timestamp.
163 func (c *Chain) TimestampMS() uint64 {
164         c.state.cond.L.Lock()
165         defer c.state.cond.L.Unlock()
166         if c.state.block == nil {
167                 return 0
168         }
169         return c.state.block.TimestampMS
170 }
171
172 // State returns the most recent state available. It will not be current
173 // unless the current process is the leader. Callers should examine the
174 // returned block header's height if they need to verify the current state.
175 func (c *Chain) State() (*legacy.Block, *state.Snapshot) {
176         c.state.cond.L.Lock()
177         defer c.state.cond.L.Unlock()
178         return c.state.block, c.state.snapshot
179 }
180
181 func (c *Chain) setState(b *legacy.Block, s *state.Snapshot) {
182         c.state.cond.L.Lock()
183         defer c.state.cond.L.Unlock()
184         c.state.block = b
185         c.state.snapshot = s
186         if b != nil && b.Height > c.state.height {
187                 c.state.height = b.Height
188                 c.state.cond.Broadcast()
189         }
190 }
191
192 // BlockSoonWaiter returns a channel that
193 // waits for the block at the given height,
194 // but it is an error to wait for a block far in the future.
195 // WaitForBlockSoon will timeout if the context times out.
196 // To wait unconditionally, the caller should use WaitForBlock.
197 func (c *Chain) BlockSoonWaiter(ctx context.Context, height uint64) <-chan error {
198         ch := make(chan error, 1)
199
200         go func() {
201                 const slop = 3
202                 if height > c.Height()+slop {
203                         ch <- ErrTheDistantFuture
204                         return
205                 }
206
207                 select {
208                 case <-c.BlockWaiter(height):
209                         ch <- nil
210                 case <-ctx.Done():
211                         ch <- ctx.Err()
212                 }
213         }()
214
215         return ch
216 }
217
218 // BlockWaiter returns a channel that
219 // waits for the block at the given height.
220 func (c *Chain) BlockWaiter(height uint64) <-chan struct{} {
221         ch := make(chan struct{}, 1)
222         go func() {
223                 c.state.cond.L.Lock()
224                 defer c.state.cond.L.Unlock()
225                 for c.state.height < height {
226                         c.state.cond.Wait()
227                 }
228                 ch <- struct{}{}
229         }()
230
231         return ch
232 }