From ebc1ba4f38a5782c0ce27230111f54592cc6b8c3 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Tue, 29 Oct 2019 18:31:39 +0300 Subject: [PATCH] rpc/core: implement invokescript method, fix #348 Extend Blockchainer with one more method to spawn a VM for test runs and use it to run scripts. Gas consumption is not counted or limited in any way at the moment (see #424). --- pkg/core/blockchain.go | 8 ++++++++ pkg/core/blockchainer.go | 3 +++ pkg/network/helper_test.go | 5 +++++ pkg/rpc/server.go | 25 +++++++++++++++++++++++++ pkg/rpc/wrappers/invoke_result.go | 13 +++++++++++++ 5 files changed, 54 insertions(+) create mode 100644 pkg/rpc/wrappers/invoke_result.go diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 88b670ab0..f3168aadf 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -1111,6 +1111,14 @@ func (bc *Blockchain) spawnVMWithInterops(interopCtx *interopContext) *vm.VM { return vm } +// GetTestVM returns a VM and a Store setup for a test run of some sort of code. +func (bc *Blockchain) GetTestVM() (*vm.VM, storage.Store) { + tmpStore := storage.NewMemCachedStore(bc.store) + systemInterop := newInteropContext(0x10, bc, tmpStore, nil, nil) + vm := bc.spawnVMWithInterops(systemInterop) + return vm, tmpStore +} + // verifyHashAgainstScript verifies given hash against the given witness. func (bc *Blockchain) verifyHashAgainstScript(hash util.Uint160, witness *transaction.Witness, checkedHash util.Uint256, interopCtx *interopContext) error { verification := witness.VerificationScript diff --git a/pkg/core/blockchainer.go b/pkg/core/blockchainer.go index 1409a2c40..1ce3247e6 100644 --- a/pkg/core/blockchainer.go +++ b/pkg/core/blockchainer.go @@ -2,8 +2,10 @@ package core import ( "github.com/CityOfZion/neo-go/config" + "github.com/CityOfZion/neo-go/pkg/core/storage" "github.com/CityOfZion/neo-go/pkg/core/transaction" "github.com/CityOfZion/neo-go/pkg/util" + "github.com/CityOfZion/neo-go/pkg/vm" ) // Blockchainer is an interface that abstract the implementation @@ -27,6 +29,7 @@ type Blockchainer interface { GetScriptHashesForVerifying(*transaction.Transaction) ([]util.Uint160, error) GetStorageItem(scripthash util.Uint160, key []byte) *StorageItem GetStorageItems(hash util.Uint160) (map[string]*StorageItem, error) + GetTestVM() (*vm.VM, storage.Store) GetTransaction(util.Uint256) (*transaction.Transaction, uint32, error) GetUnspentCoinState(util.Uint256) *UnspentCoinState References(t *transaction.Transaction) map[transaction.Input]*transaction.Output diff --git a/pkg/network/helper_test.go b/pkg/network/helper_test.go index dbb55f607..2ad71205d 100644 --- a/pkg/network/helper_test.go +++ b/pkg/network/helper_test.go @@ -9,9 +9,11 @@ import ( "github.com/CityOfZion/neo-go/config" "github.com/CityOfZion/neo-go/pkg/core" + "github.com/CityOfZion/neo-go/pkg/core/storage" "github.com/CityOfZion/neo-go/pkg/core/transaction" "github.com/CityOfZion/neo-go/pkg/network/payload" "github.com/CityOfZion/neo-go/pkg/util" + "github.com/CityOfZion/neo-go/pkg/vm" ) type testChain struct { @@ -78,6 +80,9 @@ func (chain testChain) GetScriptHashesForVerifying(*transaction.Transaction) ([] func (chain testChain) GetStorageItem(scripthash util.Uint160, key []byte) *core.StorageItem { panic("TODO") } +func (chain testChain) GetTestVM() (*vm.VM, storage.Store) { + panic("TODO") +} func (chain testChain) GetStorageItems(hash util.Uint160) (map[string]*core.StorageItem, error) { panic("TODO") } diff --git a/pkg/rpc/server.go b/pkg/rpc/server.go index 7f48848e3..fdcff9da2 100644 --- a/pkg/rpc/server.go +++ b/pkg/rpc/server.go @@ -227,6 +227,9 @@ Methods: case "getrawtransaction": results, resultsErr = s.getrawtransaction(reqParams) + case "invokescript": + results, resultsErr = s.invokescript(reqParams) + case "sendrawtransaction": results, resultsErr = s.sendrawtransaction(reqParams) @@ -280,6 +283,28 @@ func (s *Server) getrawtransaction(reqParams Params) (interface{}, error) { return results, resultsErr } +// invokescript implements the `invokescript` RPC call. +func (s *Server) invokescript(reqParams Params) (interface{}, error) { + hexScript, err := reqParams.ValueWithType(0, "string") + if err != nil { + return nil, err + } + script, err := hex.DecodeString(hexScript.StringVal) + if err != nil { + return nil, err + } + vm, _ := s.chain.GetTestVM() + vm.LoadScript(script) + _ = vm.Run() + result := &wrappers.InvokeResult{ + State: vm.State(), + GasConsumed: "0.1", + Script: hexScript.StringVal, + Stack: vm.Estack(), + } + return result, nil +} + func (s *Server) sendrawtransaction(reqParams Params) (interface{}, error) { var resultsErr error var results interface{} diff --git a/pkg/rpc/wrappers/invoke_result.go b/pkg/rpc/wrappers/invoke_result.go new file mode 100644 index 000000000..4cc790bf5 --- /dev/null +++ b/pkg/rpc/wrappers/invoke_result.go @@ -0,0 +1,13 @@ +package wrappers + +import ( + "github.com/CityOfZion/neo-go/pkg/vm" +) + +// InvokeResult is used as a wrapper to represent an invokation result. +type InvokeResult struct { + State string `json:"state"` + GasConsumed string `json:"gas_consumed"` + Script string `json:"script"` + Stack *vm.Stack +}