core: rebase core tests onto neotest

This commit is contained in:
Anna Shaleva 2022-03-11 17:47:59 +03:00
parent 35ef58a47e
commit 8965441288
16 changed files with 2401 additions and 2673 deletions

View file

@ -8,6 +8,7 @@ import (
"path" "path"
"path/filepath" "path/filepath"
"testing" "testing"
"time"
"github.com/nspcc-dev/neo-go/pkg/config" "github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/core/chaindump" "github.com/nspcc-dev/neo-go/pkg/core/chaindump"
@ -32,9 +33,15 @@ const (
// It is also used to retrieve smart contracts that should be deployed to // It is also used to retrieve smart contracts that should be deployed to
// Basic chain. // Basic chain.
basicChainPrefix = "../rpc/server/testdata/" basicChainPrefix = "../rpc/server/testdata/"
// bcPersistInterval is the time period Blockchain persists changes to the
// underlying storage.
bcPersistInterval = time.Second
) )
var notaryModulePath = filepath.Join("..", "services", "notary") var (
notaryModulePath = filepath.Join("..", "services", "notary")
pathToInternalContracts = filepath.Join("..", "..", "internal", "contracts")
)
// TestCreateBasicChain generates "../rpc/testdata/testblocks.acc" file which // TestCreateBasicChain generates "../rpc/testdata/testblocks.acc" file which
// contains data for RPC unit tests. It also is a nice integration test. // contains data for RPC unit tests. It also is a nice integration test.

View file

@ -1,34 +1,33 @@
package core package core_test
import ( import (
"fmt" "fmt"
"testing" "testing"
"github.com/nspcc-dev/neo-go/internal/random" "github.com/nspcc-dev/neo-go/internal/random"
"github.com/nspcc-dev/neo-go/internal/testchain"
"github.com/nspcc-dev/neo-go/pkg/config/netmode" "github.com/nspcc-dev/neo-go/pkg/config/netmode"
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
"github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/core/storage" "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/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/neotest"
"github.com/nspcc-dev/neo-go/pkg/neotest/chain"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
"github.com/nspcc-dev/neo-go/pkg/vm/emit" "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/vm/opcode"
"github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func BenchmarkVerifyWitness(t *testing.B) { func BenchmarkBlockchain_VerifyWitness(t *testing.B) {
bc := newTestChain(t) bc, acc := chain.NewSingle(t)
acc, err := wallet.NewAccount() e := neotest.NewExecutor(t, bc, acc, acc)
require.NoError(t, err) tx := e.NewTx(t, []neotest.Signer{acc}, e.NativeHash(t, nativenames.Gas), "transfer", acc.ScriptHash(), acc.Script(), 1, nil)
tx := bc.newTestTx(acc.Contract.ScriptHash(), []byte{byte(opcode.PUSH1)})
require.NoError(t, acc.SignTx(netmode.UnitTestNet, tx))
t.ResetTimer() t.ResetTimer()
for n := 0; n < t.N; n++ { for n := 0; n < t.N; n++ {
_, _ = bc.VerifyWitness(tx.Signers[0].Account, tx, &tx.Scripts[0], 100000000) _, err := bc.VerifyWitness(tx.Signers[0].Account, tx, &tx.Scripts[0], 100000000)
require.NoError(t, err)
} }
} }
@ -57,38 +56,35 @@ func BenchmarkBlockchain_ForEachNEP17Transfer(t *testing.B) {
func benchmarkForEachNEP17Transfer(t *testing.B, ps storage.Store, startFromBlock, nBlocksToTake int) { func benchmarkForEachNEP17Transfer(t *testing.B, ps storage.Store, startFromBlock, nBlocksToTake int) {
var ( var (
nonce uint32 = 1
chainHeight = 2_100 // constant chain height to be able to compare paging results chainHeight = 2_100 // constant chain height to be able to compare paging results
transfersPerBlock = state.TokenTransferBatchSize/4 + // 4 blocks per batch transfersPerBlock = state.TokenTransferBatchSize/4 + // 4 blocks per batch
state.TokenTransferBatchSize/32 // shift state.TokenTransferBatchSize/32 // shift
) )
bc := newTestChainWithCustomCfgAndStore(t, ps, nil) bc, validators, committee := chain.NewMultiWithCustomConfigAndStore(t, nil, ps, true)
gasHash := bc.contracts.GAS.Hash
e := neotest.NewExecutor(t, bc, validators, committee)
gasHash := e.NativeHash(t, nativenames.Gas)
acc := random.Uint160() acc := random.Uint160()
from := e.Validator.ScriptHash()
for j := 0; j < chainHeight; j++ { for j := 0; j < chainHeight; j++ {
w := io.NewBufBinWriter() w := io.NewBufBinWriter()
for i := 0; i < transfersPerBlock; i++ { for i := 0; i < transfersPerBlock; i++ {
emit.AppCall(w.BinWriter, gasHash, "transfer", callflag.All, testchain.MultisigScriptHash(), acc, 1, nil) emit.AppCall(w.BinWriter, gasHash, "transfer", callflag.All, from, acc, 1, nil)
emit.Opcodes(w.BinWriter, opcode.ASSERT) emit.Opcodes(w.BinWriter, opcode.ASSERT)
require.NoError(t, w.Err)
} }
require.NoError(t, w.Err)
script := w.Bytes() script := w.Bytes()
tx := transaction.New(script, int64(1100_0000*transfersPerBlock)) tx := transaction.New(script, int64(1100_0000*transfersPerBlock))
tx.NetworkFee = 1_0000_000
tx.ValidUntilBlock = bc.BlockHeight() + 1 tx.ValidUntilBlock = bc.BlockHeight() + 1
tx.Nonce = nonce tx.Nonce = neotest.Nonce()
nonce++ tx.Signers = []transaction.Signer{{Account: from, Scopes: transaction.CalledByEntry}}
tx.Signers = []transaction.Signer{{ require.NoError(t, validators.SignTx(netmode.UnitTestNet, tx))
Account: testchain.MultisigScriptHash(), e.AddNewBlock(t, tx)
Scopes: transaction.CalledByEntry, e.CheckHalt(t, tx.Hash())
AllowedContracts: nil,
AllowedGroups: nil,
}}
require.NoError(t, testchain.SignTx(bc, tx))
b := bc.newBlock(tx)
require.NoError(t, bc.AddBlock(b))
checkTxHalt(t, bc, tx.Hash())
} }
newestB, err := bc.GetBlock(bc.GetHeaderHash(int(bc.BlockHeight()) - startFromBlock + 1)) newestB, err := bc.GetBlock(bc.GetHeaderHash(int(bc.BlockHeight()) - startFromBlock + 1))

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,42 +1,21 @@
package core package core
import ( import (
"encoding/json"
"fmt"
"math/big"
"path/filepath"
"strings"
"testing" "testing"
"time" "time"
"github.com/nspcc-dev/neo-go/internal/testchain" "github.com/nspcc-dev/neo-go/internal/testchain"
"github.com/nspcc-dev/neo-go/pkg/compiler"
"github.com/nspcc-dev/neo-go/pkg/config" "github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/core/block" "github.com/nspcc-dev/neo-go/pkg/core/block"
"github.com/nspcc-dev/neo-go/pkg/core/blockchainer"
"github.com/nspcc-dev/neo-go/pkg/core/fee"
"github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/core/storage" "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/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm"
"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/vm/stackitem"
"github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.uber.org/zap" "go.uber.org/zap"
"go.uber.org/zap/zaptest" "go.uber.org/zap/zaptest"
) )
// multisig address which possess all NEO.
var neoOwner = testchain.MultisigScriptHash()
// newTestChain should be called before newBlock invocation to properly setup // newTestChain should be called before newBlock invocation to properly setup
// global state. // global state.
func newTestChain(t testing.TB) *Blockchain { func newTestChain(t testing.TB) *Blockchain {
@ -54,24 +33,6 @@ func newTestChainWithCustomCfgAndStore(t testing.TB, st storage.Store, f func(*c
return chain return chain
} }
func newLevelDBForTesting(t testing.TB) storage.Store {
dbPath := t.TempDir()
dbOptions := storage.LevelDBOptions{
DataDirectoryPath: dbPath,
}
newLevelStore, err := storage.NewLevelDBStore(dbOptions)
require.Nil(t, err, "NewLevelDBStore error")
return newLevelStore
}
func newBoltStoreForTesting(t testing.TB) storage.Store {
d := t.TempDir()
dbPath := filepath.Join(d, "test_bolt_db")
boltDBStore, err := storage.NewBoltDBStore(storage.BoltDBOptions{FilePath: dbPath})
require.NoError(t, err)
return boltDBStore
}
func initTestChain(t testing.TB, st storage.Store, f func(*config.Config)) *Blockchain { func initTestChain(t testing.TB, st storage.Store, f func(*config.Config)) *Blockchain {
chain, err := initTestChainNoCheck(t, st, f) chain, err := initTestChainNoCheck(t, st, f)
require.NoError(t, err) require.NoError(t, err)
@ -165,246 +126,3 @@ func (bc *Blockchain) genBlocks(n int) ([]*block.Block, error) {
} }
return blocks, nil return blocks, nil
} }
func TestBug1728(t *testing.T) {
src := `package example
import "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
func init() { if true { } else { } }
func _deploy(_ interface{}, isUpdate bool) {
runtime.Log("Deploy")
}`
nf, di, err := compiler.CompileWithOptions("foo.go", strings.NewReader(src), nil)
require.NoError(t, err)
m, err := di.ConvertToManifest(&compiler.Options{Name: "TestContract"})
require.NoError(t, err)
rawManifest, err := json.Marshal(m)
require.NoError(t, err)
rawNef, err := nf.Bytes()
require.NoError(t, err)
bc := newTestChain(t)
aer, err := invokeContractMethod(bc, 10000000000,
bc.contracts.Management.Hash, "deploy", rawNef, rawManifest)
require.NoError(t, err)
require.Equal(t, aer.VMState, vm.HaltState)
}
func newNEP17Transfer(sc, from, to util.Uint160, amount int64, additionalArgs ...interface{}) *transaction.Transaction {
return newNEP17TransferWithAssert(sc, from, to, amount, true, additionalArgs...)
}
func newNEP17TransferWithAssert(sc, from, to util.Uint160, amount int64, needAssert bool, additionalArgs ...interface{}) *transaction.Transaction {
w := io.NewBufBinWriter()
emit.AppCall(w.BinWriter, sc, "transfer", callflag.All, from, to, amount, additionalArgs)
if needAssert {
emit.Opcodes(w.BinWriter, opcode.ASSERT)
}
if w.Err != nil {
panic(fmt.Errorf("failed to create NEP-17 transfer transaction: %w", w.Err))
}
script := w.Bytes()
return transaction.New(script, 11000000)
}
func addSigners(sender util.Uint160, txs ...*transaction.Transaction) {
for _, tx := range txs {
tx.Signers = []transaction.Signer{{
Account: sender,
Scopes: transaction.Global,
AllowedContracts: nil,
AllowedGroups: nil,
}}
}
}
// Signer can be either bool or *wallet.Account.
// In the first case `true` means sign by committee, `false` means sign by validators.
func prepareContractMethodInvokeGeneric(chain *Blockchain, sysfee int64,
hash util.Uint160, method string, signer interface{}, args ...interface{}) (*transaction.Transaction, error) {
w := io.NewBufBinWriter()
emit.AppCall(w.BinWriter, hash, method, callflag.All, args...)
if w.Err != nil {
return nil, w.Err
}
script := w.Bytes()
tx := transaction.New(script, 0)
tx.ValidUntilBlock = chain.blockHeight + 1
var err error
switch s := signer.(type) {
case bool:
if s {
addSigners(testchain.CommitteeScriptHash(), tx)
setTxSystemFee(chain, sysfee, tx)
err = testchain.SignTxCommittee(chain, tx)
} else {
addSigners(neoOwner, tx)
setTxSystemFee(chain, sysfee, tx)
err = testchain.SignTx(chain, tx)
}
case *wallet.Account:
signTxWithAccounts(chain, sysfee, tx, s)
case []*wallet.Account:
signTxWithAccounts(chain, sysfee, tx, s...)
default:
panic("invalid signer")
}
if err != nil {
return nil, err
}
return tx, nil
}
func setTxSystemFee(bc *Blockchain, sysFee int64, tx *transaction.Transaction) {
if sysFee >= 0 {
tx.SystemFee = sysFee
return
}
lastBlock := bc.topBlock.Load().(*block.Block)
b := &block.Block{
Header: block.Header{
Index: lastBlock.Index + 1,
Timestamp: lastBlock.Timestamp + 1000,
},
Transactions: []*transaction.Transaction{tx},
}
ttx := *tx // prevent setting 'hash' field
ic := bc.GetTestVM(trigger.Application, &ttx, b)
defer ic.Finalize()
ic.VM.LoadWithFlags(tx.Script, callflag.All)
_ = ic.VM.Run()
tx.SystemFee = ic.VM.GasConsumed()
}
func signTxWithAccounts(chain *Blockchain, sysFee int64, tx *transaction.Transaction, accs ...*wallet.Account) {
scope := transaction.CalledByEntry
for _, acc := range accs {
accH, _ := address.StringToUint160(acc.Address)
tx.Signers = append(tx.Signers, transaction.Signer{
Account: accH,
Scopes: scope,
})
scope = transaction.Global
}
setTxSystemFee(chain, sysFee, tx)
size := io.GetVarSize(tx)
for _, acc := range accs {
if acc.Contract.Deployed {
// don't need precise calculation for tests
tx.NetworkFee += 1000_0000
continue
}
netFee, sizeDelta := fee.Calculate(chain.GetBaseExecFee(), acc.Contract.Script)
size += sizeDelta
tx.NetworkFee += netFee
}
tx.NetworkFee += int64(size) * chain.FeePerByte()
for _, acc := range accs {
if err := acc.SignTx(testchain.Network(), tx); err != nil {
panic(err)
}
}
}
func persistBlock(chain *Blockchain, txs ...*transaction.Transaction) ([]*state.AppExecResult, error) {
b := chain.newBlock(txs...)
err := chain.AddBlock(b)
if err != nil {
return nil, err
}
aers := make([]*state.AppExecResult, len(txs))
for i, tx := range txs {
res, err := chain.GetAppExecResults(tx.Hash(), trigger.Application)
if err != nil {
return nil, err
}
aers[i] = &res[0]
}
return aers, nil
}
func invokeContractMethod(chain *Blockchain, sysfee int64, hash util.Uint160, method string, args ...interface{}) (*state.AppExecResult, error) {
return invokeContractMethodGeneric(chain, sysfee, hash, method, false, args...)
}
func invokeContractMethodGeneric(chain *Blockchain, sysfee int64, hash util.Uint160, method string,
signer interface{}, args ...interface{}) (*state.AppExecResult, error) {
tx, err := prepareContractMethodInvokeGeneric(chain, sysfee, hash,
method, signer, args...)
if err != nil {
return nil, err
}
aers, err := persistBlock(chain, tx)
if err != nil {
return nil, err
}
return aers[0], nil
}
func transferTokenFromMultisigAccountCheckOK(t *testing.T, chain *Blockchain, to, tokenHash util.Uint160, amount int64, additionalArgs ...interface{}) {
transferTx := transferTokenFromMultisigAccount(t, chain, to, tokenHash, amount, additionalArgs...)
res, err := chain.GetAppExecResults(transferTx.Hash(), trigger.Application)
require.NoError(t, err)
require.Equal(t, vm.HaltState, res[0].VMState)
require.Equal(t, 0, len(res[0].Stack))
}
func transferTokenFromMultisigAccount(t *testing.T, chain *Blockchain, to, tokenHash util.Uint160, amount int64, additionalArgs ...interface{}) *transaction.Transaction {
return transferTokenFromMultisigAccountWithAssert(t, chain, to, tokenHash, amount, true, additionalArgs...)
}
func transferTokenFromMultisigAccountWithAssert(t *testing.T, chain *Blockchain, to, tokenHash util.Uint160, amount int64, needAssert bool, additionalArgs ...interface{}) *transaction.Transaction {
transferTx := newNEP17TransferWithAssert(tokenHash, testchain.MultisigScriptHash(), to, amount, needAssert, additionalArgs...)
transferTx.SystemFee = 100000000
transferTx.ValidUntilBlock = chain.BlockHeight() + 1
addSigners(neoOwner, transferTx)
require.NoError(t, testchain.SignTx(chain, transferTx))
b := chain.newBlock(transferTx)
require.NoError(t, chain.AddBlock(b))
return transferTx
}
func checkResult(t *testing.T, result *state.AppExecResult, expected stackitem.Item) {
require.Equal(t, vm.HaltState, result.VMState, result.FaultException)
require.Equal(t, 1, len(result.Stack))
require.Equal(t, expected, result.Stack[0])
}
func checkTxHalt(t testing.TB, bc *Blockchain, h util.Uint256) {
aer, err := bc.GetAppExecResults(h, trigger.Application)
require.NoError(t, err)
require.Equal(t, 1, len(aer))
require.Equal(t, vm.HaltState, aer[0].VMState, aer[0].FaultException)
}
func checkFAULTState(t *testing.T, result *state.AppExecResult) {
require.Equal(t, vm.FaultState, result.VMState)
}
func checkBalanceOf(t *testing.T, chain *Blockchain, addr util.Uint160, expected int) {
balance := chain.GetUtilityTokenBalance(addr)
require.Equal(t, int64(expected), balance.Int64())
}
type NotaryFeerStub struct {
bc blockchainer.Blockchainer
}
func (f NotaryFeerStub) FeePerByte() int64 { return f.bc.FeePerByte() }
func (f NotaryFeerStub) GetUtilityTokenBalance(acc util.Uint160) *big.Int {
return f.bc.GetNotaryBalance(acc)
}
func (f NotaryFeerStub) BlockHeight() uint32 { return f.bc.BlockHeight() }
func (f NotaryFeerStub) P2PSigExtensionsEnabled() bool { return f.bc.P2PSigExtensionsEnabled() }
func NewNotaryFeerStub(bc blockchainer.Blockchainer) NotaryFeerStub {
return NotaryFeerStub{
bc: bc,
}
}

View file

@ -3,20 +3,18 @@ package core
import ( import (
"errors" "errors"
"fmt" "fmt"
"math"
"math/big" "math/big"
"path/filepath"
"testing" "testing"
"github.com/nspcc-dev/neo-go/internal/contracts" "github.com/nspcc-dev/neo-go/internal/contracts"
"github.com/nspcc-dev/neo-go/internal/random" "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"
"github.com/nspcc-dev/neo-go/pkg/config/netmode" "github.com/nspcc-dev/neo-go/pkg/config/netmode"
"github.com/nspcc-dev/neo-go/pkg/core/block" "github.com/nspcc-dev/neo-go/pkg/core/block"
"github.com/nspcc-dev/neo-go/pkg/core/dao" "github.com/nspcc-dev/neo-go/pkg/core/dao"
"github.com/nspcc-dev/neo-go/pkg/core/interop" "github.com/nspcc-dev/neo-go/pkg/core/interop"
"github.com/nspcc-dev/neo-go/pkg/core/interop/contract" "github.com/nspcc-dev/neo-go/pkg/core/interop/contract"
"github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames"
"github.com/nspcc-dev/neo-go/pkg/core/interop/iterator" "github.com/nspcc-dev/neo-go/pkg/core/interop/iterator"
"github.com/nspcc-dev/neo-go/pkg/core/interop/runtime" "github.com/nspcc-dev/neo-go/pkg/core/interop/runtime"
istorage "github.com/nspcc-dev/neo-go/pkg/core/interop/storage" istorage "github.com/nspcc-dev/neo-go/pkg/core/interop/storage"
@ -27,7 +25,6 @@ import (
"github.com/nspcc-dev/neo-go/pkg/crypto/hash" "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/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef" "github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
@ -40,6 +37,8 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
var pathToInternalContracts = filepath.Join("..", "..", "internal", "contracts")
// Tests are taken from // Tests are taken from
// https://github.com/neo-project/neo/blob/master/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.Runtime.cs // https://github.com/neo-project/neo/blob/master/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.Runtime.cs
func TestRuntimeGetRandomCompatibility(t *testing.T) { func TestRuntimeGetRandomCompatibility(t *testing.T) {
@ -69,29 +68,6 @@ func TestRuntimeGetRandomCompatibility(t *testing.T) {
require.Equal(t, "217172703763162599519098299724476526911", ic.VM.Estack().Pop().BigInt().String()) require.Equal(t, "217172703763162599519098299724476526911", ic.VM.Estack().Pop().BigInt().String())
} }
func TestRuntimeGetRandomDifferentTransactions(t *testing.T) {
bc := newTestChain(t)
b, _ := bc.GetBlock(bc.GetHeaderHash(0))
tx1 := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
ic1 := bc.newInteropContext(trigger.Application, bc.dao.GetWrapped(), b, tx1)
ic1.VM = vm.New()
ic1.VM.LoadScript(tx1.Script)
tx2 := transaction.New([]byte{byte(opcode.PUSH2)}, 0)
ic2 := bc.newInteropContext(trigger.Application, bc.dao.GetWrapped(), b, tx2)
ic2.VM = vm.New()
ic2.VM.LoadScript(tx2.Script)
require.NoError(t, runtime.GetRandom(ic1))
require.NoError(t, runtime.GetRandom(ic2))
require.NotEqual(t, ic1.VM.Estack().Pop().BigInt(), ic2.VM.Estack().Pop().BigInt())
require.NoError(t, runtime.GetRandom(ic1))
require.NoError(t, runtime.GetRandom(ic2))
require.NotEqual(t, ic1.VM.Estack().Pop().BigInt(), ic2.VM.Estack().Pop().BigInt())
}
func getSharpTestTx(sender util.Uint160) *transaction.Transaction { func getSharpTestTx(sender util.Uint160) *transaction.Transaction {
tx := transaction.New([]byte{byte(opcode.PUSH2)}, 0) tx := transaction.New([]byte{byte(opcode.PUSH2)}, 0)
tx.Nonce = 0 tx.Nonce = 0
@ -113,81 +89,6 @@ func getSharpTestGenesis(t *testing.T) *block.Block {
return b return b
} }
func TestContractCreateAccount(t *testing.T) {
v, ic, _ := createVM(t)
t.Run("Good", func(t *testing.T) {
priv, err := keys.NewPrivateKey()
require.NoError(t, err)
pub := priv.PublicKey()
v.Estack().PushVal(pub.Bytes())
require.NoError(t, contractCreateStandardAccount(ic))
value := v.Estack().Pop().Bytes()
u, err := util.Uint160DecodeBytesBE(value)
require.NoError(t, err)
require.Equal(t, pub.GetScriptHash(), u)
})
t.Run("InvalidKey", func(t *testing.T) {
v.Estack().PushVal([]byte{1, 2, 3})
require.Error(t, contractCreateStandardAccount(ic))
})
}
func TestContractCreateMultisigAccount(t *testing.T) {
v, ic, _ := createVM(t)
t.Run("Good", func(t *testing.T) {
m, n := 3, 5
pubs := make(keys.PublicKeys, n)
arr := make([]stackitem.Item, n)
for i := range pubs {
pk, err := keys.NewPrivateKey()
require.NoError(t, err)
pubs[i] = pk.PublicKey()
arr[i] = stackitem.Make(pubs[i].Bytes())
}
v.Estack().PushVal(stackitem.Make(arr))
v.Estack().PushVal(m)
require.NoError(t, contractCreateMultisigAccount(ic))
expected, err := smartcontract.CreateMultiSigRedeemScript(m, pubs)
require.NoError(t, err)
value := v.Estack().Pop().Bytes()
u, err := util.Uint160DecodeBytesBE(value)
require.NoError(t, err)
require.Equal(t, hash.Hash160(expected), u)
})
t.Run("InvalidKey", func(t *testing.T) {
v.Estack().PushVal(stackitem.Make([]stackitem.Item{stackitem.Make([]byte{1, 2, 3})}))
v.Estack().PushVal(1)
require.Error(t, contractCreateMultisigAccount(ic))
})
t.Run("Invalid m", func(t *testing.T) {
pk, err := keys.NewPrivateKey()
require.NoError(t, err)
v.Estack().PushVal(stackitem.Make([]stackitem.Item{stackitem.Make(pk.PublicKey().Bytes())}))
v.Estack().PushVal(2)
require.Error(t, contractCreateMultisigAccount(ic))
})
t.Run("m overflows int64", func(t *testing.T) {
pk, err := keys.NewPrivateKey()
require.NoError(t, err)
v.Estack().PushVal(stackitem.Make([]stackitem.Item{stackitem.Make(pk.PublicKey().Bytes())}))
m := big.NewInt(math.MaxInt64)
m.Add(m, big.NewInt(1))
v.Estack().PushVal(stackitem.NewBigInteger(m))
require.Error(t, contractCreateMultisigAccount(ic))
})
}
func TestRuntimeGasLeft(t *testing.T) {
v, ic, _ := createVM(t)
v.GasLimit = 100
v.AddGas(58)
require.NoError(t, runtime.GasLeft(ic))
require.EqualValues(t, 42, v.Estack().Pop().BigInt().Int64())
}
func TestRuntimeGetNotifications(t *testing.T) { func TestRuntimeGetNotifications(t *testing.T) {
v, ic, _ := createVM(t) v, ic, _ := createVM(t)
@ -1067,80 +968,3 @@ func TestRuntimeCheckWitness(t *testing.T) {
}) })
}) })
} }
func TestLoadToken(t *testing.T) {
bc := newTestChain(t)
cs, _ := contracts.GetTestContractState(t, pathToInternalContracts, 4, 5, random.Uint160()) // sender and IDs are not important for the test
require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs))
t.Run("good", func(t *testing.T) {
aer, err := invokeContractMethod(bc, 1_00000000, cs.Hash, "callT0", neoOwner.BytesBE())
require.NoError(t, err)
realBalance, _ := bc.GetGoverningTokenBalance(neoOwner)
checkResult(t, aer, stackitem.Make(realBalance.Int64()+1))
})
t.Run("invalid param count", func(t *testing.T) {
aer, err := invokeContractMethod(bc, 1_00000000, cs.Hash, "callT2")
require.NoError(t, err)
checkFAULTState(t, aer)
})
t.Run("invalid contract", func(t *testing.T) {
aer, err := invokeContractMethod(bc, 1_00000000, cs.Hash, "callT1")
require.NoError(t, err)
checkFAULTState(t, aer)
})
}
func TestRuntimeGetNetwork(t *testing.T) {
bc := newTestChain(t)
w := io.NewBufBinWriter()
emit.Syscall(w.BinWriter, interopnames.SystemRuntimeGetNetwork)
require.NoError(t, w.Err)
tx := transaction.New(w.Bytes(), 10_000)
tx.ValidUntilBlock = bc.BlockHeight() + 1
addSigners(neoOwner, tx)
require.NoError(t, testchain.SignTx(bc, tx))
require.NoError(t, bc.AddBlock(bc.newBlock(tx)))
aer, err := bc.GetAppExecResults(tx.Hash(), trigger.Application)
require.NoError(t, err)
checkResult(t, &aer[0], stackitem.Make(uint32(bc.config.Magic)))
}
func TestRuntimeBurnGas(t *testing.T) {
bc := newTestChain(t)
cs, _ := contracts.GetTestContractState(t, pathToInternalContracts, 4, 5, random.Uint160()) // sender and IDs are not important for the test
require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs))
const sysFee = 2_000000
t.Run("good", func(t *testing.T) {
aer, err := invokeContractMethod(bc, sysFee, cs.Hash, "burnGas", int64(1))
require.NoError(t, err)
require.Equal(t, vm.HaltState, aer.VMState)
t.Run("gas limit exceeded", func(t *testing.T) {
aer, err = invokeContractMethod(bc, aer.GasConsumed, cs.Hash, "burnGas", int64(2))
require.NoError(t, err)
require.Equal(t, vm.FaultState, aer.VMState)
})
})
t.Run("too big integer", func(t *testing.T) {
gas := big.NewInt(math.MaxInt64)
gas.Add(gas, big.NewInt(1))
aer, err := invokeContractMethod(bc, sysFee, cs.Hash, "burnGas", gas)
require.NoError(t, err)
checkFAULTState(t, aer)
})
t.Run("zero GAS", func(t *testing.T) {
aer, err := invokeContractMethod(bc, sysFee, cs.Hash, "burnGas", int64(0))
require.NoError(t, err)
checkFAULTState(t, aer)
})
}

View file

@ -0,0 +1,245 @@
package core_test
import (
"encoding/json"
"math"
"math/big"
"testing"
"github.com/nspcc-dev/neo-go/internal/contracts"
"github.com/nspcc-dev/neo-go/pkg/core/interop"
"github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames"
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
"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"
"github.com/nspcc-dev/neo-go/pkg/neotest"
"github.com/nspcc-dev/neo-go/pkg/neotest/chain"
"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/stackitem"
"github.com/stretchr/testify/require"
)
func TestSystemRuntimeGetRandom_DifferentTransactions(t *testing.T) {
bc, acc := chain.NewSingle(t)
e := neotest.NewExecutor(t, bc, acc, acc)
w := io.NewBufBinWriter()
emit.Syscall(w.BinWriter, interopnames.SystemRuntimeGetRandom)
require.NoError(t, w.Err)
script := w.Bytes()
tx1 := e.PrepareInvocation(t, script, []neotest.Signer{e.Validator}, bc.BlockHeight()+1)
tx2 := e.PrepareInvocation(t, script, []neotest.Signer{e.Validator}, bc.BlockHeight()+1)
e.AddNewBlock(t, tx1, tx2)
e.CheckHalt(t, tx1.Hash())
e.CheckHalt(t, tx2.Hash())
res1 := e.GetTxExecResult(t, tx1.Hash())
res2 := e.GetTxExecResult(t, tx2.Hash())
r1, err := res1.Stack[0].TryInteger()
require.NoError(t, err)
r2, err := res2.Stack[0].TryInteger()
require.NoError(t, err)
require.NotEqual(t, r1, r2)
}
func TestSystemContractCreateStandardAccount(t *testing.T) {
bc, acc := chain.NewSingle(t)
e := neotest.NewExecutor(t, bc, acc, acc)
w := io.NewBufBinWriter()
t.Run("Good", func(t *testing.T) {
priv, err := keys.NewPrivateKey()
require.NoError(t, err)
pub := priv.PublicKey()
emit.Bytes(w.BinWriter, pub.Bytes())
emit.Syscall(w.BinWriter, interopnames.SystemContractCreateStandardAccount)
require.NoError(t, w.Err)
script := w.Bytes()
tx := e.PrepareInvocation(t, script, []neotest.Signer{e.Validator}, bc.BlockHeight()+1)
e.AddNewBlock(t, tx)
e.CheckHalt(t, tx.Hash())
res := e.GetTxExecResult(t, tx.Hash())
value := res.Stack[0].Value().([]byte)
u, err := util.Uint160DecodeBytesBE(value)
require.NoError(t, err)
require.Equal(t, pub.GetScriptHash(), u)
})
t.Run("InvalidKey", func(t *testing.T) {
w.Reset()
emit.Bytes(w.BinWriter, []byte{1, 2, 3})
emit.Syscall(w.BinWriter, interopnames.SystemContractCreateStandardAccount)
require.NoError(t, w.Err)
script := w.Bytes()
tx := e.PrepareInvocation(t, script, []neotest.Signer{e.Validator}, bc.BlockHeight()+1)
e.AddNewBlock(t, tx)
e.CheckFault(t, tx.Hash(), "invalid prefix 1")
})
}
func TestSystemContractCreateMultisigAccount(t *testing.T) {
bc, acc := chain.NewSingle(t)
e := neotest.NewExecutor(t, bc, acc, acc)
w := io.NewBufBinWriter()
createScript := func(t *testing.T, pubs []interface{}, m int) []byte {
w.Reset()
emit.Array(w.BinWriter, pubs...)
emit.Int(w.BinWriter, int64(m))
emit.Syscall(w.BinWriter, interopnames.SystemContractCreateMultisigAccount)
require.NoError(t, w.Err)
return w.Bytes()
}
t.Run("Good", func(t *testing.T) {
m, n := 3, 5
pubs := make(keys.PublicKeys, n)
arr := make([]interface{}, n)
for i := range pubs {
pk, err := keys.NewPrivateKey()
require.NoError(t, err)
pubs[i] = pk.PublicKey()
arr[i] = pubs[i].Bytes()
}
script := createScript(t, arr, m)
txH := e.InvokeScript(t, script, []neotest.Signer{acc})
e.CheckHalt(t, txH)
res := e.GetTxExecResult(t, txH)
value := res.Stack[0].Value().([]byte)
u, err := util.Uint160DecodeBytesBE(value)
require.NoError(t, err)
expected, err := smartcontract.CreateMultiSigRedeemScript(m, pubs)
require.NoError(t, err)
require.Equal(t, hash.Hash160(expected), u)
})
t.Run("InvalidKey", func(t *testing.T) {
script := createScript(t, []interface{}{[]byte{1, 2, 3}}, 1)
e.InvokeScriptCheckFAULT(t, script, []neotest.Signer{acc}, "invalid prefix 1")
})
t.Run("Invalid m", func(t *testing.T) {
pk, err := keys.NewPrivateKey()
require.NoError(t, err)
script := createScript(t, []interface{}{pk.PublicKey().Bytes()}, 2)
e.InvokeScriptCheckFAULT(t, script, []neotest.Signer{acc}, "length of the signatures (2) is higher then the number of public keys")
})
t.Run("m overflows int32", func(t *testing.T) {
pk, err := keys.NewPrivateKey()
require.NoError(t, err)
m := big.NewInt(math.MaxInt32)
m.Add(m, big.NewInt(1))
w.Reset()
emit.Array(w.BinWriter, pk.Bytes())
emit.BigInt(w.BinWriter, m)
emit.Syscall(w.BinWriter, interopnames.SystemContractCreateMultisigAccount)
require.NoError(t, w.Err)
e.InvokeScriptCheckFAULT(t, w.Bytes(), []neotest.Signer{acc}, "m must be positive and fit int32")
})
}
func TestSystemRuntimeGasLeft(t *testing.T) {
const runtimeGasLeftPrice = 1 << 4
bc, acc := chain.NewSingle(t)
e := neotest.NewExecutor(t, bc, acc, acc)
w := io.NewBufBinWriter()
gasLimit := 1100
emit.Syscall(w.BinWriter, interopnames.SystemRuntimeGasLeft)
emit.Syscall(w.BinWriter, interopnames.SystemRuntimeGasLeft)
require.NoError(t, w.Err)
tx := transaction.New(w.Bytes(), int64(gasLimit))
tx.Nonce = neotest.Nonce()
tx.ValidUntilBlock = e.Chain.BlockHeight() + 1
e.SignTx(t, tx, int64(gasLimit), acc)
e.AddNewBlock(t, tx)
e.CheckHalt(t, tx.Hash())
res := e.GetTxExecResult(t, tx.Hash())
l1 := res.Stack[0].Value().(*big.Int)
l2 := res.Stack[1].Value().(*big.Int)
require.Equal(t, int64(gasLimit-runtimeGasLeftPrice*interop.DefaultBaseExecFee), l1.Int64())
require.Equal(t, int64(gasLimit-2*runtimeGasLeftPrice*interop.DefaultBaseExecFee), l2.Int64())
}
func TestLoadToken(t *testing.T) {
bc, acc := chain.NewSingle(t)
e := neotest.NewExecutor(t, bc, acc, acc)
managementInvoker := e.ValidatorInvoker(e.NativeHash(t, nativenames.Management))
cs, _ := contracts.GetTestContractState(t, pathToInternalContracts, 0, 1, acc.ScriptHash())
rawManifest, err := json.Marshal(cs.Manifest)
require.NoError(t, err)
rawNef, err := cs.NEF.Bytes()
require.NoError(t, err)
tx := managementInvoker.PrepareInvoke(t, "deploy", rawNef, rawManifest)
e.AddNewBlock(t, tx)
e.CheckHalt(t, tx.Hash())
cInvoker := e.ValidatorInvoker(cs.Hash)
t.Run("good", func(t *testing.T) {
realBalance, _ := bc.GetGoverningTokenBalance(acc.ScriptHash())
cInvoker.Invoke(t, stackitem.NewBigInteger(big.NewInt(realBalance.Int64()+1)), "callT0", acc.ScriptHash())
})
t.Run("invalid param count", func(t *testing.T) {
cInvoker.InvokeFail(t, "method not found: callT2/1", "callT2", acc.ScriptHash())
})
t.Run("invalid contract", func(t *testing.T) {
cInvoker.InvokeFail(t, "token contract 0000000000000000000000000000000000000000 not found: key not found", "callT1")
})
}
func TestSystemRuntimeGetNetwork(t *testing.T) {
bc, acc := chain.NewSingle(t)
e := neotest.NewExecutor(t, bc, acc, acc)
w := io.NewBufBinWriter()
emit.Syscall(w.BinWriter, interopnames.SystemRuntimeGetNetwork)
require.NoError(t, w.Err)
e.InvokeScriptCheckHALT(t, w.Bytes(), []neotest.Signer{acc}, stackitem.NewBigInteger(big.NewInt(int64(bc.GetConfig().Magic))))
}
func TestSystemRuntimeBurnGas(t *testing.T) {
bc, acc := chain.NewSingle(t)
e := neotest.NewExecutor(t, bc, acc, acc)
managementInvoker := e.ValidatorInvoker(e.NativeHash(t, nativenames.Management))
cs, _ := contracts.GetTestContractState(t, pathToInternalContracts, 0, 1, acc.ScriptHash())
rawManifest, err := json.Marshal(cs.Manifest)
require.NoError(t, err)
rawNef, err := cs.NEF.Bytes()
require.NoError(t, err)
tx := managementInvoker.PrepareInvoke(t, "deploy", rawNef, rawManifest)
e.AddNewBlock(t, tx)
e.CheckHalt(t, tx.Hash())
cInvoker := e.ValidatorInvoker(cs.Hash)
t.Run("good", func(t *testing.T) {
h := cInvoker.Invoke(t, stackitem.Null{}, "burnGas", int64(1))
res := e.GetTxExecResult(t, h)
t.Run("gas limit exceeded", func(t *testing.T) {
tx := e.NewUnsignedTx(t, cs.Hash, "burnGas", int64(2))
e.SignTx(t, tx, res.GasConsumed, acc)
e.AddNewBlock(t, tx)
e.CheckFault(t, tx.Hash(), "GAS limit exceeded")
})
})
t.Run("too big integer", func(t *testing.T) {
gas := big.NewInt(math.MaxInt64)
gas.Add(gas, big.NewInt(1))
cInvoker.InvokeFail(t, "invalid GAS value", "burnGas", gas)
})
t.Run("zero GAS", func(t *testing.T) {
cInvoker.InvokeFail(t, "GAS must be positive", "burnGas", int64(0))
})
}

View file

@ -1,324 +1,164 @@
package core package core_test
import ( import (
"errors" "encoding/json"
"math/big" "fmt"
"strings"
"testing" "testing"
"github.com/nspcc-dev/neo-go/internal/contracts" "github.com/nspcc-dev/neo-go/internal/contracts"
"github.com/nspcc-dev/neo-go/internal/random" "github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/core/dao"
"github.com/nspcc-dev/neo-go/pkg/core/fee" "github.com/nspcc-dev/neo-go/pkg/core/fee"
"github.com/nspcc-dev/neo-go/pkg/core/interop" "github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
"github.com/nspcc-dev/neo-go/pkg/core/interop/contract"
"github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/core/storage" "github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/neotest"
"github.com/nspcc-dev/neo-go/pkg/neotest/chain"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/nspcc-dev/neo-go/pkg/vm/opcode"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
type testNative struct {
meta interop.ContractMD
blocks chan uint32
}
func (tn *testNative) Initialize(_ *interop.Context) error {
return nil
}
func (tn *testNative) Metadata() *interop.ContractMD {
return &tn.meta
}
func (tn *testNative) OnPersist(ic *interop.Context) error {
select {
case tn.blocks <- ic.Block.Index:
return nil
default:
return errors.New("can't send index")
}
}
func (tn *testNative) PostPersist(ic *interop.Context) error {
return nil
}
var _ interop.Contract = (*testNative)(nil)
// registerNative registers native contract in the blockchain.
func (bc *Blockchain) registerNative(c interop.Contract) {
bc.contracts.Contracts = append(bc.contracts.Contracts, c)
bc.config.NativeUpdateHistories[c.Metadata().Name] = c.Metadata().UpdateHistory
}
const (
testSumCPUFee = 1 << 15 // same as contract.Call
testSumStorageFee = 200
)
func newTestNative() *testNative {
cMD := interop.NewContractMD("Test.Native.Sum", 0)
cMD.UpdateHistory = []uint32{0}
tn := &testNative{
meta: *cMD,
blocks: make(chan uint32, 1),
}
defer tn.meta.UpdateHash()
desc := &manifest.Method{
Name: "sum",
Parameters: []manifest.Parameter{
manifest.NewParameter("addend1", smartcontract.IntegerType),
manifest.NewParameter("addend2", smartcontract.IntegerType),
},
ReturnType: smartcontract.IntegerType,
Safe: true,
}
md := &interop.MethodAndPrice{
Func: tn.sum,
CPUFee: testSumCPUFee,
StorageFee: testSumStorageFee,
RequiredFlags: callflag.NoneFlag,
}
tn.meta.AddMethod(md, desc)
desc = &manifest.Method{
Name: "callOtherContractNoReturn",
Parameters: []manifest.Parameter{
manifest.NewParameter("contractHash", smartcontract.Hash160Type),
manifest.NewParameter("method", smartcontract.StringType),
manifest.NewParameter("arg", smartcontract.ArrayType),
},
ReturnType: smartcontract.VoidType,
Safe: true,
}
md = &interop.MethodAndPrice{
Func: tn.callOtherContractNoReturn,
CPUFee: testSumCPUFee,
RequiredFlags: callflag.NoneFlag}
tn.meta.AddMethod(md, desc)
desc = &manifest.Method{
Name: "callOtherContractWithReturn",
Parameters: []manifest.Parameter{
manifest.NewParameter("contractHash", smartcontract.Hash160Type),
manifest.NewParameter("method", smartcontract.StringType),
manifest.NewParameter("arg", smartcontract.ArrayType),
},
ReturnType: smartcontract.IntegerType,
}
md = &interop.MethodAndPrice{
Func: tn.callOtherContractWithReturn,
CPUFee: testSumCPUFee,
RequiredFlags: callflag.NoneFlag}
tn.meta.AddMethod(md, desc)
return tn
}
func (tn *testNative) sum(_ *interop.Context, args []stackitem.Item) stackitem.Item {
s1, err := args[0].TryInteger()
if err != nil {
panic(err)
}
s2, err := args[1].TryInteger()
if err != nil {
panic(err)
}
return stackitem.NewBigInteger(s1.Add(s1, s2))
}
func toUint160(item stackitem.Item) util.Uint160 {
bs, err := item.TryBytes()
if err != nil {
panic(err)
}
u, err := util.Uint160DecodeBytesBE(bs)
if err != nil {
panic(err)
}
return u
}
func (tn *testNative) call(ic *interop.Context, args []stackitem.Item, hasReturn bool) {
cs, err := ic.GetContract(toUint160(args[0]))
if err != nil {
panic(err)
}
bs, err := args[1].TryBytes()
if err != nil {
panic(err)
}
err = contract.CallFromNative(ic, tn.meta.Hash, cs, string(bs), args[2].Value().([]stackitem.Item), hasReturn)
if err != nil {
panic(err)
}
}
func (tn *testNative) callOtherContractNoReturn(ic *interop.Context, args []stackitem.Item) stackitem.Item {
tn.call(ic, args, false)
return stackitem.Null{}
}
func (tn *testNative) callOtherContractWithReturn(ic *interop.Context, args []stackitem.Item) stackitem.Item {
tn.call(ic, args, true)
bi := ic.VM.Estack().Pop().BigInt()
return stackitem.Make(bi.Add(bi, big.NewInt(1)))
}
func TestNativeContract_Invoke(t *testing.T) { func TestNativeContract_Invoke(t *testing.T) {
chain := newTestChain(t) const (
transferCPUFee = 1 << 17
transferStorageFee = 50
systemContractCallPrice = 1 << 15
)
bc, validator, committee := chain.NewMulti(t)
e := neotest.NewExecutor(t, bc, validator, committee)
gasHash := e.NativeHash(t, nativenames.Gas)
tn := newTestNative() baseExecFee := bc.GetBaseExecFee()
chain.registerNative(tn) price := fee.Opcode(baseExecFee, opcode.SYSCALL, // System.Contract.Call
opcode.PUSHDATA1, // contract hash (20 byte)
opcode.PUSHDATA1, // method
opcode.PUSH15, // call flags
// `transfer` args:
opcode.PUSHDATA1, // from
opcode.PUSHDATA1, // to
opcode.PUSH1, // amount
opcode.PUSHNULL, // data
// end args
opcode.PUSH4, // amount of args
opcode.PACK, // pack args
)
price += systemContractCallPrice*baseExecFee + // System.Contract.Call price
transferCPUFee*baseExecFee + // `transfer` itself
transferStorageFee*bc.GetStoragePrice() // `transfer` storage price
err := chain.contracts.Management.PutContractState(chain.dao, &state.Contract{ tx := e.NewUnsignedTx(t, gasHash, "transfer", validator.ScriptHash(), validator.ScriptHash(), 1, nil)
ContractBase: state.ContractBase{ e.SignTx(t, tx, -1, validator)
ID: 1, e.AddNewBlock(t, tx)
NEF: tn.meta.NEF, e.CheckHalt(t, tx.Hash(), stackitem.Make(true))
Hash: tn.meta.Hash,
Manifest: tn.meta.Manifest,
},
})
require.NoError(t, err)
// System.Contract.Call + "sum" itself + opcodes for pushing arguments. // Enough for Call and other opcodes, but not enough for "transfer" call.
price := int64(testSumCPUFee * chain.GetBaseExecFee() * 2) tx = e.NewUnsignedTx(t, gasHash, "transfer", validator.ScriptHash(), validator.ScriptHash(), 1, nil)
price += testSumStorageFee * chain.GetStoragePrice() e.SignTx(t, tx, price-1, validator)
price += 3 * fee.Opcode(chain.GetBaseExecFee(), opcode.PUSHINT8) e.AddNewBlock(t, tx)
price += 2 * fee.Opcode(chain.GetBaseExecFee(), opcode.SYSCALL, opcode.PUSHDATA1, opcode.PUSHINT8) e.CheckFault(t, tx.Hash(), "gas limit exceeded")
price += fee.Opcode(chain.GetBaseExecFee(), opcode.PACK)
res, err := invokeContractMethod(chain, price, tn.Metadata().Hash, "sum", int64(14), int64(28))
require.NoError(t, err)
checkResult(t, res, stackitem.Make(42))
_, err = chain.persist(false)
require.NoError(t, err)
select {
case index := <-tn.blocks:
require.Equal(t, chain.blockHeight, index)
default:
require.Fail(t, "onPersist wasn't called")
}
// Enough for Call and other opcodes, but not enough for "sum" call.
res, err = invokeContractMethod(chain, price-1, tn.Metadata().Hash, "sum", int64(14), int64(28))
require.NoError(t, err)
checkFAULTState(t, res)
} }
func TestNativeContract_InvokeInternal(t *testing.T) { func TestNativeContract_InvokeInternal(t *testing.T) {
chain := newTestChain(t) bc, validator, committee := chain.NewMulti(t)
e := neotest.NewExecutor(t, bc, validator, committee)
tn := newTestNative() clState := bc.GetContractState(e.NativeHash(t, nativenames.CryptoLib))
chain.registerNative(tn) require.NotNil(t, clState)
md := clState.Manifest.ABI.GetMethod("ripemd160", 1)
err := chain.contracts.Management.PutContractState(chain.dao, &state.Contract{ require.NotNil(t, md)
ContractBase: state.ContractBase{
ID: 1,
NEF: tn.meta.NEF,
Manifest: tn.meta.Manifest,
},
})
require.NoError(t, err)
d := dao.NewSimple(storage.NewMemoryStore(), chain.config.StateRootInHeader, chain.config.P2PSigExtensions)
ic := chain.newInteropContext(trigger.Application, d, nil, nil)
sumOffset := 0
for _, md := range tn.Metadata().Methods {
if md.MD.Name == "sum" {
sumOffset = md.MD.Offset
break
}
}
t.Run("fail, bad current script hash", func(t *testing.T) { t.Run("fail, bad current script hash", func(t *testing.T) {
ic := bc.GetTestVM(trigger.Application, nil, nil)
v := ic.SpawnVM() v := ic.SpawnVM()
v.LoadScriptWithHash(tn.Metadata().NEF.Script, util.Uint160{1, 2, 3}, callflag.All) fakeH := util.Uint160{1, 2, 3}
v.Estack().PushVal(14) v.LoadScriptWithHash(clState.NEF.Script, fakeH, callflag.All)
v.Estack().PushVal(28) input := []byte{1, 2, 3, 4}
v.Context().Jump(sumOffset) v.Estack().PushVal(input)
v.Context().Jump(md.Offset)
// it's prohibited to call natives directly // Bad current script hash
require.Error(t, v.Run()) err := v.Run()
require.Error(t, err)
require.True(t, strings.Contains(err.Error(), fmt.Sprintf("native contract %s (version 0) not found", fakeH.StringLE())), err.Error())
}) })
t.Run("fail, bad NativeUpdateHistory height", func(t *testing.T) { t.Run("fail, bad NativeUpdateHistory height", func(t *testing.T) {
tn.Metadata().UpdateHistory = []uint32{chain.blockHeight + 1} bcBad, validatorBad, committeeBad := chain.NewMultiWithCustomConfig(t, func(c *config.ProtocolConfiguration) {
c.NativeUpdateHistories = map[string][]uint32{
nativenames.Policy: {0},
nativenames.Neo: {0},
nativenames.Gas: {0},
nativenames.Designation: {0},
nativenames.StdLib: {0},
nativenames.Management: {0},
nativenames.Oracle: {0},
nativenames.Ledger: {0},
nativenames.CryptoLib: {1},
}
})
eBad := neotest.NewExecutor(t, bcBad, validatorBad, committeeBad)
ic := bcBad.GetTestVM(trigger.Application, nil, nil)
v := ic.SpawnVM() v := ic.SpawnVM()
v.LoadScriptWithHash(tn.Metadata().NEF.Script, tn.Metadata().Hash, callflag.All) v.LoadScriptWithHash(clState.NEF.Script, clState.Hash, callflag.All) // hash is not affected by native update history
v.Estack().PushVal(14) input := []byte{1, 2, 3, 4}
v.Estack().PushVal(28) v.Estack().PushVal(input)
v.Context().Jump(sumOffset) v.Context().Jump(md.Offset)
// it's prohibited to call natives before NativeUpdateHistory[0] height // It's prohibited to call natives before NativeUpdateHistory[0] height.
require.Error(t, v.Run()) err := v.Run()
require.Error(t, err)
require.True(t, strings.Contains(err.Error(), "native contract CryptoLib is active after height = 1"))
// set the value back to 0 // Add new block => CryptoLib should be active now.
tn.Metadata().UpdateHistory = []uint32{0} eBad.AddNewBlock(t)
ic = bcBad.GetTestVM(trigger.Application, nil, nil)
v = ic.SpawnVM()
v.LoadScriptWithHash(clState.NEF.Script, clState.Hash, callflag.All) // hash is not affected by native update history
v.Estack().PushVal(input)
v.Context().Jump(md.Offset)
require.NoError(t, v.Run())
value := v.Estack().Pop().Bytes()
require.Equal(t, hash.RipeMD160(input).BytesBE(), value)
}) })
t.Run("success", func(t *testing.T) { t.Run("success", func(t *testing.T) {
ic := bc.GetTestVM(trigger.Application, nil, nil)
v := ic.SpawnVM() v := ic.SpawnVM()
v.LoadScriptWithHash(tn.Metadata().NEF.Script, tn.Metadata().Hash, callflag.All) v.LoadScriptWithHash(clState.NEF.Script, clState.Hash, callflag.All)
v.Estack().PushVal(14) input := []byte{1, 2, 3, 4}
v.Estack().PushVal(28) v.Estack().PushVal(input)
v.Context().Jump(sumOffset) v.Context().Jump(md.Offset)
require.NoError(t, v.Run()) require.NoError(t, v.Run())
value := v.Estack().Pop().BigInt() value := v.Estack().Pop().Bytes()
require.Equal(t, int64(42), value.Int64()) require.Equal(t, hash.RipeMD160(input).BytesBE(), value)
}) })
} }
func TestNativeContract_InvokeOtherContract(t *testing.T) { func TestNativeContract_InvokeOtherContract(t *testing.T) {
chain := newTestChain(t) bc, validator, committee := chain.NewMulti(t)
e := neotest.NewExecutor(t, bc, validator, committee)
managementInvoker := e.ValidatorInvoker(e.NativeHash(t, nativenames.Management))
gasInvoker := e.ValidatorInvoker(e.NativeHash(t, nativenames.Gas))
tn := newTestNative() cs, _ := contracts.GetTestContractState(t, pathToInternalContracts, 1, 2, validator.ScriptHash())
chain.registerNative(tn) cs.Hash = state.CreateContractHash(validator.ScriptHash(), cs.NEF.Checksum, cs.Manifest.Name) // set proper hash
manifB, err := json.Marshal(cs.Manifest)
err := chain.contracts.Management.PutContractState(chain.dao, &state.Contract{
ContractBase: state.ContractBase{
ID: 1,
Hash: tn.meta.Hash,
NEF: tn.meta.NEF,
Manifest: tn.meta.Manifest,
},
})
require.NoError(t, err) require.NoError(t, err)
nefB, err := cs.NEF.Bytes()
require.NoError(t, err)
si, err := cs.ToStackItem()
require.NoError(t, err)
managementInvoker.Invoke(t, si, "deploy", nefB, manifB)
var drainTN = func(t *testing.T) {
select {
case <-tn.blocks:
default:
require.Fail(t, "testNative didn't send us block")
}
}
cs, _ := contracts.GetTestContractState(t, pathToInternalContracts, 4, 5, random.Uint160()) // sender and IDs are not important for the test
require.NoError(t, chain.contracts.Management.PutContractState(chain.dao, cs))
baseFee := chain.GetBaseExecFee()
t.Run("non-native, no return", func(t *testing.T) { t.Run("non-native, no return", func(t *testing.T) {
res, err := invokeContractMethod(chain, testSumCPUFee*baseFee*4+10000, tn.Metadata().Hash, "callOtherContractNoReturn", cs.Hash, "justReturn", []interface{}{}) // `onNEP17Payment` will be invoked on test contract from GAS contract.
require.NoError(t, err) gasInvoker.Invoke(t, true, "transfer", validator.ScriptHash(), cs.Hash, 1, nil)
drainTN(t)
require.Equal(t, vm.HaltState, res.VMState, res.FaultException)
checkResult(t, res, stackitem.Null{}) // simple call is done with EnsureNotEmpty
})
t.Run("non-native, with return", func(t *testing.T) {
res, err := invokeContractMethod(chain, testSumCPUFee*baseFee*4+10000, tn.Metadata().Hash,
"callOtherContractWithReturn", cs.Hash, "ret7", []interface{}{})
require.NoError(t, err)
drainTN(t)
checkResult(t, res, stackitem.Make(8))
}) })
} }

View file

@ -10,69 +10,11 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles" "github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
"github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/vm"
"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/vm/opcode"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func (bc *Blockchain) setNodesByRole(t *testing.T, ok bool, r noderoles.Role, nodes keys.PublicKeys) {
w := io.NewBufBinWriter()
for _, pub := range nodes {
emit.Bytes(w.BinWriter, pub.Bytes())
}
emit.Int(w.BinWriter, int64(len(nodes)))
emit.Opcodes(w.BinWriter, opcode.PACK)
emit.Int(w.BinWriter, int64(r))
emit.Int(w.BinWriter, 2)
emit.Opcodes(w.BinWriter, opcode.PACK)
emit.AppCallNoArgs(w.BinWriter, bc.contracts.Designate.Hash, "designateAsRole", callflag.All)
require.NoError(t, w.Err)
tx := transaction.New(w.Bytes(), 0)
tx.NetworkFee = 10_000_000
tx.SystemFee = 10_000_000
tx.ValidUntilBlock = 100
tx.Signers = []transaction.Signer{
{
Account: testchain.MultisigScriptHash(),
Scopes: transaction.None,
},
{
Account: testchain.CommitteeScriptHash(),
Scopes: transaction.CalledByEntry,
},
}
require.NoError(t, testchain.SignTx(bc, tx))
tx.Scripts = append(tx.Scripts, transaction.Witness{
InvocationScript: testchain.SignCommittee(tx),
VerificationScript: testchain.CommitteeVerificationScript(),
})
require.NoError(t, bc.AddBlock(bc.newBlock(tx)))
aer, err := bc.GetAppExecResults(tx.Hash(), trigger.Application)
require.NoError(t, err)
require.Equal(t, 1, len(aer))
if ok {
require.Equal(t, vm.HaltState, aer[0].VMState)
require.Equal(t, 1, len(aer[0].Events))
ev := aer[0].Events[0]
require.Equal(t, bc.contracts.Designate.Hash, ev.ScriptHash)
require.Equal(t, native.DesignationEventName, ev.Name)
require.Equal(t, []stackitem.Item{
stackitem.Make(int64(r)),
stackitem.Make(bc.BlockHeight()),
}, ev.Item.Value().([]stackitem.Item))
} else {
require.Equal(t, vm.FaultState, aer[0].VMState)
require.Equal(t, 0, len(aer[0].Events))
}
}
func TestDesignate_DesignateAsRole(t *testing.T) { func TestDesignate_DesignateAsRole(t *testing.T) {
bc := newTestChain(t) bc := newTestChain(t)

View file

@ -1,20 +1,17 @@
package core package core_test
import ( import (
"fmt" "fmt"
"math/big" "math/big"
"path/filepath"
"testing" "testing"
"github.com/nspcc-dev/neo-go/internal/testchain" "github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
"github.com/nspcc-dev/neo-go/pkg/core/storage" "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/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/neotest"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/neotest/chain"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/vm"
"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/nspcc-dev/neo-go/pkg/wallet"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -40,96 +37,88 @@ func BenchmarkNEO_GetGASPerVote(t *testing.B) {
} }
} }
func newLevelDBForTesting(t testing.TB) storage.Store {
dbPath := t.TempDir()
dbOptions := storage.LevelDBOptions{
DataDirectoryPath: dbPath,
}
newLevelStore, err := storage.NewLevelDBStore(dbOptions)
require.Nil(t, err, "NewLevelDBStore error")
return newLevelStore
}
func newBoltStoreForTesting(t testing.TB) storage.Store {
d := t.TempDir()
dbPath := filepath.Join(d, "test_bolt_db")
boltDBStore, err := storage.NewBoltDBStore(storage.BoltDBOptions{FilePath: dbPath})
require.NoError(t, err)
return boltDBStore
}
func benchmarkGasPerVote(t *testing.B, ps storage.Store, nRewardRecords int, rewardDistance int) { func benchmarkGasPerVote(t *testing.B, ps storage.Store, nRewardRecords int, rewardDistance int) {
bc := newTestChainWithCustomCfgAndStore(t, ps, nil) bc, validators, committee := chain.NewMultiWithCustomConfigAndStore(t, nil, ps, true)
cfg := bc.GetConfig()
neo := bc.contracts.NEO e := neotest.NewExecutor(t, bc, validators, committee)
tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0) neoHash := e.NativeHash(t, nativenames.Neo)
ic := bc.newInteropContext(trigger.Application, bc.dao, nil, tx) gasHash := e.NativeHash(t, nativenames.Gas)
ic.SpawnVM() neoSuperInvoker := e.NewInvoker(neoHash, validators, committee)
ic.Block = bc.newBlock(tx) neoValidatorsInvoker := e.ValidatorInvoker(neoHash)
gasValidatorsInvoker := e.ValidatorInvoker(gasHash)
advanceChain := func(t *testing.B, count int) {
for i := 0; i < count; i++ {
require.NoError(t, bc.AddBlock(bc.newBlock()))
ic.Block.Index++
}
}
// Vote for new committee. // Vote for new committee.
sz := testchain.CommitteeSize() sz := len(cfg.StandbyCommittee)
accs := make([]*wallet.Account, sz) voters := make([]*wallet.Account, sz)
candidates := make(keys.PublicKeys, sz) candidates := make(keys.PublicKeys, sz)
txs := make([]*transaction.Transaction, 0, len(accs)) txs := make([]*transaction.Transaction, 0, len(voters)*3)
for i := 0; i < sz; i++ { for i := 0; i < sz; i++ {
priv, err := keys.NewPrivateKey() priv, err := keys.NewPrivateKey()
require.NoError(t, err) require.NoError(t, err)
candidates[i] = priv.PublicKey() candidates[i] = priv.PublicKey()
accs[i], err = wallet.NewAccount() voters[i], err = wallet.NewAccount()
require.NoError(t, err) require.NoError(t, err)
require.NoError(t, neo.RegisterCandidateInternal(ic, candidates[i])) registerTx := neoSuperInvoker.PrepareInvoke(t, "registerCandidate", candidates[i].Bytes())
txs = append(txs, registerTx)
to := accs[i].Contract.ScriptHash() to := voters[i].Contract.ScriptHash()
w := io.NewBufBinWriter() transferNeoTx := neoValidatorsInvoker.PrepareInvoke(t, "transfer", e.Validator.ScriptHash(), to, big.NewInt(int64(sz-i)*1000000).Int64(), nil)
emit.AppCall(w.BinWriter, bc.contracts.NEO.Hash, "transfer", callflag.All, txs = append(txs, transferNeoTx)
neoOwner.BytesBE(), to.BytesBE(),
big.NewInt(int64(sz-i)*1000000).Int64(), nil) transferGasTx := gasValidatorsInvoker.PrepareInvoke(t, "transfer", e.Validator.ScriptHash(), to, int64(1_000_000_000), nil)
emit.Opcodes(w.BinWriter, opcode.ASSERT) txs = append(txs, transferGasTx)
emit.AppCall(w.BinWriter, bc.contracts.GAS.Hash, "transfer", callflag.All,
neoOwner.BytesBE(), to.BytesBE(),
int64(1_000_000_000), nil)
emit.Opcodes(w.BinWriter, opcode.ASSERT)
require.NoError(t, w.Err)
tx := transaction.New(w.Bytes(), 1000_000_000)
tx.ValidUntilBlock = bc.BlockHeight() + 1
setSigner(tx, testchain.MultisigScriptHash())
require.NoError(t, testchain.SignTx(bc, tx))
txs = append(txs, tx)
} }
require.NoError(t, bc.AddBlock(bc.newBlock(txs...))) e.AddNewBlock(t, txs...)
for _, tx := range txs { for _, tx := range txs {
checkTxHalt(t, bc, tx.Hash()) e.CheckHalt(t, tx.Hash())
} }
voteTxs := make([]*transaction.Transaction, 0, sz)
for i := 0; i < sz; i++ { for i := 0; i < sz; i++ {
priv := accs[i].PrivateKey() priv := voters[i].PrivateKey()
h := priv.GetScriptHash() h := priv.GetScriptHash()
setSigner(tx, h) voteTx := e.NewTx(t, []neotest.Signer{neotest.NewSingleSigner(voters[i])}, neoHash, "vote", h, candidates[i].Bytes())
ic.VM.Load(priv.PublicKey().GetVerificationScript()) voteTxs = append(voteTxs, voteTx)
require.NoError(t, neo.VoteInternal(ic, h, candidates[i])) }
e.AddNewBlock(t, voteTxs...)
for _, tx := range voteTxs {
e.CheckHalt(t, tx.Hash())
} }
_, err := ic.DAO.Persist()
require.NoError(t, err)
// Collect set of nRewardRecords reward records for each voter. // Collect set of nRewardRecords reward records for each voter.
advanceChain(t, nRewardRecords*testchain.CommitteeSize()) e.GenerateNewBlocks(t, len(cfg.StandbyCommittee))
// Transfer some more NEO to first voter to update his balance height. // Transfer some more NEO to first voter to update his balance height.
to := accs[0].Contract.ScriptHash() to := voters[0].Contract.ScriptHash()
w := io.NewBufBinWriter() neoValidatorsInvoker.Invoke(t, true, "transfer", e.Validator.ScriptHash(), to, int64(1), nil)
emit.AppCall(w.BinWriter, bc.contracts.NEO.Hash, "transfer", callflag.All,
neoOwner.BytesBE(), to.BytesBE(), int64(1), nil)
emit.Opcodes(w.BinWriter, opcode.ASSERT)
require.NoError(t, w.Err)
tx = transaction.New(w.Bytes(), 1000_000_000)
tx.ValidUntilBlock = bc.BlockHeight() + 1
setSigner(tx, testchain.MultisigScriptHash())
require.NoError(t, testchain.SignTx(bc, tx))
require.NoError(t, bc.AddBlock(bc.newBlock(tx)))
aer, err := bc.GetAppExecResults(tx.Hash(), trigger.Application)
require.NoError(t, err)
require.Equal(t, 1, len(aer))
require.Equal(t, vm.HaltState, aer[0].VMState, aer[0].FaultException)
// Advance chain one more time to avoid same start/end rewarding bounds. // Advance chain one more time to avoid same start/end rewarding bounds.
advanceChain(t, rewardDistance) e.GenerateNewBlocks(t, rewardDistance)
end := bc.BlockHeight() end := bc.BlockHeight()
t.ResetTimer() t.ResetTimer()
t.ReportAllocs() t.ReportAllocs()
t.StartTimer() t.StartTimer()
for i := 0; i < t.N; i++ { for i := 0; i < t.N; i++ {
_, err := neo.CalculateBonus(ic.DAO, to, end) _, err := bc.CalculateClaimable(to, end)
require.NoError(t, err) require.NoError(t, err)
} }
t.StopTimer() t.StopTimer()

View file

@ -1,49 +1,67 @@
package core package core_test
import ( import (
"fmt"
"testing" "testing"
"github.com/nspcc-dev/neo-go/internal/random"
"github.com/nspcc-dev/neo-go/internal/testchain"
"github.com/nspcc-dev/neo-go/pkg/core/interop" "github.com/nspcc-dev/neo-go/pkg/core/interop"
"github.com/nspcc-dev/neo-go/pkg/core/native" "github.com/nspcc-dev/neo-go/pkg/core/native"
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
"github.com/nspcc-dev/neo-go/pkg/neotest"
"github.com/nspcc-dev/neo-go/pkg/neotest/chain"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestFeePerByte(t *testing.T) { func TestPolicy_FeePerByte(t *testing.T) {
chain := newTestChain(t) bc, _, _ := chain.NewMulti(t)
t.Run("get, internal method", func(t *testing.T) { t.Run("get, internal method", func(t *testing.T) {
n := chain.contracts.Policy.GetFeePerByteInternal(chain.dao) n := bc.FeePerByte()
require.Equal(t, 1000, int(n)) require.Equal(t, 1000, int(n))
}) })
} }
func TestExecFeeFactor(t *testing.T) { func TestPolicy_ExecFeeFactor(t *testing.T) {
chain := newTestChain(t) bc, _, _ := chain.NewMulti(t)
t.Run("get, internal method", func(t *testing.T) { t.Run("get, internal method", func(t *testing.T) {
n := chain.contracts.Policy.GetExecFeeFactorInternal(chain.dao) n := bc.GetBaseExecFee()
require.EqualValues(t, interop.DefaultBaseExecFee, n) require.EqualValues(t, interop.DefaultBaseExecFee, n)
}) })
} }
func TestStoragePrice(t *testing.T) { func TestPolicy_StoragePrice(t *testing.T) {
chain := newTestChain(t) bc, validators, committee := chain.NewMulti(t)
e := neotest.NewExecutor(t, bc, validators, committee)
t.Run("get, internal method", func(t *testing.T) { t.Run("get, internal method", func(t *testing.T) {
n := chain.contracts.Policy.GetStoragePriceInternal(chain.dao) e.AddNewBlock(t) // avoid default value got from Blockchain.
n := bc.GetStoragePrice()
require.Equal(t, int64(native.DefaultStoragePrice), n) require.Equal(t, int64(native.DefaultStoragePrice), n)
}) })
} }
func TestBlockedAccounts(t *testing.T) { func TestPolicy_BlockedAccounts(t *testing.T) {
chain := newTestChain(t) bc, validators, committee := chain.NewMulti(t)
transferTokenFromMultisigAccount(t, chain, testchain.CommitteeScriptHash(), e := neotest.NewExecutor(t, bc, validators, committee)
chain.contracts.GAS.Hash, 100_00000000) policyHash := e.NativeHash(t, nativenames.Policy)
policySuperInvoker := e.NewInvoker(policyHash, validators, committee)
unlucky := e.NewAccount(t, 5_0000_0000)
policyUnluckyInvoker := e.NewInvoker(policyHash, unlucky)
// Block unlucky account.
policySuperInvoker.Invoke(t, true, "blockAccount", unlucky.ScriptHash())
// Transaction from blocked account shouldn't be accepted.
t.Run("isBlocked, internal method", func(t *testing.T) { t.Run("isBlocked, internal method", func(t *testing.T) {
isBlocked := chain.contracts.Policy.IsBlockedInternal(chain.dao, random.Uint160()) tx := policyUnluckyInvoker.PrepareInvoke(t, "getStoragePrice")
require.Equal(t, false, isBlocked) b := e.NewUnsignedBlock(t, tx)
e.SignBlock(b)
expectedErr := fmt.Sprintf("transaction %s failed to verify: not allowed by policy: account %s is blocked", tx.Hash().StringLE(), unlucky.ScriptHash().StringLE())
err := e.Chain.AddBlock(b)
require.Error(t, err)
require.Equal(t, expectedErr, err.Error())
}) })
} }

View file

@ -1,8 +1,9 @@
package core package core_test
import ( import (
"errors" "errors"
"fmt" "fmt"
"math/big"
"math/rand" "math/rand"
"path" "path"
"path/filepath" "path/filepath"
@ -10,29 +11,32 @@ import (
"testing" "testing"
"time" "time"
"github.com/nspcc-dev/neo-go/internal/testchain"
"github.com/nspcc-dev/neo-go/pkg/config" "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/block" "github.com/nspcc-dev/neo-go/pkg/core/block"
"github.com/nspcc-dev/neo-go/pkg/core/mempool" "github.com/nspcc-dev/neo-go/pkg/core/mempool"
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles" "github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
"github.com/nspcc-dev/neo-go/pkg/core/transaction" "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/hash"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/interop/native/roles"
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/neotest"
"github.com/nspcc-dev/neo-go/pkg/neotest/chain"
"github.com/nspcc-dev/neo-go/pkg/network"
"github.com/nspcc-dev/neo-go/pkg/network/payload" "github.com/nspcc-dev/neo-go/pkg/network/payload"
"github.com/nspcc-dev/neo-go/pkg/services/notary" "github.com/nspcc-dev/neo-go/pkg/services/notary"
"github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/nspcc-dev/neo-go/pkg/vm/opcode"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/nspcc-dev/neo-go/pkg/wallet" "github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.uber.org/zap/zaptest" "go.uber.org/zap/zaptest"
) )
var notaryModulePath = filepath.Join("..", "services", "notary") func getTestNotary(t *testing.T, bc *core.Blockchain, walletPath, pass string, onTx func(tx *transaction.Transaction) error) (*wallet.Account, *notary.Notary, *mempool.Pool) {
func getTestNotary(t *testing.T, bc *Blockchain, walletPath, pass string, onTx func(tx *transaction.Transaction) error) (*wallet.Account, *notary.Notary, *mempool.Pool) {
mainCfg := config.P2PNotary{ mainCfg := config.P2PNotary{
Enabled: true, Enabled: true,
UnlockWallet: config.Wallet{ UnlockWallet: config.Wallet{
@ -46,7 +50,7 @@ func getTestNotary(t *testing.T, bc *Blockchain, walletPath, pass string, onTx f
Log: zaptest.NewLogger(t), Log: zaptest.NewLogger(t),
} }
mp := mempool.New(10, 1, true) mp := mempool.New(10, 1, true)
ntr, err := notary.NewNotary(cfg, testchain.Network(), mp, onTx) ntr, err := notary.NewNotary(cfg, netmode.UnitTestNet, mp, onTx)
require.NoError(t, err) require.NoError(t, err)
w, err := wallet.NewWalletFromFile(path.Join(notaryModulePath, walletPath)) w, err := wallet.NewWalletFromFile(path.Join(notaryModulePath, walletPath))
@ -68,7 +72,14 @@ func dupNotaryRequest(t *testing.T, p *payload.P2PNotaryRequest) *payload.P2PNot
} }
func TestNotary(t *testing.T) { func TestNotary(t *testing.T) {
bc := newTestChain(t) bc, validators, committee := chain.NewMultiWithCustomConfig(t, func(c *config.ProtocolConfiguration) {
c.P2PSigExtensions = true
})
e := neotest.NewExecutor(t, bc, validators, committee)
notaryHash := e.NativeHash(t, nativenames.Notary)
designationSuperInvoker := e.NewInvoker(e.NativeHash(t, nativenames.Designation), validators, committee)
gasValidatorInvoker := e.ValidatorInvoker(e.NativeHash(t, nativenames.Gas))
var ( var (
nonce uint32 nonce uint32
nvbDiffFallback uint32 = 20 nvbDiffFallback uint32 = 20
@ -145,8 +156,9 @@ func TestNotary(t *testing.T) {
mp1.StopSubscriptions() mp1.StopSubscriptions()
}) })
notaryNodes := keys.PublicKeys{acc1.PrivateKey().PublicKey(), acc2.PrivateKey().PublicKey()} notaryNodes := []interface{}{acc1.PrivateKey().PublicKey().Bytes(), acc2.PrivateKey().PublicKey().Bytes()}
bc.setNodesByRole(t, true, noderoles.P2PNotary, notaryNodes) designationSuperInvoker.Invoke(t, stackitem.Null{}, "designateAsRole",
int64(roles.P2PNotary), notaryNodes)
type requester struct { type requester struct {
accounts []*wallet.Account accounts []*wallet.Account
@ -193,7 +205,7 @@ func TestNotary(t *testing.T) {
VerificationScript: []byte{}, VerificationScript: []byte{},
}, },
} }
err = requester.SignTx(testchain.Network(), fallback) err = requester.SignTx(netmode.UnitTestNet, fallback)
require.NoError(t, err) require.NoError(t, err)
return fallback return fallback
} }
@ -251,7 +263,7 @@ func TestNotary(t *testing.T) {
for j := range main.Scripts { for j := range main.Scripts {
main.Scripts[j].VerificationScript = verificationScripts[j] main.Scripts[j].VerificationScript = verificationScripts[j]
if i == j { if i == j {
main.Scripts[j].InvocationScript = append([]byte{byte(opcode.PUSHDATA1), 64}, acc.PrivateKey().SignHashable(uint32(testchain.Network()), main)...) main.Scripts[j].InvocationScript = append([]byte{byte(opcode.PUSHDATA1), 64}, acc.PrivateKey().SignHashable(uint32(netmode.UnitTestNet), main)...)
} }
} }
main.Scripts = append(main.Scripts, transaction.Witness{}) // empty Notary witness main.Scripts = append(main.Scripts, transaction.Witness{}) // empty Notary witness
@ -296,12 +308,11 @@ func TestNotary(t *testing.T) {
require.Equal(t, io.GetVarSize(completedTx), completedTx.Size()) require.Equal(t, io.GetVarSize(completedTx), completedTx.Size())
for i := 0; i < len(completedTx.Scripts)-1; i++ { for i := 0; i < len(completedTx.Scripts)-1; i++ {
interopCtx := bc.newInteropContext(trigger.Verification, bc.dao, nil, completedTx) _, err := bc.VerifyWitness(completedTx.Signers[i].Account, completedTx, &completedTx.Scripts[i], -1)
_, err := bc.verifyHashAgainstScript(completedTx.Signers[i].Account, &completedTx.Scripts[i], interopCtx, -1)
require.NoError(t, err) require.NoError(t, err)
} }
require.Equal(t, transaction.Witness{ require.Equal(t, transaction.Witness{
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, acc1.PrivateKey().SignHashable(uint32(testchain.Network()), requests[0].MainTransaction)...), InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, acc1.PrivateKey().SignHashable(uint32(netmode.UnitTestNet), requests[0].MainTransaction)...),
VerificationScript: []byte{}, VerificationScript: []byte{},
}, completedTx.Scripts[len(completedTx.Scripts)-1]) }, completedTx.Scripts[len(completedTx.Scripts)-1])
} else { } else {
@ -316,15 +327,14 @@ func TestNotary(t *testing.T) {
require.Equal(t, 2, len(completedTx.Signers)) require.Equal(t, 2, len(completedTx.Signers))
require.Equal(t, 2, len(completedTx.Scripts)) require.Equal(t, 2, len(completedTx.Scripts))
require.Equal(t, transaction.Witness{ require.Equal(t, transaction.Witness{
InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, acc1.PrivateKey().SignHashable(uint32(testchain.Network()), req.FallbackTransaction)...), InvocationScript: append([]byte{byte(opcode.PUSHDATA1), 64}, acc1.PrivateKey().SignHashable(uint32(netmode.UnitTestNet), req.FallbackTransaction)...),
VerificationScript: []byte{}, VerificationScript: []byte{},
}, completedTx.Scripts[0]) }, completedTx.Scripts[0])
// check that tx size was updated // check that tx size was updated
require.Equal(t, io.GetVarSize(completedTx), completedTx.Size()) require.Equal(t, io.GetVarSize(completedTx), completedTx.Size())
interopCtx := bc.newInteropContext(trigger.Verification, bc.dao, nil, completedTx) _, err := bc.VerifyWitness(completedTx.Signers[1].Account, completedTx, &completedTx.Scripts[1], -1)
_, err := bc.verifyHashAgainstScript(completedTx.Signers[1].Account, &completedTx.Scripts[1], interopCtx, -1)
require.NoError(t, err) require.NoError(t, err)
} else { } else {
completedTx := getCompletedTx(t, false, req.FallbackTransaction.Hash()) completedTx := getCompletedTx(t, false, req.FallbackTransaction.Hash())
@ -483,7 +493,8 @@ func TestNotary(t *testing.T) {
checkFallbackTxs(t, r, false) checkFallbackTxs(t, r, false)
ntr1.UpdateNotaryNodes(keys.PublicKeys{randomAcc.PublicKey()}) ntr1.UpdateNotaryNodes(keys.PublicKeys{randomAcc.PublicKey()})
setFinalizeWithError(false) setFinalizeWithError(false)
require.NoError(t, bc.AddBlock(bc.newBlock()))
e.AddNewBlock(t)
checkMainTx(t, requesters, r, 1, false) checkMainTx(t, requesters, r, 1, false)
checkFallbackTxs(t, r, false) checkFallbackTxs(t, r, false)
// set account back for the next tests // set account back for the next tests
@ -494,11 +505,11 @@ func TestNotary(t *testing.T) {
requests, requesters := checkCompleteStandardRequest(t, 3, false) requests, requesters := checkCompleteStandardRequest(t, 3, false)
// check PostPersist with finalisation error // check PostPersist with finalisation error
setFinalizeWithError(true) setFinalizeWithError(true)
require.NoError(t, bc.AddBlock(bc.newBlock())) e.AddNewBlock(t)
checkMainTx(t, requesters, requests, len(requests), false) checkMainTx(t, requesters, requests, len(requests), false)
// check PostPersist without finalisation error // check PostPersist without finalisation error
setFinalizeWithError(false) setFinalizeWithError(false)
require.NoError(t, bc.AddBlock(bc.newBlock())) e.AddNewBlock(t)
checkMainTx(t, requesters, requests, len(requests), true) checkMainTx(t, requesters, requests, len(requests), true)
// PostPersist: complete main transaction, multisignature account // PostPersist: complete main transaction, multisignature account
@ -507,12 +518,12 @@ func TestNotary(t *testing.T) {
checkFallbackTxs(t, requests, false) checkFallbackTxs(t, requests, false)
// check PostPersist with finalisation error // check PostPersist with finalisation error
setFinalizeWithError(true) setFinalizeWithError(true)
require.NoError(t, bc.AddBlock(bc.newBlock())) e.AddNewBlock(t)
checkMainTx(t, requesters, requests, len(requests), false) checkMainTx(t, requesters, requests, len(requests), false)
checkFallbackTxs(t, requests, false) checkFallbackTxs(t, requests, false)
// check PostPersist without finalisation error // check PostPersist without finalisation error
setFinalizeWithError(false) setFinalizeWithError(false)
require.NoError(t, bc.AddBlock(bc.newBlock())) e.AddNewBlock(t)
checkMainTx(t, requesters, requests, len(requests), true) checkMainTx(t, requesters, requests, len(requests), true)
checkFallbackTxs(t, requests, false) checkFallbackTxs(t, requests, false)
@ -521,15 +532,15 @@ func TestNotary(t *testing.T) {
requests, requesters = checkCompleteStandardRequest(t, 3, false) requests, requesters = checkCompleteStandardRequest(t, 3, false)
checkFallbackTxs(t, requests, false) checkFallbackTxs(t, requests, false)
// make fallbacks valid // make fallbacks valid
_, err = bc.genBlocks(int(nvbDiffFallback)) e.GenerateNewBlocks(t, int(nvbDiffFallback))
require.NoError(t, err) require.NoError(t, err)
// check PostPersist for valid fallbacks with finalisation error // check PostPersist for valid fallbacks with finalisation error
require.NoError(t, bc.AddBlock(bc.newBlock())) e.AddNewBlock(t)
checkMainTx(t, requesters, requests, len(requests), false) checkMainTx(t, requesters, requests, len(requests), false)
checkFallbackTxs(t, requests, false) checkFallbackTxs(t, requests, false)
// check PostPersist for valid fallbacks without finalisation error // check PostPersist for valid fallbacks without finalisation error
setFinalizeWithError(false) setFinalizeWithError(false)
require.NoError(t, bc.AddBlock(bc.newBlock())) e.AddNewBlock(t)
checkMainTx(t, requesters, requests, len(requests), false) checkMainTx(t, requesters, requests, len(requests), false)
checkFallbackTxs(t, requests, true) checkFallbackTxs(t, requests, true)
@ -540,15 +551,15 @@ func TestNotary(t *testing.T) {
requests, requesters = checkCompleteMultisigRequest(t, nSigs, nKeys, false) requests, requesters = checkCompleteMultisigRequest(t, nSigs, nKeys, false)
checkFallbackTxs(t, requests, false) checkFallbackTxs(t, requests, false)
// make fallbacks valid // make fallbacks valid
_, err = bc.genBlocks(int(nvbDiffFallback)) e.GenerateNewBlocks(t, int(nvbDiffFallback))
require.NoError(t, err) require.NoError(t, err)
// check PostPersist for valid fallbacks with finalisation error // check PostPersist for valid fallbacks with finalisation error
require.NoError(t, bc.AddBlock(bc.newBlock())) e.AddNewBlock(t)
checkMainTx(t, requesters, requests, len(requests), false) checkMainTx(t, requesters, requests, len(requests), false)
checkFallbackTxs(t, requests, false) checkFallbackTxs(t, requests, false)
// check PostPersist for valid fallbacks without finalisation error // check PostPersist for valid fallbacks without finalisation error
setFinalizeWithError(false) setFinalizeWithError(false)
require.NoError(t, bc.AddBlock(bc.newBlock())) e.AddNewBlock(t)
checkMainTx(t, requesters, requests, len(requests), false) checkMainTx(t, requesters, requests, len(requests), false)
checkFallbackTxs(t, requests[:nSigs], true) checkFallbackTxs(t, requests[:nSigs], true)
// the rest of fallbacks should also be applied even if the main tx was already constructed by the moment they were sent // the rest of fallbacks should also be applied even if the main tx was already constructed by the moment they were sent
@ -559,14 +570,14 @@ func TestNotary(t *testing.T) {
requests, requesters = checkCompleteStandardRequest(t, 5, false) requests, requesters = checkCompleteStandardRequest(t, 5, false)
checkFallbackTxs(t, requests, false) checkFallbackTxs(t, requests, false)
// make fallbacks valid // make fallbacks valid
_, err = bc.genBlocks(int(nvbDiffFallback)) e.GenerateNewBlocks(t, int(nvbDiffFallback))
require.NoError(t, err) require.NoError(t, err)
// some of fallbacks should fail finalisation // some of fallbacks should fail finalisation
unluckies = []*payload.P2PNotaryRequest{requests[0], requests[4]} unluckies = []*payload.P2PNotaryRequest{requests[0], requests[4]}
lucky := requests[1:4] lucky := requests[1:4]
setChoosy(true) setChoosy(true)
// check PostPersist for lucky fallbacks // check PostPersist for lucky fallbacks
require.NoError(t, bc.AddBlock(bc.newBlock())) e.AddNewBlock(t)
checkMainTx(t, requesters, requests, len(requests), false) checkMainTx(t, requesters, requests, len(requests), false)
checkFallbackTxs(t, lucky, true) checkFallbackTxs(t, lucky, true)
checkFallbackTxs(t, unluckies, false) checkFallbackTxs(t, unluckies, false)
@ -574,7 +585,7 @@ func TestNotary(t *testing.T) {
setChoosy(false) setChoosy(false)
setFinalizeWithError(false) setFinalizeWithError(false)
// check PostPersist for unlucky fallbacks // check PostPersist for unlucky fallbacks
require.NoError(t, bc.AddBlock(bc.newBlock())) e.AddNewBlock(t)
checkMainTx(t, requesters, requests, len(requests), false) checkMainTx(t, requesters, requests, len(requests), false)
checkFallbackTxs(t, lucky, true) checkFallbackTxs(t, lucky, true)
checkFallbackTxs(t, unluckies, true) checkFallbackTxs(t, unluckies, true)
@ -585,19 +596,19 @@ func TestNotary(t *testing.T) {
requests, requesters = checkCompleteStandardRequest(t, 5, false, 1, 2, 3, 4, 5) requests, requesters = checkCompleteStandardRequest(t, 5, false, 1, 2, 3, 4, 5)
checkFallbackTxs(t, requests, false) checkFallbackTxs(t, requests, false)
// generate blocks to reach the most earlier fallback's NVB // generate blocks to reach the most earlier fallback's NVB
_, err = bc.genBlocks(int(nvbDiffFallback)) e.GenerateNewBlocks(t, int(nvbDiffFallback))
require.NoError(t, err) require.NoError(t, err)
// check PostPersist for valid fallbacks without finalisation error // check PostPersist for valid fallbacks without finalisation error
// Add block before allowing tx to finalize to exclude race condition when // Add block before allowing tx to finalize to exclude race condition when
// main transaction is finalized between `finalizeWithError` restore and adding new block. // main transaction is finalized between `finalizeWithError` restore and adding new block.
require.NoError(t, bc.AddBlock(bc.newBlock())) e.AddNewBlock(t)
mtx.RLock() mtx.RLock()
start := len(completedTxes) start := len(completedTxes)
mtx.RUnlock() mtx.RUnlock()
setFinalizeWithError(false) setFinalizeWithError(false)
for i := range requests { for i := range requests {
if i != 0 { if i != 0 {
require.NoError(t, bc.AddBlock(bc.newBlock())) e.AddNewBlock(t)
} }
require.Eventually(t, func() bool { require.Eventually(t, func() bool {
mtx.RLock() mtx.RLock()
@ -615,13 +626,13 @@ func TestNotary(t *testing.T) {
requests, requesters = checkCompleteStandardRequest(t, 4, false) requests, requesters = checkCompleteStandardRequest(t, 4, false)
checkFallbackTxs(t, requests, false) checkFallbackTxs(t, requests, false)
// make fallbacks valid and remove one fallback // make fallbacks valid and remove one fallback
_, err = bc.genBlocks(int(nvbDiffFallback)) e.GenerateNewBlocks(t, int(nvbDiffFallback))
require.NoError(t, err) require.NoError(t, err)
ntr1.UpdateNotaryNodes(keys.PublicKeys{randomAcc.PublicKey()}) ntr1.UpdateNotaryNodes(keys.PublicKeys{randomAcc.PublicKey()})
ntr1.OnRequestRemoval(requests[3]) ntr1.OnRequestRemoval(requests[3])
// non of the fallbacks should be completed // non of the fallbacks should be completed
setFinalizeWithError(false) setFinalizeWithError(false)
require.NoError(t, bc.AddBlock(bc.newBlock())) e.AddNewBlock(t)
checkMainTx(t, requesters, requests, len(requests), false) checkMainTx(t, requesters, requests, len(requests), false)
checkFallbackTxs(t, requests, false) checkFallbackTxs(t, requests, false)
// set account back for the next tests // set account back for the next tests
@ -633,13 +644,13 @@ func TestNotary(t *testing.T) {
requests, requesters = checkCompleteStandardRequest(t, 4, false) requests, requesters = checkCompleteStandardRequest(t, 4, false)
checkFallbackTxs(t, requests, false) checkFallbackTxs(t, requests, false)
// make fallbacks valid and remove one fallback // make fallbacks valid and remove one fallback
_, err = bc.genBlocks(int(nvbDiffFallback)) e.GenerateNewBlocks(t, int(nvbDiffFallback))
require.NoError(t, err) require.NoError(t, err)
unlucky := requests[3] unlucky := requests[3]
ntr1.OnRequestRemoval(unlucky) ntr1.OnRequestRemoval(unlucky)
// rest of the fallbacks should be completed // rest of the fallbacks should be completed
setFinalizeWithError(false) setFinalizeWithError(false)
require.NoError(t, bc.AddBlock(bc.newBlock())) e.AddNewBlock(t)
checkMainTx(t, requesters, requests, len(requests), false) checkMainTx(t, requesters, requests, len(requests), false)
checkFallbackTxs(t, requests[:3], true) checkFallbackTxs(t, requests[:3], true)
require.Nil(t, completedTxes[unlucky.FallbackTransaction.Hash()]) require.Nil(t, completedTxes[unlucky.FallbackTransaction.Hash()])
@ -648,20 +659,20 @@ func TestNotary(t *testing.T) {
setFinalizeWithError(true) setFinalizeWithError(true)
requests, requesters = checkCompleteStandardRequest(t, 4, false) requests, requesters = checkCompleteStandardRequest(t, 4, false)
// remove all fallbacks // remove all fallbacks
_, err = bc.genBlocks(int(nvbDiffFallback)) e.GenerateNewBlocks(t, int(nvbDiffFallback))
require.NoError(t, err) require.NoError(t, err)
for i := range requests { for i := range requests {
ntr1.OnRequestRemoval(requests[i]) ntr1.OnRequestRemoval(requests[i])
} }
// then the whole request should be removed, i.e. there are no completed transactions // then the whole request should be removed, i.e. there are no completed transactions
setFinalizeWithError(false) setFinalizeWithError(false)
require.NoError(t, bc.AddBlock(bc.newBlock())) e.AddNewBlock(t)
checkMainTx(t, requesters, requests, len(requests), false) checkMainTx(t, requesters, requests, len(requests), false)
checkFallbackTxs(t, requests, false) checkFallbackTxs(t, requests, false)
// OnRequestRemoval: signature request, remove unexisting fallback // OnRequestRemoval: signature request, remove unexisting fallback
ntr1.OnRequestRemoval(requests[0]) ntr1.OnRequestRemoval(requests[0])
require.NoError(t, bc.AddBlock(bc.newBlock())) e.AddNewBlock(t)
checkMainTx(t, requesters, requests, len(requests), false) checkMainTx(t, requesters, requests, len(requests), false)
checkFallbackTxs(t, requests, false) checkFallbackTxs(t, requests, false)
@ -673,13 +684,13 @@ func TestNotary(t *testing.T) {
checkMainTx(t, requesters, requests, len(requests), false) checkMainTx(t, requesters, requests, len(requests), false)
checkFallbackTxs(t, requests, false) checkFallbackTxs(t, requests, false)
// make fallbacks valid and remove the last fallback // make fallbacks valid and remove the last fallback
_, err = bc.genBlocks(int(nvbDiffFallback)) e.GenerateNewBlocks(t, int(nvbDiffFallback))
require.NoError(t, err) require.NoError(t, err)
unlucky = requests[nSigs-1] unlucky = requests[nSigs-1]
ntr1.OnRequestRemoval(unlucky) ntr1.OnRequestRemoval(unlucky)
// then (m-1) out of n fallbacks should be completed // then (m-1) out of n fallbacks should be completed
setFinalizeWithError(false) setFinalizeWithError(false)
require.NoError(t, bc.AddBlock(bc.newBlock())) e.AddNewBlock(t)
checkMainTx(t, requesters, requests, len(requests), false) checkMainTx(t, requesters, requests, len(requests), false)
checkFallbackTxs(t, requests[:nSigs-1], true) checkFallbackTxs(t, requests[:nSigs-1], true)
require.Nil(t, completedTxes[unlucky.FallbackTransaction.Hash()]) require.Nil(t, completedTxes[unlucky.FallbackTransaction.Hash()])
@ -690,20 +701,20 @@ func TestNotary(t *testing.T) {
setFinalizeWithError(true) setFinalizeWithError(true)
requests, requesters = checkCompleteMultisigRequest(t, nSigs, nKeys, false) requests, requesters = checkCompleteMultisigRequest(t, nSigs, nKeys, false)
// make fallbacks valid and then remove all of them // make fallbacks valid and then remove all of them
_, err = bc.genBlocks(int(nvbDiffFallback)) e.GenerateNewBlocks(t, int(nvbDiffFallback))
require.NoError(t, err) require.NoError(t, err)
for i := range requests { for i := range requests {
ntr1.OnRequestRemoval(requests[i]) ntr1.OnRequestRemoval(requests[i])
} }
// then the whole request should be removed, i.e. there are no completed transactions // then the whole request should be removed, i.e. there are no completed transactions
setFinalizeWithError(false) setFinalizeWithError(false)
require.NoError(t, bc.AddBlock(bc.newBlock())) e.AddNewBlock(t)
checkMainTx(t, requesters, requests, len(requests), false) checkMainTx(t, requesters, requests, len(requests), false)
checkFallbackTxs(t, requests, false) checkFallbackTxs(t, requests, false)
// // OnRequestRemoval: multisignature request, remove unexisting fallbac, i.e. there still shouldn't be any completed transactions after this // // OnRequestRemoval: multisignature request, remove unexisting fallbac, i.e. there still shouldn't be any completed transactions after this
ntr1.OnRequestRemoval(requests[0]) ntr1.OnRequestRemoval(requests[0])
require.NoError(t, bc.AddBlock(bc.newBlock())) e.AddNewBlock(t)
checkMainTx(t, requesters, requests, len(requests), false) checkMainTx(t, requesters, requests, len(requests), false)
checkFallbackTxs(t, requests, false) checkFallbackTxs(t, requests, false)
@ -712,11 +723,11 @@ func TestNotary(t *testing.T) {
requester1, _ := wallet.NewAccount() requester1, _ := wallet.NewAccount()
requester2, _ := wallet.NewAccount() requester2, _ := wallet.NewAccount()
amount := int64(100_0000_0000) amount := int64(100_0000_0000)
feer := NewNotaryFeerStub(bc) gasValidatorInvoker.Invoke(t, true, "transfer", e.Validator.ScriptHash(), bc.GetNotaryContractScriptHash(), amount, []interface{}{requester1.PrivateKey().PublicKey().GetScriptHash(), int64(bc.BlockHeight() + 50)})
transferTokenFromMultisigAccountCheckOK(t, bc, bc.GetNotaryContractScriptHash(), bc.contracts.GAS.Hash, amount, requester1.PrivateKey().PublicKey().GetScriptHash(), int64(bc.BlockHeight()+50)) e.CheckGASBalance(t, notaryHash, big.NewInt(amount))
checkBalanceOf(t, bc, bc.contracts.Notary.Hash, int(amount)) gasValidatorInvoker.Invoke(t, true, "transfer", e.Validator.ScriptHash(), bc.GetNotaryContractScriptHash(), amount, []interface{}{requester2.PrivateKey().PublicKey().GetScriptHash(), int64(bc.BlockHeight() + 50)})
transferTokenFromMultisigAccountCheckOK(t, bc, bc.GetNotaryContractScriptHash(), bc.contracts.GAS.Hash, amount, requester2.PrivateKey().PublicKey().GetScriptHash(), int64(bc.BlockHeight()+50)) e.CheckGASBalance(t, notaryHash, big.NewInt(2*amount))
checkBalanceOf(t, bc, bc.contracts.Notary.Hash, int(2*amount))
// create request for 2 standard signatures => main tx should be completed after the second request is added to the pool // create request for 2 standard signatures => main tx should be completed after the second request is added to the pool
requests = createMixedRequest([]requester{ requests = createMixedRequest([]requester{
{ {
@ -728,6 +739,7 @@ func TestNotary(t *testing.T) {
typ: notary.Signature, typ: notary.Signature,
}, },
}) })
feer := network.NewNotaryFeer(bc)
require.NoError(t, mp1.Add(requests[0].FallbackTransaction, feer, requests[0])) require.NoError(t, mp1.Add(requests[0].FallbackTransaction, feer, requests[0]))
require.NoError(t, mp1.Add(requests[1].FallbackTransaction, feer, requests[1])) require.NoError(t, mp1.Add(requests[1].FallbackTransaction, feer, requests[1]))
require.Eventually(t, func() bool { require.Eventually(t, func() bool {

View file

@ -1,7 +1,9 @@
package core package core_test
import ( import (
"bytes" "bytes"
"encoding/binary"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
gio "io" gio "io"
@ -16,37 +18,38 @@ import (
"github.com/nspcc-dev/neo-go/internal/contracts" "github.com/nspcc-dev/neo-go/internal/contracts"
"github.com/nspcc-dev/neo-go/pkg/config" "github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/config/netmode" "github.com/nspcc-dev/neo-go/pkg/config/netmode"
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles" "github.com/nspcc-dev/neo-go/pkg/core"
"github.com/nspcc-dev/neo-go/pkg/core/native"
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
"github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/interop/native/roles"
"github.com/nspcc-dev/neo-go/pkg/neotest"
"github.com/nspcc-dev/neo-go/pkg/neotest/chain"
"github.com/nspcc-dev/neo-go/pkg/services/oracle" "github.com/nspcc-dev/neo-go/pkg/services/oracle"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/util/slice"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/nspcc-dev/neo-go/pkg/wallet" "github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.uber.org/zap/zaptest" "go.uber.org/zap/zaptest"
) )
var ( var oracleModulePath = filepath.Join("..", "services", "oracle")
oracleModulePath = filepath.Join("..", "services", "oracle")
pathToInternalContracts = filepath.Join("..", "..", "internal", "contracts")
)
func putOracleRequest(t *testing.T, h util.Uint160, bc *Blockchain, func putOracleRequest(t *testing.T, oracleValidatorInvoker *neotest.ContractInvoker,
url string, filter *string, cb string, userData []byte, gas int64) util.Uint256 { url string, filter *string, cb string, userData []byte, gas int64) util.Uint256 {
var filtItem interface{} var filtItem interface{}
if filter != nil { if filter != nil {
filtItem = *filter filtItem = *filter
} }
res, err := invokeContractMethod(bc, gas+50_000_000+5_000_000, h, "requestURL", return oracleValidatorInvoker.Invoke(t, stackitem.Null{}, "requestURL", url, filtItem, cb, userData, gas)
url, filtItem, cb, userData, gas)
require.NoError(t, err)
return res.Container
} }
func getOracleConfig(t *testing.T, bc *Blockchain, w, pass string, returnOracleRedirectionErrOn func(address string) bool) oracle.Config { func getOracleConfig(t *testing.T, bc *core.Blockchain, w, pass string, returnOracleRedirectionErrOn func(address string) bool) oracle.Config {
return oracle.Config{ return oracle.Config{
Log: zaptest.NewLogger(t), Log: zaptest.NewLogger(t),
Network: netmode.UnitTestNet, Network: netmode.UnitTestNet,
@ -63,7 +66,7 @@ func getOracleConfig(t *testing.T, bc *Blockchain, w, pass string, returnOracleR
} }
} }
func getTestOracle(t *testing.T, bc *Blockchain, walletPath, pass string) ( func getTestOracle(t *testing.T, bc *core.Blockchain, walletPath, pass string) (
*wallet.Account, *wallet.Account,
*oracle.Oracle, *oracle.Oracle,
map[uint64]*responseWithSig, map[uint64]*responseWithSig,
@ -87,7 +90,19 @@ func getTestOracle(t *testing.T, bc *Blockchain, walletPath, pass string) (
// Compatibility test from C# code. // Compatibility test from C# code.
// https://github.com/neo-project/neo-modules/blob/master/tests/Neo.Plugins.OracleService.Tests/UT_OracleService.cs#L61 // https://github.com/neo-project/neo-modules/blob/master/tests/Neo.Plugins.OracleService.Tests/UT_OracleService.cs#L61
func TestCreateResponseTx(t *testing.T) { func TestCreateResponseTx(t *testing.T) {
bc := newTestChain(t) bc, validator, committee := chain.NewMulti(t)
e := neotest.NewExecutor(t, bc, validator, committee)
managementInvoker := e.ValidatorInvoker(e.NativeHash(t, nativenames.Management))
cs := contracts.GetOracleContractState(t, pathToInternalContracts, validator.ScriptHash(), 0)
rawManifest, err := json.Marshal(cs.Manifest)
require.NoError(t, err)
rawNef, err := cs.NEF.Bytes()
require.NoError(t, err)
tx := managementInvoker.PrepareInvoke(t, "deploy", rawNef, rawManifest)
e.AddNewBlock(t, tx)
e.CheckHalt(t, tx.Hash())
cInvoker := e.ValidatorInvoker(cs.Hash)
require.Equal(t, int64(30), bc.GetBaseExecFee()) require.Equal(t, int64(30), bc.GetBaseExecFee())
require.Equal(t, int64(1000), bc.FeePerByte()) require.Equal(t, int64(1000), bc.FeePerByte())
@ -106,10 +121,10 @@ func TestCreateResponseTx(t *testing.T) {
Code: transaction.Success, Code: transaction.Success,
Result: []byte{0}, Result: []byte{0},
} }
require.NoError(t, bc.contracts.Oracle.PutRequestInternal(1, req, bc.dao)) cInvoker.Invoke(t, stackitem.Null{}, "requestURL", req.URL, *req.Filter, req.CallbackMethod, req.UserData, int64(req.GasForResponse))
orc.UpdateOracleNodes(keys.PublicKeys{acc.PrivateKey().PublicKey()}) orc.UpdateOracleNodes(keys.PublicKeys{acc.PrivateKey().PublicKey()})
bc.SetOracle(orc) bc.SetOracle(orc)
tx, err := orc.CreateResponseTx(int64(req.GasForResponse), 1, resp) tx, err = orc.CreateResponseTx(int64(req.GasForResponse), 1, resp)
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, 166, tx.Size()) assert.Equal(t, 166, tx.Size())
assert.Equal(t, int64(2198650), tx.NetworkFee) assert.Equal(t, int64(2198650), tx.NetworkFee)
@ -117,7 +132,7 @@ func TestCreateResponseTx(t *testing.T) {
} }
func TestOracle_InvalidWallet(t *testing.T) { func TestOracle_InvalidWallet(t *testing.T) {
bc := newTestChain(t) bc, _, _ := chain.NewMulti(t)
_, err := oracle.NewOracle(getOracleConfig(t, bc, "./testdata/oracle1.json", "invalid", nil)) _, err := oracle.NewOracle(getOracleConfig(t, bc, "./testdata/oracle1.json", "invalid", nil))
require.Error(t, err) require.Error(t, err)
@ -127,45 +142,65 @@ func TestOracle_InvalidWallet(t *testing.T) {
} }
func TestOracle(t *testing.T) { func TestOracle(t *testing.T) {
bc := newTestChain(t) bc, validator, committee := chain.NewMulti(t)
e := neotest.NewExecutor(t, bc, validator, committee)
managementInvoker := e.ValidatorInvoker(e.NativeHash(t, nativenames.Management))
designationSuperInvoker := e.NewInvoker(e.NativeHash(t, nativenames.Designation), validator, committee)
nativeOracleH := e.NativeHash(t, nativenames.Oracle)
nativeOracleID := e.NativeID(t, nativenames.Oracle)
oracleCtr := bc.contracts.Oracle
acc1, orc1, m1, ch1 := getTestOracle(t, bc, "./testdata/oracle1.json", "one") acc1, orc1, m1, ch1 := getTestOracle(t, bc, "./testdata/oracle1.json", "one")
acc2, orc2, m2, ch2 := getTestOracle(t, bc, "./testdata/oracle2.json", "two") acc2, orc2, m2, ch2 := getTestOracle(t, bc, "./testdata/oracle2.json", "two")
oracleNodes := keys.PublicKeys{acc1.PrivateKey().PublicKey(), acc2.PrivateKey().PublicKey()} oracleNodes := keys.PublicKeys{acc1.PrivateKey().PublicKey(), acc2.PrivateKey().PublicKey()}
// Must be set in native contract for tx verification. // Must be set in native contract for tx verification.
bc.setNodesByRole(t, true, noderoles.Oracle, oracleNodes) designationSuperInvoker.Invoke(t, stackitem.Null{}, "designateAsRole",
int64(roles.Oracle), []interface{}{oracleNodes[0].Bytes(), oracleNodes[1].Bytes()})
orc1.UpdateOracleNodes(oracleNodes.Copy()) orc1.UpdateOracleNodes(oracleNodes.Copy())
orc2.UpdateOracleNodes(oracleNodes.Copy()) orc2.UpdateOracleNodes(oracleNodes.Copy())
orcNative := bc.contracts.Oracle nativeOracleState := bc.GetContractState(nativeOracleH)
md, ok := orcNative.GetMethod(manifest.MethodVerify, -1) require.NotNil(t, nativeOracleState)
require.True(t, ok) md := nativeOracleState.Manifest.ABI.GetMethod(manifest.MethodVerify, -1)
orc1.UpdateNativeContract(orcNative.NEF.Script, orcNative.GetOracleResponseScript(), orcNative.Hash, md.MD.Offset) require.NotNil(t, md)
orc2.UpdateNativeContract(orcNative.NEF.Script, orcNative.GetOracleResponseScript(), orcNative.Hash, md.MD.Offset) oracleRespScript := native.CreateOracleResponseScript(nativeOracleH)
orc1.UpdateNativeContract(nativeOracleState.NEF.Script, slice.Copy(oracleRespScript), nativeOracleH, md.Offset)
orc2.UpdateNativeContract(nativeOracleState.NEF.Script, slice.Copy(oracleRespScript), nativeOracleH, md.Offset)
cs := contracts.GetOracleContractState(t, pathToInternalContracts, util.Uint160{}, 42) cs := contracts.GetOracleContractState(t, pathToInternalContracts, validator.ScriptHash(), 0)
require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs)) rawManifest, err := json.Marshal(cs.Manifest)
require.NoError(t, err)
rawNef, err := cs.NEF.Bytes()
require.NoError(t, err)
tx := managementInvoker.PrepareInvoke(t, "deploy", rawNef, rawManifest)
e.AddNewBlock(t, tx)
e.CheckHalt(t, tx.Hash())
cInvoker := e.ValidatorInvoker(cs.Hash)
putOracleRequest(t, cs.Hash, bc, "https://get.1234", nil, "handle", []byte{}, 10_000_000) putOracleRequest(t, cInvoker, "https://get.1234", nil, "handle", []byte{}, 10_000_000)
putOracleRequest(t, cs.Hash, bc, "https://get.1234", nil, "handle", []byte{}, 10_000_000) putOracleRequest(t, cInvoker, "https://get.1234", nil, "handle", []byte{}, 10_000_000)
putOracleRequest(t, cs.Hash, bc, "https://get.timeout", nil, "handle", []byte{}, 10_000_000) putOracleRequest(t, cInvoker, "https://get.timeout", nil, "handle", []byte{}, 10_000_000)
putOracleRequest(t, cs.Hash, bc, "https://get.notfound", nil, "handle", []byte{}, 10_000_000) putOracleRequest(t, cInvoker, "https://get.notfound", nil, "handle", []byte{}, 10_000_000)
putOracleRequest(t, cs.Hash, bc, "https://get.forbidden", nil, "handle", []byte{}, 10_000_000) putOracleRequest(t, cInvoker, "https://get.forbidden", nil, "handle", []byte{}, 10_000_000)
putOracleRequest(t, cs.Hash, bc, "https://private.url", nil, "handle", []byte{}, 10_000_000) putOracleRequest(t, cInvoker, "https://private.url", nil, "handle", []byte{}, 10_000_000)
putOracleRequest(t, cs.Hash, bc, "https://get.big", nil, "handle", []byte{}, 10_000_000) putOracleRequest(t, cInvoker, "https://get.big", nil, "handle", []byte{}, 10_000_000)
putOracleRequest(t, cs.Hash, bc, "https://get.maxallowed", nil, "handle", []byte{}, 10_000_000) putOracleRequest(t, cInvoker, "https://get.maxallowed", nil, "handle", []byte{}, 10_000_000)
putOracleRequest(t, cs.Hash, bc, "https://get.maxallowed", nil, "handle", []byte{}, 100_000_000) putOracleRequest(t, cInvoker, "https://get.maxallowed", nil, "handle", []byte{}, 100_000_000)
flt := "$.Values[1]" flt := "$.Values[1]"
putOracleRequest(t, cs.Hash, bc, "https://get.filter", &flt, "handle", []byte{}, 10_000_000) putOracleRequest(t, cInvoker, "https://get.filter", &flt, "handle", []byte{}, 10_000_000)
putOracleRequest(t, cs.Hash, bc, "https://get.filterinv", &flt, "handle", []byte{}, 10_000_000) putOracleRequest(t, cInvoker, "https://get.filterinv", &flt, "handle", []byte{}, 10_000_000)
putOracleRequest(t, cs.Hash, bc, "https://get.invalidcontent", nil, "handle", []byte{}, 10_000_000) putOracleRequest(t, cInvoker, "https://get.invalidcontent", nil, "handle", []byte{}, 10_000_000)
checkResp := func(t *testing.T, id uint64, resp *transaction.OracleResponse) *state.OracleRequest { checkResp := func(t *testing.T, id uint64, resp *transaction.OracleResponse) *state.OracleRequest {
req, err := oracleCtr.GetRequestInternal(bc.dao, id) // Use a hack to get request from Oracle contract, because we can't use GetRequestInternal directly.
require.NoError(t, err) requestKey := make([]byte, 9)
requestKey[0] = 7 // prefixRequest from native Oracle contract
binary.BigEndian.PutUint64(requestKey[1:], id)
si := bc.GetStorageItem(nativeOracleID, requestKey)
require.NotNil(t, si)
req := new(state.OracleRequest)
require.NoError(t, stackitem.DeserializeConvertible(si, req))
reqs := map[uint64]*state.OracleRequest{id: req} reqs := map[uint64]*state.OracleRequest{id: req}
orc1.ProcessRequestsInternal(reqs) orc1.ProcessRequestsInternal(reqs)
@ -198,7 +233,7 @@ func TestOracle(t *testing.T) {
actualHash := cp.Hash() actualHash := cp.Hash()
require.Equal(t, actualHash, cachedHash, "transaction hash was changed during ") require.Equal(t, actualHash, cachedHash, "transaction hash was changed during ")
require.NoError(t, bc.verifyAndPoolTx(tx, bc.GetMemPool(), bc)) require.NoError(t, bc.PoolTx(tx))
} }
t.Run("NormalRequest", func(t *testing.T) { t.Run("NormalRequest", func(t *testing.T) {
@ -306,21 +341,34 @@ func TestOracle(t *testing.T) {
} }
func TestOracleFull(t *testing.T) { func TestOracleFull(t *testing.T) {
bc := initTestChain(t, nil, nil) bc, validator, committee := chain.NewMultiWithCustomConfigAndStore(t, nil, nil, false)
e := neotest.NewExecutor(t, bc, validator, committee)
designationSuperInvoker := e.NewInvoker(e.NativeHash(t, nativenames.Designation), validator, committee)
acc, orc, _, _ := getTestOracle(t, bc, "./testdata/oracle2.json", "two") acc, orc, _, _ := getTestOracle(t, bc, "./testdata/oracle2.json", "two")
mp := bc.GetMemPool() mp := bc.GetMemPool()
orc.OnTransaction = func(tx *transaction.Transaction) error { return mp.Add(tx, bc) } orc.OnTransaction = func(tx *transaction.Transaction) error { return mp.Add(tx, bc) }
bc.SetOracle(orc) bc.SetOracle(orc)
cs := contracts.GetOracleContractState(t, pathToInternalContracts, util.Uint160{}, 42)
require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs))
go bc.Run() go bc.Run()
orc.Start() orc.Start()
t.Cleanup(orc.Shutdown) t.Cleanup(func() {
orc.Shutdown()
bc.Close()
})
bc.setNodesByRole(t, true, noderoles.Oracle, keys.PublicKeys{acc.PrivateKey().PublicKey()}) designationSuperInvoker.Invoke(t, stackitem.Null{}, "designateAsRole",
putOracleRequest(t, cs.Hash, bc, "https://get.1234", new(string), "handle", []byte{}, 10_000_000) int64(roles.Oracle), []interface{}{acc.PrivateKey().PublicKey().Bytes()})
cs := contracts.GetOracleContractState(t, pathToInternalContracts, validator.ScriptHash(), 0)
e.DeployContract(t, &neotest.Contract{
Hash: cs.Hash,
NEF: &cs.NEF,
Manifest: &cs.Manifest,
}, nil)
cInvoker := e.ValidatorInvoker(cs.Hash)
putOracleRequest(t, cInvoker, "https://get.1234", new(string), "handle", []byte{}, 10_000_000)
require.Eventually(t, func() bool { return mp.Count() == 1 }, require.Eventually(t, func() bool { return mp.Count() == 1 },
time.Second*3, time.Millisecond*200) time.Second*3, time.Millisecond*200)
@ -331,17 +379,20 @@ func TestOracleFull(t *testing.T) {
} }
func TestNotYetRunningOracle(t *testing.T) { func TestNotYetRunningOracle(t *testing.T) {
bc := initTestChain(t, nil, nil) bc, validator, committee := chain.NewMultiWithCustomConfigAndStore(t, nil, nil, false)
e := neotest.NewExecutor(t, bc, validator, committee)
designationSuperInvoker := e.NewInvoker(e.NativeHash(t, nativenames.Designation), validator, committee)
acc, orc, _, _ := getTestOracle(t, bc, "./testdata/oracle2.json", "two") acc, orc, _, _ := getTestOracle(t, bc, "./testdata/oracle2.json", "two")
mp := bc.GetMemPool() mp := bc.GetMemPool()
orc.OnTransaction = func(tx *transaction.Transaction) error { return mp.Add(tx, bc) } orc.OnTransaction = func(tx *transaction.Transaction) error { return mp.Add(tx, bc) }
bc.SetOracle(orc) bc.SetOracle(orc)
cs := contracts.GetOracleContractState(t, pathToInternalContracts, util.Uint160{}, 42)
require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs))
go bc.Run() go bc.Run()
bc.setNodesByRole(t, true, noderoles.Oracle, keys.PublicKeys{acc.PrivateKey().PublicKey()}) t.Cleanup(bc.Close)
designationSuperInvoker.Invoke(t, stackitem.Null{}, "designateAsRole",
int64(roles.Oracle), []interface{}{acc.PrivateKey().PublicKey().Bytes()})
var req state.OracleRequest var req state.OracleRequest
var reqs = make(map[uint64]*state.OracleRequest) var reqs = make(map[uint64]*state.OracleRequest)

View file

@ -1,6 +1,7 @@
package core package core_test
import ( import (
"crypto/elliptic"
"errors" "errors"
"path/filepath" "path/filepath"
"sort" "sort"
@ -10,18 +11,25 @@ import (
"github.com/nspcc-dev/neo-go/internal/testserdes" "github.com/nspcc-dev/neo-go/internal/testserdes"
"github.com/nspcc-dev/neo-go/pkg/config" "github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/config/netmode" "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/native/nativenames"
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles" "github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
"github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/state"
corestate "github.com/nspcc-dev/neo-go/pkg/core/stateroot"
"github.com/nspcc-dev/neo-go/pkg/core/storage" "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/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto/hash" "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/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/interop/native/roles"
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/neotest"
"github.com/nspcc-dev/neo-go/pkg/neotest/chain"
"github.com/nspcc-dev/neo-go/pkg/network/payload" "github.com/nspcc-dev/neo-go/pkg/network/payload"
"github.com/nspcc-dev/neo-go/pkg/services/stateroot" "github.com/nspcc-dev/neo-go/pkg/services/stateroot"
"github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/util" "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/emit"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/nspcc-dev/neo-go/pkg/wallet" "github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.uber.org/atomic" "go.uber.org/atomic"
@ -70,64 +78,71 @@ func newMajorityMultisigWithGAS(t *testing.T, n int) (util.Uint160, keys.PublicK
} }
func TestStateRoot(t *testing.T) { func TestStateRoot(t *testing.T) {
bc := newTestChain(t) bc, validator, committee := chain.NewMulti(t)
e := neotest.NewExecutor(t, bc, validator, committee)
designationSuperInvoker := e.NewInvoker(e.NativeHash(t, nativenames.Designation), validator, committee)
gasValidatorInvoker := e.ValidatorInvoker(e.NativeHash(t, nativenames.Gas))
h, pubs, accs := newMajorityMultisigWithGAS(t, 2) h, pubs, accs := newMajorityMultisigWithGAS(t, 2)
bc.setNodesByRole(t, true, noderoles.StateValidator, pubs) validatorNodes := []interface{}{pubs[0].Bytes(), pubs[1].Bytes()}
designationSuperInvoker.Invoke(t, stackitem.Null{}, "designateAsRole",
int64(roles.StateValidator), validatorNodes)
updateIndex := bc.BlockHeight() updateIndex := bc.BlockHeight()
transferTokenFromMultisigAccount(t, bc, h, bc.contracts.GAS.Hash, 1_0000_0000)
gasValidatorInvoker.Invoke(t, true, "transfer", validator.ScriptHash(), h, 1_0000_0000, nil)
tmpDir := t.TempDir() tmpDir := t.TempDir()
w := createAndWriteWallet(t, accs[0], filepath.Join(tmpDir, "w"), "pass") w := createAndWriteWallet(t, accs[0], filepath.Join(tmpDir, "w"), "pass")
cfg := createStateRootConfig(w.Path(), "pass") cfg := createStateRootConfig(w.Path(), "pass")
srv, err := stateroot.New(cfg, bc.stateRoot, zaptest.NewLogger(t), bc, nil) srMod := bc.GetStateModule().(*corestate.Module) // Take full responsibility here.
srv, err := stateroot.New(cfg, srMod, zaptest.NewLogger(t), bc, nil)
require.NoError(t, err) require.NoError(t, err)
require.EqualValues(t, 0, bc.stateRoot.CurrentValidatedHeight()) require.EqualValues(t, 0, bc.GetStateModule().CurrentValidatedHeight())
r, err := bc.stateRoot.GetStateRoot(bc.BlockHeight()) r, err := bc.GetStateModule().GetStateRoot(bc.BlockHeight())
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, r.Root, bc.stateRoot.CurrentLocalStateRoot()) require.Equal(t, r.Root, bc.GetStateModule().CurrentLocalStateRoot())
t.Run("invalid message", func(t *testing.T) { t.Run("invalid message", func(t *testing.T) {
require.Error(t, srv.OnPayload(&payload.Extensible{Data: []byte{42}})) require.Error(t, srv.OnPayload(&payload.Extensible{Data: []byte{42}}))
require.EqualValues(t, 0, bc.stateRoot.CurrentValidatedHeight()) require.EqualValues(t, 0, bc.GetStateModule().CurrentValidatedHeight())
}) })
t.Run("drop zero index", func(t *testing.T) { t.Run("drop zero index", func(t *testing.T) {
r, err := bc.stateRoot.GetStateRoot(0) r, err := bc.GetStateModule().GetStateRoot(0)
require.NoError(t, err) require.NoError(t, err)
data, err := testserdes.EncodeBinary(stateroot.NewMessage(stateroot.RootT, r)) data, err := testserdes.EncodeBinary(stateroot.NewMessage(stateroot.RootT, r))
require.NoError(t, err) require.NoError(t, err)
require.NoError(t, srv.OnPayload(&payload.Extensible{Data: data})) require.NoError(t, srv.OnPayload(&payload.Extensible{Data: data}))
require.EqualValues(t, 0, bc.stateRoot.CurrentValidatedHeight()) require.EqualValues(t, 0, bc.GetStateModule().CurrentValidatedHeight())
}) })
t.Run("invalid height", func(t *testing.T) { t.Run("invalid height", func(t *testing.T) {
r, err := bc.stateRoot.GetStateRoot(1) r, err := bc.GetStateModule().GetStateRoot(1)
require.NoError(t, err) require.NoError(t, err)
r.Index = 10 r.Index = 10
data := testSignStateRoot(t, r, pubs, accs...) data := testSignStateRoot(t, r, pubs, accs...)
require.Error(t, srv.OnPayload(&payload.Extensible{Data: data})) require.Error(t, srv.OnPayload(&payload.Extensible{Data: data}))
require.EqualValues(t, 0, bc.stateRoot.CurrentValidatedHeight()) require.EqualValues(t, 0, bc.GetStateModule().CurrentValidatedHeight())
}) })
t.Run("invalid signer", func(t *testing.T) { t.Run("invalid signer", func(t *testing.T) {
accInv, err := wallet.NewAccount() accInv, err := wallet.NewAccount()
require.NoError(t, err) require.NoError(t, err)
pubs := keys.PublicKeys{accInv.PrivateKey().PublicKey()} pubs := keys.PublicKeys{accInv.PrivateKey().PublicKey()}
require.NoError(t, accInv.ConvertMultisig(1, pubs)) require.NoError(t, accInv.ConvertMultisig(1, pubs))
transferTokenFromMultisigAccount(t, bc, accInv.Contract.ScriptHash(), bc.contracts.GAS.Hash, 1_0000_0000) gasValidatorInvoker.Invoke(t, true, "transfer", validator.ScriptHash(), accInv.Contract.ScriptHash(), 1_0000_0000, nil)
r, err := bc.stateRoot.GetStateRoot(1) r, err := bc.GetStateModule().GetStateRoot(1)
require.NoError(t, err) require.NoError(t, err)
data := testSignStateRoot(t, r, pubs, accInv) data := testSignStateRoot(t, r, pubs, accInv)
err = srv.OnPayload(&payload.Extensible{Data: data}) err = srv.OnPayload(&payload.Extensible{Data: data})
require.True(t, errors.Is(err, ErrWitnessHashMismatch), "got: %v", err) require.True(t, errors.Is(err, core.ErrWitnessHashMismatch), "got: %v", err)
require.EqualValues(t, 0, bc.stateRoot.CurrentValidatedHeight()) require.EqualValues(t, 0, bc.GetStateModule().CurrentValidatedHeight())
}) })
r, err = bc.stateRoot.GetStateRoot(updateIndex + 1) r, err = bc.GetStateModule().GetStateRoot(updateIndex + 1)
require.NoError(t, err) require.NoError(t, err)
data := testSignStateRoot(t, r, pubs, accs...) data := testSignStateRoot(t, r, pubs, accs...)
require.NoError(t, srv.OnPayload(&payload.Extensible{Data: data})) require.NoError(t, srv.OnPayload(&payload.Extensible{Data: data}))
require.EqualValues(t, 2, bc.stateRoot.CurrentValidatedHeight()) require.EqualValues(t, 2, bc.GetStateModule().CurrentValidatedHeight())
r, err = bc.stateRoot.GetStateRoot(updateIndex + 1) r, err = bc.GetStateModule().GetStateRoot(updateIndex + 1)
require.NoError(t, err) require.NoError(t, err)
require.NotEqual(t, 0, len(r.Witness)) require.NotEqual(t, 0, len(r.Witness))
require.Equal(t, h, r.Witness[0].ScriptHash()) require.Equal(t, h, r.Witness[0].ScriptHash())
@ -145,26 +160,31 @@ func TestStateRootInitNonZeroHeight(t *testing.T) {
var root util.Uint256 var root util.Uint256
t.Run("init", func(t *testing.T) { // this is in a separate test to do proper cleanup t.Run("init", func(t *testing.T) { // this is in a separate test to do proper cleanup
bc := newTestChainWithCustomCfgAndStore(t, st, nil) bc, validator, committee := chain.NewMultiWithCustomConfigAndStore(t, nil, st, true)
bc.setNodesByRole(t, true, noderoles.StateValidator, pubs) e := neotest.NewExecutor(t, bc, validator, committee)
transferTokenFromMultisigAccount(t, bc, h, bc.contracts.GAS.Hash, 1_0000_0000) designationSuperInvoker := e.NewInvoker(e.NativeHash(t, nativenames.Designation), validator, committee)
gasValidatorInvoker := e.ValidatorInvoker(e.NativeHash(t, nativenames.Gas))
validatorNodes := []interface{}{pubs[0].Bytes(), pubs[1].Bytes()}
designationSuperInvoker.Invoke(t, stackitem.Null{}, "designateAsRole",
int64(roles.StateValidator), validatorNodes)
gasValidatorInvoker.Invoke(t, true, "transfer", validator.ScriptHash(), h, 1_0000_0000, nil)
_, err := persistBlock(bc)
require.NoError(t, err)
tmpDir := t.TempDir() tmpDir := t.TempDir()
w := createAndWriteWallet(t, accs[0], filepath.Join(tmpDir, "w"), "pass") w := createAndWriteWallet(t, accs[0], filepath.Join(tmpDir, "w"), "pass")
cfg := createStateRootConfig(w.Path(), "pass") cfg := createStateRootConfig(w.Path(), "pass")
srv, err := stateroot.New(cfg, bc.stateRoot, zaptest.NewLogger(t), bc, nil) srMod := bc.GetStateModule().(*corestate.Module) // Take full responsibility here.
srv, err := stateroot.New(cfg, srMod, zaptest.NewLogger(t), bc, nil)
require.NoError(t, err) require.NoError(t, err)
r, err := bc.stateRoot.GetStateRoot(2) r, err := bc.GetStateModule().GetStateRoot(2)
require.NoError(t, err) require.NoError(t, err)
data := testSignStateRoot(t, r, pubs, accs...) data := testSignStateRoot(t, r, pubs, accs...)
require.NoError(t, srv.OnPayload(&payload.Extensible{Data: data})) require.NoError(t, srv.OnPayload(&payload.Extensible{Data: data}))
require.EqualValues(t, 2, bc.stateRoot.CurrentValidatedHeight()) require.EqualValues(t, 2, bc.GetStateModule().CurrentValidatedHeight())
root = bc.stateRoot.CurrentLocalStateRoot() root = bc.GetStateModule().CurrentLocalStateRoot()
}) })
bc2 := newTestChainWithCustomCfgAndStore(t, st, nil) bc2, _, _ := chain.NewMultiWithCustomConfigAndStore(t, nil, st, true)
srv := bc2.GetStateModule() srv := bc2.GetStateModule()
require.EqualValues(t, 2, srv.CurrentValidatedHeight()) require.EqualValues(t, 2, srv.CurrentValidatedHeight())
require.Equal(t, root, srv.CurrentLocalStateRoot()) require.Equal(t, root, srv.CurrentLocalStateRoot())
@ -192,7 +212,22 @@ func createStateRootConfig(walletPath, password string) config.StateRoot {
func TestStateRootFull(t *testing.T) { func TestStateRootFull(t *testing.T) {
tmpDir := t.TempDir() tmpDir := t.TempDir()
bc := newTestChain(t) bc, validator, committee := chain.NewMulti(t)
e := neotest.NewExecutor(t, bc, validator, committee)
designationSuperInvoker := e.NewInvoker(e.NativeHash(t, nativenames.Designation), validator, committee)
gasValidatorInvoker := e.ValidatorInvoker(e.NativeHash(t, nativenames.Gas))
getDesignatedByRole := func(t *testing.T, h uint32) keys.PublicKeys {
res, err := designationSuperInvoker.TestInvoke(t, "getDesignatedByRole", int64(noderoles.StateValidator), h)
require.NoError(t, err)
nodes := res.Pop().Value().([]stackitem.Item)
pubs := make(keys.PublicKeys, len(nodes))
for i, node := range nodes {
pubs[i], err = keys.NewPublicKeyFromBytes(node.Value().([]byte), elliptic.P256())
require.NoError(t, err)
}
return pubs
}
h, pubs, accs := newMajorityMultisigWithGAS(t, 2) h, pubs, accs := newMajorityMultisigWithGAS(t, 2)
w := createAndWriteWallet(t, accs[1], filepath.Join(tmpDir, "wallet2"), "two") w := createAndWriteWallet(t, accs[1], filepath.Join(tmpDir, "wallet2"), "two")
@ -200,7 +235,8 @@ func TestStateRootFull(t *testing.T) {
var lastValidated atomic.Value var lastValidated atomic.Value
var lastHeight atomic.Uint32 var lastHeight atomic.Uint32
srv, err := stateroot.New(cfg, bc.stateRoot, zaptest.NewLogger(t), bc, func(ep *payload.Extensible) { srMod := bc.GetStateModule().(*corestate.Module) // Take full responsibility here.
srv, err := stateroot.New(cfg, srMod, zaptest.NewLogger(t), bc, func(ep *payload.Extensible) {
lastHeight.Store(ep.ValidBlockStart) lastHeight.Store(ep.ValidBlockStart)
lastValidated.Store(ep) lastValidated.Store(ep)
}) })
@ -208,16 +244,17 @@ func TestStateRootFull(t *testing.T) {
srv.Start() srv.Start()
t.Cleanup(srv.Shutdown) t.Cleanup(srv.Shutdown)
bc.setNodesByRole(t, true, noderoles.StateValidator, pubs) validatorNodes := []interface{}{pubs[0].Bytes(), pubs[1].Bytes()}
transferTokenFromMultisigAccount(t, bc, h, bc.contracts.GAS.Hash, 1_0000_0000) designationSuperInvoker.Invoke(t, stackitem.Null{}, "designateAsRole",
int64(roles.StateValidator), validatorNodes)
gasValidatorInvoker.Invoke(t, true, "transfer", validator.ScriptHash(), h, 1_0000_0000, nil)
require.Eventually(t, func() bool { return lastHeight.Load() == 2 }, time.Second, time.Millisecond) require.Eventually(t, func() bool { return lastHeight.Load() == 2 }, time.Second, time.Millisecond)
checkVoteBroadcasted(t, bc, lastValidated.Load().(*payload.Extensible), 2, 1) checkVoteBroadcasted(t, bc, lastValidated.Load().(*payload.Extensible), 2, 1, getDesignatedByRole)
_, err = persistBlock(bc) e.AddNewBlock(t)
require.NoError(t, err)
require.Eventually(t, func() bool { return lastHeight.Load() == 3 }, time.Second, time.Millisecond) require.Eventually(t, func() bool { return lastHeight.Load() == 3 }, time.Second, time.Millisecond)
checkVoteBroadcasted(t, bc, lastValidated.Load().(*payload.Extensible), 3, 1) checkVoteBroadcasted(t, bc, lastValidated.Load().(*payload.Extensible), 3, 1, getDesignatedByRole)
r, err := bc.stateRoot.GetStateRoot(2) r, err := bc.GetStateModule().GetStateRoot(2)
require.NoError(t, err) require.NoError(t, err)
require.NoError(t, srv.AddSignature(2, 0, accs[0].PrivateKey().SignHashable(uint32(netmode.UnitTestNet), r))) require.NoError(t, srv.AddSignature(2, 0, accs[0].PrivateKey().SignHashable(uint32(netmode.UnitTestNet), r)))
require.NotNil(t, lastValidated.Load().(*payload.Extensible)) require.NotNil(t, lastValidated.Load().(*payload.Extensible))
@ -226,7 +263,7 @@ func TestStateRootFull(t *testing.T) {
require.NoError(t, testserdes.DecodeBinary(lastValidated.Load().(*payload.Extensible).Data, msg)) require.NoError(t, testserdes.DecodeBinary(lastValidated.Load().(*payload.Extensible).Data, msg))
require.NotEqual(t, stateroot.RootT, msg.Type) // not a sender for this root require.NotEqual(t, stateroot.RootT, msg.Type) // not a sender for this root
r, err = bc.stateRoot.GetStateRoot(3) r, err = bc.GetStateModule().GetStateRoot(3)
require.NoError(t, err) require.NoError(t, err)
require.Error(t, srv.AddSignature(2, 0, accs[0].PrivateKey().SignHashable(uint32(netmode.UnitTestNet), r))) require.Error(t, srv.AddSignature(2, 0, accs[0].PrivateKey().SignHashable(uint32(netmode.UnitTestNet), r)))
require.NoError(t, srv.AddSignature(3, 0, accs[0].PrivateKey().SignHashable(uint32(netmode.UnitTestNet), r))) require.NoError(t, srv.AddSignature(3, 0, accs[0].PrivateKey().SignHashable(uint32(netmode.UnitTestNet), r)))
@ -241,8 +278,8 @@ func TestStateRootFull(t *testing.T) {
require.Equal(t, r.Root, actual.Root) require.Equal(t, r.Root, actual.Root)
} }
func checkVoteBroadcasted(t *testing.T, bc *Blockchain, p *payload.Extensible, func checkVoteBroadcasted(t *testing.T, bc *core.Blockchain, p *payload.Extensible,
height uint32, valIndex byte) { height uint32, valIndex byte, getDesignatedByRole func(t *testing.T, h uint32) keys.PublicKeys) {
require.NotNil(t, p) require.NotNil(t, p)
m := new(stateroot.Message) m := new(stateroot.Message)
require.NoError(t, testserdes.DecodeBinary(p.Data, m)) require.NoError(t, testserdes.DecodeBinary(p.Data, m))
@ -255,8 +292,7 @@ func checkVoteBroadcasted(t *testing.T, bc *Blockchain, p *payload.Extensible,
require.Equal(t, height, vote.Height) require.Equal(t, height, vote.Height)
require.Equal(t, int32(valIndex), vote.ValidatorIndex) require.Equal(t, int32(valIndex), vote.ValidatorIndex)
pubs, _, err := bc.contracts.Designate.GetDesignatedByRole(bc.dao, noderoles.StateValidator, bc.BlockHeight()) pubs := getDesignatedByRole(t, bc.BlockHeight())
require.NoError(t, err)
require.True(t, len(pubs) > int(valIndex)) require.True(t, len(pubs) > int(valIndex))
require.True(t, pubs[valIndex].VerifyHashable(vote.Signature, uint32(netmode.UnitTestNet), r)) require.True(t, pubs[valIndex].VerifyHashable(vote.Signature, uint32(netmode.UnitTestNet), r))
} }

View file

@ -174,12 +174,22 @@ func (e *Executor) DeployContractCheckFAULT(t testing.TB, c *Contract, data inte
// InvokeScript adds transaction with the specified script to the chain and // InvokeScript adds transaction with the specified script to the chain and
// returns its hash. It does no faults check. // returns its hash. It does no faults check.
func (e *Executor) InvokeScript(t testing.TB, script []byte, signers []Signer) util.Uint256 { func (e *Executor) InvokeScript(t testing.TB, script []byte, signers []Signer) util.Uint256 {
tx := e.PrepareInvocation(t, script, signers)
e.AddNewBlock(t, tx)
return tx.Hash()
}
// PrepareInvocation creates transaction with the specified script and signs it
// by the provided signer.
func (e *Executor) PrepareInvocation(t testing.TB, script []byte, signers []Signer, validUntilBlock ...uint32) *transaction.Transaction {
tx := transaction.New(script, 0) tx := transaction.New(script, 0)
tx.Nonce = Nonce() tx.Nonce = Nonce()
tx.ValidUntilBlock = e.Chain.BlockHeight() + 1 tx.ValidUntilBlock = e.Chain.BlockHeight() + 1
if len(validUntilBlock) != 0 {
tx.ValidUntilBlock = validUntilBlock[0]
}
e.SignTx(t, tx, -1, signers...) e.SignTx(t, tx, -1, signers...)
e.AddNewBlock(t, tx) return tx
return tx.Hash()
} }
// InvokeScriptCheckHALT adds transaction with the specified script to the chain // InvokeScriptCheckHALT adds transaction with the specified script to the chain
@ -323,10 +333,12 @@ func (e *Executor) AddNewBlock(t testing.TB, txs ...*transaction.Transaction) *b
} }
// GenerateNewBlocks adds specified number of empty blocks to the chain. // GenerateNewBlocks adds specified number of empty blocks to the chain.
func (e *Executor) GenerateNewBlocks(t testing.TB, count int) { func (e *Executor) GenerateNewBlocks(t testing.TB, count int) []*block.Block {
blocks := make([]*block.Block, count)
for i := 0; i < count; i++ { for i := 0; i < count; i++ {
e.AddNewBlock(t) blocks[i] = e.AddNewBlock(t)
} }
return blocks
} }
// SignBlock add validators signature to b. // SignBlock add validators signature to b.

View file

@ -128,8 +128,7 @@ func NewSingle(t testing.TB) (*core.Blockchain, neotest.Signer) {
// NewSingleWithCustomConfig is similar to NewSingle, but allows to override the // NewSingleWithCustomConfig is similar to NewSingle, but allows to override the
// default configuration. // default configuration.
func NewSingleWithCustomConfig(t testing.TB, f func(*config.ProtocolConfiguration)) (*core.Blockchain, neotest.Signer) { func NewSingleWithCustomConfig(t testing.TB, f func(*config.ProtocolConfiguration)) (*core.Blockchain, neotest.Signer) {
st := storage.NewMemoryStore() return NewSingleWithCustomConfigAndStore(t, f, nil, true)
return NewSingleWithCustomConfigAndStore(t, f, st, true)
} }
// NewSingleWithCustomConfigAndStore is similar to NewSingleWithCustomConfig, but // NewSingleWithCustomConfigAndStore is similar to NewSingleWithCustomConfig, but
@ -150,6 +149,9 @@ func NewSingleWithCustomConfigAndStore(t testing.TB, f func(cfg *config.Protocol
if f != nil { if f != nil {
f(&protoCfg) f(&protoCfg)
} }
if st == nil {
st = storage.NewMemoryStore()
}
log := zaptest.NewLogger(t) log := zaptest.NewLogger(t)
bc, err := core.NewBlockchain(st, protoCfg, log) bc, err := core.NewBlockchain(st, protoCfg, log)
require.NoError(t, err) require.NoError(t, err)