forked from TrueCloudLab/neoneo-go
core: rebase core tests onto neotest
This commit is contained in:
parent
35ef58a47e
commit
8965441288
16 changed files with 2401 additions and 2673 deletions
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
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
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
}
|
245
pkg/core/interop_system_neotest_test.go
Normal file
245
pkg/core/interop_system_neotest_test.go
Normal 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))
|
||||
})
|
||||
}
|
|
@ -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)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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 benchmarkGasPerVote(t *testing.B, ps storage.Store, nRewardRecords int, rewardDistance int) {
|
||||
bc := newTestChainWithCustomCfgAndStore(t, ps, nil)
|
||||
|
||||
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++
|
||||
}
|
||||
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, validators, committee := chain.NewMultiWithCustomConfigAndStore(t, nil, ps, true)
|
||||
cfg := bc.GetConfig()
|
||||
|
||||
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()
|
||||
|
|
|
@ -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())
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue