diff --git a/internal/fakechain/fakechain.go b/internal/fakechain/fakechain.go index 8e016724b..3704eb234 100644 --- a/internal/fakechain/fakechain.go +++ b/internal/fakechain/fakechain.go @@ -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") } diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index ad34235ed..a807a4407 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -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. diff --git a/pkg/core/blockchainer/blockchainer.go b/pkg/core/blockchainer/blockchainer.go index 7ccb738af..4a45694a5 100644 --- a/pkg/core/blockchainer/blockchainer.go +++ b/pkg/core/blockchainer/blockchainer.go @@ -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 diff --git a/pkg/core/helper_test.go b/pkg/core/helper_test.go index f117c9825..6db705df1 100644 --- a/pkg/core/helper_test.go +++ b/pkg/core/helper_test.go @@ -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) { diff --git a/pkg/neotest/basic.go b/pkg/neotest/basic.go index 46ef05c0c..3febadcd4 100644 --- a/pkg/neotest/basic.go +++ b/pkg/neotest/basic.go @@ -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 } diff --git a/pkg/neotest/client.go b/pkg/neotest/client.go index 646f58143..0bc9bf87c 100644 --- a/pkg/neotest/client.go +++ b/pkg/neotest/client.go @@ -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. diff --git a/pkg/rpc/server/client_test.go b/pkg/rpc/server/client_test.go index e24647cfe..7b92eccac 100644 --- a/pkg/rpc/server/client_test.go +++ b/pkg/rpc/server/client_test.go @@ -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()) }) } diff --git a/pkg/rpc/server/server.go b/pkg/rpc/server/server.go index 19f596260..ef91bd320 100644 --- a/pkg/rpc/server/server.go +++ b/pkg/rpc/server/server.go @@ -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. diff --git a/pkg/services/oracle/response.go b/pkg/services/oracle/response.go index fdbef6afa..779290b8a 100644 --- a/pkg/services/oracle/response.go +++ b/pkg/services/oracle/response.go @@ -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 }