neo-go/pkg/neotest/chain/chain.go
Roman Khimov 7589733017 config: add a special Blockchain type to configure Blockchain
And include some node-specific configurations there with backwards
compatibility. Note that in the future we'll remove Ledger's
fields from the ProtocolConfiguration and it'll be possible to access them in
Blockchain directly (not via .Ledger).

The other option tried was using two configuration types separately, but that
incurs more changes to the codebase, single structure that behaves almost like
the old one is better for backwards compatibility.

Fixes #2676.
2022-12-07 17:35:53 +03:00

225 lines
7.9 KiB
Go

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,
},
Ledger: config.Ledger{
VerifyBlocks: 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,
},
Ledger: config.Ledger{
VerifyBlocks: 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
}