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"
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)
25 type Option func(testing.TB, *config)
27 func WithStore(store protocol.Store) Option {
28 return func(_ testing.TB, conf *config) { conf.store = store }
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())
39 func WithBlockSigners(quorum, n int) Option {
40 return func(tb testing.TB, conf *config) {
42 for i := 0; i < n; i++ {
43 pubkey, privkey, err := ed25519.GenerateKey(nil)
45 testutil.FatalErr(tb, err)
47 conf.pubkeys = append(conf.pubkeys, pubkey)
48 conf.privkeys = append(conf.privkeys, privkey)
55 initialState *state.Snapshot
56 pubkeys []ed25519.PublicKey
57 privkeys []ed25519.PrivateKey
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.
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 {
72 ctx := context.Background()
73 b1, err := protocol.NewInitialBlock(conf.pubkeys, conf.quorum, time.Now())
75 testutil.FatalErr(tb, err)
77 c, err := protocol.NewChain(ctx, b1.Hash(), conf.store, nil)
79 testutil.FatalErr(tb, err)
81 c.MaxIssuanceWindow = 48 * time.Hour // TODO(tessr): consider adding MaxIssuanceWindow to NewChain
83 err = c.CommitAppliedBlock(ctx, b1, conf.initialState)
85 testutil.FatalErr(tb, err)
88 // save block-signing keys in global state
90 blockPubkeys[c] = conf.pubkeys
91 blockPrivkeys[c] = conf.privkeys
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)
102 testutil.FatalErr(tb, err)
107 // BlockKeyPairs returns the configured block-signing key-pairs
108 // for the provided Chain.
109 func BlockKeyPairs(c *protocol.Chain) ([]ed25519.PublicKey, []ed25519.PrivateKey) {
112 return blockPubkeys[c], blockPrivkeys[c]
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())
126 testutil.FatalErr(tb, err)
130 curState := states[c]
133 curState = state.Empty()
136 nextBlock, nextState, err := c.GenerateBlock(ctx, curBlock, curState, time.Now(), txs)
138 testutil.FatalErr(tb, err)
140 err = c.CommitAppliedBlock(ctx, nextBlock, nextState)
142 testutil.FatalErr(tb, err)
146 states[c] = nextState