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/filepath"
"testing"
"time"
"github.com/nspcc-dev/neo-go/pkg/config"
"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
// Basic chain.
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
// 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 (
"fmt"
"testing"
"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/core/native/nativenames"
"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/transaction"
"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/vm/emit"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
"github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/stretchr/testify/require"
)
func BenchmarkVerifyWitness(t *testing.B) {
bc := newTestChain(t)
acc, err := wallet.NewAccount()
require.NoError(t, err)
tx := bc.newTestTx(acc.Contract.ScriptHash(), []byte{byte(opcode.PUSH1)})
require.NoError(t, acc.SignTx(netmode.UnitTestNet, tx))
func BenchmarkBlockchain_VerifyWitness(t *testing.B) {
bc, acc := chain.NewSingle(t)
e := neotest.NewExecutor(t, bc, acc, acc)
tx := e.NewTx(t, []neotest.Signer{acc}, e.NativeHash(t, nativenames.Gas), "transfer", acc.ScriptHash(), acc.Script(), 1, nil)
t.ResetTimer()
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) {
var (
nonce uint32 = 1
chainHeight = 2_100 // constant chain height to be able to compare paging results
transfersPerBlock = state.TokenTransferBatchSize/4 + // 4 blocks per batch
state.TokenTransferBatchSize/32 // shift
)
bc := newTestChainWithCustomCfgAndStore(t, ps, nil)
gasHash := bc.contracts.GAS.Hash
bc, validators, committee := chain.NewMultiWithCustomConfigAndStore(t, nil, ps, true)
e := neotest.NewExecutor(t, bc, validators, committee)
gasHash := e.NativeHash(t, nativenames.Gas)
acc := random.Uint160()
from := e.Validator.ScriptHash()
for j := 0; j < chainHeight; j++ {
w := io.NewBufBinWriter()
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)
require.NoError(t, w.Err)
}
require.NoError(t, w.Err)
script := w.Bytes()
tx := transaction.New(script, int64(1100_0000*transfersPerBlock))
tx.NetworkFee = 1_0000_000
tx.ValidUntilBlock = bc.BlockHeight() + 1
tx.Nonce = nonce
nonce++
tx.Signers = []transaction.Signer{{
Account: testchain.MultisigScriptHash(),
Scopes: transaction.CalledByEntry,
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())
tx.Nonce = neotest.Nonce()
tx.Signers = []transaction.Signer{{Account: from, Scopes: transaction.CalledByEntry}}
require.NoError(t, validators.SignTx(netmode.UnitTestNet, tx))
e.AddNewBlock(t, tx)
e.CheckHalt(t, tx.Hash())
}
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
import (
"encoding/json"
"fmt"
"math/big"
"path/filepath"
"strings"
"testing"
"time"
"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/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/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/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/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"
"go.uber.org/zap"
"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
// global state.
func newTestChain(t testing.TB) *Blockchain {
@ -54,24 +33,6 @@ func newTestChainWithCustomCfgAndStore(t testing.TB, st storage.Store, f func(*c
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 {
chain, err := initTestChainNoCheck(t, st, f)
require.NoError(t, err)
@ -165,246 +126,3 @@ func (bc *Blockchain) genBlocks(n int) ([]*block.Block, error) {
}
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 (
"errors"
"fmt"
"math"
"math/big"
"path/filepath"
"testing"
"github.com/nspcc-dev/neo-go/internal/contracts"
"github.com/nspcc-dev/neo-go/internal/random"
"github.com/nspcc-dev/neo-go/internal/testchain"
"github.com/nspcc-dev/neo-go/pkg/config"
"github.com/nspcc-dev/neo-go/pkg/config/netmode"
"github.com/nspcc-dev/neo-go/pkg/core/block"
"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/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/runtime"
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/keys"
"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/manifest"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
@ -40,6 +37,8 @@ import (
"github.com/stretchr/testify/require"
)
var pathToInternalContracts = filepath.Join("..", "..", "internal", "contracts")
// Tests are taken from
// https://github.com/neo-project/neo/blob/master/tests/neo.UnitTests/SmartContract/UT_ApplicationEngine.Runtime.cs
func TestRuntimeGetRandomCompatibility(t *testing.T) {
@ -69,29 +68,6 @@ func TestRuntimeGetRandomCompatibility(t *testing.T) {
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 {
tx := transaction.New([]byte{byte(opcode.PUSH2)}, 0)
tx.Nonce = 0
@ -113,81 +89,6 @@ func getSharpTestGenesis(t *testing.T) *block.Block {
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) {
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 (
"errors"
"math/big"
"encoding/json"
"fmt"
"strings"
"testing"
"github.com/nspcc-dev/neo-go/internal/contracts"
"github.com/nspcc-dev/neo-go/internal/random"
"github.com/nspcc-dev/neo-go/pkg/core/dao"
"github.com/nspcc-dev/neo-go/pkg/config"
"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/interop/contract"
"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/storage"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"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/manifest"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
"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/stackitem"
"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) {
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()
chain.registerNative(tn)
baseExecFee := bc.GetBaseExecFee()
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{
ContractBase: state.ContractBase{
ID: 1,
NEF: tn.meta.NEF,
Hash: tn.meta.Hash,
Manifest: tn.meta.Manifest,
},
})
require.NoError(t, err)
tx := e.NewUnsignedTx(t, gasHash, "transfer", validator.ScriptHash(), validator.ScriptHash(), 1, nil)
e.SignTx(t, tx, -1, validator)
e.AddNewBlock(t, tx)
e.CheckHalt(t, tx.Hash(), stackitem.Make(true))
// System.Contract.Call + "sum" itself + opcodes for pushing arguments.
price := int64(testSumCPUFee * chain.GetBaseExecFee() * 2)
price += testSumStorageFee * chain.GetStoragePrice()
price += 3 * fee.Opcode(chain.GetBaseExecFee(), opcode.PUSHINT8)
price += 2 * fee.Opcode(chain.GetBaseExecFee(), opcode.SYSCALL, opcode.PUSHDATA1, opcode.PUSHINT8)
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)
// Enough for Call and other opcodes, but not enough for "transfer" call.
tx = e.NewUnsignedTx(t, gasHash, "transfer", validator.ScriptHash(), validator.ScriptHash(), 1, nil)
e.SignTx(t, tx, price-1, validator)
e.AddNewBlock(t, tx)
e.CheckFault(t, tx.Hash(), "gas limit exceeded")
}
func TestNativeContract_InvokeInternal(t *testing.T) {
chain := newTestChain(t)
tn := newTestNative()
chain.registerNative(tn)
err := chain.contracts.Management.PutContractState(chain.dao, &state.Contract{
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
}
}
bc, validator, committee := chain.NewMulti(t)
e := neotest.NewExecutor(t, bc, validator, committee)
clState := bc.GetContractState(e.NativeHash(t, nativenames.CryptoLib))
require.NotNil(t, clState)
md := clState.Manifest.ABI.GetMethod("ripemd160", 1)
require.NotNil(t, md)
t.Run("fail, bad current script hash", func(t *testing.T) {
ic := bc.GetTestVM(trigger.Application, nil, nil)
v := ic.SpawnVM()
v.LoadScriptWithHash(tn.Metadata().NEF.Script, util.Uint160{1, 2, 3}, callflag.All)
v.Estack().PushVal(14)
v.Estack().PushVal(28)
v.Context().Jump(sumOffset)
fakeH := util.Uint160{1, 2, 3}
v.LoadScriptWithHash(clState.NEF.Script, fakeH, callflag.All)
input := []byte{1, 2, 3, 4}
v.Estack().PushVal(input)
v.Context().Jump(md.Offset)
// it's prohibited to call natives directly
require.Error(t, v.Run())
// Bad current script hash
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) {
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.LoadScriptWithHash(tn.Metadata().NEF.Script, tn.Metadata().Hash, callflag.All)
v.Estack().PushVal(14)
v.Estack().PushVal(28)
v.Context().Jump(sumOffset)
v.LoadScriptWithHash(clState.NEF.Script, clState.Hash, callflag.All) // hash is not affected by native update history
input := []byte{1, 2, 3, 4}
v.Estack().PushVal(input)
v.Context().Jump(md.Offset)
// it's prohibited to call natives before NativeUpdateHistory[0] height
require.Error(t, v.Run())
// It's prohibited to call natives before NativeUpdateHistory[0] height.
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
tn.Metadata().UpdateHistory = []uint32{0}
// Add new block => CryptoLib should be active now.
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) {
ic := bc.GetTestVM(trigger.Application, nil, nil)
v := ic.SpawnVM()
v.LoadScriptWithHash(tn.Metadata().NEF.Script, tn.Metadata().Hash, callflag.All)
v.Estack().PushVal(14)
v.Estack().PushVal(28)
v.Context().Jump(sumOffset)
v.LoadScriptWithHash(clState.NEF.Script, clState.Hash, callflag.All)
input := []byte{1, 2, 3, 4}
v.Estack().PushVal(input)
v.Context().Jump(md.Offset)
require.NoError(t, v.Run())
value := v.Estack().Pop().BigInt()
require.Equal(t, int64(42), value.Int64())
value := v.Estack().Pop().Bytes()
require.Equal(t, hash.RipeMD160(input).BytesBE(), value)
})
}
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()
chain.registerNative(tn)
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,
},
})
cs, _ := contracts.GetTestContractState(t, pathToInternalContracts, 1, 2, validator.ScriptHash())
cs.Hash = state.CreateContractHash(validator.ScriptHash(), cs.NEF.Checksum, cs.Manifest.Name) // set proper hash
manifB, err := json.Marshal(cs.Manifest)
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) {
res, err := invokeContractMethod(chain, testSumCPUFee*baseFee*4+10000, tn.Metadata().Hash, "callOtherContractNoReturn", cs.Hash, "justReturn", []interface{}{})
require.NoError(t, err)
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))
// `onNEP17Payment` will be invoked on test contract from GAS contract.
gasInvoker.Invoke(t, true, "transfer", validator.ScriptHash(), cs.Hash, 1, nil)
})
}

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/transaction"
"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/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/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) {
bc := newTestChain(t)

View file

@ -1,20 +1,17 @@
package core
package core_test
import (
"fmt"
"math/big"
"path/filepath"
"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/transaction"
"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/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/neotest"
"github.com/nspcc-dev/neo-go/pkg/neotest/chain"
"github.com/nspcc-dev/neo-go/pkg/wallet"
"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) {
bc := newTestChainWithCustomCfgAndStore(t, ps, nil)
bc, validators, committee := chain.NewMultiWithCustomConfigAndStore(t, nil, ps, true)
cfg := bc.GetConfig()
neo := bc.contracts.NEO
tx := transaction.New([]byte{byte(opcode.PUSH1)}, 0)
ic := bc.newInteropContext(trigger.Application, bc.dao, nil, tx)
ic.SpawnVM()
ic.Block = bc.newBlock(tx)
advanceChain := func(t *testing.B, count int) {
for i := 0; i < count; i++ {
require.NoError(t, bc.AddBlock(bc.newBlock()))
ic.Block.Index++
}
}
e := neotest.NewExecutor(t, bc, validators, committee)
neoHash := e.NativeHash(t, nativenames.Neo)
gasHash := e.NativeHash(t, nativenames.Gas)
neoSuperInvoker := e.NewInvoker(neoHash, validators, committee)
neoValidatorsInvoker := e.ValidatorInvoker(neoHash)
gasValidatorsInvoker := e.ValidatorInvoker(gasHash)
// Vote for new committee.
sz := testchain.CommitteeSize()
accs := make([]*wallet.Account, sz)
sz := len(cfg.StandbyCommittee)
voters := make([]*wallet.Account, 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++ {
priv, err := keys.NewPrivateKey()
require.NoError(t, err)
candidates[i] = priv.PublicKey()
accs[i], err = wallet.NewAccount()
voters[i], err = wallet.NewAccount()
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()
w := io.NewBufBinWriter()
emit.AppCall(w.BinWriter, bc.contracts.NEO.Hash, "transfer", callflag.All,
neoOwner.BytesBE(), to.BytesBE(),
big.NewInt(int64(sz-i)*1000000).Int64(), nil)
emit.Opcodes(w.BinWriter, opcode.ASSERT)
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)
to := voters[i].Contract.ScriptHash()
transferNeoTx := neoValidatorsInvoker.PrepareInvoke(t, "transfer", e.Validator.ScriptHash(), to, big.NewInt(int64(sz-i)*1000000).Int64(), nil)
txs = append(txs, transferNeoTx)
transferGasTx := gasValidatorsInvoker.PrepareInvoke(t, "transfer", e.Validator.ScriptHash(), to, int64(1_000_000_000), nil)
txs = append(txs, transferGasTx)
}
require.NoError(t, bc.AddBlock(bc.newBlock(txs...)))
e.AddNewBlock(t, 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++ {
priv := accs[i].PrivateKey()
priv := voters[i].PrivateKey()
h := priv.GetScriptHash()
setSigner(tx, h)
ic.VM.Load(priv.PublicKey().GetVerificationScript())
require.NoError(t, neo.VoteInternal(ic, h, candidates[i]))
voteTx := e.NewTx(t, []neotest.Signer{neotest.NewSingleSigner(voters[i])}, neoHash, "vote", h, candidates[i].Bytes())
voteTxs = append(voteTxs, voteTx)
}
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.
advanceChain(t, nRewardRecords*testchain.CommitteeSize())
e.GenerateNewBlocks(t, len(cfg.StandbyCommittee))
// Transfer some more NEO to first voter to update his balance height.
to := accs[0].Contract.ScriptHash()
w := io.NewBufBinWriter()
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)
to := voters[0].Contract.ScriptHash()
neoValidatorsInvoker.Invoke(t, true, "transfer", e.Validator.ScriptHash(), to, int64(1), nil)
// Advance chain one more time to avoid same start/end rewarding bounds.
advanceChain(t, rewardDistance)
e.GenerateNewBlocks(t, rewardDistance)
end := bc.BlockHeight()
t.ResetTimer()
t.ReportAllocs()
t.StartTimer()
for i := 0; i < t.N; i++ {
_, err := neo.CalculateBonus(ic.DAO, to, end)
_, err := bc.CalculateClaimable(to, end)
require.NoError(t, err)
}
t.StopTimer()

View file

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

View file

@ -1,7 +1,9 @@
package core
package core_test
import (
"bytes"
"encoding/binary"
"encoding/json"
"errors"
"fmt"
gio "io"
@ -16,37 +18,38 @@ import (
"github.com/nspcc-dev/neo-go/internal/contracts"
"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/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/transaction"
"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/smartcontract/manifest"
"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/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap/zaptest"
)
var (
oracleModulePath = filepath.Join("..", "services", "oracle")
pathToInternalContracts = filepath.Join("..", "..", "internal", "contracts")
)
var oracleModulePath = filepath.Join("..", "services", "oracle")
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 {
var filtItem interface{}
if filter != nil {
filtItem = *filter
}
res, err := invokeContractMethod(bc, gas+50_000_000+5_000_000, h, "requestURL",
url, filtItem, cb, userData, gas)
require.NoError(t, err)
return res.Container
return oracleValidatorInvoker.Invoke(t, stackitem.Null{}, "requestURL", url, filtItem, cb, userData, gas)
}
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{
Log: zaptest.NewLogger(t),
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,
*oracle.Oracle,
map[uint64]*responseWithSig,
@ -87,7 +90,19 @@ func getTestOracle(t *testing.T, bc *Blockchain, walletPath, pass string) (
// Compatibility test from C# code.
// https://github.com/neo-project/neo-modules/blob/master/tests/Neo.Plugins.OracleService.Tests/UT_OracleService.cs#L61
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(1000), bc.FeePerByte())
@ -106,10 +121,10 @@ func TestCreateResponseTx(t *testing.T) {
Code: transaction.Success,
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()})
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)
assert.Equal(t, 166, tx.Size())
assert.Equal(t, int64(2198650), tx.NetworkFee)
@ -117,7 +132,7 @@ func TestCreateResponseTx(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))
require.Error(t, err)
@ -127,45 +142,65 @@ func TestOracle_InvalidWallet(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")
acc2, orc2, m2, ch2 := getTestOracle(t, bc, "./testdata/oracle2.json", "two")
oracleNodes := keys.PublicKeys{acc1.PrivateKey().PublicKey(), acc2.PrivateKey().PublicKey()}
// 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())
orc2.UpdateOracleNodes(oracleNodes.Copy())
orcNative := bc.contracts.Oracle
md, ok := orcNative.GetMethod(manifest.MethodVerify, -1)
require.True(t, ok)
orc1.UpdateNativeContract(orcNative.NEF.Script, orcNative.GetOracleResponseScript(), orcNative.Hash, md.MD.Offset)
orc2.UpdateNativeContract(orcNative.NEF.Script, orcNative.GetOracleResponseScript(), orcNative.Hash, md.MD.Offset)
nativeOracleState := bc.GetContractState(nativeOracleH)
require.NotNil(t, nativeOracleState)
md := nativeOracleState.Manifest.ABI.GetMethod(manifest.MethodVerify, -1)
require.NotNil(t, md)
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)
require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs))
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)
putOracleRequest(t, cs.Hash, bc, "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, cs.Hash, bc, "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, cs.Hash, bc, "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, cs.Hash, bc, "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, cs.Hash, bc, "https://get.maxallowed", nil, "handle", []byte{}, 100_000_000)
putOracleRequest(t, cInvoker, "https://get.1234", nil, "handle", []byte{}, 10_000_000)
putOracleRequest(t, cInvoker, "https://get.1234", nil, "handle", []byte{}, 10_000_000)
putOracleRequest(t, cInvoker, "https://get.timeout", nil, "handle", []byte{}, 10_000_000)
putOracleRequest(t, cInvoker, "https://get.notfound", nil, "handle", []byte{}, 10_000_000)
putOracleRequest(t, cInvoker, "https://get.forbidden", nil, "handle", []byte{}, 10_000_000)
putOracleRequest(t, cInvoker, "https://private.url", nil, "handle", []byte{}, 10_000_000)
putOracleRequest(t, cInvoker, "https://get.big", nil, "handle", []byte{}, 10_000_000)
putOracleRequest(t, cInvoker, "https://get.maxallowed", nil, "handle", []byte{}, 10_000_000)
putOracleRequest(t, cInvoker, "https://get.maxallowed", nil, "handle", []byte{}, 100_000_000)
flt := "$.Values[1]"
putOracleRequest(t, cs.Hash, bc, "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.filter", &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 {
req, err := oracleCtr.GetRequestInternal(bc.dao, id)
require.NoError(t, err)
// Use a hack to get request from Oracle contract, because we can't use GetRequestInternal directly.
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}
orc1.ProcessRequestsInternal(reqs)
@ -198,7 +233,7 @@ func TestOracle(t *testing.T) {
actualHash := cp.Hash()
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) {
@ -306,21 +341,34 @@ func TestOracle(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")
mp := bc.GetMemPool()
orc.OnTransaction = func(tx *transaction.Transaction) error { return mp.Add(tx, bc) }
bc.SetOracle(orc)
cs := contracts.GetOracleContractState(t, pathToInternalContracts, util.Uint160{}, 42)
require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs))
go bc.Run()
orc.Start()
t.Cleanup(orc.Shutdown)
t.Cleanup(func() {
orc.Shutdown()
bc.Close()
})
bc.setNodesByRole(t, true, noderoles.Oracle, keys.PublicKeys{acc.PrivateKey().PublicKey()})
putOracleRequest(t, cs.Hash, bc, "https://get.1234", new(string), "handle", []byte{}, 10_000_000)
designationSuperInvoker.Invoke(t, stackitem.Null{}, "designateAsRole",
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 },
time.Second*3, time.Millisecond*200)
@ -331,17 +379,20 @@ func TestOracleFull(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")
mp := bc.GetMemPool()
orc.OnTransaction = func(tx *transaction.Transaction) error { return mp.Add(tx, bc) }
bc.SetOracle(orc)
cs := contracts.GetOracleContractState(t, pathToInternalContracts, util.Uint160{}, 42)
require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs))
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 reqs = make(map[uint64]*state.OracleRequest)

View file

@ -1,6 +1,7 @@
package core
package core_test
import (
"crypto/elliptic"
"errors"
"path/filepath"
"sort"
@ -10,18 +11,25 @@ import (
"github.com/nspcc-dev/neo-go/internal/testserdes"
"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/native/nativenames"
"github.com/nspcc-dev/neo-go/pkg/core/native/noderoles"
"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/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/interop/native/roles"
"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/services/stateroot"
"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/nspcc-dev/neo-go/pkg/wallet"
"github.com/stretchr/testify/require"
"go.uber.org/atomic"
@ -70,64 +78,71 @@ func newMajorityMultisigWithGAS(t *testing.T, n int) (util.Uint160, keys.PublicK
}
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)
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()
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()
w := createAndWriteWallet(t, accs[0], filepath.Join(tmpDir, "w"), "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.EqualValues(t, 0, bc.stateRoot.CurrentValidatedHeight())
r, err := bc.stateRoot.GetStateRoot(bc.BlockHeight())
require.EqualValues(t, 0, bc.GetStateModule().CurrentValidatedHeight())
r, err := bc.GetStateModule().GetStateRoot(bc.BlockHeight())
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) {
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) {
r, err := bc.stateRoot.GetStateRoot(0)
r, err := bc.GetStateModule().GetStateRoot(0)
require.NoError(t, err)
data, err := testserdes.EncodeBinary(stateroot.NewMessage(stateroot.RootT, r))
require.NoError(t, err)
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) {
r, err := bc.stateRoot.GetStateRoot(1)
r, err := bc.GetStateModule().GetStateRoot(1)
require.NoError(t, err)
r.Index = 10
data := testSignStateRoot(t, r, pubs, accs...)
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) {
accInv, err := wallet.NewAccount()
require.NoError(t, err)
pubs := keys.PublicKeys{accInv.PrivateKey().PublicKey()}
require.NoError(t, accInv.ConvertMultisig(1, pubs))
transferTokenFromMultisigAccount(t, bc, accInv.Contract.ScriptHash(), bc.contracts.GAS.Hash, 1_0000_0000)
r, err := bc.stateRoot.GetStateRoot(1)
gasValidatorInvoker.Invoke(t, true, "transfer", validator.ScriptHash(), accInv.Contract.ScriptHash(), 1_0000_0000, nil)
r, err := bc.GetStateModule().GetStateRoot(1)
require.NoError(t, err)
data := testSignStateRoot(t, r, pubs, accInv)
err = srv.OnPayload(&payload.Extensible{Data: data})
require.True(t, errors.Is(err, ErrWitnessHashMismatch), "got: %v", err)
require.EqualValues(t, 0, bc.stateRoot.CurrentValidatedHeight())
require.True(t, errors.Is(err, core.ErrWitnessHashMismatch), "got: %v", err)
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)
data := testSignStateRoot(t, r, pubs, accs...)
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.NotEqual(t, 0, len(r.Witness))
require.Equal(t, h, r.Witness[0].ScriptHash())
@ -145,26 +160,31 @@ func TestStateRootInitNonZeroHeight(t *testing.T) {
var root util.Uint256
t.Run("init", func(t *testing.T) { // this is in a separate test to do proper cleanup
bc := newTestChainWithCustomCfgAndStore(t, st, nil)
bc.setNodesByRole(t, true, noderoles.StateValidator, pubs)
transferTokenFromMultisigAccount(t, bc, h, bc.contracts.GAS.Hash, 1_0000_0000)
bc, validator, committee := chain.NewMultiWithCustomConfigAndStore(t, nil, st, true)
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))
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()
w := createAndWriteWallet(t, accs[0], filepath.Join(tmpDir, "w"), "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)
r, err := bc.stateRoot.GetStateRoot(2)
r, err := bc.GetStateModule().GetStateRoot(2)
require.NoError(t, err)
data := testSignStateRoot(t, r, pubs, accs...)
require.NoError(t, srv.OnPayload(&payload.Extensible{Data: data}))
require.EqualValues(t, 2, bc.stateRoot.CurrentValidatedHeight())
root = bc.stateRoot.CurrentLocalStateRoot()
require.EqualValues(t, 2, bc.GetStateModule().CurrentValidatedHeight())
root = bc.GetStateModule().CurrentLocalStateRoot()
})
bc2 := newTestChainWithCustomCfgAndStore(t, st, nil)
bc2, _, _ := chain.NewMultiWithCustomConfigAndStore(t, nil, st, true)
srv := bc2.GetStateModule()
require.EqualValues(t, 2, srv.CurrentValidatedHeight())
require.Equal(t, root, srv.CurrentLocalStateRoot())
@ -192,7 +212,22 @@ func createStateRootConfig(walletPath, password string) config.StateRoot {
func TestStateRootFull(t *testing.T) {
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)
w := createAndWriteWallet(t, accs[1], filepath.Join(tmpDir, "wallet2"), "two")
@ -200,7 +235,8 @@ func TestStateRootFull(t *testing.T) {
var lastValidated atomic.Value
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)
lastValidated.Store(ep)
})
@ -208,16 +244,17 @@ func TestStateRootFull(t *testing.T) {
srv.Start()
t.Cleanup(srv.Shutdown)
bc.setNodesByRole(t, true, noderoles.StateValidator, pubs)
transferTokenFromMultisigAccount(t, bc, h, bc.contracts.GAS.Hash, 1_0000_0000)
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)
require.Eventually(t, func() bool { return lastHeight.Load() == 2 }, time.Second, time.Millisecond)
checkVoteBroadcasted(t, bc, lastValidated.Load().(*payload.Extensible), 2, 1)
_, err = persistBlock(bc)
require.NoError(t, err)
checkVoteBroadcasted(t, bc, lastValidated.Load().(*payload.Extensible), 2, 1, getDesignatedByRole)
e.AddNewBlock(t)
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, srv.AddSignature(2, 0, accs[0].PrivateKey().SignHashable(uint32(netmode.UnitTestNet), r)))
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.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.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)))
@ -241,8 +278,8 @@ func TestStateRootFull(t *testing.T) {
require.Equal(t, r.Root, actual.Root)
}
func checkVoteBroadcasted(t *testing.T, bc *Blockchain, p *payload.Extensible,
height uint32, valIndex byte) {
func checkVoteBroadcasted(t *testing.T, bc *core.Blockchain, p *payload.Extensible,
height uint32, valIndex byte, getDesignatedByRole func(t *testing.T, h uint32) keys.PublicKeys) {
require.NotNil(t, p)
m := new(stateroot.Message)
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, int32(valIndex), vote.ValidatorIndex)
pubs, _, err := bc.contracts.Designate.GetDesignatedByRole(bc.dao, noderoles.StateValidator, bc.BlockHeight())
require.NoError(t, err)
pubs := getDesignatedByRole(t, bc.BlockHeight())
require.True(t, len(pubs) > int(valIndex))
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
// returns its hash. It does no faults check.
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.Nonce = Nonce()
tx.ValidUntilBlock = e.Chain.BlockHeight() + 1
if len(validUntilBlock) != 0 {
tx.ValidUntilBlock = validUntilBlock[0]
}
e.SignTx(t, tx, -1, signers...)
e.AddNewBlock(t, tx)
return tx.Hash()
return tx
}
// 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.
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++ {
e.AddNewBlock(t)
blocks[i] = e.AddNewBlock(t)
}
return blocks
}
// 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
// default configuration.
func NewSingleWithCustomConfig(t testing.TB, f func(*config.ProtocolConfiguration)) (*core.Blockchain, neotest.Signer) {
st := storage.NewMemoryStore()
return NewSingleWithCustomConfigAndStore(t, f, st, true)
return NewSingleWithCustomConfigAndStore(t, f, nil, true)
}
// NewSingleWithCustomConfigAndStore is similar to NewSingleWithCustomConfig, but
@ -150,6 +149,9 @@ func NewSingleWithCustomConfigAndStore(t testing.TB, f func(cfg *config.Protocol
if f != nil {
f(&protoCfg)
}
if st == nil {
st = storage.NewMemoryStore()
}
log := zaptest.NewLogger(t)
bc, err := core.NewBlockchain(st, protoCfg, log)
require.NoError(t, err)