7589733017
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.
583 lines
18 KiB
Go
583 lines
18 KiB
Go
package consensus
|
|
|
|
import (
|
|
"errors"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/nspcc-dev/dbft/block"
|
|
"github.com/nspcc-dev/dbft/payload"
|
|
"github.com/nspcc-dev/dbft/timer"
|
|
"github.com/nspcc-dev/neo-go/internal/random"
|
|
"github.com/nspcc-dev/neo-go/internal/testchain"
|
|
"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/fee"
|
|
"github.com/nspcc-dev/neo-go/pkg/core/native"
|
|
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
|
"github.com/nspcc-dev/neo-go/pkg/io"
|
|
npayload "github.com/nspcc-dev/neo-go/pkg/network/payload"
|
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
|
"github.com/nspcc-dev/neo-go/pkg/wallet"
|
|
"github.com/stretchr/testify/require"
|
|
"go.uber.org/zap/zaptest"
|
|
)
|
|
|
|
func TestNewService(t *testing.T) {
|
|
srv := newTestService(t)
|
|
tx := transaction.New([]byte{byte(opcode.PUSH1)}, 100000)
|
|
tx.ValidUntilBlock = 1
|
|
addSender(t, tx)
|
|
signTx(t, srv.Chain, tx)
|
|
require.NoError(t, srv.Chain.PoolTx(tx))
|
|
|
|
var txx []block.Transaction
|
|
require.NotPanics(t, func() { txx = srv.getVerifiedTx() })
|
|
require.Len(t, txx, 1)
|
|
require.Equal(t, tx, txx[0])
|
|
}
|
|
|
|
func TestNewWatchingService(t *testing.T) {
|
|
bc := newTestChain(t, false)
|
|
srv, err := NewService(Config{
|
|
Logger: zaptest.NewLogger(t),
|
|
Broadcast: func(*npayload.Extensible) {},
|
|
Chain: bc,
|
|
ProtocolConfiguration: bc.GetConfig().ProtocolConfiguration,
|
|
RequestTx: func(...util.Uint256) {},
|
|
StopTxFlow: func() {},
|
|
TimePerBlock: bc.GetConfig().TimePerBlock,
|
|
// No wallet provided.
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
require.NotPanics(t, srv.Start)
|
|
require.NotPanics(t, srv.Shutdown)
|
|
}
|
|
|
|
func initServiceNextConsensus(t *testing.T, newAcc *wallet.Account, offset uint32) (*service, *wallet.Account) {
|
|
acc, err := wallet.NewAccountFromWIF(testchain.WIF(testchain.IDToOrder(0)))
|
|
require.NoError(t, err)
|
|
priv := acc.PrivateKey()
|
|
require.NoError(t, acc.ConvertMultisig(1, keys.PublicKeys{priv.PublicKey()}))
|
|
|
|
bc := newSingleTestChain(t)
|
|
newPriv := newAcc.PrivateKey()
|
|
|
|
// Transfer funds to new validator.
|
|
b := smartcontract.NewBuilder()
|
|
b.InvokeWithAssert(bc.GoverningTokenHash(), "transfer",
|
|
acc.Contract.ScriptHash().BytesBE(), newPriv.GetScriptHash().BytesBE(), int64(native.NEOTotalSupply), nil)
|
|
|
|
b.InvokeWithAssert(bc.UtilityTokenHash(), "transfer",
|
|
acc.Contract.ScriptHash().BytesBE(), newPriv.GetScriptHash().BytesBE(), int64(10000_000_000_000), nil)
|
|
script, err := b.Script()
|
|
require.NoError(t, err)
|
|
|
|
tx := transaction.New(script, 21_000_000)
|
|
tx.ValidUntilBlock = bc.BlockHeight() + 1
|
|
tx.NetworkFee = 10_000_000
|
|
tx.Signers = []transaction.Signer{{Scopes: transaction.Global, Account: acc.Contract.ScriptHash()}}
|
|
require.NoError(t, acc.SignTx(netmode.UnitTestNet, tx))
|
|
require.NoError(t, bc.PoolTx(tx))
|
|
|
|
srv := newTestServiceWithChain(t, bc)
|
|
srv.dbft.Start(0)
|
|
|
|
// Register new candidate.
|
|
b.Reset()
|
|
b.InvokeWithAssert(bc.GoverningTokenHash(), "registerCandidate", newPriv.PublicKey().Bytes())
|
|
script, err = b.Script()
|
|
require.NoError(t, err)
|
|
|
|
tx = transaction.New(script, 1001_00000000)
|
|
tx.ValidUntilBlock = bc.BlockHeight() + 1
|
|
tx.NetworkFee = 20_000_000
|
|
tx.Signers = []transaction.Signer{{Scopes: transaction.Global, Account: newPriv.GetScriptHash()}}
|
|
require.NoError(t, newAcc.SignTx(netmode.UnitTestNet, tx))
|
|
|
|
require.NoError(t, bc.PoolTx(tx))
|
|
srv.dbft.OnTimeout(timer.HV{Height: srv.dbft.Context.BlockIndex})
|
|
|
|
cfg := bc.GetConfig()
|
|
for i := srv.dbft.BlockIndex; !cfg.ShouldUpdateCommitteeAt(i + offset); i++ {
|
|
srv.dbft.OnTimeout(timer.HV{Height: srv.dbft.Context.BlockIndex})
|
|
}
|
|
|
|
// Vote for new candidate.
|
|
b.Reset()
|
|
b.InvokeWithAssert(bc.GoverningTokenHash(), "vote",
|
|
newPriv.GetScriptHash(), newPriv.PublicKey().Bytes())
|
|
script, err = b.Script()
|
|
require.NoError(t, err)
|
|
|
|
tx = transaction.New(script, 20_000_000)
|
|
tx.ValidUntilBlock = bc.BlockHeight() + 1
|
|
tx.NetworkFee = 20_000_000
|
|
tx.Signers = []transaction.Signer{{Scopes: transaction.Global, Account: newPriv.GetScriptHash()}}
|
|
require.NoError(t, newAcc.SignTx(netmode.UnitTestNet, tx))
|
|
|
|
require.NoError(t, bc.PoolTx(tx))
|
|
srv.dbft.OnTimeout(timer.HV{Height: srv.dbft.BlockIndex})
|
|
|
|
return srv, acc
|
|
}
|
|
|
|
func TestService_NextConsensus(t *testing.T) {
|
|
newAcc, err := wallet.NewAccount()
|
|
require.NoError(t, err)
|
|
script, err := smartcontract.CreateMajorityMultiSigRedeemScript(keys.PublicKeys{newAcc.PublicKey()})
|
|
require.NoError(t, err)
|
|
|
|
checkNextConsensus := func(t *testing.T, bc *core.Blockchain, height uint32, h util.Uint160) {
|
|
hdrHash := bc.GetHeaderHash(height)
|
|
hdr, err := bc.GetHeader(hdrHash)
|
|
require.NoError(t, err)
|
|
require.Equal(t, h, hdr.NextConsensus)
|
|
}
|
|
|
|
t.Run("vote 1 block before update", func(t *testing.T) { // voting occurs every block in SingleTestChain
|
|
srv, acc := initServiceNextConsensus(t, newAcc, 1)
|
|
bc := srv.Chain.(*core.Blockchain)
|
|
|
|
height := bc.BlockHeight()
|
|
checkNextConsensus(t, bc, height, acc.Contract.ScriptHash())
|
|
// Reset <- we are here, update NextConsensus
|
|
// OnPersist <- update committee
|
|
// Block <-
|
|
|
|
srv.dbft.OnTimeout(timer.HV{Height: srv.dbft.BlockIndex})
|
|
checkNextConsensus(t, bc, height+1, hash.Hash160(script))
|
|
})
|
|
/*
|
|
t.Run("vote 2 blocks before update", func(t *testing.T) {
|
|
srv, acc := initServiceNextConsensus(t, newAcc, 2)
|
|
bc := srv.Chain.(*core.Blockchain)
|
|
defer bc.Close()
|
|
|
|
height := bc.BlockHeight()
|
|
checkNextConsensus(t, bc, height, acc.Contract.ScriptHash())
|
|
// Reset <- we are here
|
|
// OnPersist <- nothing to do
|
|
// Block <-
|
|
//
|
|
// Reset <- update next consensus
|
|
// OnPersist <- update committee
|
|
// Block <-
|
|
srv.dbft.OnTimeout(timer.HV{Height: srv.dbft.BlockIndex})
|
|
checkNextConsensus(t, bc, height+1, acc.Contract.ScriptHash())
|
|
|
|
srv.dbft.OnTimeout(timer.HV{Height: srv.dbft.BlockIndex})
|
|
checkNextConsensus(t, bc, height+2, hash.Hash160(script))
|
|
})
|
|
*/
|
|
}
|
|
|
|
func TestService_GetVerified(t *testing.T) {
|
|
srv := newTestService(t)
|
|
srv.dbft.Start(0)
|
|
var txs []*transaction.Transaction
|
|
for i := 0; i < 4; i++ {
|
|
tx := transaction.New([]byte{byte(opcode.PUSH1)}, 100000)
|
|
tx.Nonce = 123 + uint32(i)
|
|
tx.ValidUntilBlock = 1
|
|
txs = append(txs, tx)
|
|
}
|
|
addSender(t, txs...)
|
|
signTx(t, srv.Chain, txs...)
|
|
require.NoError(t, srv.Chain.PoolTx(txs[3]))
|
|
|
|
hashes := []util.Uint256{txs[0].Hash(), txs[1].Hash(), txs[2].Hash()}
|
|
|
|
// Everyone sends a message.
|
|
for i := 0; i < 4; i++ {
|
|
p := new(Payload)
|
|
// One PrepareRequest and three ChangeViews.
|
|
if i == 1 {
|
|
p.SetType(payload.PrepareRequestType)
|
|
p.SetPayload(&prepareRequest{prevHash: srv.Chain.CurrentBlockHash(), transactionHashes: hashes})
|
|
} else {
|
|
p.SetType(payload.ChangeViewType)
|
|
p.SetPayload(&changeView{newViewNumber: 1, timestamp: uint64(time.Now().UnixNano() / nsInMs)})
|
|
}
|
|
p.SetHeight(1)
|
|
p.SetValidatorIndex(uint16(i))
|
|
|
|
priv, _ := getTestValidator(i)
|
|
require.NoError(t, p.Sign(priv))
|
|
|
|
// Skip srv.OnPayload, because the service is not really started.
|
|
srv.dbft.OnReceive(p)
|
|
}
|
|
require.Equal(t, uint8(1), srv.dbft.ViewNumber)
|
|
require.Equal(t, hashes, srv.lastProposal)
|
|
|
|
t.Run("new transactions will be proposed in case of failure", func(t *testing.T) {
|
|
txx := srv.getVerifiedTx()
|
|
require.Equal(t, 1, len(txx), "there is only 1 tx in mempool")
|
|
require.Equal(t, txs[3], txx[0])
|
|
})
|
|
|
|
t.Run("more than half of the last proposal will be reused", func(t *testing.T) {
|
|
for _, tx := range txs[:2] {
|
|
require.NoError(t, srv.Chain.PoolTx(tx))
|
|
}
|
|
|
|
txx := srv.getVerifiedTx()
|
|
require.Contains(t, txx, txs[0])
|
|
require.Contains(t, txx, txs[1])
|
|
require.NotContains(t, txx, txs[2])
|
|
})
|
|
}
|
|
|
|
func TestService_ValidatePayload(t *testing.T) {
|
|
srv := newTestService(t)
|
|
priv, _ := getTestValidator(1)
|
|
p := new(Payload)
|
|
p.Sender = priv.GetScriptHash()
|
|
p.SetPayload(&prepareRequest{})
|
|
|
|
t.Run("invalid validator index", func(t *testing.T) {
|
|
p.SetValidatorIndex(11)
|
|
require.NoError(t, p.Sign(priv))
|
|
|
|
var ok bool
|
|
require.NotPanics(t, func() { ok = srv.validatePayload(p) })
|
|
require.False(t, ok)
|
|
})
|
|
|
|
t.Run("wrong validator index", func(t *testing.T) {
|
|
p.SetValidatorIndex(2)
|
|
require.NoError(t, p.Sign(priv))
|
|
require.False(t, srv.validatePayload(p))
|
|
})
|
|
|
|
t.Run("invalid sender", func(t *testing.T) {
|
|
p.SetValidatorIndex(1)
|
|
p.Sender = util.Uint160{}
|
|
require.NoError(t, p.Sign(priv))
|
|
require.False(t, srv.validatePayload(p))
|
|
})
|
|
|
|
t.Run("normal case", func(t *testing.T) {
|
|
p.SetValidatorIndex(1)
|
|
p.Sender = priv.GetScriptHash()
|
|
require.NoError(t, p.Sign(priv))
|
|
require.True(t, srv.validatePayload(p))
|
|
})
|
|
}
|
|
|
|
func TestService_getTx(t *testing.T) {
|
|
srv := newTestService(t)
|
|
|
|
t.Run("transaction in mempool", func(t *testing.T) {
|
|
tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
|
|
tx.Nonce = 1234
|
|
tx.ValidUntilBlock = 1
|
|
addSender(t, tx)
|
|
signTx(t, srv.Chain, tx)
|
|
h := tx.Hash()
|
|
|
|
require.Equal(t, nil, srv.getTx(h))
|
|
|
|
require.NoError(t, srv.Chain.PoolTx(tx))
|
|
|
|
got := srv.getTx(h)
|
|
require.NotNil(t, got)
|
|
require.Equal(t, h, got.Hash())
|
|
})
|
|
|
|
t.Run("transaction in local cache", func(t *testing.T) {
|
|
tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
|
|
tx.Nonce = 4321
|
|
tx.ValidUntilBlock = 1
|
|
h := tx.Hash()
|
|
|
|
require.Equal(t, nil, srv.getTx(h))
|
|
|
|
srv.txx.Add(tx)
|
|
|
|
got := srv.getTx(h)
|
|
require.NotNil(t, got)
|
|
require.Equal(t, h, got.Hash())
|
|
})
|
|
}
|
|
|
|
func TestService_PrepareRequest(t *testing.T) {
|
|
srv := newTestServiceWithState(t, true)
|
|
srv.dbft.Start(0)
|
|
t.Cleanup(srv.dbft.Timer.Stop)
|
|
|
|
priv, _ := getTestValidator(1)
|
|
p := new(Payload)
|
|
p.SetValidatorIndex(1)
|
|
|
|
prevHash := srv.Chain.CurrentBlockHash()
|
|
|
|
checkRequest := func(t *testing.T, expectedErr error, req *prepareRequest) {
|
|
p.SetPayload(req)
|
|
require.NoError(t, p.Sign(priv))
|
|
err := srv.verifyRequest(p)
|
|
if expectedErr == nil {
|
|
require.NoError(t, err)
|
|
return
|
|
}
|
|
require.True(t, errors.Is(err, expectedErr), "got: %v", err)
|
|
}
|
|
|
|
checkRequest(t, errInvalidVersion, &prepareRequest{version: 0xFF, prevHash: prevHash})
|
|
checkRequest(t, errInvalidPrevHash, &prepareRequest{prevHash: random.Uint256()})
|
|
checkRequest(t, errInvalidStateRoot, &prepareRequest{
|
|
stateRootEnabled: true,
|
|
prevHash: prevHash,
|
|
})
|
|
|
|
sr, err := srv.Chain.GetStateRoot(srv.dbft.BlockIndex - 1)
|
|
require.NoError(t, err)
|
|
|
|
checkRequest(t, errInvalidTransactionsCount, &prepareRequest{stateRootEnabled: true,
|
|
prevHash: prevHash,
|
|
stateRoot: sr.Root,
|
|
transactionHashes: make([]util.Uint256, srv.ProtocolConfiguration.MaxTransactionsPerBlock+1),
|
|
})
|
|
|
|
checkRequest(t, nil, &prepareRequest{
|
|
stateRootEnabled: true,
|
|
prevHash: prevHash,
|
|
stateRoot: sr.Root,
|
|
})
|
|
}
|
|
|
|
func TestService_OnPayload(t *testing.T) {
|
|
srv := newTestService(t)
|
|
// This test directly reads things from srv.messages that normally
|
|
// is read by internal goroutine started with Start(). So let's
|
|
// pretend we really did start already.
|
|
srv.started.Store(true)
|
|
|
|
priv, _ := getTestValidator(1)
|
|
p := new(Payload)
|
|
p.SetValidatorIndex(1)
|
|
p.SetPayload(&prepareRequest{})
|
|
p.encodeData()
|
|
|
|
// sender is invalid
|
|
require.NoError(t, srv.OnPayload(&p.Extensible))
|
|
shouldNotReceive(t, srv.messages)
|
|
|
|
p = new(Payload)
|
|
p.SetValidatorIndex(1)
|
|
p.Sender = priv.GetScriptHash()
|
|
p.SetPayload(&prepareRequest{})
|
|
require.NoError(t, p.Sign(priv))
|
|
require.NoError(t, srv.OnPayload(&p.Extensible))
|
|
shouldReceive(t, srv.messages)
|
|
}
|
|
|
|
func TestVerifyBlock(t *testing.T) {
|
|
srv := newTestService(t)
|
|
|
|
bc := srv.Chain.(*core.Blockchain)
|
|
srv.lastTimestamp = 1
|
|
t.Run("good empty", func(t *testing.T) {
|
|
b := testchain.NewBlock(t, bc, 1, 0)
|
|
require.True(t, srv.verifyBlock(&neoBlock{Block: *b}))
|
|
})
|
|
t.Run("good pooled tx", func(t *testing.T) {
|
|
tx := transaction.New([]byte{byte(opcode.RET)}, 100000)
|
|
tx.ValidUntilBlock = 1
|
|
addSender(t, tx)
|
|
signTx(t, srv.Chain, tx)
|
|
require.NoError(t, srv.Chain.PoolTx(tx))
|
|
b := testchain.NewBlock(t, bc, 1, 0, tx)
|
|
require.True(t, srv.verifyBlock(&neoBlock{Block: *b}))
|
|
})
|
|
t.Run("good non-pooled tx", func(t *testing.T) {
|
|
tx := transaction.New([]byte{byte(opcode.RET)}, 100000)
|
|
tx.ValidUntilBlock = 1
|
|
addSender(t, tx)
|
|
signTx(t, srv.Chain, tx)
|
|
b := testchain.NewBlock(t, bc, 1, 0, tx)
|
|
require.True(t, srv.verifyBlock(&neoBlock{Block: *b}))
|
|
})
|
|
t.Run("good conflicting tx", func(t *testing.T) {
|
|
initGAS := srv.Chain.GetConfig().InitialGASSupply
|
|
tx1 := transaction.New([]byte{byte(opcode.RET)}, 100000)
|
|
tx1.NetworkFee = int64(initGAS)/2 + 1
|
|
tx1.ValidUntilBlock = 1
|
|
addSender(t, tx1)
|
|
signTx(t, srv.Chain, tx1)
|
|
tx2 := transaction.New([]byte{byte(opcode.RET)}, 100000)
|
|
tx2.NetworkFee = int64(initGAS)/2 + 1
|
|
tx2.ValidUntilBlock = 1
|
|
addSender(t, tx2)
|
|
signTx(t, srv.Chain, tx2)
|
|
require.NoError(t, srv.Chain.PoolTx(tx1))
|
|
require.Error(t, srv.Chain.PoolTx(tx2))
|
|
b := testchain.NewBlock(t, bc, 1, 0, tx2)
|
|
require.True(t, srv.verifyBlock(&neoBlock{Block: *b}))
|
|
})
|
|
t.Run("bad old", func(t *testing.T) {
|
|
b := testchain.NewBlock(t, bc, 1, 0)
|
|
b.Index = srv.Chain.BlockHeight()
|
|
require.False(t, srv.verifyBlock(&neoBlock{Block: *b}))
|
|
})
|
|
t.Run("bad big size", func(t *testing.T) {
|
|
script := make([]byte, int(srv.ProtocolConfiguration.MaxBlockSize))
|
|
script[0] = byte(opcode.RET)
|
|
tx := transaction.New(script, 100000)
|
|
tx.ValidUntilBlock = 1
|
|
addSender(t, tx)
|
|
signTx(t, srv.Chain, tx)
|
|
b := testchain.NewBlock(t, bc, 1, 0, tx)
|
|
require.False(t, srv.verifyBlock(&neoBlock{Block: *b}))
|
|
})
|
|
t.Run("bad timestamp", func(t *testing.T) {
|
|
b := testchain.NewBlock(t, bc, 1, 0)
|
|
b.Timestamp = srv.lastTimestamp - 1
|
|
require.False(t, srv.verifyBlock(&neoBlock{Block: *b}))
|
|
})
|
|
t.Run("bad tx", func(t *testing.T) {
|
|
tx := transaction.New([]byte{byte(opcode.RET)}, 100000)
|
|
tx.ValidUntilBlock = 1
|
|
addSender(t, tx)
|
|
signTx(t, srv.Chain, tx)
|
|
tx.Scripts[0].InvocationScript[16] = ^tx.Scripts[0].InvocationScript[16]
|
|
b := testchain.NewBlock(t, bc, 1, 0, tx)
|
|
require.False(t, srv.verifyBlock(&neoBlock{Block: *b}))
|
|
})
|
|
t.Run("bad big sys fee", func(t *testing.T) {
|
|
txes := make([]*transaction.Transaction, 2)
|
|
for i := range txes {
|
|
txes[i] = transaction.New([]byte{byte(opcode.RET)}, srv.ProtocolConfiguration.MaxBlockSystemFee/2+1)
|
|
txes[i].ValidUntilBlock = 1
|
|
addSender(t, txes[i])
|
|
signTx(t, srv.Chain, txes[i])
|
|
}
|
|
b := testchain.NewBlock(t, bc, 1, 0, txes...)
|
|
require.False(t, srv.verifyBlock(&neoBlock{Block: *b}))
|
|
})
|
|
}
|
|
|
|
func shouldReceive(t *testing.T, ch chan Payload) {
|
|
select {
|
|
case <-ch:
|
|
default:
|
|
require.Fail(t, "missing expected message")
|
|
}
|
|
}
|
|
|
|
func shouldNotReceive(t *testing.T, ch chan Payload) {
|
|
select {
|
|
case <-ch:
|
|
require.Fail(t, "unexpected message receive")
|
|
default:
|
|
}
|
|
}
|
|
|
|
func newTestServiceWithState(t *testing.T, stateRootInHeader bool) *service {
|
|
return newTestServiceWithChain(t, newTestChain(t, stateRootInHeader))
|
|
}
|
|
|
|
func newTestService(t *testing.T) *service {
|
|
return newTestServiceWithState(t, false)
|
|
}
|
|
|
|
func newTestServiceWithChain(t *testing.T, bc *core.Blockchain) *service {
|
|
srv, err := NewService(Config{
|
|
Logger: zaptest.NewLogger(t),
|
|
Broadcast: func(*npayload.Extensible) {},
|
|
Chain: bc,
|
|
ProtocolConfiguration: bc.GetConfig().ProtocolConfiguration,
|
|
RequestTx: func(...util.Uint256) {},
|
|
StopTxFlow: func() {},
|
|
TimePerBlock: bc.GetConfig().TimePerBlock,
|
|
Wallet: config.Wallet{
|
|
Path: "./testdata/wallet1.json",
|
|
Password: "one",
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
return srv.(*service)
|
|
}
|
|
|
|
func getTestValidator(i int) (*privateKey, *publicKey) {
|
|
key := testchain.PrivateKey(i)
|
|
return &privateKey{PrivateKey: key}, &publicKey{PublicKey: key.PublicKey()}
|
|
}
|
|
|
|
func newSingleTestChain(t *testing.T) *core.Blockchain {
|
|
configPath := "../../config/protocol.unit_testnet.single.yml"
|
|
cfg, err := config.LoadFile(configPath)
|
|
require.NoError(t, err, "could not load config")
|
|
|
|
chain, err := core.NewBlockchain(storage.NewMemoryStore(), cfg.Blockchain(), zaptest.NewLogger(t))
|
|
require.NoError(t, err, "could not create chain")
|
|
|
|
go chain.Run()
|
|
t.Cleanup(chain.Close)
|
|
return chain
|
|
}
|
|
|
|
func newTestChain(t *testing.T, stateRootInHeader bool) *core.Blockchain {
|
|
unitTestNetCfg, err := config.Load("../../config", netmode.UnitTestNet)
|
|
require.NoError(t, err)
|
|
unitTestNetCfg.ProtocolConfiguration.StateRootInHeader = stateRootInHeader
|
|
|
|
chain, err := core.NewBlockchain(storage.NewMemoryStore(), unitTestNetCfg.Blockchain(), zaptest.NewLogger(t))
|
|
require.NoError(t, err)
|
|
|
|
go chain.Run()
|
|
t.Cleanup(chain.Close)
|
|
return chain
|
|
}
|
|
|
|
var neoOwner = testchain.MultisigScriptHash()
|
|
|
|
func addSender(t *testing.T, txs ...*transaction.Transaction) {
|
|
for _, tx := range txs {
|
|
tx.Signers = []transaction.Signer{
|
|
{
|
|
Account: neoOwner,
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
func signTx(t *testing.T, bc Ledger, txs ...*transaction.Transaction) {
|
|
validators := make([]*keys.PublicKey, 4)
|
|
privNetKeys := make([]*keys.PrivateKey, 4)
|
|
for i := 0; i < 4; i++ {
|
|
privNetKeys[i] = testchain.PrivateKey(i)
|
|
validators[i] = privNetKeys[i].PublicKey()
|
|
}
|
|
privNetKeys = privNetKeys[:3]
|
|
rawScript, err := smartcontract.CreateMultiSigRedeemScript(3, validators)
|
|
require.NoError(t, err)
|
|
for _, tx := range txs {
|
|
size := io.GetVarSize(tx)
|
|
netFee, sizeDelta := fee.Calculate(bc.GetBaseExecFee(), rawScript)
|
|
tx.NetworkFee += +netFee
|
|
size += sizeDelta
|
|
tx.NetworkFee += int64(size) * bc.FeePerByte()
|
|
|
|
buf := io.NewBufBinWriter()
|
|
for _, key := range privNetKeys {
|
|
signature := key.SignHashable(uint32(testchain.Network()), tx)
|
|
emit.Bytes(buf.BinWriter, signature)
|
|
}
|
|
|
|
tx.Scripts = []transaction.Witness{{
|
|
InvocationScript: buf.Bytes(),
|
|
VerificationScript: rawScript,
|
|
}}
|
|
}
|
|
}
|