/
chain.go
219 lines (192 loc) · 7.77 KB
/
chain.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
package chain
import (
"encoding/hex"
"sort"
"testing"
"time"
"github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
"github.com/nspcc-dev/neo-go/pkg/core"
"github.com/nspcc-dev/neo-go/pkg/core/storage"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/neotest"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/stretchr/testify/require"
"go.uber.org/zap/zaptest"
)
const (
// MaxTraceableBlocks is the default MaxTraceableBlocks setting used for test chains.
// We don't need a lot of traceable blocks for tests.
MaxTraceableBlocks = 1000
// TimePerBlock is the default TimePerBlock setting used for test chains (1s).
// Usually blocks are created by tests bypassing this setting.
TimePerBlock = time.Second
)
const singleValidatorWIF = "KxyjQ8eUa4FHt3Gvioyt1Wz29cTUrE4eTqX3yFSk1YFCsPL8uNsY"
// committeeWIFs is a list of unencrypted WIFs sorted by the public key.
var committeeWIFs = []string{
"KzfPUYDC9n2yf4fK5ro4C8KMcdeXtFuEnStycbZgX3GomiUsvX6W",
"KzgWE3u3EDp13XPXXuTKZxeJ3Gi8Bsm8f9ijY3ZsCKKRvZUo1Cdn",
singleValidatorWIF,
"L2oEXKRAAMiPEZukwR5ho2S6SMeQLhcK9mF71ZnF7GvT8dU4Kkgz",
// Provide 2 committee extra members so that the committee address differs from
// the validators one.
"L1Tr1iq5oz1jaFaMXP21sHDkJYDDkuLtpvQ4wRf1cjKvJYvnvpAb",
"Kz6XTUrExy78q8f4MjDHnwz8fYYyUE8iPXwPRAkHa3qN2JcHYm7e",
}
var (
// committeeAcc is an account used to sign a tx as a committee.
committeeAcc *wallet.Account
// multiCommitteeAcc contains committee accounts used in a multi-node setup.
multiCommitteeAcc []*wallet.Account
// multiValidatorAcc contains validator accounts used in a multi-node setup.
multiValidatorAcc []*wallet.Account
// standByCommittee contains a list of committee public keys to use in config.
standByCommittee []string
)
func init() {
committeeAcc, _ = wallet.NewAccountFromWIF(singleValidatorWIF)
pubs := keys.PublicKeys{committeeAcc.PublicKey()}
err := committeeAcc.ConvertMultisig(1, pubs)
if err != nil {
panic(err)
}
mc := smartcontract.GetMajorityHonestNodeCount(len(committeeWIFs))
mv := smartcontract.GetDefaultHonestNodeCount(4)
accs := make([]*wallet.Account, len(committeeWIFs))
pubs = make(keys.PublicKeys, len(accs))
for i := range committeeWIFs {
accs[i], _ = wallet.NewAccountFromWIF(committeeWIFs[i])
pubs[i] = accs[i].PublicKey()
}
// Config entry must contain validators first in a specific order.
standByCommittee = make([]string, len(pubs))
standByCommittee[0] = hex.EncodeToString(pubs[2].Bytes())
standByCommittee[1] = hex.EncodeToString(pubs[0].Bytes())
standByCommittee[2] = hex.EncodeToString(pubs[3].Bytes())
standByCommittee[3] = hex.EncodeToString(pubs[1].Bytes())
standByCommittee[4] = hex.EncodeToString(pubs[4].Bytes())
standByCommittee[5] = hex.EncodeToString(pubs[5].Bytes())
multiValidatorAcc = make([]*wallet.Account, 4)
sort.Sort(pubs[:4])
sort.Slice(accs[:4], func(i, j int) bool {
p1 := accs[i].PublicKey()
p2 := accs[j].PublicKey()
return p1.Cmp(p2) == -1
})
for i := range multiValidatorAcc {
multiValidatorAcc[i] = wallet.NewAccountFromPrivateKey(accs[i].PrivateKey())
err := multiValidatorAcc[i].ConvertMultisig(mv, pubs[:4])
if err != nil {
panic(err)
}
}
multiCommitteeAcc = make([]*wallet.Account, len(committeeWIFs))
sort.Sort(pubs)
sort.Slice(accs, func(i, j int) bool {
p1 := accs[i].PublicKey()
p2 := accs[j].PublicKey()
return p1.Cmp(p2) == -1
})
for i := range multiCommitteeAcc {
multiCommitteeAcc[i] = wallet.NewAccountFromPrivateKey(accs[i].PrivateKey())
err := multiCommitteeAcc[i].ConvertMultisig(mc, pubs)
if err != nil {
panic(err)
}
}
}
// NewSingle creates a new blockchain instance with a single validator and
// setups cleanup functions. The configuration used is with netmode.UnitTestNet
// magic and TimePerBlock/MaxTraceableBlocks options defined by constants in
// this package. MemoryStore is used as the backend storage, so all of the chain
// contents is always in RAM. The Signer returned is the validator (and the committee at
// the same time).
func NewSingle(t testing.TB) (*core.Blockchain, neotest.Signer) {
return NewSingleWithCustomConfig(t, nil)
}
// NewSingleWithCustomConfig is similar to NewSingle, but allows to override the
// default configuration.
func NewSingleWithCustomConfig(t testing.TB, f func(*config.Blockchain)) (*core.Blockchain, neotest.Signer) {
return NewSingleWithCustomConfigAndStore(t, f, nil, true)
}
// NewSingleWithCustomConfigAndStore is similar to NewSingleWithCustomConfig, but
// also allows to override backend Store being used. The last parameter controls if
// Run method is called on the Blockchain instance. If not, it is its caller's
// responsibility to do that before using the chain and
// to properly Close the chain when done.
func NewSingleWithCustomConfigAndStore(t testing.TB, f func(cfg *config.Blockchain), st storage.Store, run bool) (*core.Blockchain, neotest.Signer) {
var cfg = config.Blockchain{
ProtocolConfiguration: config.ProtocolConfiguration{
Magic: netmode.UnitTestNet,
MaxTraceableBlocks: MaxTraceableBlocks,
TimePerBlock: TimePerBlock,
StandbyCommittee: []string{hex.EncodeToString(committeeAcc.PublicKey().Bytes())},
ValidatorsCount: 1,
VerifyTransactions: true,
},
}
if f != nil {
f(&cfg)
}
if st == nil {
st = storage.NewMemoryStore()
}
log := zaptest.NewLogger(t)
bc, err := core.NewBlockchain(st, cfg, log)
require.NoError(t, err)
if run {
go bc.Run()
t.Cleanup(bc.Close)
}
return bc, neotest.NewMultiSigner(committeeAcc)
}
// NewMulti creates a new blockchain instance with four validators and six
// committee members. Otherwise, it does not differ much from NewSingle. The
// second value returned contains the validators Signer, the third -- the committee one.
func NewMulti(t testing.TB) (*core.Blockchain, neotest.Signer, neotest.Signer) {
return NewMultiWithCustomConfig(t, nil)
}
// NewMultiWithCustomConfig is similar to NewMulti, except it allows to override the
// default configuration.
func NewMultiWithCustomConfig(t testing.TB, f func(*config.Blockchain)) (*core.Blockchain, neotest.Signer, neotest.Signer) {
return NewMultiWithCustomConfigAndStore(t, f, nil, true)
}
// NewMultiWithCustomConfigAndStore is similar to NewMultiWithCustomConfig, but
// also allows to override backend Store being used. The last parameter controls if
// Run method is called on the Blockchain instance. If not, it is its caller's
// responsibility to do that before using the chain and
// to properly Close the chain when done.
func NewMultiWithCustomConfigAndStore(t testing.TB, f func(*config.Blockchain), st storage.Store, run bool) (*core.Blockchain, neotest.Signer, neotest.Signer) {
bc, validator, committee, err := NewMultiWithCustomConfigAndStoreNoCheck(t, f, st)
require.NoError(t, err)
if run {
go bc.Run()
t.Cleanup(bc.Close)
}
return bc, validator, committee
}
// NewMultiWithCustomConfigAndStoreNoCheck is similar to NewMultiWithCustomConfig,
// but do not perform Blockchain run and do not check Blockchain constructor error.
func NewMultiWithCustomConfigAndStoreNoCheck(t testing.TB, f func(*config.Blockchain), st storage.Store) (*core.Blockchain, neotest.Signer, neotest.Signer, error) {
var cfg = config.Blockchain{
ProtocolConfiguration: config.ProtocolConfiguration{
Magic: netmode.UnitTestNet,
MaxTraceableBlocks: MaxTraceableBlocks,
TimePerBlock: TimePerBlock,
StandbyCommittee: standByCommittee,
ValidatorsCount: 4,
VerifyTransactions: true,
},
}
if f != nil {
f(&cfg)
}
if st == nil {
st = storage.NewMemoryStore()
}
log := zaptest.NewLogger(t)
bc, err := core.NewBlockchain(st, cfg, log)
return bc, neotest.NewMultiSigner(multiValidatorAcc...), neotest.NewMultiSigner(multiCommitteeAcc...), err
}