core: update System.Contract.Call syscall

1. Remove `System.Contract.CallEx`.
2. Extend number of parameters.
3. Add return value count to `VM.Context`.
This commit is contained in:
Evgenii Stratonikov 2020-12-29 13:44:07 +03:00 committed by Evgeniy Stratonikov
parent 86b0e76bf0
commit 1c0c331e25
38 changed files with 170 additions and 171 deletions

View file

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

Binary file not shown.

View file

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

View file

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

View file

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

View file

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

View file

@ -866,15 +866,8 @@ 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)
}
}
// Check builtin first to avoid nil pointer on funcScope!
switch {

View file

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

View file

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

View file

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

View file

@ -33,7 +33,6 @@ import (
"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"
)
@ -1675,7 +1674,6 @@ func (bc *Blockchain) initVerificationVM(ic *interop.Context, hash util.Uint160,
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

View file

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

View file

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

View file

@ -7,6 +7,7 @@ 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"
@ -14,27 +15,19 @@ import (
"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, callflag.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()
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, fs)
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 callflag.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,6 +43,10 @@ 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 &^= callflag.WriteStates
} else if ctx := ic.VM.Context(); ctx != nil && ctx.IsDeployed() {
@ -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 callflag.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 callflag.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
}
}
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])
}
if isNative {
ic.VM.Estack().PushVal(name)
} else {
// 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, callflag.All, checkReturn); err != nil {
if err := callExFromNative(ic, caller, cs, method, args, callflag.All, hasReturn); err != nil {
return err
}

View file

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

View file

@ -502,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)
@ -685,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))
@ -696,11 +696,11 @@ 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) {
@ -735,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))
@ -743,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))
@ -756,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))
@ -767,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))

View file

@ -51,8 +51,6 @@ var systemInterops = []interop.Function{
{Name: interopnames.SystemBlockchainGetTransactionHeight, Func: bcGetTransactionHeight, Price: 1 << 15,
RequiredFlags: callflag.ReadStates, ParamCount: 1},
{Name: interopnames.SystemContractCall, Func: contract.Call, Price: 1 << 15,
RequiredFlags: callflag.AllowCall, ParamCount: 3},
{Name: interopnames.SystemContractCallEx, Func: contract.CallEx, Price: 1 << 15,
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},

View file

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

View file

@ -21,7 +21,6 @@ import (
"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"
)
@ -222,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)
}
@ -278,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{}
}
@ -380,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)}, callflag.All, vm.EnsureIsEmpty)
err := contract.CallFromNative(ic, m.Hash, cs, manifest.MethodDeploy,
[]stackitem.Item{stackitem.NewBool(isUpdate)}, false)
if err != nil {
panic(err)
}

View file

@ -15,7 +15,6 @@ import (
"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"
)
@ -144,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)
}
}

View file

@ -20,7 +20,6 @@ import (
"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"
)
@ -277,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))
}

View file

@ -23,7 +23,6 @@ import (
"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"
@ -57,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)
@ -239,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 {

View file

@ -138,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)
@ -147,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)))
}
@ -184,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))
@ -222,7 +223,8 @@ func TestNativeContract_InvokeInternal(t *testing.T) {
t.Run("fail, bad current script hash", func(t *testing.T) {
v.LoadScriptWithHash([]byte{1}, util.Uint160{1, 2, 3}, callflag.All)
v.Estack().PushVal(stackitem.NewArray([]stackitem.Item{stackitem.NewBigInteger(big.NewInt(14)), stackitem.NewBigInteger(big.NewInt(28))}))
v.Estack().PushVal(14)
v.Estack().PushVal(28)
v.Estack().PushVal("sum")
v.Estack().PushVal(tn.Metadata().Name)
@ -232,7 +234,8 @@ func TestNativeContract_InvokeInternal(t *testing.T) {
t.Run("success", func(t *testing.T) {
v.LoadScriptWithHash([]byte{1}, tn.Metadata().Hash, callflag.All)
v.Estack().PushVal(stackitem.NewArray([]stackitem.Item{stackitem.NewBigInteger(big.NewInt(14)), stackitem.NewBigInteger(big.NewInt(28))}))
v.Estack().PushVal(14)
v.Estack().PushVal(28)
v.Estack().PushVal("sum")
v.Estack().PushVal(tn.Metadata().Name)
@ -273,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) {

View file

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

View file

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

View file

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

View file

@ -33,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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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(),

Binary file not shown.

View file

@ -48,8 +48,10 @@ type Context struct {
// Call flags this context was created with.
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,
}
}

View file

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

View file

@ -278,7 +278,7 @@ func (v *VM) LoadScript(b []byte) {
// LoadScriptWithFlags loads script and sets call flag to f.
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")
@ -296,17 +296,24 @@ func (v *VM) LoadScriptWithFlags(b []byte, f callflag.CallFlag) {
// each other.
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 callflag.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")

View file

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