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).
This commit is contained in:
Roman Khimov 2019-10-29 18:31:39 +03:00
parent 94776b8a1f
commit ebc1ba4f38
5 changed files with 54 additions and 0 deletions

View file

@ -1111,6 +1111,14 @@ func (bc *Blockchain) spawnVMWithInterops(interopCtx *interopContext) *vm.VM {
return 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. // verifyHashAgainstScript verifies given hash against the given witness.
func (bc *Blockchain) verifyHashAgainstScript(hash util.Uint160, witness *transaction.Witness, checkedHash util.Uint256, interopCtx *interopContext) error { func (bc *Blockchain) verifyHashAgainstScript(hash util.Uint160, witness *transaction.Witness, checkedHash util.Uint256, interopCtx *interopContext) error {
verification := witness.VerificationScript verification := witness.VerificationScript

View file

@ -2,8 +2,10 @@ package core
import ( import (
"github.com/CityOfZion/neo-go/config" "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/core/transaction"
"github.com/CityOfZion/neo-go/pkg/util" "github.com/CityOfZion/neo-go/pkg/util"
"github.com/CityOfZion/neo-go/pkg/vm"
) )
// Blockchainer is an interface that abstract the implementation // Blockchainer is an interface that abstract the implementation
@ -27,6 +29,7 @@ type Blockchainer interface {
GetScriptHashesForVerifying(*transaction.Transaction) ([]util.Uint160, error) GetScriptHashesForVerifying(*transaction.Transaction) ([]util.Uint160, error)
GetStorageItem(scripthash util.Uint160, key []byte) *StorageItem GetStorageItem(scripthash util.Uint160, key []byte) *StorageItem
GetStorageItems(hash util.Uint160) (map[string]*StorageItem, error) GetStorageItems(hash util.Uint160) (map[string]*StorageItem, error)
GetTestVM() (*vm.VM, storage.Store)
GetTransaction(util.Uint256) (*transaction.Transaction, uint32, error) GetTransaction(util.Uint256) (*transaction.Transaction, uint32, error)
GetUnspentCoinState(util.Uint256) *UnspentCoinState GetUnspentCoinState(util.Uint256) *UnspentCoinState
References(t *transaction.Transaction) map[transaction.Input]*transaction.Output References(t *transaction.Transaction) map[transaction.Input]*transaction.Output

View file

@ -9,9 +9,11 @@ import (
"github.com/CityOfZion/neo-go/config" "github.com/CityOfZion/neo-go/config"
"github.com/CityOfZion/neo-go/pkg/core" "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/core/transaction"
"github.com/CityOfZion/neo-go/pkg/network/payload" "github.com/CityOfZion/neo-go/pkg/network/payload"
"github.com/CityOfZion/neo-go/pkg/util" "github.com/CityOfZion/neo-go/pkg/util"
"github.com/CityOfZion/neo-go/pkg/vm"
) )
type testChain struct { type testChain struct {
@ -78,6 +80,9 @@ func (chain testChain) GetScriptHashesForVerifying(*transaction.Transaction) ([]
func (chain testChain) GetStorageItem(scripthash util.Uint160, key []byte) *core.StorageItem { func (chain testChain) GetStorageItem(scripthash util.Uint160, key []byte) *core.StorageItem {
panic("TODO") panic("TODO")
} }
func (chain testChain) GetTestVM() (*vm.VM, storage.Store) {
panic("TODO")
}
func (chain testChain) GetStorageItems(hash util.Uint160) (map[string]*core.StorageItem, error) { func (chain testChain) GetStorageItems(hash util.Uint160) (map[string]*core.StorageItem, error) {
panic("TODO") panic("TODO")
} }

View file

@ -227,6 +227,9 @@ Methods:
case "getrawtransaction": case "getrawtransaction":
results, resultsErr = s.getrawtransaction(reqParams) results, resultsErr = s.getrawtransaction(reqParams)
case "invokescript":
results, resultsErr = s.invokescript(reqParams)
case "sendrawtransaction": case "sendrawtransaction":
results, resultsErr = s.sendrawtransaction(reqParams) results, resultsErr = s.sendrawtransaction(reqParams)
@ -280,6 +283,28 @@ func (s *Server) getrawtransaction(reqParams Params) (interface{}, error) {
return results, resultsErr 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) { func (s *Server) sendrawtransaction(reqParams Params) (interface{}, error) {
var resultsErr error var resultsErr error
var results interface{} var results interface{}

View file

@ -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
}