core: return *interop.Context from GetTestVM

Return everything needed, fix #2273.
This commit is contained in:
Roman Khimov 2022-01-13 01:20:08 +03:00
parent ab2e60458d
commit 9f9bd7261c
9 changed files with 57 additions and 56 deletions

View file

@ -327,7 +327,7 @@ func (chain *FakeChain) GetStorageItem(id int32, key []byte) state.StorageItem {
}
// GetTestVM implements Blockchainer interface.
func (chain *FakeChain) GetTestVM(t trigger.Type, tx *transaction.Transaction, b *block.Block) (*vm.VM, func()) {
func (chain *FakeChain) GetTestVM(t trigger.Type, tx *transaction.Transaction, b *block.Block) *interop.Context {
panic("TODO")
}

View file

@ -2138,14 +2138,14 @@ func (bc *Blockchain) GetEnrollments() ([]state.Validator, error) {
return bc.contracts.NEO.GetCandidates(bc.dao)
}
// GetTestVM returns a VM setup for a test run of some sort of code and finalizer function.
func (bc *Blockchain) GetTestVM(t trigger.Type, tx *transaction.Transaction, b *block.Block) (*vm.VM, func()) {
// GetTestVM returns an interop context with VM set up for a test run.
func (bc *Blockchain) GetTestVM(t trigger.Type, tx *transaction.Transaction, b *block.Block) *interop.Context {
d := bc.dao.GetWrapped().(*dao.Simple)
systemInterop := bc.newInteropContext(t, d, b, tx)
vm := systemInterop.SpawnVM()
vm.SetPriceGetter(systemInterop.GetPrice)
vm.LoadToken = contract.LoadToken(systemInterop)
return vm, systemInterop.Finalize
return systemInterop
}
// Various witness verification errors.

View file

@ -6,6 +6,7 @@ import (
"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/services"
"github.com/nspcc-dev/neo-go/pkg/core/interop"
"github.com/nspcc-dev/neo-go/pkg/core/mempool"
"github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
@ -59,7 +60,7 @@ type Blockchainer interface {
GetStateModule() StateRoot
GetStorageItem(id int32, key []byte) state.StorageItem
GetStorageItems(id int32) ([]state.StorageItemWithKey, error)
GetTestVM(t trigger.Type, tx *transaction.Transaction, b *block.Block) (*vm.VM, func())
GetTestVM(t trigger.Type, tx *transaction.Transaction, b *block.Block) *interop.Context
GetTransaction(util.Uint256) (*transaction.Transaction, uint32, error)
SetOracle(service services.Oracle)
mempool.Feer // fee interface

View file

@ -613,12 +613,12 @@ func setTxSystemFee(bc *Blockchain, sysFee int64, tx *transaction.Transaction) {
}
ttx := *tx // prevent setting 'hash' field
v, f := bc.GetTestVM(trigger.Application, &ttx, b)
defer f()
ic := bc.GetTestVM(trigger.Application, &ttx, b)
defer ic.Finalize()
v.LoadWithFlags(tx.Script, callflag.All)
_ = v.Run()
tx.SystemFee = v.GasConsumed()
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) {

View file

@ -309,10 +309,10 @@ func TestInvoke(bc blockchainer.Blockchainer, tx *transaction.Transaction) (*vm.
// `GetTestVM` as well as `Run` can use transaction hash which will set cached value.
// This is unwanted behaviour so we explicitly copy transaction to perform execution.
ttx := *tx
v, f := bc.GetTestVM(trigger.Application, &ttx, b)
defer f()
ic := bc.GetTestVM(trigger.Application, &ttx, b)
defer ic.Finalize()
v.LoadWithFlags(tx.Script, callflag.All)
err = v.Run()
return v, err
ic.VM.LoadWithFlags(tx.Script, callflag.All)
err = ic.VM.Run()
return ic.VM, err
}

View file

@ -31,12 +31,12 @@ func (e *Executor) CommitteeInvoker(h util.Uint160) *ContractInvoker {
func (c *ContractInvoker) TestInvoke(t *testing.T, method string, args ...interface{}) (*vm.Stack, error) {
tx := c.PrepareInvokeNoSign(t, method, args...)
b := c.NewUnsignedBlock(t, tx)
v, f := c.Chain.GetTestVM(trigger.Application, tx, b)
t.Cleanup(f)
ic := c.Chain.GetTestVM(trigger.Application, tx, b)
t.Cleanup(ic.Finalize)
v.LoadWithFlags(tx.Script, callflag.All)
err := v.Run()
return v.Estack(), err
ic.VM.LoadWithFlags(tx.Script, callflag.All)
err := ic.VM.Run()
return ic.VM.Estack(), err
}
// WithSigners creates new client with the provided signer.

View file

@ -715,9 +715,9 @@ func TestCreateNEP17TransferTx(t *testing.T) {
require.NoError(t, err)
require.NoError(t, acc.SignTx(testchain.Network(), tx))
require.NoError(t, chain.VerifyTx(tx))
v, _ := chain.GetTestVM(trigger.Application, tx, nil)
v.LoadScriptWithFlags(tx.Script, callflag.All)
require.NoError(t, v.Run())
ic := chain.GetTestVM(trigger.Application, tx, nil)
ic.VM.LoadScriptWithFlags(tx.Script, callflag.All)
require.NoError(t, ic.VM.Run())
})
t.Run("none scope", func(t *testing.T) {
_, err := c.CreateNEP17TransferTx(acc, util.Uint160{}, gasContractHash, 1000, 0, nil, []client.SignerAccount{{
@ -739,9 +739,9 @@ func TestCreateNEP17TransferTx(t *testing.T) {
require.NoError(t, err)
require.NoError(t, acc.SignTx(testchain.Network(), tx))
require.NoError(t, chain.VerifyTx(tx))
v, _ := chain.GetTestVM(trigger.Application, tx, nil)
v.LoadScriptWithFlags(tx.Script, callflag.All)
require.NoError(t, v.Run())
ic := chain.GetTestVM(trigger.Application, tx, nil)
ic.VM.LoadScriptWithFlags(tx.Script, callflag.All)
require.NoError(t, ic.VM.Run())
})
}

View file

@ -853,19 +853,19 @@ func (s *Server) invokeReadOnly(bw *io.BufBinWriter, h util.Uint160, method stri
if err != nil {
return nil, nil, err
}
v, finalize := s.chain.GetTestVM(trigger.Application, tx, b)
v.GasLimit = core.HeaderVerificationGasLimit
v.LoadScriptWithFlags(script, callflag.All)
err = v.Run()
ic := s.chain.GetTestVM(trigger.Application, tx, b)
ic.VM.GasLimit = core.HeaderVerificationGasLimit
ic.VM.LoadScriptWithFlags(script, callflag.All)
err = ic.VM.Run()
if err != nil {
finalize()
ic.Finalize()
return nil, nil, fmt.Errorf("failed to run `%s` for %s: %w", method, h.StringLE(), err)
}
if v.Estack().Len() != 1 {
finalize()
return nil, nil, fmt.Errorf("invalid `%s` return values count: expected 1, got %d", method, v.Estack().Len())
if ic.VM.Estack().Len() != 1 {
ic.Finalize()
return nil, nil, fmt.Errorf("invalid `%s` return values count: expected 1, got %d", method, ic.VM.Estack().Len())
}
return v.Estack().Pop().Item(), finalize, nil
return ic.VM.Estack().Pop().Item(), ic.Finalize, nil
}
func (s *Server) getTokenBalance(h util.Uint160, acc util.Uint160, id []byte, bw *io.BufBinWriter) (*big.Int, error) {
@ -1690,20 +1690,20 @@ func (s *Server) runScriptInVM(t trigger.Type, script []byte, contractScriptHash
if err != nil {
return nil, response.NewInternalServerError("can't create fake block", err)
}
vm, finalize := s.chain.GetTestVM(t, tx, b)
ic := s.chain.GetTestVM(t, tx, b)
if verbose {
vm.EnableInvocationTree()
ic.VM.EnableInvocationTree()
}
vm.GasLimit = int64(s.config.MaxGasInvoke)
ic.VM.GasLimit = int64(s.config.MaxGasInvoke)
if t == trigger.Verification {
// We need this special case because witnesses verification is not the simple System.Contract.Call,
// and we need to define exactly the amount of gas consumed for a contract witness verification.
gasPolicy := s.chain.GetMaxVerificationGAS()
if vm.GasLimit > gasPolicy {
vm.GasLimit = gasPolicy
if ic.VM.GasLimit > gasPolicy {
ic.VM.GasLimit = gasPolicy
}
err := s.chain.InitVerificationVM(vm, func(h util.Uint160) (*state.Contract, error) {
err := s.chain.InitVerificationVM(ic.VM, func(h util.Uint160) (*state.Contract, error) {
res := s.chain.GetContractState(h)
if res == nil {
return nil, fmt.Errorf("unknown contract: %s", h.StringBE())
@ -1714,14 +1714,14 @@ func (s *Server) runScriptInVM(t trigger.Type, script []byte, contractScriptHash
return nil, response.NewInternalServerError("can't prepare verification VM", err)
}
} else {
vm.LoadScriptWithFlags(script, callflag.All)
ic.VM.LoadScriptWithFlags(script, callflag.All)
}
err = vm.Run()
err = ic.VM.Run()
var faultException string
if err != nil {
faultException = err.Error()
}
return result.NewInvoke(vm, finalize, script, faultException, s.config.MaxIteratorResultItems), nil
return result.NewInvoke(ic.VM, ic.Finalize, script, faultException, s.config.MaxIteratorResultItems), nil
}
// submitBlock broadcasts a raw block over the NEO network.

View file

@ -6,13 +6,13 @@ import (
gio "io"
"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/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/smartcontract/callflag"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/vm"
"go.uber.org/zap"
)
@ -138,24 +138,24 @@ func (o *Oracle) testVerify(tx *transaction.Transaction) (int64, bool) {
// method caches transaction hash, but tx building is not yet completed and hash will be changed.
// So make a copy of tx to avoid wrong hash caching.
cp := *tx
v, finalize := o.Chain.GetTestVM(trigger.Verification, &cp, nil)
v.GasLimit = o.Chain.GetMaxVerificationGAS()
v.LoadScriptWithHash(o.oracleScript, o.oracleHash, callflag.ReadOnly)
v.Context().Jump(o.verifyOffset)
ic := o.Chain.GetTestVM(trigger.Verification, &cp, nil)
ic.VM.GasLimit = o.Chain.GetMaxVerificationGAS()
ic.VM.LoadScriptWithHash(o.oracleScript, o.oracleHash, callflag.ReadOnly)
ic.VM.Context().Jump(o.verifyOffset)
ok := isVerifyOk(v, finalize)
return v.GasConsumed(), ok
ok := isVerifyOk(ic)
return ic.VM.GasConsumed(), ok
}
func isVerifyOk(v *vm.VM, finalize func()) bool {
defer finalize()
if err := v.Run(); err != nil {
func isVerifyOk(ic *interop.Context) bool {
defer ic.Finalize()
if err := ic.VM.Run(); err != nil {
return false
}
if v.Estack().Len() != 1 {
if ic.VM.Estack().Len() != 1 {
return false
}
ok, err := v.Estack().Pop().Item().TryBool()
ok, err := ic.VM.Estack().Pop().Item().TryBool()
return err == nil && ok
}