vm,compiler: replace APPCALL with System.Contract.Call

Contract calls are performed via syscall System.Contract.Call
in NEO3. This implements this in compiler and removes APPCALL from the
VM.
This commit is contained in:
Evgenii Stratonikov 2020-05-07 14:38:19 +03:00
parent ec900c7ff7
commit 73c82584a3
13 changed files with 108 additions and 241 deletions

View file

@ -1080,16 +1080,12 @@ func (c *codegen) convertBuiltin(expr *ast.CallExpr) {
case "SHA256": case "SHA256":
emit.Syscall(c.prog.BinWriter, "Neo.Crypto.SHA256") emit.Syscall(c.prog.BinWriter, "Neo.Crypto.SHA256")
case "AppCall": case "AppCall":
numArgs := len(expr.Args) - 1 c.emitReverse(len(expr.Args))
c.emitReverse(numArgs)
emit.Opcode(c.prog.BinWriter, opcode.APPCALL)
buf := c.getByteArray(expr.Args[0]) buf := c.getByteArray(expr.Args[0])
if len(buf) != 20 { if len(buf) != 20 {
c.prog.Err = errors.New("invalid script hash") c.prog.Err = errors.New("invalid script hash")
} }
emit.Syscall(c.prog.BinWriter, "System.Contract.Call")
c.prog.WriteBytes(buf)
case "Equals": case "Equals":
emit.Opcode(c.prog.BinWriter, opcode.EQUAL) emit.Opcode(c.prog.BinWriter, opcode.EQUAL)
case "FromAddress": case "FromAddress":
@ -1111,16 +1107,14 @@ func (c *codegen) convertBuiltin(expr *ast.CallExpr) {
// transformArgs returns a list of function arguments // transformArgs returns a list of function arguments
// which should be put on stack. // which should be put on stack.
// There are special cases for builtins: // There are special cases for builtins:
// 1. When using AppCall, script hash is a part of the instruction so // 1. With FromAddress, parameter conversion is happening at compile-time
// it should be emitted after APPCALL.
// 2. With FromAddress, parameter conversion is happening at compile-time
// so there is no need to push parameters on stack and perform an actual call // so there is no need to push parameters on stack and perform an actual call
// 3. With panic, generated code depends on if argument was nil or a string so // 2. With panic, generated code depends on if argument was nil or a string so
// it should be handled accordingly. // it should be handled accordingly.
func transformArgs(fun ast.Expr, args []ast.Expr) []ast.Expr { func transformArgs(fun ast.Expr, args []ast.Expr) []ast.Expr {
switch f := fun.(type) { switch f := fun.(type) {
case *ast.SelectorExpr: case *ast.SelectorExpr:
if f.Sel.Name == "AppCall" || f.Sel.Name == "FromAddress" { if f.Sel.Name == "FromAddress" {
return args[1:] return args[1:]
} }
case *ast.Ident: case *ast.Ident:

View file

@ -6,10 +6,17 @@ import (
"testing" "testing"
"github.com/nspcc-dev/neo-go/pkg/compiler" "github.com/nspcc-dev/neo-go/pkg/compiler"
"github.com/nspcc-dev/neo-go/pkg/core"
"github.com/nspcc-dev/neo-go/pkg/core/dao"
"github.com/nspcc-dev/neo-go/pkg/core/interop"
"github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/core/storage"
"github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/encoding/address" "github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/vm"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"go.uber.org/zap/zaptest"
) )
func TestFromAddress(t *testing.T) { func TestFromAddress(t *testing.T) {
@ -51,6 +58,14 @@ func TestFromAddress(t *testing.T) {
}) })
} }
func spawnVM(t *testing.T, ic *interop.Context, src string) *vm.VM {
b, err := compiler.Compile(strings.NewReader(src))
require.NoError(t, err)
v := core.SpawnVM(ic)
v.Load(b)
return v
}
func TestAppCall(t *testing.T) { func TestAppCall(t *testing.T) {
srcInner := ` srcInner := `
package foo package foo
@ -62,19 +77,13 @@ func TestAppCall(t *testing.T) {
inner, err := compiler.Compile(strings.NewReader(srcInner)) inner, err := compiler.Compile(strings.NewReader(srcInner))
require.NoError(t, err) require.NoError(t, err)
ih := hash.Hash160(inner) ic := interop.NewContext(trigger.Application, nil, dao.NewSimple(storage.NewMemoryStore()), nil, nil, nil, zaptest.NewLogger(t))
getScript := func(u util.Uint160) ([]byte, bool) { require.NoError(t, ic.DAO.PutContractState(&state.Contract{Script: inner}))
if u.Equals(ih) {
return inner, true
}
return nil, false
}
ih := hash.Hash160(inner)
t.Run("valid script", func(t *testing.T) { t.Run("valid script", func(t *testing.T) {
src := getAppCallScript(fmt.Sprintf("%#v", ih.BytesBE())) src := getAppCallScript(fmt.Sprintf("%#v", ih.BytesBE()))
v := vmAndCompile(t, src) v := spawnVM(t, ic, src)
v.SetScriptGetter(getScript)
require.NoError(t, v.Run()) require.NoError(t, v.Run())
assertResult(t, v, []byte{1, 2, 3, 4}) assertResult(t, v, []byte{1, 2, 3, 4})
@ -85,9 +94,7 @@ func TestAppCall(t *testing.T) {
h[0] = ^h[0] h[0] = ^h[0]
src := getAppCallScript(fmt.Sprintf("%#v", h.BytesBE())) src := getAppCallScript(fmt.Sprintf("%#v", h.BytesBE()))
v := vmAndCompile(t, src) v := spawnVM(t, ic, src)
v.SetScriptGetter(getScript)
require.Error(t, v.Run()) require.Error(t, v.Run())
}) })
@ -111,9 +118,7 @@ func TestAppCall(t *testing.T) {
} }
` `
v := vmAndCompile(t, src) v := spawnVM(t, ic, src)
v.SetScriptGetter(getScript)
require.NoError(t, v.Run()) require.NoError(t, v.Run())
assertResult(t, v, []byte{1, 2, 3, 4}) assertResult(t, v, []byte{1, 2, 3, 4})

View file

@ -20,8 +20,6 @@ func getPrice(v *vm.VM, op opcode.Opcode, parameter []byte) util.Fixed8 {
} }
switch op { switch op {
case opcode.APPCALL, opcode.TAILCALL:
return toFixed8(10)
case opcode.SYSCALL: case opcode.SYSCALL:
interopID := vm.GetInteropID(parameter) interopID := vm.GetInteropID(parameter)
return getSyscallPrice(v, interopID) return getSyscallPrice(v, interopID)

View file

@ -24,11 +24,11 @@ import (
// up for current blockchain. // up for current blockchain.
func SpawnVM(ic *interop.Context) *vm.VM { func SpawnVM(ic *interop.Context) *vm.VM {
vm := vm.New() vm := vm.New()
bc := ic.Chain.(*Blockchain)
vm.SetScriptGetter(ic.GetContract)
vm.RegisterInteropGetter(getSystemInterop(ic)) vm.RegisterInteropGetter(getSystemInterop(ic))
vm.RegisterInteropGetter(getNeoInterop(ic)) vm.RegisterInteropGetter(getNeoInterop(ic))
vm.RegisterInteropGetter(bc.contracts.GetNativeInterop(ic)) if ic.Chain != nil {
vm.RegisterInteropGetter(ic.Chain.(*Blockchain).contracts.GetNativeInterop(ic))
}
return vm return vm
} }

View file

@ -19,43 +19,43 @@ func TestInvocationScriptCreationGood(t *testing.T) {
ps Params ps Params
script string script string
}{{ }{{
script: "676f459162ceeb248b071ec157d9e4f6fd26fdbe50", script: "0c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52",
}, { }, {
ps: Params{{Type: StringT, Value: "transfer"}}, ps: Params{{Type: StringT, Value: "transfer"}},
script: "0c087472616e73666572676f459162ceeb248b071ec157d9e4f6fd26fdbe50", script: "0c087472616e736665720c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52",
}, { }, {
ps: Params{{Type: NumberT, Value: 42}}, ps: Params{{Type: NumberT, Value: 42}},
script: "0c023432676f459162ceeb248b071ec157d9e4f6fd26fdbe50", script: "0c0234320c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52",
}, { }, {
ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{}}}, ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{}}},
script: "10c00c0161676f459162ceeb248b071ec157d9e4f6fd26fdbe50", script: "10c00c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52",
}, { }, {
ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.ByteArrayType, Value: Param{Type: StringT, Value: "50befd26fdf6e4d957c11e078b24ebce6291456f"}}}}}}, ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.ByteArrayType, Value: Param{Type: StringT, Value: "50befd26fdf6e4d957c11e078b24ebce6291456f"}}}}}},
script: "0c1450befd26fdf6e4d957c11e078b24ebce6291456f11c00c0161676f459162ceeb248b071ec157d9e4f6fd26fdbe50", script: "0c1450befd26fdf6e4d957c11e078b24ebce6291456f11c00c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52",
}, { }, {
ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.SignatureType, Value: Param{Type: StringT, Value: "4edf5005771de04619235d5a4c7a9a11bb78e008541f1da7725f654c33380a3c87e2959a025da706d7255cb3a3fa07ebe9c6559d0d9e6213c68049168eb1056f"}}}}}}, ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.SignatureType, Value: Param{Type: StringT, Value: "4edf5005771de04619235d5a4c7a9a11bb78e008541f1da7725f654c33380a3c87e2959a025da706d7255cb3a3fa07ebe9c6559d0d9e6213c68049168eb1056f"}}}}}},
script: "0c404edf5005771de04619235d5a4c7a9a11bb78e008541f1da7725f654c33380a3c87e2959a025da706d7255cb3a3fa07ebe9c6559d0d9e6213c68049168eb1056f11c00c0161676f459162ceeb248b071ec157d9e4f6fd26fdbe50", script: "0c404edf5005771de04619235d5a4c7a9a11bb78e008541f1da7725f654c33380a3c87e2959a025da706d7255cb3a3fa07ebe9c6559d0d9e6213c68049168eb1056f11c00c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52",
}, { }, {
ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.StringType, Value: Param{Type: StringT, Value: "50befd26fdf6e4d957c11e078b24ebce6291456f"}}}}}}, ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.StringType, Value: Param{Type: StringT, Value: "50befd26fdf6e4d957c11e078b24ebce6291456f"}}}}}},
script: "0c283530626566643236666466366534643935376331316530373862323465626365363239313435366611c00c0161676f459162ceeb248b071ec157d9e4f6fd26fdbe50", script: "0c283530626566643236666466366534643935376331316530373862323465626365363239313435366611c00c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52",
}, { }, {
ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.Hash160Type, Value: Param{Type: StringT, Value: "50befd26fdf6e4d957c11e078b24ebce6291456f"}}}}}}, ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.Hash160Type, Value: Param{Type: StringT, Value: "50befd26fdf6e4d957c11e078b24ebce6291456f"}}}}}},
script: "0c146f459162ceeb248b071ec157d9e4f6fd26fdbe5011c00c0161676f459162ceeb248b071ec157d9e4f6fd26fdbe50", script: "0c146f459162ceeb248b071ec157d9e4f6fd26fdbe5011c00c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52",
}, { }, {
ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.Hash256Type, Value: Param{Type: StringT, Value: "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"}}}}}}, ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.Hash256Type, Value: Param{Type: StringT, Value: "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"}}}}}},
script: "0c20e72d286979ee6cb1b7e65dfddfb2e384100b8d148e7758de42e4168b71792c6011c00c0161676f459162ceeb248b071ec157d9e4f6fd26fdbe50", script: "0c20e72d286979ee6cb1b7e65dfddfb2e384100b8d148e7758de42e4168b71792c6011c00c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52",
}, { }, {
ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.PublicKeyType, Value: Param{Type: StringT, Value: "03c089d7122b840a4935234e82e26ae5efd0c2acb627239dc9f207311337b6f2c1"}}}}}}, ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.PublicKeyType, Value: Param{Type: StringT, Value: "03c089d7122b840a4935234e82e26ae5efd0c2acb627239dc9f207311337b6f2c1"}}}}}},
script: "0c2103c089d7122b840a4935234e82e26ae5efd0c2acb627239dc9f207311337b6f2c111c00c0161676f459162ceeb248b071ec157d9e4f6fd26fdbe50", script: "0c2103c089d7122b840a4935234e82e26ae5efd0c2acb627239dc9f207311337b6f2c111c00c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52",
}, { }, {
ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.IntegerType, Value: Param{Type: NumberT, Value: 42}}}}}}, ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.IntegerType, Value: Param{Type: NumberT, Value: 42}}}}}},
script: "002a11c00c0161676f459162ceeb248b071ec157d9e4f6fd26fdbe50", script: "002a11c00c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52",
}, { }, {
ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.BoolType, Value: Param{Type: StringT, Value: "true"}}}}}}, ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.BoolType, Value: Param{Type: StringT, Value: "true"}}}}}},
script: "1111c00c0161676f459162ceeb248b071ec157d9e4f6fd26fdbe50", script: "1111c00c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52",
}, { }, {
ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.BoolType, Value: Param{Type: StringT, Value: "false"}}}}}}, ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.BoolType, Value: Param{Type: StringT, Value: "false"}}}}}},
script: "1011c00c0161676f459162ceeb248b071ec157d9e4f6fd26fdbe50", script: "1011c00c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52",
}} }}
for _, ps := range paramScripts { for _, ps := range paramScripts {
script, err := CreateFunctionInvocationScript(contract, ps.ps) script, err := CreateFunctionInvocationScript(contract, ps.ps)

View file

@ -54,12 +54,12 @@ var rpcTestCases = map[string][]rpcTestCase{
"getapplicationlog": { "getapplicationlog": {
{ {
name: "positive", name: "positive",
params: `["caccb0e0465d87970190c90094e3fb54dbbcfa3e48683bd9a54b1fb834118d87"]`, params: `["c296c0929350d051b9b40cace54db5a3eac4b730a8851e958795d44918f23c08"]`,
result: func(e *executor) interface{} { return &result.ApplicationLog{} }, result: func(e *executor) interface{} { return &result.ApplicationLog{} },
check: func(t *testing.T, e *executor, acc interface{}) { check: func(t *testing.T, e *executor, acc interface{}) {
res, ok := acc.(*result.ApplicationLog) res, ok := acc.(*result.ApplicationLog)
require.True(t, ok) require.True(t, ok)
expectedTxHash, err := util.Uint256DecodeStringLE("caccb0e0465d87970190c90094e3fb54dbbcfa3e48683bd9a54b1fb834118d87") expectedTxHash, err := util.Uint256DecodeStringLE("c296c0929350d051b9b40cace54db5a3eac4b730a8851e958795d44918f23c08")
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, expectedTxHash, res.TxHash) assert.Equal(t, expectedTxHash, res.TxHash)
assert.Equal(t, 1, len(res.Executions)) assert.Equal(t, 1, len(res.Executions))
@ -647,7 +647,7 @@ var rpcTestCases = map[string][]rpcTestCase{
check: func(t *testing.T, e *executor, inv interface{}) { check: func(t *testing.T, e *executor, inv interface{}) {
res, ok := inv.(*result.Invoke) res, ok := inv.(*result.Invoke)
require.True(t, ok) require.True(t, ok)
assert.Equal(t, "0c06717765727479676f459162ceeb248b071ec157d9e4f6fd26fdbe50", res.Script) assert.Equal(t, "0c067177657274790c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52", res.Script)
assert.NotEqual(t, "", res.State) assert.NotEqual(t, "", res.State)
assert.NotEqual(t, 0, res.GasConsumed) assert.NotEqual(t, 0, res.GasConsumed)
}, },

Binary file not shown.

View file

@ -113,8 +113,6 @@ func (c *Context) Next() (opcode.Opcode, []byte, error) {
opcode.JMPGTL, opcode.JMPGEL, opcode.JMPLTL, opcode.JMPLEL, opcode.JMPGTL, opcode.JMPGEL, opcode.JMPLTL, opcode.JMPLEL,
opcode.CALLL, opcode.SYSCALL: opcode.CALLL, opcode.SYSCALL:
numtoread = 4 numtoread = 4
case opcode.APPCALL, opcode.TAILCALL:
numtoread = 20
default: default:
if instr <= opcode.PUSHINT256 { if instr <= opcode.PUSHINT256 {
numtoread = 1 << instr numtoread = 1 << instr

View file

@ -144,11 +144,8 @@ func Jmp(w *io.BinWriter, op opcode.Opcode, label uint16) {
// AppCall emits an appcall, if tailCall is true, tailCall opcode will be // AppCall emits an appcall, if tailCall is true, tailCall opcode will be
// emitted instead. // emitted instead.
func AppCall(w *io.BinWriter, scriptHash util.Uint160, tailCall bool) { func AppCall(w *io.BinWriter, scriptHash util.Uint160, tailCall bool) {
op := opcode.APPCALL Bytes(w, scriptHash.BytesBE())
if tailCall { Syscall(w, "System.Contract.Call")
op = opcode.TAILCALL
}
Instruction(w, op, scriptHash.BytesBE())
} }
// AppCallWithOperationAndArgs emits an APPCALL with the given operation and arguments. // AppCallWithOperationAndArgs emits an APPCALL with the given operation and arguments.

View file

@ -91,9 +91,6 @@ const (
REVERSE4 Opcode = 0x54 REVERSE4 Opcode = 0x54
REVERSEN Opcode = 0x55 REVERSEN Opcode = 0x55
APPCALL Opcode = 0x67
TAILCALL Opcode = 0x69
// Old stack opcodes // Old stack opcodes
DUPFROMALTSTACK Opcode = 0x6A DUPFROMALTSTACK Opcode = 0x6A
TOALTSTACK Opcode = 0x6B TOALTSTACK Opcode = 0x6B

View file

@ -80,8 +80,6 @@ func _() {
_ = x[REVERSE3-83] _ = x[REVERSE3-83]
_ = x[REVERSE4-84] _ = x[REVERSE4-84]
_ = x[REVERSEN-85] _ = x[REVERSEN-85]
_ = x[APPCALL-103]
_ = x[TAILCALL-105]
_ = x[DUPFROMALTSTACK-106] _ = x[DUPFROMALTSTACK-106]
_ = x[TOALTSTACK-107] _ = x[TOALTSTACK-107]
_ = x[FROMALTSTACK-108] _ = x[FROMALTSTACK-108]
@ -143,7 +141,7 @@ func _() {
_ = x[CONVERT-219] _ = x[CONVERT-219]
} }
const _Opcode_name = "PUSHINT8PUSHINT16PUSHINT32PUSHINT64PUSHINT128PUSHINT256PUSHNULLPUSHDATA1PUSHDATA2PUSHDATA4PUSHM1PUSH0PUSH1PUSH2PUSH3PUSH4PUSH5PUSH6PUSH7PUSH8PUSH9PUSH10PUSH11PUSH12PUSH13PUSH14PUSH15PUSH16NOPJMPJMPLJMPIFJMPIFLJMPIFNOTJMPIFNOTLJMPEQJMPEQLJMPNEJMPNELJMPGTJMPGTLJMPGEJMPGELJMPLTJMPLTLJMPLEJMPLELCALLCALLLABORTASSERTTHROWRETSYSCALLDEPTHDROPNIPXDROPCLEARDUPOVERPICKTUCKSWAPOLDPUSH1ROLLREVERSE3REVERSE4REVERSENAPPCALLTAILCALLDUPFROMALTSTACKTOALTSTACKFROMALTSTACKCATSUBSTRLEFTRIGHTINVERTANDORXOREQUALNOTEQUALSIGNABSNEGATEINCDECADDSUBMULDIVMODSHLSHRNOTBOOLANDBOOLORNZNUMEQUALNUMNOTEQUALLTLTEGTGTEMINMAXWITHINPACKUNPACKNEWARRAY0NEWARRAYNEWARRAYTNEWSTRUCT0NEWSTRUCTNEWMAPSIZEHASKEYKEYSVALUESPICKITEMAPPENDSETITEMREVERSEITEMSREMOVECLEARITEMSISNULLISTYPECONVERT" const _Opcode_name = "PUSHINT8PUSHINT16PUSHINT32PUSHINT64PUSHINT128PUSHINT256PUSHNULLPUSHDATA1PUSHDATA2PUSHDATA4PUSHM1PUSH0PUSH1PUSH2PUSH3PUSH4PUSH5PUSH6PUSH7PUSH8PUSH9PUSH10PUSH11PUSH12PUSH13PUSH14PUSH15PUSH16NOPJMPJMPLJMPIFJMPIFLJMPIFNOTJMPIFNOTLJMPEQJMPEQLJMPNEJMPNELJMPGTJMPGTLJMPGEJMPGELJMPLTJMPLTLJMPLEJMPLELCALLCALLLABORTASSERTTHROWRETSYSCALLDEPTHDROPNIPXDROPCLEARDUPOVERPICKTUCKSWAPOLDPUSH1ROLLREVERSE3REVERSE4REVERSENDUPFROMALTSTACKTOALTSTACKFROMALTSTACKCATSUBSTRLEFTRIGHTINVERTANDORXOREQUALNOTEQUALSIGNABSNEGATEINCDECADDSUBMULDIVMODSHLSHRNOTBOOLANDBOOLORNZNUMEQUALNUMNOTEQUALLTLTEGTGTEMINMAXWITHINPACKUNPACKNEWARRAY0NEWARRAYNEWARRAYTNEWSTRUCT0NEWSTRUCTNEWMAPSIZEHASKEYKEYSVALUESPICKITEMAPPENDSETITEMREVERSEITEMSREMOVECLEARITEMSISNULLISTYPECONVERT"
var _Opcode_map = map[Opcode]string{ var _Opcode_map = map[Opcode]string{
0: _Opcode_name[0:8], 0: _Opcode_name[0:8],
@ -215,67 +213,65 @@ var _Opcode_map = map[Opcode]string{
83: _Opcode_name[380:388], 83: _Opcode_name[380:388],
84: _Opcode_name[388:396], 84: _Opcode_name[388:396],
85: _Opcode_name[396:404], 85: _Opcode_name[396:404],
103: _Opcode_name[404:411], 106: _Opcode_name[404:419],
105: _Opcode_name[411:419], 107: _Opcode_name[419:429],
106: _Opcode_name[419:434], 108: _Opcode_name[429:441],
107: _Opcode_name[434:444], 126: _Opcode_name[441:444],
108: _Opcode_name[444:456], 127: _Opcode_name[444:450],
126: _Opcode_name[456:459], 128: _Opcode_name[450:454],
127: _Opcode_name[459:465], 129: _Opcode_name[454:459],
128: _Opcode_name[465:469], 144: _Opcode_name[459:465],
129: _Opcode_name[469:474], 145: _Opcode_name[465:468],
144: _Opcode_name[474:480], 146: _Opcode_name[468:470],
145: _Opcode_name[480:483], 147: _Opcode_name[470:473],
146: _Opcode_name[483:485], 151: _Opcode_name[473:478],
147: _Opcode_name[485:488], 152: _Opcode_name[478:486],
151: _Opcode_name[488:493], 153: _Opcode_name[486:490],
152: _Opcode_name[493:501], 154: _Opcode_name[490:493],
153: _Opcode_name[501:505], 155: _Opcode_name[493:499],
154: _Opcode_name[505:508], 156: _Opcode_name[499:502],
155: _Opcode_name[508:514], 157: _Opcode_name[502:505],
156: _Opcode_name[514:517], 158: _Opcode_name[505:508],
157: _Opcode_name[517:520], 159: _Opcode_name[508:511],
158: _Opcode_name[520:523], 160: _Opcode_name[511:514],
159: _Opcode_name[523:526], 161: _Opcode_name[514:517],
160: _Opcode_name[526:529], 162: _Opcode_name[517:520],
161: _Opcode_name[529:532], 168: _Opcode_name[520:523],
162: _Opcode_name[532:535], 169: _Opcode_name[523:526],
168: _Opcode_name[535:538], 170: _Opcode_name[526:529],
169: _Opcode_name[538:541], 171: _Opcode_name[529:536],
170: _Opcode_name[541:544], 172: _Opcode_name[536:542],
171: _Opcode_name[544:551], 177: _Opcode_name[542:544],
172: _Opcode_name[551:557], 179: _Opcode_name[544:552],
177: _Opcode_name[557:559], 180: _Opcode_name[552:563],
179: _Opcode_name[559:567], 181: _Opcode_name[563:565],
180: _Opcode_name[567:578], 182: _Opcode_name[565:568],
181: _Opcode_name[578:580], 183: _Opcode_name[568:570],
182: _Opcode_name[580:583], 184: _Opcode_name[570:573],
183: _Opcode_name[583:585], 185: _Opcode_name[573:576],
184: _Opcode_name[585:588], 186: _Opcode_name[576:579],
185: _Opcode_name[588:591], 187: _Opcode_name[579:585],
186: _Opcode_name[591:594], 192: _Opcode_name[585:589],
187: _Opcode_name[594:600], 193: _Opcode_name[589:595],
192: _Opcode_name[600:604], 194: _Opcode_name[595:604],
193: _Opcode_name[604:610], 195: _Opcode_name[604:612],
194: _Opcode_name[610:619], 196: _Opcode_name[612:621],
195: _Opcode_name[619:627], 197: _Opcode_name[621:631],
196: _Opcode_name[627:636], 198: _Opcode_name[631:640],
197: _Opcode_name[636:646], 200: _Opcode_name[640:646],
198: _Opcode_name[646:655], 202: _Opcode_name[646:650],
200: _Opcode_name[655:661], 203: _Opcode_name[650:656],
202: _Opcode_name[661:665], 204: _Opcode_name[656:660],
203: _Opcode_name[665:671], 205: _Opcode_name[660:666],
204: _Opcode_name[671:675], 206: _Opcode_name[666:674],
205: _Opcode_name[675:681], 207: _Opcode_name[674:680],
206: _Opcode_name[681:689], 208: _Opcode_name[680:687],
207: _Opcode_name[689:695], 209: _Opcode_name[687:699],
208: _Opcode_name[695:702], 210: _Opcode_name[699:705],
209: _Opcode_name[702:714], 211: _Opcode_name[705:715],
210: _Opcode_name[714:720], 216: _Opcode_name[715:721],
211: _Opcode_name[720:730], 217: _Opcode_name[721:727],
216: _Opcode_name[730:736], 219: _Opcode_name[727:734],
217: _Opcode_name[736:742],
219: _Opcode_name[742:749],
} }
func (i Opcode) String() string { func (i Opcode) String() string {

View file

@ -71,9 +71,6 @@ type VM struct {
// callback to get interop price // callback to get interop price
getPrice func(*VM, opcode.Opcode, []byte) util.Fixed8 getPrice func(*VM, opcode.Opcode, []byte) util.Fixed8
// callback to get scripts.
getScript func(util.Uint160) ([]byte, bool)
istack *Stack // invocation stack. istack *Stack // invocation stack.
estack *Stack // execution stack. estack *Stack // execution stack.
astack *Stack // alt stack. astack *Stack // alt stack.
@ -95,7 +92,6 @@ type VM struct {
func New() *VM { func New() *VM {
vm := &VM{ vm := &VM{
getInterop: make([]InteropGetterFunc, 0, 3), // 3 functions is typical for our default usage. getInterop: make([]InteropGetterFunc, 0, 3), // 3 functions is typical for our default usage.
getScript: nil,
state: haltState, state: haltState,
istack: NewStack("invocation"), istack: NewStack("invocation"),
@ -204,8 +200,6 @@ func (v *VM) PrintOps() {
desc = fmt.Sprintf("%d (%d/%x)", ctx.ip+int(offset), offset, parameter) desc = fmt.Sprintf("%d (%d/%x)", ctx.ip+int(offset), offset, parameter)
case opcode.SYSCALL: case opcode.SYSCALL:
desc = fmt.Sprintf("%q", parameter) desc = fmt.Sprintf("%q", parameter)
case opcode.APPCALL, opcode.TAILCALL:
desc = fmt.Sprintf("%x", parameter)
default: default:
if utf8.Valid(parameter) { if utf8.Valid(parameter) {
desc = fmt.Sprintf("%x (%q)", parameter, parameter) desc = fmt.Sprintf("%x (%q)", parameter, parameter)
@ -477,11 +471,6 @@ func (v *VM) SetCheckedHash(h []byte) {
copy(v.checkhash, h) copy(v.checkhash, h)
} }
// SetScriptGetter sets the script getter for CALL instructions.
func (v *VM) SetScriptGetter(gs func(util.Uint160) ([]byte, bool)) {
v.getScript = gs
}
// GetInteropID converts instruction parameter to an interop ID. // GetInteropID converts instruction parameter to an interop ID.
func GetInteropID(parameter []byte) uint32 { func GetInteropID(parameter []byte) uint32 {
return binary.LittleEndian.Uint32(parameter) return binary.LittleEndian.Uint32(parameter)
@ -520,25 +509,6 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
} }
} }
switch op {
case opcode.APPCALL, opcode.TAILCALL:
isZero := true
for i := range parameter {
if parameter[i] != 0 {
isZero = false
break
}
}
if !isZero {
break
}
parameter = v.estack.Pop().Bytes()
if !ctx.hasDynamicInvoke {
panic("contract is not allowed to make dynamic invocations")
}
}
if op <= opcode.PUSHINT256 { if op <= opcode.PUSHINT256 {
v.estack.PushVal(emit.BytesToInt(parameter)) v.estack.PushVal(emit.BytesToInt(parameter))
return return
@ -1175,31 +1145,6 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro
panic(fmt.Sprintf("failed to invoke syscall: %s", err)) panic(fmt.Sprintf("failed to invoke syscall: %s", err))
} }
case opcode.APPCALL, opcode.TAILCALL:
if v.getScript == nil {
panic("no getScript callback is set up")
}
if op == opcode.APPCALL {
v.checkInvocationStackSize()
}
hash, err := util.Uint160DecodeBytesBE(parameter)
if err != nil {
panic(err)
}
script, hasDynamicInvoke := v.getScript(hash)
if script == nil {
panic("could not find script")
}
if op == opcode.TAILCALL {
_ = v.istack.Pop()
}
v.loadScriptWithHash(script, hash, hasDynamicInvoke)
case opcode.RET: case opcode.RET:
oldCtx := v.istack.Pop().Value().(*Context) oldCtx := v.istack.Pop().Value().(*Context)
rvcount := oldCtx.rvcount rvcount := oldCtx.rvcount

View file

@ -1413,69 +1413,6 @@ func TestSIGN(t *testing.T) {
t.Run("ByteArray", getTestFuncForVM(prog, 1, []byte{0, 1})) t.Run("ByteArray", getTestFuncForVM(prog, 1, []byte{0, 1}))
} }
func TestAppCall(t *testing.T) {
prog := []byte{byte(opcode.APPCALL)}
hash := util.Uint160{1, 2}
prog = append(prog, hash.BytesBE()...)
prog = append(prog, byte(opcode.RET))
vm := load(prog)
vm.SetScriptGetter(func(in util.Uint160) ([]byte, bool) {
if in.Equals(hash) {
return makeProgram(opcode.DEPTH), true
}
return nil, false
})
vm.estack.PushVal(2)
runVM(t, vm)
elem := vm.estack.Pop() // depth should be 1
assert.Equal(t, int64(1), elem.BigInt().Int64())
}
func TestAppCallDynamicBad(t *testing.T) {
prog := []byte{byte(opcode.APPCALL)}
hash := util.Uint160{}
prog = append(prog, hash.BytesBE()...)
prog = append(prog, byte(opcode.RET))
vm := load(prog)
vm.SetScriptGetter(func(in util.Uint160) ([]byte, bool) {
if in.Equals(hash) {
return makeProgram(opcode.DEPTH), true
}
return nil, false
})
vm.estack.PushVal(2)
vm.estack.PushVal(hash.BytesBE())
checkVMFailed(t, vm)
}
func TestAppCallDynamicGood(t *testing.T) {
prog := []byte{byte(opcode.APPCALL)}
zeroHash := util.Uint160{}
hash := util.Uint160{1, 2, 3}
prog = append(prog, zeroHash.BytesBE()...)
prog = append(prog, byte(opcode.RET))
vm := load(prog)
vm.SetScriptGetter(func(in util.Uint160) ([]byte, bool) {
if in.Equals(hash) {
return makeProgram(opcode.DEPTH), true
}
return nil, false
})
vm.estack.PushVal(42)
vm.estack.PushVal(42)
vm.estack.PushVal(hash.BytesBE())
vm.Context().hasDynamicInvoke = true
runVM(t, vm)
elem := vm.estack.Pop() // depth should be 2
assert.Equal(t, int64(2), elem.BigInt().Int64())
}
func TestSimpleCall(t *testing.T) { func TestSimpleCall(t *testing.T) {
buf := io.NewBufBinWriter() buf := io.NewBufBinWriter()
w := buf.BinWriter w := buf.BinWriter