diff --git a/cli/smartcontract/smart_contract.go b/cli/smartcontract/smart_contract.go index c18bdb40b4..6819491586 100644 --- a/cli/smartcontract/smart_contract.go +++ b/cli/smartcontract/smart_contract.go @@ -24,6 +24,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/rpc/response/result" "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/smartcontract/nef" "github.com/nspcc-dev/neo-go/pkg/util" @@ -821,7 +822,9 @@ func contractDeploy(ctx *cli.Context) error { return cli.NewExitError(fmt.Errorf("failed to get management contract's hash: %w", err), 1) } buf := io.NewBufBinWriter() - emit.AppCallWithOperationAndArgs(buf.BinWriter, mgmtHash, "deploy", f, manifestBytes) + emit.AppCall(buf.BinWriter, mgmtHash, "deploy", + callflag.ReadStates|callflag.WriteStates|callflag.AllowNotify, + f, manifestBytes) if buf.Err != nil { return cli.NewExitError(fmt.Errorf("failed to create deployment script: %w", buf.Err), 1) } diff --git a/cli/testdata/chain50x2.acc b/cli/testdata/chain50x2.acc index 21277914ff..3c32024903 100644 Binary files a/cli/testdata/chain50x2.acc and b/cli/testdata/chain50x2.acc differ diff --git a/cli/testdata/deploy/main.go b/cli/testdata/deploy/main.go index 525d932d94..dcadc30ea2 100644 --- a/cli/testdata/deploy/main.go +++ b/cli/testdata/deploy/main.go @@ -36,7 +36,7 @@ func Fail() { func Update(script, manifest []byte) { ctx := storage.GetReadOnlyContext() mgmt := storage.Get(ctx, mgmtKey).(interop.Hash160) - contract.Call(mgmt, "update", script, manifest) + contract.Call(mgmt, "update", contract.All, script, manifest) } // GetValue returns stored value. diff --git a/cli/wallet/validator.go b/cli/wallet/validator.go index 3d8a337a03..dbc5f1d333 100644 --- a/cli/wallet/validator.go +++ b/cli/wallet/validator.go @@ -11,6 +11,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/encoding/address" "github.com/nspcc-dev/neo-go/pkg/io" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm/emit" "github.com/nspcc-dev/neo-go/pkg/vm/opcode" @@ -105,7 +106,7 @@ func handleCandidate(ctx *cli.Context, method string) error { return err } w := io.NewBufBinWriter() - emit.AppCallWithOperationAndArgs(w.BinWriter, neoContractHash, method, acc.PrivateKey().PublicKey().Bytes()) + emit.AppCall(w.BinWriter, neoContractHash, method, callflag.WriteStates, acc.PrivateKey().PublicKey().Bytes()) emit.Opcodes(w.BinWriter, opcode.ASSERT) tx, err := c.CreateTxFromScript(w.Bytes(), acc, -1, int64(gas), transaction.Signer{ Account: acc.Contract.ScriptHash(), @@ -167,7 +168,7 @@ func handleVote(ctx *cli.Context) error { return cli.NewExitError(err, 1) } w := io.NewBufBinWriter() - emit.AppCallWithOperationAndArgs(w.BinWriter, neoContractHash, "vote", addr.BytesBE(), pubArg) + emit.AppCall(w.BinWriter, neoContractHash, "vote", callflag.WriteStates, addr.BytesBE(), pubArg) emit.Opcodes(w.BinWriter, opcode.ASSERT) tx, err := c.CreateTxFromScript(w.Bytes(), acc, -1, int64(gas), transaction.Signer{ diff --git a/examples/timer/timer.go b/examples/timer/timer.go index 1d58a20fec..91f52a3ef4 100644 --- a/examples/timer/timer.go +++ b/examples/timer/timer.go @@ -46,7 +46,7 @@ func Migrate(script []byte, manifest []byte) bool { return false } mgmt := storage.Get(ctx, mgmtKey).(interop.Hash160) - contract.Call(mgmt, "update", script, manifest) + contract.Call(mgmt, "update", contract.All, script, manifest) runtime.Log("Contract updated.") return true } @@ -58,7 +58,7 @@ func Tick() bool { ticksLeft = ticksLeft.(int) - 1 if ticksLeft == 0 { runtime.Log("Fired!") - return contract.Call(runtime.GetExecutingScriptHash(), "selfDestroy").(bool) + return contract.Call(runtime.GetExecutingScriptHash(), "selfDestroy", contract.All).(bool) } storage.Put(ctx, ticksKey, ticksLeft) i := binary.Itoa(ticksLeft.(int), 10) @@ -73,7 +73,7 @@ func SelfDestroy() bool { return false } mgmt := storage.Get(ctx, mgmtKey).(interop.Hash160) - contract.Call(mgmt, "destroy") + contract.Call(mgmt, "destroy", contract.All) runtime.Log("Destroyed.") return true } diff --git a/internal/testchain/transaction.go b/internal/testchain/transaction.go index 302cbb694b..e4400e74e3 100644 --- a/internal/testchain/transaction.go +++ b/internal/testchain/transaction.go @@ -13,6 +13,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/io" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/nef" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm/emit" @@ -28,7 +29,7 @@ var ( func NewTransferFromOwner(bc blockchainer.Blockchainer, contractHash, to util.Uint160, amount int64, nonce, validUntil uint32) (*transaction.Transaction, error) { w := io.NewBufBinWriter() - emit.AppCallWithOperationAndArgs(w.BinWriter, contractHash, "transfer", ownerHash, to, amount, nil) + emit.AppCall(w.BinWriter, contractHash, "transfer", callflag.All, ownerHash, to, amount, nil) emit.Opcodes(w.BinWriter, opcode.ASSERT) if w.Err != nil { return nil, w.Err @@ -76,7 +77,7 @@ func NewDeployTx(bc blockchainer.Blockchainer, name string, sender util.Uint160, return nil, util.Uint160{}, err } buf := io.NewBufBinWriter() - emit.AppCallWithOperationAndArgs(buf.BinWriter, bc.ManagementContractHash(), "deploy", neb, rawManifest) + emit.AppCall(buf.BinWriter, bc.ManagementContractHash(), "deploy", callflag.All, neb, rawManifest) if buf.Err != nil { return nil, util.Uint160{}, buf.Err } diff --git a/pkg/compiler/codegen.go b/pkg/compiler/codegen.go index f10e5e4404..d62474aefc 100644 --- a/pkg/compiler/codegen.go +++ b/pkg/compiler/codegen.go @@ -866,14 +866,7 @@ func (c *codegen) Visit(node ast.Node) ast.Visitor { emit.Opcodes(c.prog.BinWriter, opcode.PACK) numArgs -= varSize - 1 } - // CallFlag in CallEx interop should be the last argument - // but this can't be reflected in signature due to varargs. - // It is first in compiler interop though, thus we just need to reverse 1 values less. - if f != nil && isSyscall(f) && f.pkg.Name() == "contract" && f.name == "CallEx" { - c.emitReverse(numArgs - 1) - } else { - c.emitReverse(numArgs) - } + c.emitReverse(numArgs) } // Check builtin first to avoid nil pointer on funcScope! diff --git a/pkg/compiler/interop_test.go b/pkg/compiler/interop_test.go index 57514d3313..cb0504e078 100644 --- a/pkg/compiler/interop_test.go +++ b/pkg/compiler/interop_test.go @@ -17,7 +17,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/nspcc-dev/neo-go/pkg/encoding/address" cinterop "github.com/nspcc-dev/neo-go/pkg/interop" - "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/nef" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "github.com/nspcc-dev/neo-go/pkg/util" @@ -120,7 +120,7 @@ func spawnVM(t *testing.T, ic *interop.Context, src string) *vm.VM { require.NoError(t, err) v := core.SpawnVM(ic) invokeMethod(t, testMainIdent, b, v, di) - v.LoadScriptWithFlags(b, smartcontract.All) + v.LoadScriptWithFlags(b, callflag.All) return v } @@ -150,7 +150,7 @@ func TestAppCall(t *testing.T) { return a + n } func CallInner() int { - return contract.Call(%s, "get42").(int) + return contract.Call(%s, "get42", contract.All).(int) }` srcInner = fmt.Sprintf(srcInner, fmt.Sprintf("%#v", cinterop.Hash160(barH.BytesBE()))) @@ -222,7 +222,7 @@ func TestAppCall(t *testing.T) { func Main() []byte { x := []byte{1, 2} y := []byte{3, 4} - result := contract.Call([]byte(scriptHash), "append", x, y) + result := contract.Call([]byte(scriptHash), "append", contract.All, x, y) return result.([]byte) } ` @@ -241,7 +241,7 @@ func TestAppCall(t *testing.T) { x := []byte{1, 2} y := []byte{3, 4} var addr = []byte(` + fmt.Sprintf("%#v", string(ih.BytesBE())) + `) - result := contract.Call(addr, "append", x, y) + result := contract.Call(addr, "append", contract.All, x, y) return result.([]byte) } ` @@ -257,7 +257,7 @@ func TestAppCall(t *testing.T) { import "github.com/nspcc-dev/neo-go/pkg/interop/contract" func Main() int { var addr = []byte(` + fmt.Sprintf("%#v", string(ih.BytesBE())) + `) - result := contract.Call(addr, "add3", 39) + result := contract.Call(addr, "add3", contract.All, 39) return result.(int) }` @@ -272,7 +272,7 @@ func TestAppCall(t *testing.T) { import ee "github.com/nspcc-dev/neo-go/pkg/interop/contract" func Main() int { var addr = []byte(` + fmt.Sprintf("%#v", string(ih.BytesBE())) + `) - result := ee.Call(addr, "add3", 39) + result := ee.Call(addr, "add3", ee.All, 39) return result.(int) }` v := spawnVM(t, ic, src) @@ -288,7 +288,7 @@ func getAppCallScript(h string) string { func Main() []byte { x := []byte{1, 2} y := []byte{3, 4} - result := contract.Call(` + h + `, "append", x, y) + result := contract.Call(` + h + `, "append", contract.All, x, y) return result.([]byte) } ` @@ -298,7 +298,7 @@ func getCallExScript(h string, flags string) string { return `package foo import "github.com/nspcc-dev/neo-go/pkg/interop/contract" func Main() int { - result := contract.CallEx(` + flags + `, ` + h + `, "callInner") + result := contract.Call(` + h + `, "callInner", ` + flags + `) return result.(int) }` } diff --git a/pkg/compiler/syscall.go b/pkg/compiler/syscall.go index abc5bebe00..c30617ab35 100644 --- a/pkg/compiler/syscall.go +++ b/pkg/compiler/syscall.go @@ -23,7 +23,6 @@ var syscalls = map[string]map[string]string{ }, "contract": { "Call": interopnames.SystemContractCall, - "CallEx": interopnames.SystemContractCallEx, "CreateStandardAccount": interopnames.SystemContractCreateStandardAccount, "IsStandard": interopnames.SystemContractIsStandard, "GetCallFlags": interopnames.SystemContractGetCallFlags, diff --git a/pkg/compiler/syscall_test.go b/pkg/compiler/syscall_test.go index ffab59d837..71328df2a3 100644 --- a/pkg/compiler/syscall_test.go +++ b/pkg/compiler/syscall_test.go @@ -5,7 +5,7 @@ import ( "testing" "github.com/nspcc-dev/neo-go/pkg/interop/contract" - "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -13,14 +13,14 @@ import ( // Checks that changes in `smartcontract` are reflected in compiler interop package. func TestCallFlags(t *testing.T) { - require.EqualValues(t, contract.ReadStates, smartcontract.ReadStates) - require.EqualValues(t, contract.WriteStates, smartcontract.WriteStates) - require.EqualValues(t, contract.AllowCall, smartcontract.AllowCall) - require.EqualValues(t, contract.AllowNotify, smartcontract.AllowNotify) - require.EqualValues(t, contract.States, smartcontract.States) - require.EqualValues(t, contract.ReadOnly, smartcontract.ReadOnly) - require.EqualValues(t, contract.All, smartcontract.All) - require.EqualValues(t, contract.NoneFlag, smartcontract.NoneFlag) + require.EqualValues(t, contract.ReadStates, callflag.ReadStates) + require.EqualValues(t, contract.WriteStates, callflag.WriteStates) + require.EqualValues(t, contract.AllowCall, callflag.AllowCall) + require.EqualValues(t, contract.AllowNotify, callflag.AllowNotify) + require.EqualValues(t, contract.States, callflag.States) + require.EqualValues(t, contract.ReadOnly, callflag.ReadOnly) + require.EqualValues(t, contract.All, callflag.All) + require.EqualValues(t, contract.NoneFlag, callflag.NoneFlag) } func TestStoragePutGet(t *testing.T) { diff --git a/pkg/compiler/vm_test.go b/pkg/compiler/vm_test.go index d2141a9017..bb16a22383 100644 --- a/pkg/compiler/vm_test.go +++ b/pkg/compiler/vm_test.go @@ -9,7 +9,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/compiler" "github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames" "github.com/nspcc-dev/neo-go/pkg/core/state" - "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" @@ -85,7 +85,7 @@ func invokeMethod(t *testing.T, method string, script []byte, v *vm.VM, di *comp } } require.True(t, mainOffset >= 0) - v.LoadScriptWithFlags(script, smartcontract.All) + v.LoadScriptWithFlags(script, callflag.All) v.Jump(v.Context(), mainOffset) if initOffset >= 0 { v.Call(v.Context(), initOffset) diff --git a/pkg/consensus/consensus_test.go b/pkg/consensus/consensus_test.go index 99ae07bb09..f67ef00e84 100644 --- a/pkg/consensus/consensus_test.go +++ b/pkg/consensus/consensus_test.go @@ -20,6 +20,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm/emit" "github.com/nspcc-dev/neo-go/pkg/vm/opcode" @@ -54,10 +55,10 @@ func initServiceNextConsensus(t *testing.T, newAcc *wallet.Account, offset uint3 // Transfer funds to new validator. w := io.NewBufBinWriter() - emit.AppCallWithOperationAndArgs(w.BinWriter, bc.GoverningTokenHash(), "transfer", + emit.AppCall(w.BinWriter, bc.GoverningTokenHash(), "transfer", callflag.All, acc.Contract.ScriptHash().BytesBE(), newPriv.GetScriptHash().BytesBE(), int64(native.NEOTotalSupply), nil) emit.Opcodes(w.BinWriter, opcode.ASSERT) - emit.AppCallWithOperationAndArgs(w.BinWriter, bc.UtilityTokenHash(), "transfer", + emit.AppCall(w.BinWriter, bc.UtilityTokenHash(), "transfer", callflag.All, acc.Contract.ScriptHash().BytesBE(), newPriv.GetScriptHash().BytesBE(), int64(1_000_000_000), nil) emit.Opcodes(w.BinWriter, opcode.ASSERT) require.NoError(t, w.Err) @@ -74,7 +75,7 @@ func initServiceNextConsensus(t *testing.T, newAcc *wallet.Account, offset uint3 // Register new candidate. w.Reset() - emit.AppCallWithOperationAndArgs(w.BinWriter, bc.GoverningTokenHash(), "registerCandidate", newPriv.PublicKey().Bytes()) + emit.AppCall(w.BinWriter, bc.GoverningTokenHash(), "registerCandidate", callflag.All, newPriv.PublicKey().Bytes()) require.NoError(t, w.Err) tx = transaction.New(netmode.UnitTestNet, w.Bytes(), 20_000_000) @@ -92,7 +93,7 @@ func initServiceNextConsensus(t *testing.T, newAcc *wallet.Account, offset uint3 // Vote for new candidate. w.Reset() - emit.AppCallWithOperationAndArgs(w.BinWriter, bc.GoverningTokenHash(), "vote", + emit.AppCall(w.BinWriter, bc.GoverningTokenHash(), "vote", callflag.All, newPriv.GetScriptHash(), newPriv.PublicKey().Bytes()) emit.Opcodes(w.BinWriter, opcode.ASSERT) require.NoError(t, w.Err) diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 69fc2e2ae4..e4fa6b0f27 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -27,13 +27,12 @@ import ( "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/encoding/bigint" "github.com/nspcc-dev/neo-go/pkg/io" - "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm/emit" - "github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" "go.uber.org/zap" ) @@ -624,7 +623,7 @@ func (bc *Blockchain) storeBlock(block *block.Block, txpool *mempool.Pool) error systemInterop := bc.newInteropContext(trigger.Application, cache, block, tx) v := systemInterop.SpawnVM() - v.LoadScriptWithFlags(tx.Script, smartcontract.All) + v.LoadScriptWithFlags(tx.Script, callflag.All) v.SetPriceGetter(bc.getPrice) v.GasLimit = tx.SystemFee @@ -765,7 +764,7 @@ func (bc *Blockchain) storeBlock(block *block.Block, txpool *mempool.Pool) error func (bc *Blockchain) runPersist(script []byte, block *block.Block, cache *dao.Cached, trig trigger.Type) (*state.AppExecResult, error) { systemInterop := bc.newInteropContext(trig, cache, block, nil) v := systemInterop.SpawnVM() - v.LoadScriptWithFlags(script, smartcontract.WriteStates|smartcontract.AllowCall) + v.LoadScriptWithFlags(script, callflag.WriteStates|callflag.AllowCall) v.SetPriceGetter(bc.getPrice) if err := v.Run(); err != nil { return nil, fmt.Errorf("VM has failed: %w", err) @@ -1659,7 +1658,7 @@ func (bc *Blockchain) initVerificationVM(ic *interop.Context, hash util.Uint160, if bc.contracts.ByHash(hash) != nil { return ErrNativeContractWitness } - v.LoadScriptWithFlags(witness.VerificationScript, smartcontract.NoneFlag) + v.LoadScriptWithFlags(witness.VerificationScript, callflag.NoneFlag) } else { cs, err := ic.GetContract(hash) if err != nil { @@ -1670,12 +1669,11 @@ func (bc *Blockchain) initVerificationVM(ic *interop.Context, hash util.Uint160, return ErrInvalidVerificationContract } initMD := cs.Manifest.ABI.GetMethod(manifest.MethodInit) - v.LoadScriptWithHash(cs.NEF.Script, hash, smartcontract.ReadStates) + v.LoadScriptWithHash(cs.NEF.Script, hash, callflag.ReadStates) v.Jump(v.Context(), md.Offset) if cs.ID <= 0 { w := io.NewBufBinWriter() - emit.Opcodes(w.BinWriter, opcode.DEPTH, opcode.PACK) emit.String(w.BinWriter, manifest.MethodVerify) if w.Err != nil { return w.Err diff --git a/pkg/core/blockchain_test.go b/pkg/core/blockchain_test.go index a7e40b18ba..a3a09c7a7d 100644 --- a/pkg/core/blockchain_test.go +++ b/pkg/core/blockchain_test.go @@ -28,6 +28,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm" @@ -272,12 +273,12 @@ func TestVerifyTx(t *testing.T) { if sc.Equals(gasHash) { amount = 1_000_000_000 } - emit.AppCallWithOperationAndArgs(w.BinWriter, sc, "transfer", + emit.AppCall(w.BinWriter, sc, "transfer", callflag.All, neoOwner, a.Contract.ScriptHash(), amount, nil) emit.Opcodes(w.BinWriter, opcode.ASSERT) } } - emit.AppCallWithOperationAndArgs(w.BinWriter, gasHash, "transfer", + emit.AppCall(w.BinWriter, gasHash, "transfer", callflag.All, neoOwner, testchain.CommitteeScriptHash(), int64(1_000_000_000), nil) emit.Opcodes(w.BinWriter, opcode.ASSERT) require.NoError(t, w.Err) @@ -973,7 +974,7 @@ func TestVerifyTx(t *testing.T) { transaction.NotaryServiceFeePerKey + // fee for Notary attribute fee.Opcode(bc.GetBaseExecFee(), // Notary verification script opcode.PUSHDATA1, opcode.RET, // invocation script - opcode.DEPTH, opcode.PACK, opcode.PUSHDATA1, opcode.RET, // arguments for native verification call + opcode.PUSHDATA1, opcode.RET, // arguments for native verification call opcode.PUSHDATA1, opcode.SYSCALL, opcode.RET) + // Neo.Native.Call native.NotaryVerificationPrice // Notary witness verification price tx.Scripts = []transaction.Witness{ diff --git a/pkg/core/helper_test.go b/pkg/core/helper_test.go index dca80e2c00..9cb78bb748 100644 --- a/pkg/core/helper_test.go +++ b/pkg/core/helper_test.go @@ -25,6 +25,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/encoding/fixedn" "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm" @@ -298,7 +299,7 @@ func initBasicChain(t *testing.T, bc *Blockchain) { // Now invoke this contract. script := io.NewBufBinWriter() - emit.AppCallWithOperationAndArgs(script.BinWriter, cHash, "putValue", "testkey", "testvalue") + emit.AppCall(script.BinWriter, cHash, "putValue", callflag.All, "testkey", "testvalue") txInv := transaction.New(testchain.Network(), script.Bytes(), 1*native.GASFactor) txInv.Nonce = getNextNonce() @@ -328,7 +329,7 @@ func initBasicChain(t *testing.T, bc *Blockchain) { require.NoError(t, bc.AddBlock(b)) w := io.NewBufBinWriter() - emit.AppCallWithOperationAndArgs(w.BinWriter, cHash, "init") + emit.AppCall(w.BinWriter, cHash, "init", callflag.All) initTx := transaction.New(testchain.Network(), w.Bytes(), 1*native.GASFactor) initTx.Nonce = getNextNonce() initTx.ValidUntilBlock = validUntilBlock @@ -383,7 +384,7 @@ func initBasicChain(t *testing.T, bc *Blockchain) { func newNEP17Transfer(sc, from, to util.Uint160, amount int64, additionalArgs ...interface{}) *transaction.Transaction { w := io.NewBufBinWriter() - emit.AppCallWithOperationAndArgs(w.BinWriter, sc, "transfer", from, to, amount, additionalArgs) + emit.AppCall(w.BinWriter, sc, "transfer", callflag.All, from, to, amount, additionalArgs) emit.Opcodes(w.BinWriter, opcode.ASSERT) script := w.Bytes() @@ -430,7 +431,7 @@ func addNetworkFee(bc *Blockchain, tx *transaction.Transaction, sender *wallet.A func prepareContractMethodInvoke(chain *Blockchain, sysfee int64, hash util.Uint160, method string, args ...interface{}) (*transaction.Transaction, error) { w := io.NewBufBinWriter() - emit.AppCallWithOperationAndArgs(w.BinWriter, hash, method, args...) + emit.AppCall(w.BinWriter, hash, method, callflag.All, args...) if w.Err != nil { return nil, w.Err } @@ -487,7 +488,7 @@ func invokeContractMethodBy(t *testing.T, chain *Blockchain, signer *wallet.Acco require.Equal(t, 0, len(res[0].Stack)) w := io.NewBufBinWriter() - emit.AppCallWithOperationAndArgs(w.BinWriter, hash, method, args...) + emit.AppCall(w.BinWriter, hash, method, callflag.All, args...) if w.Err != nil { return nil, w.Err } diff --git a/pkg/core/interop/callback/callback.go b/pkg/core/interop/callback/callback.go deleted file mode 100644 index 754616bb40..0000000000 --- a/pkg/core/interop/callback/callback.go +++ /dev/null @@ -1,37 +0,0 @@ -package callback - -import ( - "errors" - - "github.com/nspcc-dev/neo-go/pkg/core/interop" - "github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames" - "github.com/nspcc-dev/neo-go/pkg/vm" - "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" -) - -// Callback is an interface for arbitrary callbacks. -type Callback interface { - // ArgCount returns expected number of arguments. - ArgCount() int - // LoadContext loads context and arguments on stack. - LoadContext(*vm.VM, []stackitem.Item) -} - -// Invoke invokes provided callback. -func Invoke(ic *interop.Context) error { - cb := ic.VM.Estack().Pop().Interop().Value().(Callback) - args := ic.VM.Estack().Pop().Array() - if cb.ArgCount() != len(args) { - return errors.New("invalid argument count") - } - cb.LoadContext(ic.VM, args) - switch t := cb.(type) { - case *MethodCallback: - id := interopnames.ToID([]byte(interopnames.SystemContractCall)) - return ic.SyscallHandler(ic.VM, id) - case *SyscallCallback: - return ic.SyscallHandler(ic.VM, t.desc.ID) - default: - return nil - } -} diff --git a/pkg/core/interop/callback/method.go b/pkg/core/interop/callback/method.go deleted file mode 100644 index 4c6764a896..0000000000 --- a/pkg/core/interop/callback/method.go +++ /dev/null @@ -1,61 +0,0 @@ -package callback - -import ( - "errors" - "fmt" - "strings" - - "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/smartcontract/manifest" - "github.com/nspcc-dev/neo-go/pkg/util" - "github.com/nspcc-dev/neo-go/pkg/vm" - "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" -) - -// MethodCallback represents callback for contract method. -type MethodCallback struct { - contract *state.Contract - method *manifest.Method -} - -var _ Callback = (*MethodCallback)(nil) - -// ArgCount implements Callback interface. -func (s *MethodCallback) ArgCount() int { - return len(s.method.Parameters) -} - -// LoadContext implements Callback interface. -func (s *MethodCallback) LoadContext(v *vm.VM, args []stackitem.Item) { - v.Estack().PushVal(args) - v.Estack().PushVal(s.method.Name) - v.Estack().PushVal(s.contract.Hash.BytesBE()) -} - -// CreateFromMethod creates callback for a contract method. -func CreateFromMethod(ic *interop.Context) error { - rawHash := ic.VM.Estack().Pop().Bytes() - h, err := util.Uint160DecodeBytesBE(rawHash) - if err != nil { - return err - } - cs, err := ic.GetContract(h) - if err != nil { - return fmt.Errorf("contract not found: %w", err) - } - method := string(ic.VM.Estack().Pop().Bytes()) - if strings.HasPrefix(method, "_") { - return errors.New("invalid method name") - } - currCs, err := ic.GetContract(ic.VM.GetCurrentScriptHash()) - if err == nil && !currCs.Manifest.CanCall(h, &cs.Manifest, method) { - return errors.New("method call is not allowed") - } - md := cs.Manifest.ABI.GetMethod(method) - ic.VM.Estack().PushVal(stackitem.NewInterop(&MethodCallback{ - contract: cs, - method: md, - })) - return nil -} diff --git a/pkg/core/interop/callback/pointer.go b/pkg/core/interop/callback/pointer.go deleted file mode 100644 index 2a896b7e64..0000000000 --- a/pkg/core/interop/callback/pointer.go +++ /dev/null @@ -1,42 +0,0 @@ -package callback - -import ( - "github.com/nspcc-dev/neo-go/pkg/core/interop" - "github.com/nspcc-dev/neo-go/pkg/vm" - "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" -) - -// PointerCallback represents callback for a pointer. -type PointerCallback struct { - paramCount int - offset int - context *vm.Context -} - -var _ Callback = (*PointerCallback)(nil) - -// ArgCount implements Callback interface. -func (p *PointerCallback) ArgCount() int { - return p.paramCount -} - -// LoadContext implements Callback interface. -func (p *PointerCallback) LoadContext(v *vm.VM, args []stackitem.Item) { - v.Call(p.context, p.offset) - for i := len(args) - 1; i >= 0; i-- { - v.Estack().PushVal(args[i]) - } -} - -// Create creates callback using pointer and parameters count. -func Create(ic *interop.Context) error { - ctx := ic.VM.Estack().Pop().Item().(*vm.Context) - offset := ic.VM.Estack().Pop().Item().(*stackitem.Pointer).Position() - count := ic.VM.Estack().Pop().BigInt().Int64() - ic.VM.Estack().PushVal(stackitem.NewInterop(&PointerCallback{ - paramCount: int(count), - offset: offset, - context: ctx, - })) - return nil -} diff --git a/pkg/core/interop/callback/syscall.go b/pkg/core/interop/callback/syscall.go deleted file mode 100644 index a7d078104d..0000000000 --- a/pkg/core/interop/callback/syscall.go +++ /dev/null @@ -1,42 +0,0 @@ -package callback - -import ( - "errors" - - "github.com/nspcc-dev/neo-go/pkg/core/interop" - "github.com/nspcc-dev/neo-go/pkg/vm" - "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" -) - -// SyscallCallback represents callback for a syscall. -type SyscallCallback struct { - desc *interop.Function -} - -var _ Callback = (*SyscallCallback)(nil) - -// ArgCount implements Callback interface. -func (p *SyscallCallback) ArgCount() int { - return p.desc.ParamCount -} - -// LoadContext implements Callback interface. -func (p *SyscallCallback) LoadContext(v *vm.VM, args []stackitem.Item) { - for i := len(args) - 1; i >= 0; i-- { - v.Estack().PushVal(args[i]) - } -} - -// CreateFromSyscall creates callback from syscall. -func CreateFromSyscall(ic *interop.Context) error { - id := uint32(ic.VM.Estack().Pop().BigInt().Int64()) - f := ic.GetFunction(id) - if f == nil { - return errors.New("syscall not found") - } - if f.DisallowCallback { - return errors.New("syscall is not allowed to be used in a callback") - } - ic.VM.Estack().PushVal(stackitem.NewInterop(&SyscallCallback{f})) - return nil -} diff --git a/pkg/core/interop/context.go b/pkg/core/interop/context.go index 1fc51562df..a11bb32f8e 100644 --- a/pkg/core/interop/context.go +++ b/pkg/core/interop/context.go @@ -11,7 +11,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/crypto" - "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/smartcontract/nef" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" @@ -70,14 +70,12 @@ type Function struct { ID uint32 Name string Func func(*Context) error - // DisallowCallback is true iff syscall can't be used in a callback. - DisallowCallback bool // ParamCount is a number of function parameters. ParamCount int Price int64 // RequiredFlags is a set of flags which must be set during script invocations. // Default value is NoneFlag i.e. no flags are required. - RequiredFlags smartcontract.CallFlag + RequiredFlags callflag.CallFlag } // Method is a signature for a native method. @@ -88,7 +86,7 @@ type MethodAndPrice struct { Func Method MD *manifest.Method Price int64 - RequiredFlags smartcontract.CallFlag + RequiredFlags callflag.CallFlag } // Contract is an interface for all native contracts. @@ -132,7 +130,7 @@ func NewContractMD(name string) *ContractMD { func (c *ContractMD) AddMethod(md *MethodAndPrice, desc *manifest.Method) { c.Manifest.ABI.Methods = append(c.Manifest.ABI.Methods, *desc) md.MD = desc - desc.Safe = md.RequiredFlags&(smartcontract.All^smartcontract.ReadOnly) == 0 + desc.Safe = md.RequiredFlags&(callflag.All^callflag.ReadOnly) == 0 c.Methods[desc.Name] = *md } diff --git a/pkg/core/interop/contract/call.go b/pkg/core/interop/contract/call.go index 6b33dfbbf2..d0b155544e 100644 --- a/pkg/core/interop/contract/call.go +++ b/pkg/core/interop/contract/call.go @@ -8,33 +8,26 @@ import ( "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/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" ) -// Call calls a contract. +// Call calls a contract with flags. func Call(ic *interop.Context) error { h := ic.VM.Estack().Pop().Bytes() method := ic.VM.Estack().Pop().String() - args := ic.VM.Estack().Pop().Array() - return callExInternal(ic, h, method, args, smartcontract.All) -} - -// CallEx calls a contract with flags. -func CallEx(ic *interop.Context) error { - h := ic.VM.Estack().Pop().Bytes() - method := ic.VM.Estack().Pop().String() - args := ic.VM.Estack().Pop().Array() - flags := smartcontract.CallFlag(int32(ic.VM.Estack().Pop().BigInt().Int64())) - if flags&^smartcontract.All != 0 { + fs := callflag.CallFlag(int32(ic.VM.Estack().Pop().BigInt().Int64())) + if fs&^callflag.All != 0 { return errors.New("call flags out of range") } - return callExInternal(ic, h, method, args, flags) + args := ic.VM.Estack().Pop().Array() + return callInternal(ic, h, method, fs, args) } -func callExInternal(ic *interop.Context, h []byte, name string, args []stackitem.Item, f smartcontract.CallFlag) error { +func callInternal(ic *interop.Context, h []byte, name string, f callflag.CallFlag, args []stackitem.Item) error { u, err := util.Uint160DecodeBytesBE(h) if err != nil { return errors.New("invalid contract hash") @@ -50,8 +43,12 @@ func callExInternal(ic *interop.Context, h []byte, name string, args []stackitem if md == nil { return errors.New("method not found") } + hasReturn := md.ReturnType != smartcontract.VoidType + if !hasReturn { + ic.VM.Estack().PushVal(stackitem.Null{}) + } if md.Safe { - f &^= smartcontract.WriteStates + f &^= callflag.WriteStates } else if ctx := ic.VM.Context(); ctx != nil && ctx.IsDeployed() { curr, err := ic.GetContract(ic.VM.GetCurrentScriptHash()) if err == nil { @@ -60,18 +57,12 @@ func callExInternal(ic *interop.Context, h []byte, name string, args []stackitem } } } - return CallExInternal(ic, cs, name, args, f, vm.EnsureNotEmpty) -} - -// CallExInternal calls a contract with flags and can't be invoked directly by user. -func CallExInternal(ic *interop.Context, cs *state.Contract, - name string, args []stackitem.Item, f smartcontract.CallFlag, checkReturn vm.CheckReturnState) error { - return callExFromNative(ic, ic.VM.GetCurrentScriptHash(), cs, name, args, f, checkReturn) + return callExFromNative(ic, ic.VM.GetCurrentScriptHash(), cs, name, args, f, hasReturn) } // callExFromNative calls a contract with flags using provided calling hash. func callExFromNative(ic *interop.Context, caller util.Uint160, cs *state.Contract, - name string, args []stackitem.Item, f smartcontract.CallFlag, checkReturn vm.CheckReturnState) error { + name string, args []stackitem.Item, f callflag.CallFlag, hasReturn bool) error { md := cs.Manifest.ABI.GetMethod(name) if md == nil { return fmt.Errorf("method '%s' not found", name) @@ -82,7 +73,7 @@ func callExFromNative(ic *interop.Context, caller util.Uint160, cs *state.Contra } ic.VM.Invocations[cs.Hash]++ - ic.VM.LoadScriptWithCallingHash(caller, cs.NEF.Script, cs.Hash, ic.VM.Context().GetCallFlags()&f) + ic.VM.LoadScriptWithCallingHash(caller, cs.NEF.Script, cs.Hash, ic.VM.Context().GetCallFlags()&f, true, uint16(len(args))) var isNative bool for i := range ic.Natives { if ic.Natives[i].Metadata().Hash.Equals(cs.Hash) { @@ -90,17 +81,20 @@ func callExFromNative(ic *interop.Context, caller util.Uint160, cs *state.Contra break } } + for i := len(args) - 1; i >= 0; i-- { + ic.VM.Estack().PushVal(args[i]) + } if isNative { - ic.VM.Estack().PushVal(args) ic.VM.Estack().PushVal(name) } else { - for i := len(args) - 1; i >= 0; i-- { - ic.VM.Estack().PushVal(args[i]) - } // use Jump not Call here because context was loaded in LoadScript above. ic.VM.Jump(ic.VM.Context(), md.Offset) } - ic.VM.Context().CheckReturn = checkReturn + if hasReturn { + ic.VM.Context().RetCount = 1 + } else { + ic.VM.Context().RetCount = 0 + } md = cs.Manifest.ABI.GetMethod(manifest.MethodInit) if md != nil { @@ -114,9 +108,9 @@ func callExFromNative(ic *interop.Context, caller util.Uint160, cs *state.Contra var ErrNativeCall = errors.New("error during call from native") // CallFromNative performs synchronous call from native contract. -func CallFromNative(ic *interop.Context, caller util.Uint160, cs *state.Contract, method string, args []stackitem.Item, checkReturn vm.CheckReturnState) error { +func CallFromNative(ic *interop.Context, caller util.Uint160, cs *state.Contract, method string, args []stackitem.Item, hasReturn bool) error { startSize := ic.VM.Istack().Len() - if err := callExFromNative(ic, caller, cs, method, args, smartcontract.All, checkReturn); err != nil { + if err := callExFromNative(ic, caller, cs, method, args, callflag.All, hasReturn); err != nil { return err } diff --git a/pkg/core/interop/interopnames/names.go b/pkg/core/interop/interopnames/names.go index a96b8dee1b..1d642cc9b2 100644 --- a/pkg/core/interop/interopnames/names.go +++ b/pkg/core/interop/interopnames/names.go @@ -20,7 +20,6 @@ const ( SystemCallbackCreateFromSyscall = "System.Callback.CreateFromSyscall" SystemCallbackInvoke = "System.Callback.Invoke" SystemContractCall = "System.Contract.Call" - SystemContractCallEx = "System.Contract.CallEx" SystemContractCallNative = "System.Contract.CallNative" SystemContractCreateStandardAccount = "System.Contract.CreateStandardAccount" SystemContractIsStandard = "System.Contract.IsStandard" @@ -86,7 +85,6 @@ var names = []string{ SystemCallbackCreateFromSyscall, SystemCallbackInvoke, SystemContractCall, - SystemContractCallEx, SystemContractCallNative, SystemContractCreateStandardAccount, SystemContractIsStandard, diff --git a/pkg/core/interop/runtime/engine_test.go b/pkg/core/interop/runtime/engine_test.go index 18be7d1ef9..a6a23ae3e7 100644 --- a/pkg/core/interop/runtime/engine_test.go +++ b/pkg/core/interop/runtime/engine_test.go @@ -11,7 +11,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/block" "github.com/nspcc-dev/neo-go/pkg/core/interop" "github.com/nspcc-dev/neo-go/pkg/crypto/hash" - "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm" @@ -64,7 +64,7 @@ func TestGetScriptHash(t *testing.T) { } ic := &interop.Context{VM: vm.New()} - ic.VM.LoadScriptWithFlags(scripts[0].s, smartcontract.All) + ic.VM.LoadScriptWithFlags(scripts[0].s, callflag.All) require.NoError(t, GetEntryScriptHash(ic)) checkStack(t, ic.VM, scripts[0].h.BytesBE()) require.NoError(t, GetCallingScriptHash(ic)) @@ -72,7 +72,7 @@ func TestGetScriptHash(t *testing.T) { require.NoError(t, GetExecutingScriptHash(ic)) checkStack(t, ic.VM, scripts[0].h.BytesBE()) - ic.VM.LoadScriptWithHash(scripts[1].s, scripts[1].h, smartcontract.All) + ic.VM.LoadScriptWithHash(scripts[1].s, scripts[1].h, callflag.All) require.NoError(t, GetEntryScriptHash(ic)) checkStack(t, ic.VM, scripts[0].h.BytesBE()) require.NoError(t, GetCallingScriptHash(ic)) @@ -108,7 +108,7 @@ func TestLog(t *testing.T) { t.Run("big message", func(t *testing.T) { ic := &interop.Context{Log: zap.NewNop(), VM: vm.New()} - ic.VM.LoadScriptWithHash([]byte{1}, h, smartcontract.All) + ic.VM.LoadScriptWithHash([]byte{1}, h, callflag.All) ic.VM.Estack().PushVal(string(make([]byte, MaxNotificationSize+1))) require.Error(t, Log(ic)) }) @@ -116,7 +116,7 @@ func TestLog(t *testing.T) { t.Run("good", func(t *testing.T) { log, buf := newL(zapcore.InfoLevel) ic := &interop.Context{Log: log, VM: vm.New()} - ic.VM.LoadScriptWithHash([]byte{1}, h, smartcontract.All) + ic.VM.LoadScriptWithHash([]byte{1}, h, callflag.All) ic.VM.Estack().PushVal("hello") require.NoError(t, Log(ic)) @@ -137,7 +137,7 @@ func TestNotify(t *testing.T) { h := random.Uint160() newIC := func(name string, args interface{}) *interop.Context { ic := &interop.Context{VM: vm.New()} - ic.VM.LoadScriptWithHash([]byte{1}, h, smartcontract.NoneFlag) + ic.VM.LoadScriptWithHash([]byte{1}, h, callflag.NoneFlag) ic.VM.Estack().PushVal(args) ic.VM.Estack().PushVal(name) return ic diff --git a/pkg/core/interop/runtime/util_test.go b/pkg/core/interop/runtime/util_test.go index e48238d569..b614188a32 100644 --- a/pkg/core/interop/runtime/util_test.go +++ b/pkg/core/interop/runtime/util_test.go @@ -6,7 +6,7 @@ import ( "github.com/nspcc-dev/neo-go/internal/random" "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/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" @@ -103,13 +103,13 @@ func TestRuntimeGetInvocationCounter(t *testing.T) { t.Run("No invocations", func(t *testing.T) { h1 := h h1[0] ^= 0xFF - ic.VM.LoadScriptWithHash([]byte{1}, h1, smartcontract.NoneFlag) + ic.VM.LoadScriptWithHash([]byte{1}, h1, callflag.NoneFlag) // do not return an error in this case. require.NoError(t, GetInvocationCounter(ic)) checkStack(t, ic.VM, 1) }) t.Run("NonZero", func(t *testing.T) { - ic.VM.LoadScriptWithHash([]byte{1}, h, smartcontract.NoneFlag) + ic.VM.LoadScriptWithHash([]byte{1}, h, callflag.NoneFlag) require.NoError(t, GetInvocationCounter(ic)) checkStack(t, ic.VM, 42) }) diff --git a/pkg/core/interop/runtime/witness.go b/pkg/core/interop/runtime/witness.go index 5123128a08..900f5d282e 100644 --- a/pkg/core/interop/runtime/witness.go +++ b/pkg/core/interop/runtime/witness.go @@ -8,7 +8,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/interop" "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" - "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm" ) @@ -53,7 +53,7 @@ func checkScope(ic *interop.Context, tx *transaction.Transaction, v *vm.VM, hash if callingScriptHash.Equals(util.Uint160{}) { return false, nil } - if !v.Context().GetCallFlags().Has(smartcontract.ReadStates) { + if !v.Context().GetCallFlags().Has(callflag.ReadStates) { return false, errors.New("missing ReadStates call flag") } cs, err := ic.GetContract(callingScriptHash) diff --git a/pkg/core/interop_system_test.go b/pkg/core/interop_system_test.go index b0ff75c2ac..d4a333f585 100644 --- a/pkg/core/interop_system_test.go +++ b/pkg/core/interop_system_test.go @@ -9,7 +9,6 @@ import ( "github.com/nspcc-dev/neo-go/internal/random" "github.com/nspcc-dev/neo-go/pkg/config/netmode" "github.com/nspcc-dev/neo-go/pkg/core/interop" - "github.com/nspcc-dev/neo-go/pkg/core/interop/callback" "github.com/nspcc-dev/neo-go/pkg/core/interop/contract" "github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames" "github.com/nspcc-dev/neo-go/pkg/core/interop/runtime" @@ -20,10 +19,10 @@ import ( "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/smartcontract/nef" "github.com/nspcc-dev/neo-go/pkg/util" - "github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm/emit" "github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" @@ -418,7 +417,7 @@ func TestStorageDelete(t *testing.T) { defer bc.Close() require.NoError(t, bc.contracts.Management.PutContractState(ic.DAO, cs)) - v.LoadScriptWithHash(cs.NEF.Script, cs.Hash, smartcontract.All) + v.LoadScriptWithHash(cs.NEF.Script, cs.Hash, callflag.All) put := func(key, value string, flag int) { v.Estack().PushVal(flag) v.Estack().PushVal(value) @@ -503,13 +502,12 @@ func getTestContractState(bc *Blockchain) (*state.Contract, *state.Contract) { updateOff := w.Len() emit.Int(w.BinWriter, 2) emit.Opcodes(w.BinWriter, opcode.PACK) - emit.String(w.BinWriter, "update") - emit.AppCall(w.BinWriter, mgmtHash) + emit.AppCallNoArgs(w.BinWriter, mgmtHash, "update", callflag.All) + emit.Opcodes(w.BinWriter, opcode.DROP) emit.Opcodes(w.BinWriter, opcode.RET) destroyOff := w.Len() - emit.Opcodes(w.BinWriter, opcode.NEWARRAY0) - emit.String(w.BinWriter, "destroy") - emit.AppCall(w.BinWriter, mgmtHash) + emit.AppCall(w.BinWriter, mgmtHash, "destroy", callflag.All) + emit.Opcodes(w.BinWriter, opcode.DROP) emit.Opcodes(w.BinWriter, opcode.RET) invalidStackOff := w.Len() emit.Opcodes(w.BinWriter, opcode.NEWARRAY0, opcode.DUP, opcode.DUP, opcode.APPEND, opcode.NEWMAP) @@ -655,14 +653,14 @@ func getTestContractState(bc *Blockchain) (*state.Contract, *state.Contract) { func loadScript(ic *interop.Context, script []byte, args ...interface{}) { ic.SpawnVM() - ic.VM.LoadScriptWithFlags(script, smartcontract.AllowCall) + ic.VM.LoadScriptWithFlags(script, callflag.AllowCall) for i := range args { ic.VM.Estack().PushVal(args[i]) } ic.VM.GasLimit = -1 } -func loadScriptWithHashAndFlags(ic *interop.Context, script []byte, hash util.Uint160, f smartcontract.CallFlag, args ...interface{}) { +func loadScriptWithHashAndFlags(ic *interop.Context, script []byte, hash util.Uint160, f callflag.CallFlag, args ...interface{}) { ic.SpawnVM() ic.VM.LoadScriptWithHash(script, hash, f) for i := range args { @@ -686,6 +684,7 @@ func TestContractCall(t *testing.T) { t.Run("Good", func(t *testing.T) { loadScript(ic, currScript, 42) ic.VM.Estack().PushVal(addArgs) + ic.VM.Estack().PushVal(callflag.All) ic.VM.Estack().PushVal("add") ic.VM.Estack().PushVal(h.BytesBE()) require.NoError(t, contract.Call(ic)) @@ -697,16 +696,16 @@ func TestContractCall(t *testing.T) { t.Run("CallExInvalidFlag", func(t *testing.T) { loadScript(ic, currScript, 42) - ic.VM.Estack().PushVal(byte(0xFF)) ic.VM.Estack().PushVal(addArgs) + ic.VM.Estack().PushVal(byte(0xFF)) ic.VM.Estack().PushVal("add") ic.VM.Estack().PushVal(h.BytesBE()) - require.Error(t, contract.CallEx(ic)) + require.Error(t, contract.Call(ic)) }) runInvalid := func(args ...interface{}) func(t *testing.T) { return func(t *testing.T) { - loadScriptWithHashAndFlags(ic, currScript, h, smartcontract.All, 42) + loadScriptWithHashAndFlags(ic, currScript, h, callflag.All, 42) for i := range args { ic.VM.Estack().PushVal(args[i]) } @@ -736,6 +735,7 @@ func TestContractCall(t *testing.T) { t.Run("Many", func(t *testing.T) { loadScript(ic, currScript, 42) ic.VM.Estack().PushVal(stackitem.NewArray(nil)) + ic.VM.Estack().PushVal(callflag.All) ic.VM.Estack().PushVal("invalidReturn") ic.VM.Estack().PushVal(h.BytesBE()) require.NoError(t, contract.Call(ic)) @@ -744,6 +744,7 @@ func TestContractCall(t *testing.T) { t.Run("Void", func(t *testing.T) { loadScript(ic, currScript, 42) ic.VM.Estack().PushVal(stackitem.NewArray(nil)) + ic.VM.Estack().PushVal(callflag.All) ic.VM.Estack().PushVal("justReturn") ic.VM.Estack().PushVal(h.BytesBE()) require.NoError(t, contract.Call(ic)) @@ -757,6 +758,7 @@ func TestContractCall(t *testing.T) { t.Run("IsolatedStack", func(t *testing.T) { loadScript(ic, currScript, 42) ic.VM.Estack().PushVal(stackitem.NewArray(nil)) + ic.VM.Estack().PushVal(callflag.All) ic.VM.Estack().PushVal("drop") ic.VM.Estack().PushVal(h.BytesBE()) require.NoError(t, contract.Call(ic)) @@ -768,6 +770,7 @@ func TestContractCall(t *testing.T) { loadScript(ic, currScript, 42) ic.VM.Estack().PushVal(stackitem.NewArray([]stackitem.Item{stackitem.Make(5)})) + ic.VM.Estack().PushVal(callflag.All) ic.VM.Estack().PushVal("add3") ic.VM.Estack().PushVal(h.BytesBE()) require.NoError(t, contract.Call(ic)) @@ -782,153 +785,9 @@ func TestContractGetCallFlags(t *testing.T) { v, ic, bc := createVM(t) defer bc.Close() - v.LoadScriptWithHash([]byte{byte(opcode.RET)}, util.Uint160{1, 2, 3}, smartcontract.All) + v.LoadScriptWithHash([]byte{byte(opcode.RET)}, util.Uint160{1, 2, 3}, callflag.All) require.NoError(t, contractGetCallFlags(ic)) - require.Equal(t, int64(smartcontract.All), v.Estack().Pop().Value().(*big.Int).Int64()) -} - -func TestPointerCallback(t *testing.T) { - _, ic, bc := createVM(t) - defer bc.Close() - - script := []byte{ - byte(opcode.NOP), byte(opcode.INC), byte(opcode.RET), - byte(opcode.DIV), byte(opcode.RET), - } - t.Run("Good", func(t *testing.T) { - loadScript(ic, script, 2, stackitem.NewPointer(3, script)) - ic.VM.Estack().PushVal(ic.VM.Context()) - require.NoError(t, callback.Create(ic)) - - args := stackitem.NewArray([]stackitem.Item{stackitem.Make(3), stackitem.Make(12)}) - ic.VM.Estack().InsertAt(vm.NewElement(args), 1) - require.NoError(t, callback.Invoke(ic)) - - require.NoError(t, ic.VM.Run()) - require.Equal(t, 1, ic.VM.Estack().Len()) - require.Equal(t, big.NewInt(5), ic.VM.Estack().Pop().Item().Value()) - }) - t.Run("Invalid", func(t *testing.T) { - t.Run("NotEnoughParameters", func(t *testing.T) { - loadScript(ic, script, 2, stackitem.NewPointer(3, script)) - ic.VM.Estack().PushVal(ic.VM.Context()) - require.NoError(t, callback.Create(ic)) - - args := stackitem.NewArray([]stackitem.Item{stackitem.Make(3)}) - ic.VM.Estack().InsertAt(vm.NewElement(args), 1) - require.Error(t, callback.Invoke(ic)) - }) - }) - -} - -func TestMethodCallback(t *testing.T) { - _, ic, bc := createVM(t) - defer bc.Close() - - cs, currCs := getTestContractState(bc) - require.NoError(t, bc.contracts.Management.PutContractState(ic.DAO, cs)) - require.NoError(t, bc.contracts.Management.PutContractState(ic.DAO, currCs)) - - ic.Functions = append(ic.Functions, systemInterops) - rawHash := cs.Hash.BytesBE() - - t.Run("Invalid", func(t *testing.T) { - runInvalid := func(args ...interface{}) func(t *testing.T) { - return func(t *testing.T) { - loadScript(ic, currCs.NEF.Script, 42) - for i := range args { - ic.VM.Estack().PushVal(args[i]) - } - require.Error(t, callback.CreateFromMethod(ic)) - } - } - t.Run("Hash", runInvalid("add", rawHash[1:])) - t.Run("MissingHash", runInvalid("add", util.Uint160{}.BytesBE())) - t.Run("MissingMethod", runInvalid("sub", rawHash)) - t.Run("DisallowedMethod", runInvalid("ret7", rawHash)) - t.Run("Initialize", runInvalid("_initialize", rawHash)) - t.Run("NotEnoughArguments", func(t *testing.T) { - loadScript(ic, currCs.NEF.Script, 42, "add", rawHash) - require.NoError(t, callback.CreateFromMethod(ic)) - - ic.VM.Estack().InsertAt(vm.NewElement(stackitem.NewArray([]stackitem.Item{stackitem.Make(1)})), 1) - require.Error(t, callback.Invoke(ic)) - }) - t.Run("CallIsNotAllowed", func(t *testing.T) { - ic.SpawnVM() - ic.VM.Load(currCs.NEF.Script) - ic.VM.Estack().PushVal("add") - ic.VM.Estack().PushVal(rawHash) - require.NoError(t, callback.CreateFromMethod(ic)) - - args := stackitem.NewArray([]stackitem.Item{stackitem.Make(1), stackitem.Make(5)}) - ic.VM.Estack().InsertAt(vm.NewElement(args), 1) - require.Error(t, callback.Invoke(ic)) - }) - }) - - t.Run("Good", func(t *testing.T) { - loadScript(ic, currCs.NEF.Script, 42, "add", rawHash) - require.NoError(t, callback.CreateFromMethod(ic)) - - args := stackitem.NewArray([]stackitem.Item{stackitem.Make(1), stackitem.Make(5)}) - ic.VM.Estack().InsertAt(vm.NewElement(args), 1) - - require.NoError(t, callback.Invoke(ic)) - require.NoError(t, ic.VM.Run()) - require.Equal(t, 2, ic.VM.Estack().Len()) - require.Equal(t, big.NewInt(6), ic.VM.Estack().Pop().Item().Value()) - require.Equal(t, big.NewInt(42), ic.VM.Estack().Pop().Item().Value()) - }) -} -func TestSyscallCallback(t *testing.T) { - _, ic, bc := createVM(t) - defer bc.Close() - - ic.Functions = append(ic.Functions, []interop.Function{ - { - ID: 0x42, - Func: func(ic *interop.Context) error { - a := ic.VM.Estack().Pop().BigInt() - b := ic.VM.Estack().Pop().BigInt() - ic.VM.Estack().PushVal(new(big.Int).Add(a, b)) - return nil - }, - ParamCount: 2, - }, - { - ID: 0x53, - Func: func(_ *interop.Context) error { return nil }, - DisallowCallback: true, - }, - }) - - t.Run("Good", func(t *testing.T) { - args := stackitem.NewArray([]stackitem.Item{stackitem.Make(12), stackitem.Make(30)}) - loadScript(ic, []byte{byte(opcode.RET)}, args, 0x42) - require.NoError(t, callback.CreateFromSyscall(ic)) - require.NoError(t, callback.Invoke(ic)) - require.Equal(t, 1, ic.VM.Estack().Len()) - require.Equal(t, big.NewInt(42), ic.VM.Estack().Pop().Item().Value()) - }) - - t.Run("Invalid", func(t *testing.T) { - t.Run("InvalidParameterCount", func(t *testing.T) { - args := stackitem.NewArray([]stackitem.Item{stackitem.Make(12)}) - loadScript(ic, []byte{byte(opcode.RET)}, args, 0x42) - require.NoError(t, callback.CreateFromSyscall(ic)) - require.Error(t, callback.Invoke(ic)) - }) - t.Run("MissingSyscall", func(t *testing.T) { - loadScript(ic, []byte{byte(opcode.RET)}, stackitem.NewArray(nil), 0x43) - require.Error(t, callback.CreateFromSyscall(ic)) - }) - t.Run("Disallowed", func(t *testing.T) { - loadScript(ic, []byte{byte(opcode.RET)}, stackitem.NewArray(nil), 0x53) - require.Error(t, callback.CreateFromSyscall(ic)) - }) - }) + require.Equal(t, int64(callflag.All), v.Estack().Pop().Value().(*big.Int).Int64()) } func TestRuntimeCheckWitness(t *testing.T) { @@ -955,7 +814,7 @@ func TestRuntimeCheckWitness(t *testing.T) { check(t, ic, []byte{1, 2, 3}, true) }) t.Run("script container is not a transaction", func(t *testing.T) { - loadScriptWithHashAndFlags(ic, script, scriptHash, smartcontract.ReadStates) + loadScriptWithHashAndFlags(ic, script, scriptHash, callflag.ReadStates) check(t, ic, random.Uint160().BytesBE(), true) }) t.Run("check scope", func(t *testing.T) { @@ -972,8 +831,8 @@ func TestRuntimeCheckWitness(t *testing.T) { } ic.Container = tx callingScriptHash := scriptHash - loadScriptWithHashAndFlags(ic, script, callingScriptHash, smartcontract.All) - ic.VM.LoadScriptWithHash([]byte{0x1}, random.Uint160(), smartcontract.AllowCall) + loadScriptWithHashAndFlags(ic, script, callingScriptHash, callflag.All) + ic.VM.LoadScriptWithHash([]byte{0x1}, random.Uint160(), callflag.AllowCall) check(t, ic, hash.BytesBE(), true) }) t.Run("CustomGroups, unknown contract", func(t *testing.T) { @@ -989,8 +848,8 @@ func TestRuntimeCheckWitness(t *testing.T) { } ic.Container = tx callingScriptHash := scriptHash - loadScriptWithHashAndFlags(ic, script, callingScriptHash, smartcontract.All) - ic.VM.LoadScriptWithHash([]byte{0x1}, random.Uint160(), smartcontract.ReadStates) + loadScriptWithHashAndFlags(ic, script, callingScriptHash, callflag.All) + ic.VM.LoadScriptWithHash([]byte{0x1}, random.Uint160(), callflag.ReadStates) check(t, ic, hash.BytesBE(), true) }) }) @@ -999,16 +858,16 @@ func TestRuntimeCheckWitness(t *testing.T) { t.Run("calling scripthash", func(t *testing.T) { t.Run("hashed witness", func(t *testing.T) { callingScriptHash := scriptHash - loadScriptWithHashAndFlags(ic, script, callingScriptHash, smartcontract.All) - ic.VM.LoadScriptWithHash([]byte{0x1}, random.Uint160(), smartcontract.All) + loadScriptWithHashAndFlags(ic, script, callingScriptHash, callflag.All) + ic.VM.LoadScriptWithHash([]byte{0x1}, random.Uint160(), callflag.All) check(t, ic, callingScriptHash.BytesBE(), false, true) }) t.Run("keyed witness", func(t *testing.T) { pk, err := keys.NewPrivateKey() require.NoError(t, err) callingScriptHash := pk.PublicKey().GetScriptHash() - loadScriptWithHashAndFlags(ic, script, callingScriptHash, smartcontract.All) - ic.VM.LoadScriptWithHash([]byte{0x1}, random.Uint160(), smartcontract.All) + loadScriptWithHashAndFlags(ic, script, callingScriptHash, callflag.All) + ic.VM.LoadScriptWithHash([]byte{0x1}, random.Uint160(), callflag.All) check(t, ic, pk.PublicKey().Bytes(), false, true) }) }) @@ -1023,7 +882,7 @@ func TestRuntimeCheckWitness(t *testing.T) { }, }, } - loadScriptWithHashAndFlags(ic, script, scriptHash, smartcontract.ReadStates) + loadScriptWithHashAndFlags(ic, script, scriptHash, callflag.ReadStates) ic.Container = tx check(t, ic, hash.BytesBE(), false, true) }) @@ -1037,7 +896,7 @@ func TestRuntimeCheckWitness(t *testing.T) { }, }, } - loadScriptWithHashAndFlags(ic, script, scriptHash, smartcontract.ReadStates) + loadScriptWithHashAndFlags(ic, script, scriptHash, callflag.ReadStates) ic.Container = tx check(t, ic, hash.BytesBE(), false, true) }) @@ -1052,7 +911,7 @@ func TestRuntimeCheckWitness(t *testing.T) { }, }, } - loadScriptWithHashAndFlags(ic, script, scriptHash, smartcontract.ReadStates) + loadScriptWithHashAndFlags(ic, script, scriptHash, callflag.ReadStates) ic.Container = tx check(t, ic, hash.BytesBE(), false, true) }) @@ -1068,7 +927,7 @@ func TestRuntimeCheckWitness(t *testing.T) { }, }, } - loadScriptWithHashAndFlags(ic, script, scriptHash, smartcontract.ReadStates) + loadScriptWithHashAndFlags(ic, script, scriptHash, callflag.ReadStates) ic.Container = tx check(t, ic, hash.BytesBE(), false, false) }) @@ -1098,8 +957,8 @@ func TestRuntimeCheckWitness(t *testing.T) { }, } require.NoError(t, bc.contracts.Management.PutContractState(ic.DAO, contractState)) - loadScriptWithHashAndFlags(ic, contractScript, contractScriptHash, smartcontract.All) - ic.VM.LoadScriptWithHash([]byte{0x1}, random.Uint160(), smartcontract.ReadStates) + loadScriptWithHashAndFlags(ic, contractScript, contractScriptHash, callflag.All) + ic.VM.LoadScriptWithHash([]byte{0x1}, random.Uint160(), callflag.ReadStates) ic.Container = tx check(t, ic, targetHash.BytesBE(), false, true) }) @@ -1114,7 +973,7 @@ func TestRuntimeCheckWitness(t *testing.T) { }, }, } - loadScriptWithHashAndFlags(ic, script, scriptHash, smartcontract.ReadStates) + loadScriptWithHashAndFlags(ic, script, scriptHash, callflag.ReadStates) ic.Container = tx check(t, ic, hash.BytesBE(), false, false) }) diff --git a/pkg/core/interops.go b/pkg/core/interops.go index 4e061ac8c8..41cbb1f25e 100644 --- a/pkg/core/interops.go +++ b/pkg/core/interops.go @@ -10,7 +10,6 @@ package core import ( "github.com/nspcc-dev/neo-go/pkg/core/interop" "github.com/nspcc-dev/neo-go/pkg/core/interop/binary" - "github.com/nspcc-dev/neo-go/pkg/core/interop/callback" "github.com/nspcc-dev/neo-go/pkg/core/interop/contract" "github.com/nspcc-dev/neo-go/pkg/core/interop/crypto" "github.com/nspcc-dev/neo-go/pkg/core/interop/enumerator" @@ -19,7 +18,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/interop/json" "github.com/nspcc-dev/neo-go/pkg/core/interop/runtime" "github.com/nspcc-dev/neo-go/pkg/core/native" - "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/vm" ) @@ -42,42 +41,36 @@ var systemInterops = []interop.Function{ {Name: interopnames.SystemBinaryItoa, Func: binary.Itoa, Price: 1 << 12, ParamCount: 2}, {Name: interopnames.SystemBinarySerialize, Func: binary.Serialize, Price: 1 << 12, ParamCount: 1}, {Name: interopnames.SystemBlockchainGetBlock, Func: bcGetBlock, Price: 1 << 16, - RequiredFlags: smartcontract.ReadStates, ParamCount: 1}, + RequiredFlags: callflag.ReadStates, ParamCount: 1}, {Name: interopnames.SystemBlockchainGetHeight, Func: bcGetHeight, Price: 1 << 4, - RequiredFlags: smartcontract.ReadStates}, + RequiredFlags: callflag.ReadStates}, {Name: interopnames.SystemBlockchainGetTransaction, Func: bcGetTransaction, Price: 1 << 15, - RequiredFlags: smartcontract.ReadStates, ParamCount: 1}, + RequiredFlags: callflag.ReadStates, ParamCount: 1}, {Name: interopnames.SystemBlockchainGetTransactionFromBlock, Func: bcGetTransactionFromBlock, Price: 1 << 15, - RequiredFlags: smartcontract.ReadStates, ParamCount: 2}, + RequiredFlags: callflag.ReadStates, ParamCount: 2}, {Name: interopnames.SystemBlockchainGetTransactionHeight, Func: bcGetTransactionHeight, Price: 1 << 15, - RequiredFlags: smartcontract.ReadStates, ParamCount: 1}, - {Name: interopnames.SystemCallbackCreate, Func: callback.Create, Price: 1 << 4, ParamCount: 3, DisallowCallback: true}, - {Name: interopnames.SystemCallbackCreateFromMethod, Func: callback.CreateFromMethod, Price: 1 << 15, ParamCount: 2, DisallowCallback: true}, - {Name: interopnames.SystemCallbackCreateFromSyscall, Func: callback.CreateFromSyscall, Price: 1 << 4, ParamCount: 1, DisallowCallback: true}, - {Name: interopnames.SystemCallbackInvoke, Func: callback.Invoke, Price: 1 << 15, ParamCount: 2, DisallowCallback: true}, + RequiredFlags: callflag.ReadStates, ParamCount: 1}, {Name: interopnames.SystemContractCall, Func: contract.Call, Price: 1 << 15, - RequiredFlags: smartcontract.AllowCall, ParamCount: 3, DisallowCallback: true}, - {Name: interopnames.SystemContractCallEx, Func: contract.CallEx, Price: 1 << 15, - RequiredFlags: smartcontract.AllowCall, ParamCount: 4, DisallowCallback: true}, - {Name: interopnames.SystemContractCallNative, Func: native.Call, Price: 0, ParamCount: 1, DisallowCallback: true}, - {Name: interopnames.SystemContractCreateStandardAccount, Func: contractCreateStandardAccount, Price: 1 << 8, ParamCount: 1, DisallowCallback: true}, - {Name: interopnames.SystemContractIsStandard, Func: contractIsStandard, Price: 1 << 10, RequiredFlags: smartcontract.ReadStates, ParamCount: 1}, - {Name: interopnames.SystemContractGetCallFlags, Func: contractGetCallFlags, Price: 1 << 10, DisallowCallback: true}, - {Name: interopnames.SystemContractNativeOnPersist, Func: native.OnPersist, Price: 0, RequiredFlags: smartcontract.WriteStates, DisallowCallback: true}, - {Name: interopnames.SystemContractNativePostPersist, Func: native.PostPersist, Price: 0, RequiredFlags: smartcontract.WriteStates, DisallowCallback: true}, - {Name: interopnames.SystemEnumeratorConcat, Func: enumerator.Concat, Price: 1 << 4, ParamCount: 2, DisallowCallback: true}, - {Name: interopnames.SystemEnumeratorCreate, Func: enumerator.Create, Price: 1 << 4, ParamCount: 1, DisallowCallback: true}, - {Name: interopnames.SystemEnumeratorNext, Func: enumerator.Next, Price: 1 << 15, ParamCount: 1, DisallowCallback: true}, - {Name: interopnames.SystemEnumeratorValue, Func: enumerator.Value, Price: 1 << 4, ParamCount: 1, DisallowCallback: true}, - {Name: interopnames.SystemIteratorConcat, Func: iterator.Concat, Price: 1 << 4, ParamCount: 2, DisallowCallback: true}, - {Name: interopnames.SystemIteratorCreate, Func: iterator.Create, Price: 1 << 4, ParamCount: 1, DisallowCallback: true}, - {Name: interopnames.SystemIteratorKey, Func: iterator.Key, Price: 1 << 4, ParamCount: 1, DisallowCallback: true}, - {Name: interopnames.SystemIteratorKeys, Func: iterator.Keys, Price: 1 << 4, ParamCount: 1, DisallowCallback: true}, - {Name: interopnames.SystemIteratorValues, Func: iterator.Values, Price: 1 << 4, ParamCount: 1, DisallowCallback: true}, + RequiredFlags: callflag.AllowCall, ParamCount: 4}, + {Name: interopnames.SystemContractCallNative, Func: native.Call, Price: 0, ParamCount: 1}, + {Name: interopnames.SystemContractCreateStandardAccount, Func: contractCreateStandardAccount, Price: 1 << 8, ParamCount: 1}, + {Name: interopnames.SystemContractIsStandard, Func: contractIsStandard, Price: 1 << 10, RequiredFlags: callflag.ReadStates, ParamCount: 1}, + {Name: interopnames.SystemContractGetCallFlags, Func: contractGetCallFlags, Price: 1 << 10}, + {Name: interopnames.SystemContractNativeOnPersist, Func: native.OnPersist, Price: 0, RequiredFlags: callflag.WriteStates}, + {Name: interopnames.SystemContractNativePostPersist, Func: native.PostPersist, Price: 0, RequiredFlags: callflag.WriteStates}, + {Name: interopnames.SystemEnumeratorConcat, Func: enumerator.Concat, Price: 1 << 4, ParamCount: 2}, + {Name: interopnames.SystemEnumeratorCreate, Func: enumerator.Create, Price: 1 << 4, ParamCount: 1}, + {Name: interopnames.SystemEnumeratorNext, Func: enumerator.Next, Price: 1 << 15, ParamCount: 1}, + {Name: interopnames.SystemEnumeratorValue, Func: enumerator.Value, Price: 1 << 4, ParamCount: 1}, + {Name: interopnames.SystemIteratorConcat, Func: iterator.Concat, Price: 1 << 4, ParamCount: 2}, + {Name: interopnames.SystemIteratorCreate, Func: iterator.Create, Price: 1 << 4, ParamCount: 1}, + {Name: interopnames.SystemIteratorKey, Func: iterator.Key, Price: 1 << 4, ParamCount: 1}, + {Name: interopnames.SystemIteratorKeys, Func: iterator.Keys, Price: 1 << 4, ParamCount: 1}, + {Name: interopnames.SystemIteratorValues, Func: iterator.Values, Price: 1 << 4, ParamCount: 1}, {Name: interopnames.SystemJSONDeserialize, Func: json.Deserialize, Price: 1 << 14, ParamCount: 1}, {Name: interopnames.SystemJSONSerialize, Func: json.Serialize, Price: 1 << 12, ParamCount: 1}, {Name: interopnames.SystemRuntimeCheckWitness, Func: runtime.CheckWitness, Price: 1 << 10, - RequiredFlags: smartcontract.NoneFlag, ParamCount: 1}, + RequiredFlags: callflag.NoneFlag, ParamCount: 1}, {Name: interopnames.SystemRuntimeGasLeft, Func: runtime.GasLeft, Price: 1 << 4}, {Name: interopnames.SystemRuntimeGetCallingScriptHash, Func: runtime.GetCallingScriptHash, Price: 1 << 4}, {Name: interopnames.SystemRuntimeGetEntryScriptHash, Func: runtime.GetEntryScriptHash, Price: 1 << 4}, @@ -85,29 +78,29 @@ var systemInterops = []interop.Function{ {Name: interopnames.SystemRuntimeGetInvocationCounter, Func: runtime.GetInvocationCounter, Price: 1 << 4}, {Name: interopnames.SystemRuntimeGetNotifications, Func: runtime.GetNotifications, Price: 1 << 8, ParamCount: 1}, {Name: interopnames.SystemRuntimeGetScriptContainer, Func: engineGetScriptContainer, Price: 1 << 3}, - {Name: interopnames.SystemRuntimeGetTime, Func: runtime.GetTime, Price: 1 << 3, RequiredFlags: smartcontract.ReadStates}, + {Name: interopnames.SystemRuntimeGetTime, Func: runtime.GetTime, Price: 1 << 3, RequiredFlags: callflag.ReadStates}, {Name: interopnames.SystemRuntimeGetTrigger, Func: runtime.GetTrigger, Price: 1 << 3}, - {Name: interopnames.SystemRuntimeLog, Func: runtime.Log, Price: 1 << 15, RequiredFlags: smartcontract.AllowNotify, - ParamCount: 1, DisallowCallback: true}, - {Name: interopnames.SystemRuntimeNotify, Func: runtime.Notify, Price: 1 << 15, RequiredFlags: smartcontract.AllowNotify, - ParamCount: 2, DisallowCallback: true}, + {Name: interopnames.SystemRuntimeLog, Func: runtime.Log, Price: 1 << 15, RequiredFlags: callflag.AllowNotify, + ParamCount: 1}, + {Name: interopnames.SystemRuntimeNotify, Func: runtime.Notify, Price: 1 << 15, RequiredFlags: callflag.AllowNotify, + ParamCount: 2}, {Name: interopnames.SystemRuntimePlatform, Func: runtime.Platform, Price: 1 << 3}, {Name: interopnames.SystemStorageDelete, Func: storageDelete, Price: 0, - RequiredFlags: smartcontract.WriteStates, ParamCount: 2, DisallowCallback: true}, - {Name: interopnames.SystemStorageFind, Func: storageFind, Price: 1 << 15, RequiredFlags: smartcontract.ReadStates, - ParamCount: 2, DisallowCallback: true}, - {Name: interopnames.SystemStorageGet, Func: storageGet, Price: 1 << 15, RequiredFlags: smartcontract.ReadStates, - ParamCount: 2, DisallowCallback: true}, + RequiredFlags: callflag.WriteStates, ParamCount: 2}, + {Name: interopnames.SystemStorageFind, Func: storageFind, Price: 1 << 15, RequiredFlags: callflag.ReadStates, + ParamCount: 2}, + {Name: interopnames.SystemStorageGet, Func: storageGet, Price: 1 << 15, RequiredFlags: callflag.ReadStates, + ParamCount: 2}, {Name: interopnames.SystemStorageGetContext, Func: storageGetContext, Price: 1 << 4, - RequiredFlags: smartcontract.ReadStates, DisallowCallback: true}, + RequiredFlags: callflag.ReadStates}, {Name: interopnames.SystemStorageGetReadOnlyContext, Func: storageGetReadOnlyContext, Price: 1 << 4, - RequiredFlags: smartcontract.ReadStates, DisallowCallback: true}, - {Name: interopnames.SystemStoragePut, Func: storagePut, Price: 0, RequiredFlags: smartcontract.WriteStates, - ParamCount: 3, DisallowCallback: true}, // These don't have static price in C# code. - {Name: interopnames.SystemStoragePutEx, Func: storagePutEx, Price: 0, RequiredFlags: smartcontract.WriteStates, - ParamCount: 4, DisallowCallback: true}, + RequiredFlags: callflag.ReadStates}, + {Name: interopnames.SystemStoragePut, Func: storagePut, Price: 0, RequiredFlags: callflag.WriteStates, + ParamCount: 3}, // These don't have static price in C# code. + {Name: interopnames.SystemStoragePutEx, Func: storagePutEx, Price: 0, RequiredFlags: callflag.WriteStates, + ParamCount: 4}, {Name: interopnames.SystemStorageAsReadOnly, Func: storageContextAsReadOnly, Price: 1 << 4, - RequiredFlags: smartcontract.ReadStates, ParamCount: 1, DisallowCallback: true}, + RequiredFlags: callflag.ReadStates, ParamCount: 1}, } var neoInterops = []interop.Function{ diff --git a/pkg/core/native/designate.go b/pkg/core/native/designate.go index 5fa9ec8221..0453c7bf4f 100644 --- a/pkg/core/native/designate.go +++ b/pkg/core/native/designate.go @@ -16,6 +16,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" @@ -78,13 +79,13 @@ func newDesignate(p2pSigExtensionsEnabled bool) *Designate { desc := newDescriptor("getDesignatedByRole", smartcontract.ArrayType, manifest.NewParameter("role", smartcontract.IntegerType), manifest.NewParameter("index", smartcontract.IntegerType)) - md := newMethodAndPrice(s.getDesignatedByRole, 1000000, smartcontract.ReadStates) + md := newMethodAndPrice(s.getDesignatedByRole, 1000000, callflag.ReadStates) s.AddMethod(md, desc) desc = newDescriptor("designateAsRole", smartcontract.VoidType, manifest.NewParameter("role", smartcontract.IntegerType), manifest.NewParameter("nodes", smartcontract.ArrayType)) - md = newMethodAndPrice(s.designateAsRole, 0, smartcontract.WriteStates) + md = newMethodAndPrice(s.designateAsRole, 0, callflag.WriteStates) s.AddMethod(md, desc) return s diff --git a/pkg/core/native/interop.go b/pkg/core/native/interop.go index ef5e434de4..ba4a613a1f 100644 --- a/pkg/core/native/interop.go +++ b/pkg/core/native/interop.go @@ -7,6 +7,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/interop" "github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" + "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" ) // Call calls specified native contract method. @@ -27,7 +28,6 @@ func Call(ic *interop.Context) error { return errors.New("it is not allowed to use Neo.Native.Call directly to call native contracts. System.Contract.Call should be used") } operation := ic.VM.Estack().Pop().String() - args := ic.VM.Estack().Pop().Array() m, ok := c.Metadata().Methods[operation] if !ok { return fmt.Errorf("method %s not found", operation) @@ -40,6 +40,10 @@ func Call(ic *interop.Context) error { return errors.New("gas limit exceeded") } ctx := ic.VM.Context() + args := make([]stackitem.Item, len(m.MD.Parameters)) + for i := range args { + args[i] = ic.VM.Estack().Pop().Item() + } result := m.Func(ic, args) if m.MD.ReturnType != smartcontract.VoidType { ctx.Estack().PushVal(result) diff --git a/pkg/core/native/management.go b/pkg/core/native/management.go index 40bbe885f4..df9d7eaed8 100644 --- a/pkg/core/native/management.go +++ b/pkg/core/native/management.go @@ -17,10 +17,10 @@ import ( "github.com/nspcc-dev/neo-go/pkg/encoding/bigint" "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/smartcontract/nef" "github.com/nspcc-dev/neo-go/pkg/util" - "github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" ) @@ -65,32 +65,32 @@ func newManagement() *Management { desc := newDescriptor("getContract", smartcontract.ArrayType, manifest.NewParameter("hash", smartcontract.Hash160Type)) - md := newMethodAndPrice(m.getContract, 1000000, smartcontract.ReadStates) + md := newMethodAndPrice(m.getContract, 1000000, callflag.ReadStates) m.AddMethod(md, desc) desc = newDescriptor("deploy", smartcontract.ArrayType, manifest.NewParameter("script", smartcontract.ByteArrayType), manifest.NewParameter("manifest", smartcontract.ByteArrayType)) - md = newMethodAndPrice(m.deploy, 0, smartcontract.WriteStates|smartcontract.AllowNotify) + md = newMethodAndPrice(m.deploy, 0, callflag.WriteStates|callflag.AllowNotify) m.AddMethod(md, desc) desc = newDescriptor("update", smartcontract.VoidType, manifest.NewParameter("script", smartcontract.ByteArrayType), manifest.NewParameter("manifest", smartcontract.ByteArrayType)) - md = newMethodAndPrice(m.update, 0, smartcontract.WriteStates|smartcontract.AllowNotify) + md = newMethodAndPrice(m.update, 0, callflag.WriteStates|callflag.AllowNotify) m.AddMethod(md, desc) desc = newDescriptor("destroy", smartcontract.VoidType) - md = newMethodAndPrice(m.destroy, 1000000, smartcontract.WriteStates|smartcontract.AllowNotify) + md = newMethodAndPrice(m.destroy, 1000000, callflag.WriteStates|callflag.AllowNotify) m.AddMethod(md, desc) desc = newDescriptor("getMinimumDeploymentFee", smartcontract.IntegerType) - md = newMethodAndPrice(m.getMinimumDeploymentFee, 100_0000, smartcontract.ReadStates) + md = newMethodAndPrice(m.getMinimumDeploymentFee, 100_0000, callflag.ReadStates) m.AddMethod(md, desc) desc = newDescriptor("setMinimumDeploymentFee", smartcontract.BoolType, manifest.NewParameter("value", smartcontract.IntegerType)) - md = newMethodAndPrice(m.setMinimumDeploymentFee, 300_0000, smartcontract.WriteStates) + md = newMethodAndPrice(m.setMinimumDeploymentFee, 300_0000, callflag.WriteStates) m.AddMethod(md, desc) hashParam := manifest.NewParameter("Hash", smartcontract.Hash160Type) @@ -221,7 +221,7 @@ func (m *Management) deploy(ic *interop.Context, args []stackitem.Item) stackite if err != nil { panic(err) } - callDeploy(ic, newcontract, false) + m.callDeploy(ic, newcontract, false) m.emitNotification(ic, contractDeployNotificationName, newcontract.Hash) return contractToStack(newcontract) } @@ -277,7 +277,7 @@ func (m *Management) update(ic *interop.Context, args []stackitem.Item) stackite if err != nil { panic(err) } - callDeploy(ic, contract, true) + m.callDeploy(ic, contract, true) m.emitNotification(ic, contractUpdateNotificationName, contract.Hash) return stackitem.Null{} } @@ -379,11 +379,11 @@ func (m *Management) setMinimumDeploymentFee(ic *interop.Context, args []stackit return stackitem.NewBool(true) } -func callDeploy(ic *interop.Context, cs *state.Contract, isUpdate bool) { +func (m *Management) callDeploy(ic *interop.Context, cs *state.Contract, isUpdate bool) { md := cs.Manifest.ABI.GetMethod(manifest.MethodDeploy) if md != nil { - err := contract.CallExInternal(ic, cs, manifest.MethodDeploy, - []stackitem.Item{stackitem.NewBool(isUpdate)}, smartcontract.All, vm.EnsureIsEmpty) + err := contract.CallFromNative(ic, m.Hash, cs, manifest.MethodDeploy, + []stackitem.Item{stackitem.NewBool(isUpdate)}, false) if err != nil { panic(err) } diff --git a/pkg/core/native/native_neo.go b/pkg/core/native/native_neo.go index 97af919114..8bfed4bbd1 100644 --- a/pkg/core/native/native_neo.go +++ b/pkg/core/native/native_neo.go @@ -22,6 +22,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/encoding/bigint" "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" @@ -110,44 +111,44 @@ func newNEO() *NEO { desc := newDescriptor("unclaimedGas", smartcontract.IntegerType, manifest.NewParameter("account", smartcontract.Hash160Type), manifest.NewParameter("end", smartcontract.IntegerType)) - md := newMethodAndPrice(n.unclaimedGas, 3000000, smartcontract.ReadStates) + md := newMethodAndPrice(n.unclaimedGas, 3000000, callflag.ReadStates) n.AddMethod(md, desc) desc = newDescriptor("registerCandidate", smartcontract.BoolType, manifest.NewParameter("pubkey", smartcontract.ByteArrayType)) - md = newMethodAndPrice(n.registerCandidate, 5000000, smartcontract.WriteStates) + md = newMethodAndPrice(n.registerCandidate, 5000000, callflag.WriteStates) n.AddMethod(md, desc) desc = newDescriptor("unregisterCandidate", smartcontract.BoolType, manifest.NewParameter("pubkey", smartcontract.ByteArrayType)) - md = newMethodAndPrice(n.unregisterCandidate, 5000000, smartcontract.WriteStates) + md = newMethodAndPrice(n.unregisterCandidate, 5000000, callflag.WriteStates) n.AddMethod(md, desc) desc = newDescriptor("vote", smartcontract.BoolType, manifest.NewParameter("account", smartcontract.Hash160Type), manifest.NewParameter("pubkey", smartcontract.ByteArrayType)) - md = newMethodAndPrice(n.vote, 5000000, smartcontract.WriteStates) + md = newMethodAndPrice(n.vote, 5000000, callflag.WriteStates) n.AddMethod(md, desc) desc = newDescriptor("getCandidates", smartcontract.ArrayType) - md = newMethodAndPrice(n.getCandidatesCall, 100000000, smartcontract.ReadStates) + md = newMethodAndPrice(n.getCandidatesCall, 100000000, callflag.ReadStates) n.AddMethod(md, desc) desc = newDescriptor("getŠ”ommittee", smartcontract.ArrayType) - md = newMethodAndPrice(n.getCommittee, 100000000, smartcontract.ReadStates) + md = newMethodAndPrice(n.getCommittee, 100000000, callflag.ReadStates) n.AddMethod(md, desc) desc = newDescriptor("getNextBlockValidators", smartcontract.ArrayType) - md = newMethodAndPrice(n.getNextBlockValidators, 100000000, smartcontract.ReadStates) + md = newMethodAndPrice(n.getNextBlockValidators, 100000000, callflag.ReadStates) n.AddMethod(md, desc) desc = newDescriptor("getGasPerBlock", smartcontract.IntegerType) - md = newMethodAndPrice(n.getGASPerBlock, 100_0000, smartcontract.ReadStates) + md = newMethodAndPrice(n.getGASPerBlock, 100_0000, callflag.ReadStates) n.AddMethod(md, desc) desc = newDescriptor("setGasPerBlock", smartcontract.BoolType, manifest.NewParameter("gasPerBlock", smartcontract.IntegerType)) - md = newMethodAndPrice(n.setGASPerBlock, 500_0000, smartcontract.WriteStates) + md = newMethodAndPrice(n.setGASPerBlock, 500_0000, callflag.WriteStates) n.AddMethod(md, desc) return n diff --git a/pkg/core/native/native_nep17.go b/pkg/core/native/native_nep17.go index cfb2cf7cd8..81dfa6493e 100644 --- a/pkg/core/native/native_nep17.go +++ b/pkg/core/native/native_nep17.go @@ -12,9 +12,9 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/encoding/bigint" "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/util" - "github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" ) @@ -47,20 +47,20 @@ func newNEP17Native(name string) *nep17TokenNative { n.Manifest.SupportedStandards = []string{manifest.NEP17StandardName} desc := newDescriptor("symbol", smartcontract.StringType) - md := newMethodAndPrice(n.Symbol, 0, smartcontract.NoneFlag) + md := newMethodAndPrice(n.Symbol, 0, callflag.NoneFlag) n.AddMethod(md, desc) desc = newDescriptor("decimals", smartcontract.IntegerType) - md = newMethodAndPrice(n.Decimals, 0, smartcontract.NoneFlag) + md = newMethodAndPrice(n.Decimals, 0, callflag.NoneFlag) n.AddMethod(md, desc) desc = newDescriptor("totalSupply", smartcontract.IntegerType) - md = newMethodAndPrice(n.TotalSupply, 1000000, smartcontract.ReadStates) + md = newMethodAndPrice(n.TotalSupply, 1000000, callflag.ReadStates) n.AddMethod(md, desc) desc = newDescriptor("balanceOf", smartcontract.IntegerType, manifest.NewParameter("account", smartcontract.Hash160Type)) - md = newMethodAndPrice(n.balanceOf, 1000000, smartcontract.ReadStates) + md = newMethodAndPrice(n.balanceOf, 1000000, callflag.ReadStates) n.AddMethod(md, desc) transferParams := []manifest.Parameter{ @@ -71,7 +71,7 @@ func newNEP17Native(name string) *nep17TokenNative { desc = newDescriptor("transfer", smartcontract.BoolType, append(transferParams, manifest.NewParameter("data", smartcontract.AnyType))..., ) - md = newMethodAndPrice(n.Transfer, 9000000, smartcontract.WriteStates|smartcontract.AllowCall|smartcontract.AllowNotify) + md = newMethodAndPrice(n.Transfer, 9000000, callflag.WriteStates|callflag.AllowCall|callflag.AllowNotify) n.AddMethod(md, desc) n.AddEvent("Transfer", transferParams...) @@ -143,7 +143,7 @@ func (c *nep17TokenNative) postTransfer(ic *interop.Context, from, to *util.Uint stackitem.NewBigInteger(amount), data, } - if err := contract.CallFromNative(ic, c.Hash, cs, manifest.MethodOnPayment, args, vm.EnsureIsEmpty); err != nil { + if err := contract.CallFromNative(ic, c.Hash, cs, manifest.MethodOnPayment, args, false); err != nil { panic(err) } } @@ -278,7 +278,7 @@ func newDescriptor(name string, ret smartcontract.ParamType, ps ...manifest.Para } } -func newMethodAndPrice(f interop.Method, price int64, flags smartcontract.CallFlag) *interop.MethodAndPrice { +func newMethodAndPrice(f interop.Method, price int64, flags callflag.CallFlag) *interop.MethodAndPrice { return &interop.MethodAndPrice{ Func: f, Price: price, diff --git a/pkg/core/native/notary.go b/pkg/core/native/notary.go index 1103556888..215169c441 100644 --- a/pkg/core/native/notary.go +++ b/pkg/core/native/notary.go @@ -17,9 +17,9 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/util" - "github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" ) @@ -59,43 +59,43 @@ func newNotary() *Notary { manifest.NewParameter("from", smartcontract.Hash160Type), manifest.NewParameter("amount", smartcontract.IntegerType), manifest.NewParameter("data", smartcontract.AnyType)) - md := newMethodAndPrice(n.onPayment, 100_0000, smartcontract.WriteStates) + md := newMethodAndPrice(n.onPayment, 100_0000, callflag.WriteStates) n.AddMethod(md, desc) desc = newDescriptor("lockDepositUntil", smartcontract.BoolType, manifest.NewParameter("address", smartcontract.Hash160Type), manifest.NewParameter("till", smartcontract.IntegerType)) - md = newMethodAndPrice(n.lockDepositUntil, 100_0000, smartcontract.WriteStates) + md = newMethodAndPrice(n.lockDepositUntil, 100_0000, callflag.WriteStates) n.AddMethod(md, desc) desc = newDescriptor("withdraw", smartcontract.BoolType, manifest.NewParameter("from", smartcontract.Hash160Type), manifest.NewParameter("to", smartcontract.Hash160Type)) - md = newMethodAndPrice(n.withdraw, 100_0000, smartcontract.WriteStates) + md = newMethodAndPrice(n.withdraw, 100_0000, callflag.WriteStates) n.AddMethod(md, desc) desc = newDescriptor("balanceOf", smartcontract.IntegerType, manifest.NewParameter("addr", smartcontract.Hash160Type)) - md = newMethodAndPrice(n.balanceOf, 100_0000, smartcontract.ReadStates) + md = newMethodAndPrice(n.balanceOf, 100_0000, callflag.ReadStates) n.AddMethod(md, desc) desc = newDescriptor("expirationOf", smartcontract.IntegerType, manifest.NewParameter("addr", smartcontract.Hash160Type)) - md = newMethodAndPrice(n.expirationOf, 100_0000, smartcontract.ReadStates) + md = newMethodAndPrice(n.expirationOf, 100_0000, callflag.ReadStates) n.AddMethod(md, desc) desc = newDescriptor("verify", smartcontract.BoolType, manifest.NewParameter("signature", smartcontract.SignatureType)) - md = newMethodAndPrice(n.verify, 100_0000, smartcontract.ReadStates) + md = newMethodAndPrice(n.verify, 100_0000, callflag.ReadStates) n.AddMethod(md, desc) desc = newDescriptor("getMaxNotValidBeforeDelta", smartcontract.IntegerType) - md = newMethodAndPrice(n.getMaxNotValidBeforeDelta, 100_0000, smartcontract.ReadStates) + md = newMethodAndPrice(n.getMaxNotValidBeforeDelta, 100_0000, callflag.ReadStates) n.AddMethod(md, desc) desc = newDescriptor("setMaxNotValidBeforeDelta", smartcontract.BoolType, manifest.NewParameter("value", smartcontract.IntegerType)) - md = newMethodAndPrice(n.setMaxNotValidBeforeDelta, 300_0000, smartcontract.WriteStates) + md = newMethodAndPrice(n.setMaxNotValidBeforeDelta, 300_0000, callflag.WriteStates) n.AddMethod(md, desc) return n @@ -276,7 +276,7 @@ func (n *Notary) withdraw(ic *interop.Context, args []stackitem.Item) stackitem. panic(fmt.Errorf("failed to get GAS contract state: %w", err)) } transferArgs := []stackitem.Item{stackitem.NewByteArray(n.Hash.BytesBE()), stackitem.NewByteArray(to.BytesBE()), stackitem.NewBigInteger(deposit.Amount), stackitem.Null{}} - err = contract.CallFromNative(ic, n.Hash, cs, "transfer", transferArgs, vm.EnsureNotEmpty) + err = contract.CallFromNative(ic, n.Hash, cs, "transfer", transferArgs, true) if err != nil { panic(fmt.Errorf("failed to transfer GAS from Notary account: %w", err)) } diff --git a/pkg/core/native/oracle.go b/pkg/core/native/oracle.go index dd5f79aae6..ff6548088e 100644 --- a/pkg/core/native/oracle.go +++ b/pkg/core/native/oracle.go @@ -20,9 +20,9 @@ import ( "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/util" - "github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm/emit" "github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" @@ -56,6 +56,7 @@ func init() { w := io.NewBufBinWriter() emit.Int(w.BinWriter, 0) emit.Opcodes(w.BinWriter, opcode.NEWARRAY) + emit.Int(w.BinWriter, int64(callflag.All)) emit.String(w.BinWriter, "finish") emit.Bytes(w.BinWriter, h.BytesBE()) emit.Syscall(w.BinWriter, interopnames.SystemContractCall) @@ -94,15 +95,15 @@ func newOracle() *Oracle { manifest.NewParameter("callback", smartcontract.StringType), manifest.NewParameter("userData", smartcontract.AnyType), manifest.NewParameter("gasForResponse", smartcontract.IntegerType)) - md := newMethodAndPrice(o.request, oracleRequestPrice, smartcontract.WriteStates|smartcontract.AllowNotify) + md := newMethodAndPrice(o.request, oracleRequestPrice, callflag.WriteStates|callflag.AllowNotify) o.AddMethod(md, desc) desc = newDescriptor("finish", smartcontract.VoidType) - md = newMethodAndPrice(o.finish, 0, smartcontract.WriteStates|smartcontract.AllowCall|smartcontract.AllowNotify) + md = newMethodAndPrice(o.finish, 0, callflag.WriteStates|callflag.AllowCall|callflag.AllowNotify) o.AddMethod(md, desc) desc = newDescriptor("verify", smartcontract.BoolType) - md = newMethodAndPrice(o.verify, 100_0000, smartcontract.NoneFlag) + md = newMethodAndPrice(o.verify, 100_0000, callflag.NoneFlag) o.AddMethod(md, desc) o.AddEvent("OracleRequest", manifest.NewParameter("Id", smartcontract.IntegerType), @@ -238,7 +239,7 @@ func (o *Oracle) FinishInternal(ic *interop.Context) error { if err != nil { return err } - return contract.CallFromNative(ic, o.Hash, cs, req.CallbackMethod, args, vm.EnsureIsEmpty) + return contract.CallFromNative(ic, o.Hash, cs, req.CallbackMethod, args, false) } func (o *Oracle) request(ic *interop.Context, args []stackitem.Item) stackitem.Item { diff --git a/pkg/core/native/policy.go b/pkg/core/native/policy.go index 3c74113153..6861586e85 100644 --- a/pkg/core/native/policy.go +++ b/pkg/core/native/policy.go @@ -14,6 +14,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/network/payload" "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" @@ -85,72 +86,72 @@ func newPolicy() *Policy { p.ContractID = policyContractID desc := newDescriptor("getMaxTransactionsPerBlock", smartcontract.IntegerType) - md := newMethodAndPrice(p.getMaxTransactionsPerBlock, 1000000, smartcontract.ReadStates) + md := newMethodAndPrice(p.getMaxTransactionsPerBlock, 1000000, callflag.ReadStates) p.AddMethod(md, desc) desc = newDescriptor("getMaxBlockSize", smartcontract.IntegerType) - md = newMethodAndPrice(p.getMaxBlockSize, 1000000, smartcontract.ReadStates) + md = newMethodAndPrice(p.getMaxBlockSize, 1000000, callflag.ReadStates) p.AddMethod(md, desc) desc = newDescriptor("getFeePerByte", smartcontract.IntegerType) - md = newMethodAndPrice(p.getFeePerByte, 1000000, smartcontract.ReadStates) + md = newMethodAndPrice(p.getFeePerByte, 1000000, callflag.ReadStates) p.AddMethod(md, desc) desc = newDescriptor("isBlocked", smartcontract.BoolType, manifest.NewParameter("account", smartcontract.Hash160Type)) - md = newMethodAndPrice(p.isBlocked, 1000000, smartcontract.ReadStates) + md = newMethodAndPrice(p.isBlocked, 1000000, callflag.ReadStates) p.AddMethod(md, desc) desc = newDescriptor("getMaxBlockSystemFee", smartcontract.IntegerType) - md = newMethodAndPrice(p.getMaxBlockSystemFee, 1000000, smartcontract.ReadStates) + md = newMethodAndPrice(p.getMaxBlockSystemFee, 1000000, callflag.ReadStates) p.AddMethod(md, desc) desc = newDescriptor("getExecFeeFactor", smartcontract.IntegerType) - md = newMethodAndPrice(p.getExecFeeFactor, 1000000, smartcontract.ReadStates) + md = newMethodAndPrice(p.getExecFeeFactor, 1000000, callflag.ReadStates) p.AddMethod(md, desc) desc = newDescriptor("setExecFeeFactor", smartcontract.BoolType, manifest.NewParameter("value", smartcontract.IntegerType)) - md = newMethodAndPrice(p.setExecFeeFactor, 3000000, smartcontract.WriteStates) + md = newMethodAndPrice(p.setExecFeeFactor, 3000000, callflag.WriteStates) p.AddMethod(md, desc) desc = newDescriptor("getStoragePrice", smartcontract.IntegerType) - md = newMethodAndPrice(p.getStoragePrice, 1000000, smartcontract.ReadStates) + md = newMethodAndPrice(p.getStoragePrice, 1000000, callflag.ReadStates) p.AddMethod(md, desc) desc = newDescriptor("setStoragePrice", smartcontract.BoolType, manifest.NewParameter("value", smartcontract.IntegerType)) - md = newMethodAndPrice(p.setStoragePrice, 3000000, smartcontract.WriteStates) + md = newMethodAndPrice(p.setStoragePrice, 3000000, callflag.WriteStates) p.AddMethod(md, desc) desc = newDescriptor("setMaxBlockSize", smartcontract.BoolType, manifest.NewParameter("value", smartcontract.IntegerType)) - md = newMethodAndPrice(p.setMaxBlockSize, 3000000, smartcontract.WriteStates) + md = newMethodAndPrice(p.setMaxBlockSize, 3000000, callflag.WriteStates) p.AddMethod(md, desc) desc = newDescriptor("setMaxTransactionsPerBlock", smartcontract.BoolType, manifest.NewParameter("value", smartcontract.IntegerType)) - md = newMethodAndPrice(p.setMaxTransactionsPerBlock, 3000000, smartcontract.WriteStates) + md = newMethodAndPrice(p.setMaxTransactionsPerBlock, 3000000, callflag.WriteStates) p.AddMethod(md, desc) desc = newDescriptor("setFeePerByte", smartcontract.BoolType, manifest.NewParameter("value", smartcontract.IntegerType)) - md = newMethodAndPrice(p.setFeePerByte, 3000000, smartcontract.WriteStates) + md = newMethodAndPrice(p.setFeePerByte, 3000000, callflag.WriteStates) p.AddMethod(md, desc) desc = newDescriptor("setMaxBlockSystemFee", smartcontract.BoolType, manifest.NewParameter("value", smartcontract.IntegerType)) - md = newMethodAndPrice(p.setMaxBlockSystemFee, 3000000, smartcontract.WriteStates) + md = newMethodAndPrice(p.setMaxBlockSystemFee, 3000000, callflag.WriteStates) p.AddMethod(md, desc) desc = newDescriptor("blockAccount", smartcontract.BoolType, manifest.NewParameter("account", smartcontract.Hash160Type)) - md = newMethodAndPrice(p.blockAccount, 3000000, smartcontract.WriteStates) + md = newMethodAndPrice(p.blockAccount, 3000000, callflag.WriteStates) p.AddMethod(md, desc) desc = newDescriptor("unblockAccount", smartcontract.BoolType, manifest.NewParameter("account", smartcontract.Hash160Type)) - md = newMethodAndPrice(p.unblockAccount, 3000000, smartcontract.WriteStates) + md = newMethodAndPrice(p.unblockAccount, 3000000, callflag.WriteStates) p.AddMethod(md, desc) return p diff --git a/pkg/core/native_contract_test.go b/pkg/core/native_contract_test.go index a5fdb3fa04..865b48021a 100644 --- a/pkg/core/native_contract_test.go +++ b/pkg/core/native_contract_test.go @@ -14,6 +14,7 @@ import ( "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/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "github.com/nspcc-dev/neo-go/pkg/util" @@ -75,7 +76,7 @@ func newTestNative() *testNative { md := &interop.MethodAndPrice{ Func: tn.sum, Price: testSumPrice, - RequiredFlags: smartcontract.NoneFlag, + RequiredFlags: callflag.NoneFlag, } tn.meta.AddMethod(md, desc) @@ -92,7 +93,7 @@ func newTestNative() *testNative { md = &interop.MethodAndPrice{ Func: tn.callOtherContractNoReturn, Price: testSumPrice, - RequiredFlags: smartcontract.NoneFlag} + RequiredFlags: callflag.NoneFlag} tn.meta.AddMethod(md, desc) desc = &manifest.Method{ @@ -107,7 +108,7 @@ func newTestNative() *testNative { md = &interop.MethodAndPrice{ Func: tn.callOtherContractWithReturn, Price: testSumPrice, - RequiredFlags: smartcontract.NoneFlag} + RequiredFlags: callflag.NoneFlag} tn.meta.AddMethod(md, desc) return tn @@ -137,7 +138,7 @@ func toUint160(item stackitem.Item) util.Uint160 { return u } -func (tn *testNative) call(ic *interop.Context, args []stackitem.Item, checkReturn vm.CheckReturnState) { +func (tn *testNative) call(ic *interop.Context, args []stackitem.Item, hasReturn bool) { cs, err := ic.GetContract(toUint160(args[0])) if err != nil { panic(err) @@ -146,19 +147,19 @@ func (tn *testNative) call(ic *interop.Context, args []stackitem.Item, checkRetu if err != nil { panic(err) } - err = contract.CallFromNative(ic, tn.meta.Hash, cs, string(bs), args[2].Value().([]stackitem.Item), checkReturn) + err = contract.CallFromNative(ic, tn.meta.Hash, cs, string(bs), args[2].Value().([]stackitem.Item), hasReturn) if err != nil { panic(err) } } func (tn *testNative) callOtherContractNoReturn(ic *interop.Context, args []stackitem.Item) stackitem.Item { - tn.call(ic, args, vm.EnsureIsEmpty) + tn.call(ic, args, false) return stackitem.Null{} } func (tn *testNative) callOtherContractWithReturn(ic *interop.Context, args []stackitem.Item) stackitem.Item { - tn.call(ic, args, vm.EnsureNotEmpty) + tn.call(ic, args, true) bi := ic.VM.Estack().Pop().BigInt() return stackitem.Make(bi.Add(bi, big.NewInt(1))) } @@ -183,6 +184,7 @@ func TestNativeContract_Invoke(t *testing.T) { price += 3 * fee.Opcode(chain.GetBaseExecFee(), opcode.PUSHINT8, opcode.PUSHDATA1) price += 2 * fee.Opcode(chain.GetBaseExecFee(), opcode.SYSCALL) price += fee.Opcode(chain.GetBaseExecFee(), opcode.PACK) + price += fee.Opcode(chain.GetBaseExecFee(), opcode.PUSHINT8) res, err := invokeContractMethod(chain, price, tn.Metadata().Hash, "sum", int64(14), int64(28)) require.NoError(t, err) checkResult(t, res, stackitem.Make(42)) @@ -220,8 +222,9 @@ func TestNativeContract_InvokeInternal(t *testing.T) { v := ic.SpawnVM() t.Run("fail, bad current script hash", func(t *testing.T) { - v.LoadScriptWithHash([]byte{1}, util.Uint160{1, 2, 3}, smartcontract.All) - v.Estack().PushVal(stackitem.NewArray([]stackitem.Item{stackitem.NewBigInteger(big.NewInt(14)), stackitem.NewBigInteger(big.NewInt(28))})) + v.LoadScriptWithHash([]byte{1}, util.Uint160{1, 2, 3}, callflag.All) + v.Estack().PushVal(14) + v.Estack().PushVal(28) v.Estack().PushVal("sum") v.Estack().PushVal(tn.Metadata().Name) @@ -230,8 +233,9 @@ func TestNativeContract_InvokeInternal(t *testing.T) { }) t.Run("success", func(t *testing.T) { - v.LoadScriptWithHash([]byte{1}, tn.Metadata().Hash, smartcontract.All) - v.Estack().PushVal(stackitem.NewArray([]stackitem.Item{stackitem.NewBigInteger(big.NewInt(14)), stackitem.NewBigInteger(big.NewInt(28))})) + v.LoadScriptWithHash([]byte{1}, tn.Metadata().Hash, callflag.All) + v.Estack().PushVal(14) + v.Estack().PushVal(28) v.Estack().PushVal("sum") v.Estack().PushVal(tn.Metadata().Name) @@ -272,6 +276,7 @@ func TestNativeContract_InvokeOtherContract(t *testing.T) { res, err := invokeContractMethod(chain, testSumPrice*4+10000, tn.Metadata().Hash, "callOtherContractNoReturn", cs.Hash, "justReturn", []interface{}{}) require.NoError(t, err) drainTN(t) + require.Equal(t, vm.HaltState, res.VMState, res.FaultException) checkResult(t, res, stackitem.Null{}) // simple call is done with EnsureNotEmpty }) t.Run("non-native, with return", func(t *testing.T) { diff --git a/pkg/core/native_designate_test.go b/pkg/core/native_designate_test.go index b381a923f4..7b0c0b6c13 100644 --- a/pkg/core/native_designate_test.go +++ b/pkg/core/native_designate_test.go @@ -11,6 +11,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/io" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm/emit" @@ -29,8 +30,7 @@ func (bc *Blockchain) setNodesByRole(t *testing.T, ok bool, r native.Role, nodes emit.Int(w.BinWriter, int64(r)) emit.Int(w.BinWriter, 2) emit.Opcodes(w.BinWriter, opcode.PACK) - emit.String(w.BinWriter, "designateAsRole") - emit.AppCall(w.BinWriter, bc.contracts.Designate.Hash) + emit.AppCallNoArgs(w.BinWriter, bc.contracts.Designate.Hash, "designateAsRole", callflag.All) require.NoError(t, w.Err) tx := transaction.New(netmode.UnitTestNet, w.Bytes(), 0) tx.NetworkFee = 10_000_000 diff --git a/pkg/core/native_neo_test.go b/pkg/core/native_neo_test.go index a88fecea53..9d7d87ea5e 100644 --- a/pkg/core/native_neo_test.go +++ b/pkg/core/native_neo_test.go @@ -11,6 +11,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/io" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm" @@ -75,11 +76,11 @@ func TestNEO_Vote(t *testing.T) { to := accs[i].Contract.ScriptHash() w := io.NewBufBinWriter() - emit.AppCallWithOperationAndArgs(w.BinWriter, bc.contracts.NEO.Hash, "transfer", + emit.AppCall(w.BinWriter, bc.contracts.NEO.Hash, "transfer", callflag.All, neoOwner.BytesBE(), to.BytesBE(), big.NewInt(int64(sz-i)*1000000).Int64(), nil) emit.Opcodes(w.BinWriter, opcode.ASSERT) - emit.AppCallWithOperationAndArgs(w.BinWriter, bc.contracts.GAS.Hash, "transfer", + emit.AppCall(w.BinWriter, bc.contracts.GAS.Hash, "transfer", callflag.All, neoOwner.BytesBE(), to.BytesBE(), int64(1_000_000_000), nil) emit.Opcodes(w.BinWriter, opcode.ASSERT) @@ -141,7 +142,7 @@ func TestNEO_Vote(t *testing.T) { h := accs[i].PrivateKey().GetScriptHash() gasBalance[i] = bc.GetUtilityTokenBalance(h) neoBalance[i], _ = bc.GetGoverningTokenBalance(h) - emit.AppCallWithOperationAndArgs(w.BinWriter, bc.contracts.NEO.Hash, "transfer", + emit.AppCall(w.BinWriter, bc.contracts.NEO.Hash, "transfer", callflag.All, h.BytesBE(), h.BytesBE(), int64(1), nil) emit.Opcodes(w.BinWriter, opcode.ASSERT) require.NoError(t, w.Err) diff --git a/pkg/core/native_notary_test.go b/pkg/core/native_notary_test.go index a9d2ff6146..75f6fd3e9b 100644 --- a/pkg/core/native_notary_test.go +++ b/pkg/core/native_notary_test.go @@ -11,6 +11,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/encoding/bigint" "github.com/nspcc-dev/neo-go/pkg/io" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm" @@ -174,7 +175,8 @@ func TestNotaryContractPipeline(t *testing.T) { // `withdraw`: bad witness w := io.NewBufBinWriter() - emit.AppCallWithOperationAndArgs(w.BinWriter, notaryHash, "withdraw", testchain.MultisigScriptHash(), acc.PrivateKey().PublicKey().GetScriptHash()) + emit.AppCall(w.BinWriter, notaryHash, "withdraw", callflag.All, + testchain.MultisigScriptHash(), acc.PrivateKey().PublicKey().GetScriptHash()) require.NoError(t, w.Err) script := w.Bytes() withdrawTx := transaction.New(chain.GetConfig().Magic, script, 10000000) diff --git a/pkg/core/native_oracle_test.go b/pkg/core/native_oracle_test.go index 5341b848ab..b316bc3e5c 100644 --- a/pkg/core/native_oracle_test.go +++ b/pkg/core/native_oracle_test.go @@ -16,6 +16,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/smartcontract/nef" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" @@ -32,9 +33,11 @@ func getOracleContractState(h util.Uint160) *state.Contract { w := io.NewBufBinWriter() emit.Int(w.BinWriter, 5) emit.Opcodes(w.BinWriter, opcode.PACK) + emit.Int(w.BinWriter, int64(callflag.All)) emit.String(w.BinWriter, "request") emit.Bytes(w.BinWriter, h.BytesBE()) emit.Syscall(w.BinWriter, interopnames.SystemContractCall) + emit.Opcodes(w.BinWriter, opcode.DROP) emit.Opcodes(w.BinWriter, opcode.RET) // `handle` method aborts if len(userData) == 2 @@ -171,7 +174,7 @@ func TestOracle_Request(t *testing.T) { // We need to ensure that callback is called thus, executing full script is necessary. resp.ID = 1 - ic.VM.LoadScriptWithFlags(tx.Script, smartcontract.All) + ic.VM.LoadScriptWithFlags(tx.Script, callflag.All) require.NoError(t, ic.VM.Run()) si := ic.DAO.GetStorageItem(cs.ID, []byte("lastOracleResponse")) @@ -211,7 +214,7 @@ func TestOracle_Request(t *testing.T) { }} ic := bc.newInteropContext(trigger.Application, bc.dao, bc.newBlock(tx), tx) ic.VM = ic.SpawnVM() - ic.VM.LoadScriptWithFlags(tx.Script, smartcontract.All) + ic.VM.LoadScriptWithFlags(tx.Script, callflag.All) require.Error(t, ic.VM.Run()) // Request is cleaned up even if callback failed. diff --git a/pkg/interop/contract/contract.go b/pkg/interop/contract/contract.go index a42be36c5b..4e628b0d18 100644 --- a/pkg/interop/contract/contract.go +++ b/pkg/interop/contract/contract.go @@ -40,17 +40,9 @@ func GetCallFlags() int64 { } // Call executes previously deployed blockchain contract with specified hash -// (20 bytes in BE form) using provided arguments. -// It returns whatever this contract returns. This function uses -// `System.Contract.Call` syscall. -func Call(scriptHash interop.Hash160, method string, args ...interface{}) interface{} { - return nil -} - -// CallEx executes previously deployed blockchain contract with specified hash // (20 bytes in BE form) using provided arguments and call flags. // It returns whatever this contract returns. This function uses -// `System.Contract.CallEx` syscall. -func CallEx(f CallFlag, scriptHash interop.Hash160, method string, args ...interface{}) interface{} { +// `System.Contract.Call` syscall. +func Call(scriptHash interop.Hash160, method string, f CallFlag, args ...interface{}) interface{} { return nil } diff --git a/pkg/rpc/client/nep17.go b/pkg/rpc/client/nep17.go index 941cd908f2..250432ef12 100644 --- a/pkg/rpc/client/nep17.go +++ b/pkg/rpc/client/nep17.go @@ -9,6 +9,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/rpc/response/result" "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm/emit" "github.com/nspcc-dev/neo-go/pkg/vm/opcode" @@ -120,8 +121,8 @@ func (c *Client) CreateNEP17MultiTransferTx(acc *wallet.Account, gas int64, reci } w := io.NewBufBinWriter() for i := range recipients { - emit.AppCallWithOperationAndArgs(w.BinWriter, recipients[i].Token, "transfer", from, - recipients[i].Address, recipients[i].Amount, nil) + emit.AppCall(w.BinWriter, recipients[i].Token, "transfer", + callflag.WriteStates|callflag.AllowCall|callflag.AllowNotify, from, recipients[i].Address, recipients[i].Amount, nil) emit.Opcodes(w.BinWriter, opcode.ASSERT) } accAddr, err := address.StringToUint160(acc.Address) diff --git a/pkg/rpc/request/txBuilder.go b/pkg/rpc/request/txBuilder.go index 0c5f58336d..e43e8b1bd8 100644 --- a/pkg/rpc/request/txBuilder.go +++ b/pkg/rpc/request/txBuilder.go @@ -8,6 +8,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm/emit" "github.com/nspcc-dev/neo-go/pkg/vm/opcode" @@ -101,7 +102,7 @@ func expandArrayIntoScript(script *io.BinWriter, slice []Param) error { // CreateFunctionInvocationScript creates a script to invoke given contract with // given parameters. -func CreateFunctionInvocationScript(contract util.Uint160, params Params) ([]byte, error) { +func CreateFunctionInvocationScript(contract util.Uint160, method string, params Params) ([]byte, error) { script := io.NewBufBinWriter() for i := len(params) - 1; i >= 0; i-- { switch params[i].Type { @@ -127,6 +128,6 @@ func CreateFunctionInvocationScript(contract util.Uint160, params Params) ([]byt } } - emit.AppCall(script.BinWriter, contract) + emit.AppCallNoArgs(script.BinWriter, contract, method, callflag.All) return script.Bytes(), nil } diff --git a/pkg/rpc/request/tx_builder_test.go b/pkg/rpc/request/tx_builder_test.go index 6c1f1587e5..af0fcbaa72 100644 --- a/pkg/rpc/request/tx_builder_test.go +++ b/pkg/rpc/request/tx_builder_test.go @@ -21,46 +21,44 @@ func TestInvocationScriptCreationGood(t *testing.T) { ps Params script string }{{ - script: "0c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52", - }, { ps: Params{{Type: StringT, Value: "transfer"}}, - script: "0c087472616e736665720c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52", + script: "1f0c087472616e736665720c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52", }, { ps: Params{{Type: NumberT, Value: 42}}, - script: "0c0234320c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52", + script: "1f0c0234320c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52", }, { ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{}}}, - script: "10c00c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52", + script: "10c01f0c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52", }, { ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.ByteArrayType, Value: Param{Type: StringT, Value: "AwEtR+diEK7HO+Oas9GG4KQP6Nhr+j1Pq/2le6E7iPlq"}}}}}}, - script: "0c2103012d47e76210aec73be39ab3d186e0a40fe8d86bfa3d4fabfda57ba13b88f96a11c00c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52", + script: "0c2103012d47e76210aec73be39ab3d186e0a40fe8d86bfa3d4fabfda57ba13b88f96a11c01f0c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52", }, { ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.SignatureType, Value: Param{Type: StringT, Value: "4edf5005771de04619235d5a4c7a9a11bb78e008541f1da7725f654c33380a3c87e2959a025da706d7255cb3a3fa07ebe9c6559d0d9e6213c68049168eb1056f"}}}}}}, - script: "0c404edf5005771de04619235d5a4c7a9a11bb78e008541f1da7725f654c33380a3c87e2959a025da706d7255cb3a3fa07ebe9c6559d0d9e6213c68049168eb1056f11c00c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52", + script: "0c404edf5005771de04619235d5a4c7a9a11bb78e008541f1da7725f654c33380a3c87e2959a025da706d7255cb3a3fa07ebe9c6559d0d9e6213c68049168eb1056f11c01f0c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52", }, { ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.StringType, Value: Param{Type: StringT, Value: "50befd26fdf6e4d957c11e078b24ebce6291456f"}}}}}}, - script: "0c283530626566643236666466366534643935376331316530373862323465626365363239313435366611c00c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52", + script: "0c283530626566643236666466366534643935376331316530373862323465626365363239313435366611c01f0c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52", }, { ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.Hash160Type, Value: Param{Type: StringT, Value: "50befd26fdf6e4d957c11e078b24ebce6291456f"}}}}}}, - script: "0c146f459162ceeb248b071ec157d9e4f6fd26fdbe5011c00c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52", + script: "0c146f459162ceeb248b071ec157d9e4f6fd26fdbe5011c01f0c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52", }, { ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.Hash256Type, Value: Param{Type: StringT, Value: "602c79718b16e442de58778e148d0b1084e3b2dffd5de6b7b16cee7969282de7"}}}}}}, - script: "0c20e72d286979ee6cb1b7e65dfddfb2e384100b8d148e7758de42e4168b71792c6011c00c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52", + script: "0c20e72d286979ee6cb1b7e65dfddfb2e384100b8d148e7758de42e4168b71792c6011c01f0c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52", }, { ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.PublicKeyType, Value: Param{Type: StringT, Value: "03c089d7122b840a4935234e82e26ae5efd0c2acb627239dc9f207311337b6f2c1"}}}}}}, - script: "0c2103c089d7122b840a4935234e82e26ae5efd0c2acb627239dc9f207311337b6f2c111c00c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52", + script: "0c2103c089d7122b840a4935234e82e26ae5efd0c2acb627239dc9f207311337b6f2c111c01f0c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52", }, { ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.IntegerType, Value: Param{Type: NumberT, Value: 42}}}}}}, - script: "002a11c00c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52", + script: "002a11c01f0c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52", }, { ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.BoolType, Value: Param{Type: StringT, Value: "true"}}}}}}, - script: "1111c00c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52", + script: "1111c01f0c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52", }, { ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.BoolType, Value: Param{Type: StringT, Value: "false"}}}}}}, - script: "1011c00c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52", + script: "1011c01f0c01610c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52", }} for _, ps := range paramScripts { - script, err := CreateFunctionInvocationScript(contract, ps.ps) + script, err := CreateFunctionInvocationScript(contract, ps.ps[0].String(), ps.ps[1:]) assert.Nil(t, err) assert.Equal(t, ps.script, hex.EncodeToString(script)) } @@ -86,7 +84,7 @@ func TestInvocationScriptCreationBad(t *testing.T) { {{Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.UnknownType, Value: Param{}}}}}}, } for _, ps := range testParams { - _, err := CreateFunctionInvocationScript(contract, ps) + _, err := CreateFunctionInvocationScript(contract, "", ps) assert.NotNil(t, err) } } diff --git a/pkg/rpc/server/client_test.go b/pkg/rpc/server/client_test.go index 61d3cde8fa..ddc2b1c1d6 100644 --- a/pkg/rpc/server/client_test.go +++ b/pkg/rpc/server/client_test.go @@ -14,6 +14,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/rpc/client" "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm/opcode" @@ -276,7 +277,7 @@ func TestCreateNEP17TransferTx(t *testing.T) { require.NoError(t, acc.SignTx(tx)) require.NoError(t, chain.VerifyTx(tx)) v := chain.GetTestVM(trigger.Application, tx, nil) - v.LoadScriptWithFlags(tx.Script, smartcontract.All) + v.LoadScriptWithFlags(tx.Script, callflag.All) require.NoError(t, v.Run()) } diff --git a/pkg/rpc/server/server.go b/pkg/rpc/server/server.go index cf7959d729..8edafd530d 100644 --- a/pkg/rpc/server/server.go +++ b/pkg/rpc/server/server.go @@ -31,7 +31,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/rpc/request" "github.com/nspcc-dev/neo-go/pkg/rpc/response" "github.com/nspcc-dev/neo-go/pkg/rpc/response/result" - "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "github.com/nspcc-dev/neo-go/pkg/util" @@ -1097,7 +1097,7 @@ func (s *Server) invokeFunction(reqParams request.Params) (interface{}, *respons if len(tx.Signers) == 0 { tx.Signers = []transaction.Signer{{Account: util.Uint160{}, Scopes: transaction.None}} } - script, err := request.CreateFunctionInvocationScript(scriptHash, reqParams[1:checkWitnessHashesIndex]) + script, err := request.CreateFunctionInvocationScript(scriptHash, reqParams[1].String(), reqParams[2:checkWitnessHashesIndex]) if err != nil { return nil, response.NewInternalServerError("can't create invocation script", err) } @@ -1138,14 +1138,7 @@ func (s *Server) invokeContractVerify(reqParams request.Params) (interface{}, *r return nil, responseErr } - args := make(request.Params, 1) - args[0] = request.Param{ - Type: request.StringT, - Value: manifest.MethodVerify, - } - if len(reqParams) > 1 { - args = append(args, reqParams[1]) - } + args := reqParams[1:2] var tx *transaction.Transaction if len(reqParams) > 2 { signers, witnesses, err := reqParams[2].GetSignersWithWitnesses() @@ -1162,7 +1155,7 @@ func (s *Server) invokeContractVerify(reqParams request.Params) (interface{}, *r if cs == nil { return nil, response.NewRPCError("unknown contract", scriptHash.StringBE(), nil) } - script, err := request.CreateFunctionInvocationScript(cs.Hash, args) + script, err := request.CreateFunctionInvocationScript(cs.Hash, manifest.MethodVerify, args) if err != nil { return nil, response.NewInternalServerError("can't create invocation script", err) } @@ -1188,7 +1181,7 @@ func (s *Server) runScriptInVM(t trigger.Type, script []byte, tx *transaction.Tr vm := s.chain.GetTestVM(t, tx, b) vm.GasLimit = int64(s.config.MaxGasInvoke) - vm.LoadScriptWithFlags(script, smartcontract.All) + vm.LoadScriptWithFlags(script, callflag.All) err = vm.Run() var faultException string if err != nil { diff --git a/pkg/rpc/server/server_test.go b/pkg/rpc/server/server_test.go index 9037cc7a32..4a532f9832 100644 --- a/pkg/rpc/server/server_test.go +++ b/pkg/rpc/server/server_test.go @@ -57,7 +57,7 @@ type rpcTestCase struct { } const testContractHash = "743ed26f78e29ecd595535b74a943b1f9ccbc444" -const deploymentTxHash = "a4644e08389ced59e875a8d5bdcb8fdded7507ed7dc3915a37e5be71736ac7b4" +const deploymentTxHash = "2aef5684c6cf60884cc00400b78c65c5105ed4261a4f614bce74c74927a66bf3" const genesisBlockHash = "0542f4350c6e236d0509bcd98188b0034bfbecc1a0c7fcdb8e4295310d468b70" const verifyContractHash = "a2eb22340979804cb10cc1add0b8822c201f4d8a" @@ -1408,7 +1408,7 @@ func checkNep17Balances(t *testing.T, e *executor, acc interface{}) { }, { Asset: e.chain.UtilityTokenHash(), - Amount: "80006564770", + Amount: "80006675650", LastUpdated: 7, }}, Address: testchain.PrivateKeyByID(0).GetScriptHash().StringLE(), diff --git a/pkg/rpc/server/testdata/testblocks.acc b/pkg/rpc/server/testdata/testblocks.acc index 4bed865540..21aa35b50c 100644 Binary files a/pkg/rpc/server/testdata/testblocks.acc and b/pkg/rpc/server/testdata/testblocks.acc differ diff --git a/pkg/smartcontract/call_flags.go b/pkg/smartcontract/callflag/call_flags.go similarity index 95% rename from pkg/smartcontract/call_flags.go rename to pkg/smartcontract/callflag/call_flags.go index 6921d4887c..07bbb15b42 100644 --- a/pkg/smartcontract/call_flags.go +++ b/pkg/smartcontract/callflag/call_flags.go @@ -1,4 +1,4 @@ -package smartcontract +package callflag // CallFlag represents call flag. type CallFlag byte diff --git a/pkg/smartcontract/call_flags_test.go b/pkg/smartcontract/callflag/call_flags_test.go similarity index 93% rename from pkg/smartcontract/call_flags_test.go rename to pkg/smartcontract/callflag/call_flags_test.go index f50f12ce52..7dd9048b84 100644 --- a/pkg/smartcontract/call_flags_test.go +++ b/pkg/smartcontract/callflag/call_flags_test.go @@ -1,4 +1,4 @@ -package smartcontract +package callflag import ( "testing" diff --git a/pkg/vm/context.go b/pkg/vm/context.go index 7d70b08e90..340a6a5b2f 100644 --- a/pkg/vm/context.go +++ b/pkg/vm/context.go @@ -6,7 +6,7 @@ import ( "math/big" "github.com/nspcc-dev/neo-go/pkg/crypto/hash" - "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" @@ -46,10 +46,12 @@ type Context struct { isDeployed bool // Call flags this context was created with. - callFlag smartcontract.CallFlag + callFlag callflag.CallFlag - // CheckReturn specifies if amount of return values needs to be checked. - CheckReturn CheckReturnState + // ParamCount specifies number of parameters. + ParamCount int + // RetCount specifies number of return values. + RetCount int } // CheckReturnState represents possible states of stack after opcode.RET was processed. @@ -69,9 +71,18 @@ var errNoInstParam = errors.New("failed to read instruction parameter") // NewContext returns a new Context object. func NewContext(b []byte) *Context { + return NewContextWithParams(b, 0, -1, 0) +} + +// NewContextWithParams creates new Context objects using script, parameter count, +// return value count and initial position in script. +func NewContextWithParams(b []byte, pcount int, rvcount int, pos int) *Context { return &Context{ prog: b, breakPoints: []int{}, + ParamCount: pcount, + RetCount: rvcount, + nextip: pos, } } @@ -194,7 +205,7 @@ func (c *Context) Copy() *Context { } // GetCallFlags returns calling flags context was created with. -func (c *Context) GetCallFlags() smartcontract.CallFlag { +func (c *Context) GetCallFlags() callflag.CallFlag { return c.callFlag } diff --git a/pkg/vm/emit/emit.go b/pkg/vm/emit/emit.go index 37d6bd7c7a..bee1b4c1a9 100644 --- a/pkg/vm/emit/emit.go +++ b/pkg/vm/emit/emit.go @@ -10,6 +10,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames" "github.com/nspcc-dev/neo-go/pkg/encoding/bigint" "github.com/nspcc-dev/neo-go/pkg/io" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" @@ -150,17 +151,18 @@ func Jmp(w *io.BinWriter, op opcode.Opcode, label uint16) { Instruction(w, op, buf) } -// AppCall emits call to provided contract. -func AppCall(w *io.BinWriter, scriptHash util.Uint160) { +// AppCallNoArgs emits call to provided contract. +func AppCallNoArgs(w *io.BinWriter, scriptHash util.Uint160, operation string, f callflag.CallFlag) { + Int(w, int64(f)) + String(w, operation) Bytes(w, scriptHash.BytesBE()) Syscall(w, interopnames.SystemContractCall) } -// AppCallWithOperationAndArgs emits an APPCALL with the given operation and arguments. -func AppCallWithOperationAndArgs(w *io.BinWriter, scriptHash util.Uint160, operation string, args ...interface{}) { +// AppCall emits an APPCALL with the default parameters given operation and arguments. +func AppCall(w *io.BinWriter, scriptHash util.Uint160, operation string, f callflag.CallFlag, args ...interface{}) { Array(w, args...) - String(w, operation) - AppCall(w, scriptHash) + AppCallNoArgs(w, scriptHash, operation, f) } func isInstructionJmp(op opcode.Opcode) bool { diff --git a/pkg/vm/interop.go b/pkg/vm/interop.go index b91f1ebc57..be45f39715 100644 --- a/pkg/vm/interop.go +++ b/pkg/vm/interop.go @@ -6,7 +6,7 @@ import ( "sort" "github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames" - "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" ) @@ -15,7 +15,7 @@ type interopIDFuncPrice struct { ID uint32 Func func(vm *VM) error Price int64 - RequiredFlags smartcontract.CallFlag + RequiredFlags callflag.CallFlag } var defaultVMInterops = []interopIDFuncPrice{ @@ -24,9 +24,9 @@ var defaultVMInterops = []interopIDFuncPrice{ {ID: interopnames.ToID([]byte(interopnames.SystemBinarySerialize)), Func: RuntimeSerialize, Price: 1 << 12}, {ID: interopnames.ToID([]byte(interopnames.SystemRuntimeLog)), - Func: runtimeLog, Price: 1 << 15, RequiredFlags: smartcontract.AllowNotify}, + Func: runtimeLog, Price: 1 << 15, RequiredFlags: callflag.AllowNotify}, {ID: interopnames.ToID([]byte(interopnames.SystemRuntimeNotify)), - Func: runtimeNotify, Price: 1 << 15, RequiredFlags: smartcontract.AllowNotify}, + Func: runtimeNotify, Price: 1 << 15, RequiredFlags: callflag.AllowNotify}, {ID: interopnames.ToID([]byte(interopnames.SystemEnumeratorCreate)), Func: EnumeratorCreate, Price: 1 << 4}, {ID: interopnames.ToID([]byte(interopnames.SystemEnumeratorNext)), diff --git a/pkg/vm/json_test.go b/pkg/vm/json_test.go index d41b6cd965..f5f85695c5 100644 --- a/pkg/vm/json_test.go +++ b/pkg/vm/json_test.go @@ -15,7 +15,7 @@ import ( "testing" "github.com/nspcc-dev/neo-go/pkg/encoding/bigint" - "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" "github.com/stretchr/testify/require" @@ -114,7 +114,7 @@ func testSyscallHandler(v *VM, id uint32) error { case 0x77777777: v.Estack().PushVal(stackitem.NewInterop(new(int))) case 0x66666666: - if !v.Context().callFlag.Has(smartcontract.ReadOnly) { + if !v.Context().callFlag.Has(callflag.ReadOnly) { return errors.New("invalid call flags") } v.Estack().PushVal(stackitem.NewInterop(new(int))) diff --git a/pkg/vm/vm.go b/pkg/vm/vm.go index a586086b84..0b751e0430 100644 --- a/pkg/vm/vm.go +++ b/pkg/vm/vm.go @@ -17,7 +17,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/encoding/bigint" - "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/smartcontract/nef" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "github.com/nspcc-dev/neo-go/pkg/util" @@ -272,13 +272,13 @@ func (v *VM) Load(prog []byte) { // will immediately push a new context created from this script to // the invocation stack and starts executing it. func (v *VM) LoadScript(b []byte) { - v.LoadScriptWithFlags(b, smartcontract.NoneFlag) + v.LoadScriptWithFlags(b, callflag.NoneFlag) } // LoadScriptWithFlags loads script and sets call flag to f. -func (v *VM) LoadScriptWithFlags(b []byte, f smartcontract.CallFlag) { +func (v *VM) LoadScriptWithFlags(b []byte, f callflag.CallFlag) { v.checkInvocationStackSize() - ctx := NewContext(b) + ctx := NewContextWithParams(b, 0, -1, 0) v.estack = v.newItemStack("estack") ctx.estack = v.estack ctx.tryStack = NewStack("exception") @@ -294,19 +294,26 @@ func (v *VM) LoadScriptWithFlags(b []byte, f smartcontract.CallFlag) { // assumes that it is used for deployed contracts setting context's parameters // accordingly). It's up to user of this function to make sure the script and hash match // each other. -func (v *VM) LoadScriptWithHash(b []byte, hash util.Uint160, f smartcontract.CallFlag) { +func (v *VM) LoadScriptWithHash(b []byte, hash util.Uint160, f callflag.CallFlag) { shash := v.GetCurrentScriptHash() - v.LoadScriptWithCallingHash(shash, b, hash, f) + v.LoadScriptWithCallingHash(shash, b, hash, f, true, 0) } // LoadScriptWithCallingHash is similar to LoadScriptWithHash but sets calling hash explicitly. // It should be used for calling from native contracts. -func (v *VM) LoadScriptWithCallingHash(caller util.Uint160, b []byte, hash util.Uint160, f smartcontract.CallFlag) { +func (v *VM) LoadScriptWithCallingHash(caller util.Uint160, b []byte, hash util.Uint160, + f callflag.CallFlag, hasReturn bool, paramCount uint16) { v.LoadScriptWithFlags(b, f) ctx := v.Context() ctx.isDeployed = true ctx.scriptHash = hash ctx.callingScriptHash = caller + if hasReturn { + ctx.RetCount = 1 + } else { + ctx.RetCount = 0 + } + ctx.ParamCount = int(paramCount) } // Context returns the current executed context. Nil if there is no context, @@ -1274,6 +1281,10 @@ func (v *VM) execute(ctx *Context, op opcode.Opcode, parameter []byte) (err erro newEstack := v.Context().estack if oldEstack != newEstack { + if oldCtx.RetCount >= 0 && oldEstack.Len() != oldCtx.RetCount { + panic(fmt.Errorf("invalid return values count: expected %d, got %d", + oldCtx.RetCount, oldEstack.Len())) + } rvcount := oldEstack.Len() for i := rvcount; i > 0; i-- { elem := oldEstack.RemoveAt(i - 1) @@ -1425,19 +1436,6 @@ func (v *VM) unloadContext(ctx *Context) { if ctx.static != nil && currCtx != nil && ctx.static != currCtx.static { ctx.static.Clear() } - switch ctx.CheckReturn { - case NoCheck: - case EnsureIsEmpty: - if currCtx != nil && ctx.estack.len != 0 { - panic("return value amount is > 0") - } - case EnsureNotEmpty: - if currCtx != nil && ctx.estack.len == 0 { - currCtx.estack.PushVal(stackitem.Null{}) - } else if ctx.estack.len > 1 { - panic("return value amount is > 1") - } - } } // getTryParams splits TRY(L) instruction parameter into offsets for catch and finally blocks. @@ -1494,7 +1492,7 @@ func (v *VM) Call(ctx *Context, offset int) { func (v *VM) call(ctx *Context, offset int) { v.checkInvocationStackSize() newCtx := ctx.Copy() - newCtx.CheckReturn = NoCheck + newCtx.RetCount = -1 newCtx.local = nil newCtx.arguments = nil newCtx.tryStack = NewStack("exception") diff --git a/pkg/vm/vm_test.go b/pkg/vm/vm_test.go index a813304bfd..7a924c10dd 100644 --- a/pkg/vm/vm_test.go +++ b/pkg/vm/vm_test.go @@ -14,7 +14,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/interop/interopnames" "github.com/nspcc-dev/neo-go/pkg/encoding/bigint" "github.com/nspcc-dev/neo-go/pkg/io" - "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/vm/emit" "github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" @@ -805,7 +805,7 @@ func TestSerializeInterop(t *testing.T) { require.True(t, vm.HasFailed()) } -func getTestCallFlagsFunc(syscall []byte, flags smartcontract.CallFlag, result interface{}) func(t *testing.T) { +func getTestCallFlagsFunc(syscall []byte, flags callflag.CallFlag, result interface{}) func(t *testing.T) { return func(t *testing.T) { script := append([]byte{byte(opcode.SYSCALL)}, syscall...) v := newTestVM() @@ -823,11 +823,11 @@ func getTestCallFlagsFunc(syscall []byte, flags smartcontract.CallFlag, result i func TestCallFlags(t *testing.T) { noFlags := []byte{0x77, 0x77, 0x77, 0x77} readOnly := []byte{0x66, 0x66, 0x66, 0x66} - t.Run("NoFlagsNoRequired", getTestCallFlagsFunc(noFlags, smartcontract.NoneFlag, new(int))) - t.Run("ProvideFlagsNoRequired", getTestCallFlagsFunc(noFlags, smartcontract.AllowCall, new(int))) - t.Run("NoFlagsSomeRequired", getTestCallFlagsFunc(readOnly, smartcontract.NoneFlag, nil)) - t.Run("OnlyOneProvided", getTestCallFlagsFunc(readOnly, smartcontract.AllowCall, nil)) - t.Run("AllFlagsProvided", getTestCallFlagsFunc(readOnly, smartcontract.ReadOnly, new(int))) + t.Run("NoFlagsNoRequired", getTestCallFlagsFunc(noFlags, callflag.NoneFlag, new(int))) + t.Run("ProvideFlagsNoRequired", getTestCallFlagsFunc(noFlags, callflag.AllowCall, new(int))) + t.Run("NoFlagsSomeRequired", getTestCallFlagsFunc(readOnly, callflag.NoneFlag, nil)) + t.Run("OnlyOneProvided", getTestCallFlagsFunc(readOnly, callflag.AllowCall, nil)) + t.Run("AllFlagsProvided", getTestCallFlagsFunc(readOnly, callflag.ReadOnly, new(int))) } func callNTimes(n uint16) []byte { diff --git a/scripts/gendump/main.go b/scripts/gendump/main.go index 3a8cfc35b9..8b93aea5a1 100644 --- a/scripts/gendump/main.go +++ b/scripts/gendump/main.go @@ -20,6 +20,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag" "github.com/nspcc-dev/neo-go/pkg/vm/emit" "github.com/nspcc-dev/neo-go/pkg/wallet" "go.uber.org/zap" @@ -95,7 +96,7 @@ func main() { rand.Read(value) w := io.NewBufBinWriter() - emit.AppCallWithOperationAndArgs(w.BinWriter, contractHash, "put", key, value) + emit.AppCall(w.BinWriter, contractHash, "put", callflag.All, key, value) handleError("can't create transaction", w.Err) tx := transaction.New(netmode.UnitTestNet, w.Bytes(), 4_000_000)