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":
emit.Syscall(c.prog.BinWriter, "Neo.Crypto.SHA256")
case "AppCall":
numArgs := len(expr.Args) - 1
c.emitReverse(numArgs)
emit.Opcode(c.prog.BinWriter, opcode.APPCALL)
c.emitReverse(len(expr.Args))
buf := c.getByteArray(expr.Args[0])
if len(buf) != 20 {
c.prog.Err = errors.New("invalid script hash")
}
c.prog.WriteBytes(buf)
emit.Syscall(c.prog.BinWriter, "System.Contract.Call")
case "Equals":
emit.Opcode(c.prog.BinWriter, opcode.EQUAL)
case "FromAddress":
@ -1111,16 +1107,14 @@ func (c *codegen) convertBuiltin(expr *ast.CallExpr) {
// transformArgs returns a list of function arguments
// which should be put on stack.
// There are special cases for builtins:
// 1. When using AppCall, script hash is a part of the instruction so
// it should be emitted after APPCALL.
// 2. With FromAddress, parameter conversion is happening at compile-time
// 1. With FromAddress, parameter conversion is happening at compile-time
// 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.
func transformArgs(fun ast.Expr, args []ast.Expr) []ast.Expr {
switch f := fun.(type) {
case *ast.SelectorExpr:
if f.Sel.Name == "AppCall" || f.Sel.Name == "FromAddress" {
if f.Sel.Name == "FromAddress" {
return args[1:]
}
case *ast.Ident:

View file

@ -6,10 +6,17 @@ import (
"testing"
"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/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"
"go.uber.org/zap/zaptest"
)
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) {
srcInner := `
package foo
@ -62,19 +77,13 @@ func TestAppCall(t *testing.T) {
inner, err := compiler.Compile(strings.NewReader(srcInner))
require.NoError(t, err)
ih := hash.Hash160(inner)
getScript := func(u util.Uint160) ([]byte, bool) {
if u.Equals(ih) {
return inner, true
}
return nil, false
}
ic := interop.NewContext(trigger.Application, nil, dao.NewSimple(storage.NewMemoryStore()), nil, nil, nil, zaptest.NewLogger(t))
require.NoError(t, ic.DAO.PutContractState(&state.Contract{Script: inner}))
ih := hash.Hash160(inner)
t.Run("valid script", func(t *testing.T) {
src := getAppCallScript(fmt.Sprintf("%#v", ih.BytesBE()))
v := vmAndCompile(t, src)
v.SetScriptGetter(getScript)
v := spawnVM(t, ic, src)
require.NoError(t, v.Run())
assertResult(t, v, []byte{1, 2, 3, 4})
@ -85,9 +94,7 @@ func TestAppCall(t *testing.T) {
h[0] = ^h[0]
src := getAppCallScript(fmt.Sprintf("%#v", h.BytesBE()))
v := vmAndCompile(t, src)
v.SetScriptGetter(getScript)
v := spawnVM(t, ic, src)
require.Error(t, v.Run())
})
@ -111,9 +118,7 @@ func TestAppCall(t *testing.T) {
}
`
v := vmAndCompile(t, src)
v.SetScriptGetter(getScript)
v := spawnVM(t, ic, src)
require.NoError(t, v.Run())
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 {
case opcode.APPCALL, opcode.TAILCALL:
return toFixed8(10)
case opcode.SYSCALL:
interopID := vm.GetInteropID(parameter)
return getSyscallPrice(v, interopID)

View file

@ -24,11 +24,11 @@ import (
// up for current blockchain.
func SpawnVM(ic *interop.Context) *vm.VM {
vm := vm.New()
bc := ic.Chain.(*Blockchain)
vm.SetScriptGetter(ic.GetContract)
vm.RegisterInteropGetter(getSystemInterop(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
}

View file

@ -19,43 +19,43 @@ func TestInvocationScriptCreationGood(t *testing.T) {
ps Params
script string
}{{
script: "676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
script: "0c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52",
}, {
ps: Params{{Type: StringT, Value: "transfer"}},
script: "0c087472616e73666572676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
script: "0c087472616e736665720c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52",
}, {
ps: Params{{Type: NumberT, Value: 42}},
script: "0c023432676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
script: "0c0234320c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52",
}, {
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"}}}}}},
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"}}}}}},
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"}}}}}},
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"}}}}}},
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"}}}}}},
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"}}}}}},
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}}}}}},
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"}}}}}},
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"}}}}}},
script: "1011c00c0161676f459162ceeb248b071ec157d9e4f6fd26fdbe50",
script: "1011c00c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52",
}}
for _, ps := range paramScripts {
script, err := CreateFunctionInvocationScript(contract, ps.ps)

View file

@ -54,12 +54,12 @@ var rpcTestCases = map[string][]rpcTestCase{
"getapplicationlog": {
{
name: "positive",
params: `["caccb0e0465d87970190c90094e3fb54dbbcfa3e48683bd9a54b1fb834118d87"]`,
params: `["c296c0929350d051b9b40cace54db5a3eac4b730a8851e958795d44918f23c08"]`,
result: func(e *executor) interface{} { return &result.ApplicationLog{} },
check: func(t *testing.T, e *executor, acc interface{}) {
res, ok := acc.(*result.ApplicationLog)
require.True(t, ok)
expectedTxHash, err := util.Uint256DecodeStringLE("caccb0e0465d87970190c90094e3fb54dbbcfa3e48683bd9a54b1fb834118d87")
expectedTxHash, err := util.Uint256DecodeStringLE("c296c0929350d051b9b40cace54db5a3eac4b730a8851e958795d44918f23c08")
require.NoError(t, err)
assert.Equal(t, expectedTxHash, res.TxHash)
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{}) {
res, ok := inv.(*result.Invoke)
require.True(t, ok)
assert.Equal(t, "0c06717765727479676f459162ceeb248b071ec157d9e4f6fd26fdbe50", res.Script)
assert.Equal(t, "0c067177657274790c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52", res.Script)
assert.NotEqual(t, "", res.State)
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.CALLL, opcode.SYSCALL:
numtoread = 4
case opcode.APPCALL, opcode.TAILCALL:
numtoread = 20
default:
if instr <= opcode.PUSHINT256 {
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
// emitted instead.
func AppCall(w *io.BinWriter, scriptHash util.Uint160, tailCall bool) {
op := opcode.APPCALL
if tailCall {
op = opcode.TAILCALL
}
Instruction(w, op, scriptHash.BytesBE())
Bytes(w, scriptHash.BytesBE())
Syscall(w, "System.Contract.Call")
}
// AppCallWithOperationAndArgs emits an APPCALL with the given operation and arguments.

View file

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

View file

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

View file

@ -71,9 +71,6 @@ type VM struct {
// callback to get interop price
getPrice func(*VM, opcode.Opcode, []byte) util.Fixed8
// callback to get scripts.
getScript func(util.Uint160) ([]byte, bool)
istack *Stack // invocation stack.
estack *Stack // execution stack.
astack *Stack // alt stack.
@ -95,7 +92,6 @@ type VM struct {
func New() *VM {
vm := &VM{
getInterop: make([]InteropGetterFunc, 0, 3), // 3 functions is typical for our default usage.
getScript: nil,
state: haltState,
istack: NewStack("invocation"),
@ -204,8 +200,6 @@ func (v *VM) PrintOps() {
desc = fmt.Sprintf("%d (%d/%x)", ctx.ip+int(offset), offset, parameter)
case opcode.SYSCALL:
desc = fmt.Sprintf("%q", parameter)
case opcode.APPCALL, opcode.TAILCALL:
desc = fmt.Sprintf("%x", parameter)
default:
if utf8.Valid(parameter) {
desc = fmt.Sprintf("%x (%q)", parameter, parameter)
@ -477,11 +471,6 @@ func (v *VM) SetCheckedHash(h []byte) {
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.
func GetInteropID(parameter []byte) uint32 {
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 {
v.estack.PushVal(emit.BytesToInt(parameter))
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))
}
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:
oldCtx := v.istack.Pop().Value().(*Context)
rvcount := oldCtx.rvcount

View file

@ -1413,69 +1413,6 @@ func TestSIGN(t *testing.T) {
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) {
buf := io.NewBufBinWriter()
w := buf.BinWriter