OSDN Git Service

merge node_p2p and chain.
[bytom/bytom.git] / protocol / prottest / block.go
1 package prottest
2
3 import (
4         "context"
5         "sync"
6         "testing"
7         "time"
8
9         "github.com/blockchain/crypto/ed25519"
10         "github.com/blockchain/protocol"
11         "github.com/blockchain/protocol/bc"
12         "github.com/blockchain/protocol/bc/legacy"
13         "github.com/blockchain/protocol/prottest/memstore"
14         "github.com/blockchain/protocol/state"
15         "github.com/blockchain/testutil"
16 )
17
18 var (
19         mutex         sync.Mutex // protects the following
20         states        = make(map[*protocol.Chain]*state.Snapshot)
21         blockPubkeys  = make(map[*protocol.Chain][]ed25519.PublicKey)
22         blockPrivkeys = make(map[*protocol.Chain][]ed25519.PrivateKey)
23 )
24
25 type Option func(testing.TB, *config)
26
27 func WithStore(store protocol.Store) Option {
28         return func(_ testing.TB, conf *config) { conf.store = store }
29 }
30
31 func WithOutputIDs(outputIDs ...bc.Hash) Option {
32         return func(_ testing.TB, conf *config) {
33                 for _, oid := range outputIDs {
34                         conf.initialState.Tree.Insert(oid.Bytes())
35                 }
36         }
37 }
38
39 func WithBlockSigners(quorum, n int) Option {
40         return func(tb testing.TB, conf *config) {
41                 conf.quorum = quorum
42                 for i := 0; i < n; i++ {
43                         pubkey, privkey, err := ed25519.GenerateKey(nil)
44                         if err != nil {
45                                 testutil.FatalErr(tb, err)
46                         }
47                         conf.pubkeys = append(conf.pubkeys, pubkey)
48                         conf.privkeys = append(conf.privkeys, privkey)
49                 }
50         }
51 }
52
53 type config struct {
54         store        protocol.Store
55         initialState *state.Snapshot
56         pubkeys      []ed25519.PublicKey
57         privkeys     []ed25519.PrivateKey
58         quorum       int
59 }
60
61 // NewChain makes a new Chain. By default it uses a memstore for
62 // storage and creates an initial block using a 0/0 multisig program.
63 // It commits the initial block before returning the Chain.
64 //
65 // Its defaults may be overridden by providing Options.
66 func NewChain(tb testing.TB, opts ...Option) *protocol.Chain {
67         conf := config{store: memstore.New(), initialState: state.Empty()}
68         for _, opt := range opts {
69                 opt(tb, &conf)
70         }
71
72         ctx := context.Background()
73         b1, err := protocol.NewInitialBlock(conf.pubkeys, conf.quorum, time.Now())
74         if err != nil {
75                 testutil.FatalErr(tb, err)
76         }
77         c, err := protocol.NewChain(ctx, b1.Hash(), conf.store, nil)
78         if err != nil {
79                 testutil.FatalErr(tb, err)
80         }
81         c.MaxIssuanceWindow = 48 * time.Hour // TODO(tessr): consider adding MaxIssuanceWindow to NewChain
82
83         err = c.CommitAppliedBlock(ctx, b1, conf.initialState)
84         if err != nil {
85                 testutil.FatalErr(tb, err)
86         }
87
88         // save block-signing keys in global state
89         mutex.Lock()
90         blockPubkeys[c] = conf.pubkeys
91         blockPrivkeys[c] = conf.privkeys
92         mutex.Unlock()
93
94         return c
95 }
96
97 // Initial returns the provided Chain's initial block.
98 func Initial(tb testing.TB, c *protocol.Chain) *legacy.Block {
99         ctx := context.Background()
100         b1, err := c.GetBlock(ctx, 1)
101         if err != nil {
102                 testutil.FatalErr(tb, err)
103         }
104         return b1
105 }
106
107 // BlockKeyPairs returns the configured block-signing key-pairs
108 // for the provided Chain.
109 func BlockKeyPairs(c *protocol.Chain) ([]ed25519.PublicKey, []ed25519.PrivateKey) {
110         mutex.Lock()
111         defer mutex.Unlock()
112         return blockPubkeys[c], blockPrivkeys[c]
113 }
114
115 // MakeBlock makes a new block from txs, commits it, and returns it.
116 // It assumes c's consensus program requires 0 signatures.
117 // (This is true for chains returned by NewChain.)
118 // If c requires more than 0 signatures, MakeBlock will fail.
119 // MakeBlock always makes a block;
120 // if there are no transactions in txs,
121 // it makes an empty block.
122 func MakeBlock(tb testing.TB, c *protocol.Chain, txs []*legacy.Tx) *legacy.Block {
123         ctx := context.Background()
124         curBlock, err := c.GetBlock(ctx, c.Height())
125         if err != nil {
126                 testutil.FatalErr(tb, err)
127         }
128
129         mutex.Lock()
130         curState := states[c]
131         mutex.Unlock()
132         if curState == nil {
133                 curState = state.Empty()
134         }
135
136         nextBlock, nextState, err := c.GenerateBlock(ctx, curBlock, curState, time.Now(), txs)
137         if err != nil {
138                 testutil.FatalErr(tb, err)
139         }
140         err = c.CommitAppliedBlock(ctx, nextBlock, nextState)
141         if err != nil {
142                 testutil.FatalErr(tb, err)
143         }
144
145         mutex.Lock()
146         states[c] = nextState
147         mutex.Unlock()
148         return nextBlock
149 }