mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-11-22 19:29:39 +00:00
parent
3549515fd7
commit
d5355acfa9
9 changed files with 229 additions and 11 deletions
|
@ -1245,8 +1245,8 @@ func (bc *Blockchain) GetScriptHashesForVerifying(t *transaction.Transaction) ([
|
|||
}
|
||||
|
||||
// GetTestVM returns a VM and a Store setup for a test run of some sort of code.
|
||||
func (bc *Blockchain) GetTestVM() *vm.VM {
|
||||
systemInterop := bc.newInteropContext(trigger.Application, bc.dao, nil, nil)
|
||||
func (bc *Blockchain) GetTestVM(tx *transaction.Transaction) *vm.VM {
|
||||
systemInterop := bc.newInteropContext(trigger.Application, bc.dao, nil, tx)
|
||||
vm := SpawnVM(systemInterop)
|
||||
vm.SetPriceGetter(getPrice)
|
||||
return vm
|
||||
|
|
|
@ -41,7 +41,7 @@ type Blockchainer interface {
|
|||
GetScriptHashesForVerifying(*transaction.Transaction) ([]util.Uint160, error)
|
||||
GetStorageItem(scripthash util.Uint160, key []byte) *state.StorageItem
|
||||
GetStorageItems(hash util.Uint160) (map[string]*state.StorageItem, error)
|
||||
GetTestVM() *vm.VM
|
||||
GetTestVM(tx *transaction.Transaction) *vm.VM
|
||||
GetTransaction(util.Uint256) (*transaction.Transaction, uint32, error)
|
||||
mempool.Feer // fee interface
|
||||
PoolTx(*transaction.Transaction) error
|
||||
|
|
|
@ -97,7 +97,7 @@ func (chain testChain) GetScriptHashesForVerifying(*transaction.Transaction) ([]
|
|||
func (chain testChain) GetStorageItem(scripthash util.Uint160, key []byte) *state.StorageItem {
|
||||
panic("TODO")
|
||||
}
|
||||
func (chain testChain) GetTestVM() *vm.VM {
|
||||
func (chain testChain) GetTestVM(tx *transaction.Transaction) *vm.VM {
|
||||
panic("TODO")
|
||||
}
|
||||
func (chain testChain) GetStorageItems(hash util.Uint160) (map[string]*state.StorageItem, error) {
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"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/smartcontract"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
|
@ -65,6 +66,7 @@ const (
|
|||
TxFilterT
|
||||
NotificationFilterT
|
||||
ExecutionFilterT
|
||||
Cosigner
|
||||
)
|
||||
|
||||
func (p Param) String() string {
|
||||
|
@ -154,6 +156,47 @@ func (p Param) GetBytesHex() ([]byte, error) {
|
|||
return hex.DecodeString(s)
|
||||
}
|
||||
|
||||
// GetCosigner returns transaction.Cosigner value of the parameter.
|
||||
func (p Param) GetCosigner() (transaction.Cosigner, error) {
|
||||
c, ok := p.Value.(transaction.Cosigner)
|
||||
if !ok {
|
||||
return transaction.Cosigner{}, errors.New("not a cosigner")
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
// GetCosigners returns a slice of transaction.Cosigner with global scope from
|
||||
// array of Uint160 or array of serialized transaction.Cosigner stored in the
|
||||
// parameter.
|
||||
func (p Param) GetCosigners() ([]transaction.Cosigner, error) {
|
||||
hashes, err := p.GetArray()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cosigners := make([]transaction.Cosigner, len(hashes))
|
||||
// try to extract hashes first
|
||||
for i, h := range hashes {
|
||||
var u util.Uint160
|
||||
u, err = h.GetUint160FromHex()
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
cosigners[i] = transaction.Cosigner{
|
||||
Account: u,
|
||||
Scopes: transaction.Global,
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
for i, h := range hashes {
|
||||
cosigners[i], err = h.GetCosigner()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
return cosigners, nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaler interface.
|
||||
func (p *Param) UnmarshalJSON(data []byte) error {
|
||||
var s string
|
||||
|
@ -167,6 +210,7 @@ func (p *Param) UnmarshalJSON(data []byte) error {
|
|||
{TxFilterT, &TxFilter{}},
|
||||
{NotificationFilterT, &NotificationFilter{}},
|
||||
{ExecutionFilterT, &ExecutionFilter{}},
|
||||
{Cosigner, &transaction.Cosigner{}},
|
||||
{ArrayT, &[]Param{}},
|
||||
}
|
||||
|
||||
|
@ -196,6 +240,8 @@ func (p *Param) UnmarshalJSON(data []byte) error {
|
|||
} else {
|
||||
continue
|
||||
}
|
||||
case *transaction.Cosigner:
|
||||
p.Value = *val
|
||||
case *[]Param:
|
||||
p.Value = *val
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"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/smartcontract"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
|
@ -19,9 +20,13 @@ func TestParam_UnmarshalJSON(t *testing.T) {
|
|||
{"cosigner": "f84d6a337fbc3d3a201d41da99e86b479e7a2554"},
|
||||
{"sender": "f84d6a337fbc3d3a201d41da99e86b479e7a2554", "cosigner": "f84d6a337fbc3d3a201d41da99e86b479e7a2554"},
|
||||
{"contract": "f84d6a337fbc3d3a201d41da99e86b479e7a2554"},
|
||||
{"state": "HALT"}]`
|
||||
{"state": "HALT"},
|
||||
{"account": "0xcadb3dc2faa3ef14a13b619c9a43124755aa2569", "scopes": 0},
|
||||
[{"account": "0xcadb3dc2faa3ef14a13b619c9a43124755aa2569", "scopes": 0}]]`
|
||||
contr, err := util.Uint160DecodeStringLE("f84d6a337fbc3d3a201d41da99e86b479e7a2554")
|
||||
require.NoError(t, err)
|
||||
accountHash, err := util.Uint160DecodeStringLE("cadb3dc2faa3ef14a13b619c9a43124755aa2569")
|
||||
require.NoError(t, err)
|
||||
expected := Params{
|
||||
{
|
||||
Type: StringT,
|
||||
|
@ -83,6 +88,25 @@ func TestParam_UnmarshalJSON(t *testing.T) {
|
|||
Type: ExecutionFilterT,
|
||||
Value: ExecutionFilter{State: "HALT"},
|
||||
},
|
||||
{
|
||||
Type: Cosigner,
|
||||
Value: transaction.Cosigner{
|
||||
Account: accountHash,
|
||||
Scopes: transaction.Global,
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: ArrayT,
|
||||
Value: []Param{
|
||||
{
|
||||
Type: Cosigner,
|
||||
Value: transaction.Cosigner{
|
||||
Account: accountHash,
|
||||
Scopes: transaction.Global,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var ps Params
|
||||
|
@ -214,3 +238,67 @@ func TestParamGetBytesHex(t *testing.T) {
|
|||
_, err = p.GetBytesHex()
|
||||
require.NotNil(t, err)
|
||||
}
|
||||
|
||||
func TestParamGetCosigner(t *testing.T) {
|
||||
c := transaction.Cosigner{
|
||||
Account: util.Uint160{1, 2, 3, 4},
|
||||
Scopes: transaction.Global,
|
||||
}
|
||||
p := Param{Type: Cosigner, Value: c}
|
||||
actual, err := p.GetCosigner()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, c, actual)
|
||||
|
||||
p = Param{Type: Cosigner, Value: `{"account": "0xcadb3dc2faa3ef14a13b619c9a43124755aa2569", "scopes": 0}`}
|
||||
_, err = p.GetCosigner()
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func TestParamGetCosigners(t *testing.T) {
|
||||
u1 := util.Uint160{1, 2, 3, 4}
|
||||
u2 := util.Uint160{5, 6, 7, 8}
|
||||
t.Run("from hashes", func(t *testing.T) {
|
||||
p := Param{ArrayT, []Param{
|
||||
{Type: StringT, Value: u1.StringLE()},
|
||||
{Type: StringT, Value: u2.StringLE()},
|
||||
}}
|
||||
actual, err := p.GetCosigners()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 2, len(actual))
|
||||
require.True(t, u1.Equals(actual[0].Account))
|
||||
require.True(t, u2.Equals(actual[1].Account))
|
||||
})
|
||||
|
||||
t.Run("from cosigners", func(t *testing.T) {
|
||||
c1 := transaction.Cosigner{
|
||||
Account: u1,
|
||||
Scopes: transaction.Global,
|
||||
}
|
||||
c2 := transaction.Cosigner{
|
||||
Account: u2,
|
||||
Scopes: transaction.CustomContracts,
|
||||
AllowedContracts: []util.Uint160{
|
||||
{1, 2, 3},
|
||||
{4, 5, 6},
|
||||
},
|
||||
}
|
||||
p := Param{ArrayT, []Param{
|
||||
{Type: Cosigner, Value: c1},
|
||||
{Type: Cosigner, Value: c2},
|
||||
}}
|
||||
actual, err := p.GetCosigners()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 2, len(actual))
|
||||
require.Equal(t, c1, actual[0])
|
||||
require.Equal(t, c2, actual[1])
|
||||
})
|
||||
|
||||
t.Run("bad format", func(t *testing.T) {
|
||||
p := Param{ArrayT, []Param{
|
||||
{Type: StringT, Value: u1.StringLE()},
|
||||
{Type: StringT, Value: "bla"},
|
||||
}}
|
||||
_, err := p.GetCosigners()
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -618,7 +618,7 @@ func (s *Server) getDecimals(h util.Uint160, cache map[util.Uint160]int64) (int6
|
|||
if err != nil {
|
||||
return 0, response.NewInternalServerError("Can't create script", err)
|
||||
}
|
||||
res := s.runScriptInVM(script)
|
||||
res := s.runScriptInVM(script, nil)
|
||||
if res == nil || res.State != "HALT" || len(res.Stack) == 0 {
|
||||
return 0, response.NewInternalServerError("execution error", errors.New("no result"))
|
||||
}
|
||||
|
@ -864,11 +864,21 @@ func (s *Server) invokeFunction(reqParams request.Params) (interface{}, *respons
|
|||
if err != nil {
|
||||
return nil, response.ErrInvalidParams
|
||||
}
|
||||
script, err := request.CreateFunctionInvocationScript(scriptHash, reqParams[1:])
|
||||
tx := &transaction.Transaction{}
|
||||
checkWitnessHashesIndex := len(reqParams)
|
||||
if checkWitnessHashesIndex > 3 {
|
||||
cosigners, err := reqParams[3].GetCosigners()
|
||||
if err != nil {
|
||||
return nil, response.ErrInvalidParams
|
||||
}
|
||||
tx.Cosigners = cosigners
|
||||
checkWitnessHashesIndex--
|
||||
}
|
||||
script, err := request.CreateFunctionInvocationScript(scriptHash, reqParams[1:checkWitnessHashesIndex])
|
||||
if err != nil {
|
||||
return nil, response.NewInternalServerError("can't create invocation script", err)
|
||||
}
|
||||
return s.runScriptInVM(script), nil
|
||||
return s.runScriptInVM(script, tx), nil
|
||||
}
|
||||
|
||||
// invokescript implements the `invokescript` RPC call.
|
||||
|
@ -882,13 +892,21 @@ func (s *Server) invokescript(reqParams request.Params) (interface{}, *response.
|
|||
return nil, response.ErrInvalidParams
|
||||
}
|
||||
|
||||
return s.runScriptInVM(script), nil
|
||||
tx := &transaction.Transaction{}
|
||||
if len(reqParams) > 1 {
|
||||
cosigners, err := reqParams[1].GetCosigners()
|
||||
if err != nil {
|
||||
return nil, response.ErrInvalidParams
|
||||
}
|
||||
tx.Cosigners = cosigners
|
||||
}
|
||||
return s.runScriptInVM(script, tx), nil
|
||||
}
|
||||
|
||||
// runScriptInVM runs given script in a new test VM and returns the invocation
|
||||
// result.
|
||||
func (s *Server) runScriptInVM(script []byte) *result.Invoke {
|
||||
vm := s.chain.GetTestVM()
|
||||
func (s *Server) runScriptInVM(script []byte, tx *transaction.Transaction) *result.Invoke {
|
||||
vm := s.chain.GetTestVM(tx)
|
||||
vm.SetGasLimit(s.config.MaxGasInvoke)
|
||||
vm.LoadScript(script)
|
||||
_ = vm.Run()
|
||||
|
|
|
@ -620,6 +620,55 @@ var rpcTestCases = map[string][]rpcTestCase{
|
|||
assert.NotEqual(t, 0, res.GasConsumed)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "positive, good witness",
|
||||
// script is hex-encoded `test_verify.avm` representation, hashes are hex-encoded LE bytes of hashes used in the contract with `0x` prefix
|
||||
params: `["5707000c14010c030e05060c0d020e0f0d030e070900000000db307068115541f827ec8c21aa270700000011400c140d0f03020900020103070304050201000e060c09db307169115541f827ec8c21aa270700000012401340",["0x0000000009070e030d0f0e020d0c06050e030c01","0x090c060e00010205040307030102000902030f0d"]]`,
|
||||
result: func(e *executor) interface{} { return &result.Invoke{} },
|
||||
check: func(t *testing.T, e *executor, inv interface{}) {
|
||||
res, ok := inv.(*result.Invoke)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, "HALT", res.State)
|
||||
require.Equal(t, 1, len(res.Stack))
|
||||
require.Equal(t, int64(3), res.Stack[0].Value)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "positive, bad witness of second hash",
|
||||
params: `["5707000c14010c030e05060c0d020e0f0d030e070900000000db307068115541f827ec8c21aa270700000011400c140d0f03020900020103070304050201000e060c09db307169115541f827ec8c21aa270700000012401340",["0x0000000009070e030d0f0e020d0c06050e030c01"]]`,
|
||||
result: func(e *executor) interface{} { return &result.Invoke{} },
|
||||
check: func(t *testing.T, e *executor, inv interface{}) {
|
||||
res, ok := inv.(*result.Invoke)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, "HALT", res.State)
|
||||
require.Equal(t, 1, len(res.Stack))
|
||||
require.Equal(t, int64(2), res.Stack[0].Value)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "positive, no good hashes",
|
||||
params: `["5707000c14010c030e05060c0d020e0f0d030e070900000000db307068115541f827ec8c21aa270700000011400c140d0f03020900020103070304050201000e060c09db307169115541f827ec8c21aa270700000012401340"]`,
|
||||
result: func(e *executor) interface{} { return &result.Invoke{} },
|
||||
check: func(t *testing.T, e *executor, inv interface{}) {
|
||||
res, ok := inv.(*result.Invoke)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, "HALT", res.State)
|
||||
require.Equal(t, 1, len(res.Stack))
|
||||
require.Equal(t, int64(1), res.Stack[0].Value)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "positive, bad hashes witness",
|
||||
params: `["5707000c14010c030e05060c0d020e0f0d030e070900000000db307068115541f827ec8c21aa270700000011400c140d0f03020900020103070304050201000e060c09db307169115541f827ec8c21aa270700000012401340",["0x0000000009070e030d0f0e020d0c06050e030c02"]]`,
|
||||
result: func(e *executor) interface{} { return &result.Invoke{} },
|
||||
check: func(t *testing.T, e *executor, inv interface{}) {
|
||||
res, ok := inv.(*result.Invoke)
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, "HALT", res.State)
|
||||
assert.Equal(t, 1, len(res.Stack))
|
||||
assert.Equal(t, int64(1), res.Stack[0].Value)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no params",
|
||||
params: `[]`,
|
||||
|
|
BIN
pkg/rpc/server/testdata/test_verify.avm
vendored
Executable file
BIN
pkg/rpc/server/testdata/test_verify.avm
vendored
Executable file
Binary file not shown.
17
pkg/rpc/server/testdata/test_verify.go
vendored
Normal file
17
pkg/rpc/server/testdata/test_verify.go
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
package testdata
|
||||
|
||||
import "github.com/nspcc-dev/neo-go/pkg/interop/runtime"
|
||||
|
||||
// This contract is used to test `invokescript` and `invokefunction` RPC-calls
|
||||
func Main() int {
|
||||
// h1 and h2 are just random uint160 hashes
|
||||
h1 := []byte{1, 12, 3, 14, 5, 6, 12, 13, 2, 14, 15, 13, 3, 14, 7, 9, 0, 0, 0, 0}
|
||||
if !runtime.CheckWitness(h1) {
|
||||
return 1
|
||||
}
|
||||
h2 := []byte{13, 15, 3, 2, 9, 0, 2, 1, 3, 7, 3, 4, 5, 2, 1, 0, 14, 6, 12, 9}
|
||||
if !runtime.CheckWitness(h2) {
|
||||
return 2
|
||||
}
|
||||
return 3
|
||||
}
|
Loading…
Reference in a new issue