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/io"
"github.com/nspcc-dev/neo-go/pkg/rpc/response/result" "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"
"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/manifest"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef" "github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
"github.com/nspcc-dev/neo-go/pkg/util" "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) return cli.NewExitError(fmt.Errorf("failed to get management contract's hash: %w", err), 1)
} }
buf := io.NewBufBinWriter() 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 { if buf.Err != nil {
return cli.NewExitError(fmt.Errorf("failed to create deployment script: %w", buf.Err), 1) 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) { func Update(script, manifest []byte) {
ctx := storage.GetReadOnlyContext() ctx := storage.GetReadOnlyContext()
mgmt := storage.Get(ctx, mgmtKey).(interop.Hash160) 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. // 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/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/encoding/address" "github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/io" "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/util"
"github.com/nspcc-dev/neo-go/pkg/vm/emit" "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/opcode"
@ -105,7 +106,7 @@ func handleCandidate(ctx *cli.Context, method string) error {
return err return err
} }
w := io.NewBufBinWriter() 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) emit.Opcodes(w.BinWriter, opcode.ASSERT)
tx, err := c.CreateTxFromScript(w.Bytes(), acc, -1, int64(gas), transaction.Signer{ tx, err := c.CreateTxFromScript(w.Bytes(), acc, -1, int64(gas), transaction.Signer{
Account: acc.Contract.ScriptHash(), Account: acc.Contract.ScriptHash(),
@ -167,7 +168,7 @@ func handleVote(ctx *cli.Context) error {
return cli.NewExitError(err, 1) return cli.NewExitError(err, 1)
} }
w := io.NewBufBinWriter() 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) emit.Opcodes(w.BinWriter, opcode.ASSERT)
tx, err := c.CreateTxFromScript(w.Bytes(), acc, -1, int64(gas), transaction.Signer{ 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 return false
} }
mgmt := storage.Get(ctx, mgmtKey).(interop.Hash160) 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.") runtime.Log("Contract updated.")
return true return true
} }
@ -58,7 +58,7 @@ func Tick() bool {
ticksLeft = ticksLeft.(int) - 1 ticksLeft = ticksLeft.(int) - 1
if ticksLeft == 0 { if ticksLeft == 0 {
runtime.Log("Fired!") runtime.Log("Fired!")
return contract.Call(runtime.GetExecutingScriptHash(), "selfDestroy").(bool) return contract.Call(runtime.GetExecutingScriptHash(), "selfDestroy", contract.All).(bool)
} }
storage.Put(ctx, ticksKey, ticksLeft) storage.Put(ctx, ticksKey, ticksLeft)
i := binary.Itoa(ticksLeft.(int), 10) i := binary.Itoa(ticksLeft.(int), 10)
@ -73,7 +73,7 @@ func SelfDestroy() bool {
return false return false
} }
mgmt := storage.Get(ctx, mgmtKey).(interop.Hash160) mgmt := storage.Get(ctx, mgmtKey).(interop.Hash160)
contract.Call(mgmt, "destroy") contract.Call(mgmt, "destroy", contract.All)
runtime.Log("Destroyed.") runtime.Log("Destroyed.")
return true 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/state"
"github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/io" "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/smartcontract/nef"
"github.com/nspcc-dev/neo-go/pkg/util" "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/emit"
@ -28,7 +29,7 @@ var (
func NewTransferFromOwner(bc blockchainer.Blockchainer, contractHash, to util.Uint160, amount int64, func NewTransferFromOwner(bc blockchainer.Blockchainer, contractHash, to util.Uint160, amount int64,
nonce, validUntil uint32) (*transaction.Transaction, error) { nonce, validUntil uint32) (*transaction.Transaction, error) {
w := io.NewBufBinWriter() 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) emit.Opcodes(w.BinWriter, opcode.ASSERT)
if w.Err != nil { if w.Err != nil {
return nil, w.Err return nil, w.Err
@ -76,7 +77,7 @@ func NewDeployTx(bc blockchainer.Blockchainer, name string, sender util.Uint160,
return nil, util.Uint160{}, err return nil, util.Uint160{}, err
} }
buf := io.NewBufBinWriter() 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 { if buf.Err != nil {
return nil, util.Uint160{}, buf.Err 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) emit.Opcodes(c.prog.BinWriter, opcode.PACK)
numArgs -= varSize - 1 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! // Check builtin first to avoid nil pointer on funcScope!
switch { switch {

View file

@ -150,7 +150,7 @@ func TestAppCall(t *testing.T) {
return a + n return a + n
} }
func CallInner() int { func CallInner() int {
return contract.Call(%s, "get42").(int) return contract.Call(%s, "get42", contract.All).(int)
}` }`
srcInner = fmt.Sprintf(srcInner, srcInner = fmt.Sprintf(srcInner,
fmt.Sprintf("%#v", cinterop.Hash160(barH.BytesBE()))) fmt.Sprintf("%#v", cinterop.Hash160(barH.BytesBE())))
@ -222,7 +222,7 @@ func TestAppCall(t *testing.T) {
func Main() []byte { func Main() []byte {
x := []byte{1, 2} x := []byte{1, 2}
y := []byte{3, 4} 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) return result.([]byte)
} }
` `
@ -241,7 +241,7 @@ func TestAppCall(t *testing.T) {
x := []byte{1, 2} x := []byte{1, 2}
y := []byte{3, 4} y := []byte{3, 4}
var addr = []byte(` + fmt.Sprintf("%#v", string(ih.BytesBE())) + `) 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) return result.([]byte)
} }
` `
@ -257,7 +257,7 @@ func TestAppCall(t *testing.T) {
import "github.com/nspcc-dev/neo-go/pkg/interop/contract" import "github.com/nspcc-dev/neo-go/pkg/interop/contract"
func Main() int { func Main() int {
var addr = []byte(` + fmt.Sprintf("%#v", string(ih.BytesBE())) + `) 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) return result.(int)
}` }`
@ -272,7 +272,7 @@ func TestAppCall(t *testing.T) {
import ee "github.com/nspcc-dev/neo-go/pkg/interop/contract" import ee "github.com/nspcc-dev/neo-go/pkg/interop/contract"
func Main() int { func Main() int {
var addr = []byte(` + fmt.Sprintf("%#v", string(ih.BytesBE())) + `) 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) return result.(int)
}` }`
v := spawnVM(t, ic, src) v := spawnVM(t, ic, src)
@ -288,7 +288,7 @@ func getAppCallScript(h string) string {
func Main() []byte { func Main() []byte {
x := []byte{1, 2} x := []byte{1, 2}
y := []byte{3, 4} y := []byte{3, 4}
result := contract.Call(` + h + `, "append", x, y) result := contract.Call(` + h + `, "append", contract.All, x, y)
return result.([]byte) return result.([]byte)
} }
` `
@ -298,7 +298,7 @@ func getCallExScript(h string, flags string) string {
return `package foo return `package foo
import "github.com/nspcc-dev/neo-go/pkg/interop/contract" import "github.com/nspcc-dev/neo-go/pkg/interop/contract"
func Main() int { func Main() int {
result := contract.CallEx(` + flags + `, ` + h + `, "callInner") result := contract.Call(` + h + `, "callInner", ` + flags + `)
return result.(int) return result.(int)
}` }`
} }

View file

@ -23,7 +23,6 @@ var syscalls = map[string]map[string]string{
}, },
"contract": { "contract": {
"Call": interopnames.SystemContractCall, "Call": interopnames.SystemContractCall,
"CallEx": interopnames.SystemContractCallEx,
"CreateStandardAccount": interopnames.SystemContractCreateStandardAccount, "CreateStandardAccount": interopnames.SystemContractCreateStandardAccount,
"IsStandard": interopnames.SystemContractIsStandard, "IsStandard": interopnames.SystemContractIsStandard,
"GetCallFlags": interopnames.SystemContractGetCallFlags, "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/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/smartcontract" "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/util"
"github.com/nspcc-dev/neo-go/pkg/vm/emit" "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/opcode"
@ -54,10 +55,10 @@ func initServiceNextConsensus(t *testing.T, newAcc *wallet.Account, offset uint3
// Transfer funds to new validator. // Transfer funds to new validator.
w := io.NewBufBinWriter() 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) acc.Contract.ScriptHash().BytesBE(), newPriv.GetScriptHash().BytesBE(), int64(native.NEOTotalSupply), nil)
emit.Opcodes(w.BinWriter, opcode.ASSERT) 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) acc.Contract.ScriptHash().BytesBE(), newPriv.GetScriptHash().BytesBE(), int64(1_000_000_000), nil)
emit.Opcodes(w.BinWriter, opcode.ASSERT) emit.Opcodes(w.BinWriter, opcode.ASSERT)
require.NoError(t, w.Err) require.NoError(t, w.Err)
@ -74,7 +75,7 @@ func initServiceNextConsensus(t *testing.T, newAcc *wallet.Account, offset uint3
// Register new candidate. // Register new candidate.
w.Reset() 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) require.NoError(t, w.Err)
tx = transaction.New(netmode.UnitTestNet, w.Bytes(), 20_000_000) 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. // Vote for new candidate.
w.Reset() w.Reset()
emit.AppCallWithOperationAndArgs(w.BinWriter, bc.GoverningTokenHash(), "vote", emit.AppCall(w.BinWriter, bc.GoverningTokenHash(), "vote", callflag.All,
newPriv.GetScriptHash(), newPriv.PublicKey().Bytes()) newPriv.GetScriptHash(), newPriv.PublicKey().Bytes())
emit.Opcodes(w.BinWriter, opcode.ASSERT) emit.Opcodes(w.BinWriter, opcode.ASSERT)
require.NoError(t, w.Err) 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/util"
"github.com/nspcc-dev/neo-go/pkg/vm" "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/emit"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"go.uber.org/zap" "go.uber.org/zap"
) )
@ -1675,7 +1674,6 @@ func (bc *Blockchain) initVerificationVM(ic *interop.Context, hash util.Uint160,
if cs.ID <= 0 { if cs.ID <= 0 {
w := io.NewBufBinWriter() w := io.NewBufBinWriter()
emit.Opcodes(w.BinWriter, opcode.DEPTH, opcode.PACK)
emit.String(w.BinWriter, manifest.MethodVerify) emit.String(w.BinWriter, manifest.MethodVerify)
if w.Err != nil { if w.Err != nil {
return w.Err 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/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/smartcontract" "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/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm"
@ -272,12 +273,12 @@ func TestVerifyTx(t *testing.T) {
if sc.Equals(gasHash) { if sc.Equals(gasHash) {
amount = 1_000_000_000 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) neoOwner, a.Contract.ScriptHash(), amount, nil)
emit.Opcodes(w.BinWriter, opcode.ASSERT) 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) neoOwner, testchain.CommitteeScriptHash(), int64(1_000_000_000), nil)
emit.Opcodes(w.BinWriter, opcode.ASSERT) emit.Opcodes(w.BinWriter, opcode.ASSERT)
require.NoError(t, w.Err) require.NoError(t, w.Err)
@ -973,7 +974,7 @@ func TestVerifyTx(t *testing.T) {
transaction.NotaryServiceFeePerKey + // fee for Notary attribute transaction.NotaryServiceFeePerKey + // fee for Notary attribute
fee.Opcode(bc.GetBaseExecFee(), // Notary verification script fee.Opcode(bc.GetBaseExecFee(), // Notary verification script
opcode.PUSHDATA1, opcode.RET, // invocation 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 opcode.PUSHDATA1, opcode.SYSCALL, opcode.RET) + // Neo.Native.Call
native.NotaryVerificationPrice // Notary witness verification price native.NotaryVerificationPrice // Notary witness verification price
tx.Scripts = []transaction.Witness{ 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/encoding/fixedn"
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/smartcontract" "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/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm"
@ -298,7 +299,7 @@ func initBasicChain(t *testing.T, bc *Blockchain) {
// Now invoke this contract. // Now invoke this contract.
script := io.NewBufBinWriter() 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 := transaction.New(testchain.Network(), script.Bytes(), 1*native.GASFactor)
txInv.Nonce = getNextNonce() txInv.Nonce = getNextNonce()
@ -328,7 +329,7 @@ func initBasicChain(t *testing.T, bc *Blockchain) {
require.NoError(t, bc.AddBlock(b)) require.NoError(t, bc.AddBlock(b))
w := io.NewBufBinWriter() 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 := transaction.New(testchain.Network(), w.Bytes(), 1*native.GASFactor)
initTx.Nonce = getNextNonce() initTx.Nonce = getNextNonce()
initTx.ValidUntilBlock = validUntilBlock 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 { func newNEP17Transfer(sc, from, to util.Uint160, amount int64, additionalArgs ...interface{}) *transaction.Transaction {
w := io.NewBufBinWriter() 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) emit.Opcodes(w.BinWriter, opcode.ASSERT)
script := w.Bytes() script := w.Bytes()
@ -430,7 +431,7 @@ func addNetworkFee(bc *Blockchain, tx *transaction.Transaction, sender *wallet.A
func prepareContractMethodInvoke(chain *Blockchain, sysfee int64, func prepareContractMethodInvoke(chain *Blockchain, sysfee int64,
hash util.Uint160, method string, args ...interface{}) (*transaction.Transaction, error) { hash util.Uint160, method string, args ...interface{}) (*transaction.Transaction, error) {
w := io.NewBufBinWriter() w := io.NewBufBinWriter()
emit.AppCallWithOperationAndArgs(w.BinWriter, hash, method, args...) emit.AppCall(w.BinWriter, hash, method, callflag.All, args...)
if w.Err != nil { if w.Err != nil {
return nil, w.Err 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)) require.Equal(t, 0, len(res[0].Stack))
w := io.NewBufBinWriter() w := io.NewBufBinWriter()
emit.AppCallWithOperationAndArgs(w.BinWriter, hash, method, args...) emit.AppCall(w.BinWriter, hash, method, callflag.All, args...)
if w.Err != nil { if w.Err != nil {
return nil, w.Err 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/interop"
"github.com/nspcc-dev/neo-go/pkg/core/state" "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/callflag"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
@ -14,27 +15,19 @@ import (
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem" "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 { func Call(ic *interop.Context) error {
h := ic.VM.Estack().Pop().Bytes() h := ic.VM.Estack().Pop().Bytes()
method := ic.VM.Estack().Pop().String() 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())) fs := callflag.CallFlag(int32(ic.VM.Estack().Pop().BigInt().Int64()))
if fs&^callflag.All != 0 { if fs&^callflag.All != 0 {
return errors.New("call flags out of range") 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) u, err := util.Uint160DecodeBytesBE(h)
if err != nil { if err != nil {
return errors.New("invalid contract hash") return errors.New("invalid contract hash")
@ -50,6 +43,10 @@ func callExInternal(ic *interop.Context, h []byte, name string, args []stackitem
if md == nil { if md == nil {
return errors.New("method not found") return errors.New("method not found")
} }
hasReturn := md.ReturnType != smartcontract.VoidType
if !hasReturn {
ic.VM.Estack().PushVal(stackitem.Null{})
}
if md.Safe { if md.Safe {
f &^= callflag.WriteStates f &^= callflag.WriteStates
} else if ctx := ic.VM.Context(); ctx != nil && ctx.IsDeployed() { } 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) return callExFromNative(ic, ic.VM.GetCurrentScriptHash(), cs, name, args, f, hasReturn)
}
// 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)
} }
// callExFromNative calls a contract with flags using provided calling hash. // callExFromNative calls a contract with flags using provided calling hash.
func callExFromNative(ic *interop.Context, caller util.Uint160, cs *state.Contract, 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) md := cs.Manifest.ABI.GetMethod(name)
if md == nil { if md == nil {
return fmt.Errorf("method '%s' not found", name) 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.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 var isNative bool
for i := range ic.Natives { for i := range ic.Natives {
if ic.Natives[i].Metadata().Hash.Equals(cs.Hash) { 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 break
} }
} }
if isNative {
ic.VM.Estack().PushVal(args)
ic.VM.Estack().PushVal(name)
} else {
for i := len(args) - 1; i >= 0; i-- { for i := len(args) - 1; i >= 0; i-- {
ic.VM.Estack().PushVal(args[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. // use Jump not Call here because context was loaded in LoadScript above.
ic.VM.Jump(ic.VM.Context(), md.Offset) 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) md = cs.Manifest.ABI.GetMethod(manifest.MethodInit)
if md != nil { 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") var ErrNativeCall = errors.New("error during call from native")
// CallFromNative performs synchronous call from native contract. // 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() 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 return err
} }

View file

@ -20,7 +20,6 @@ const (
SystemCallbackCreateFromSyscall = "System.Callback.CreateFromSyscall" SystemCallbackCreateFromSyscall = "System.Callback.CreateFromSyscall"
SystemCallbackInvoke = "System.Callback.Invoke" SystemCallbackInvoke = "System.Callback.Invoke"
SystemContractCall = "System.Contract.Call" SystemContractCall = "System.Contract.Call"
SystemContractCallEx = "System.Contract.CallEx"
SystemContractCallNative = "System.Contract.CallNative" SystemContractCallNative = "System.Contract.CallNative"
SystemContractCreateStandardAccount = "System.Contract.CreateStandardAccount" SystemContractCreateStandardAccount = "System.Contract.CreateStandardAccount"
SystemContractIsStandard = "System.Contract.IsStandard" SystemContractIsStandard = "System.Contract.IsStandard"
@ -86,7 +85,6 @@ var names = []string{
SystemCallbackCreateFromSyscall, SystemCallbackCreateFromSyscall,
SystemCallbackInvoke, SystemCallbackInvoke,
SystemContractCall, SystemContractCall,
SystemContractCallEx,
SystemContractCallNative, SystemContractCallNative,
SystemContractCreateStandardAccount, SystemContractCreateStandardAccount,
SystemContractIsStandard, SystemContractIsStandard,

View file

@ -502,13 +502,12 @@ func getTestContractState(bc *Blockchain) (*state.Contract, *state.Contract) {
updateOff := w.Len() updateOff := w.Len()
emit.Int(w.BinWriter, 2) emit.Int(w.BinWriter, 2)
emit.Opcodes(w.BinWriter, opcode.PACK) emit.Opcodes(w.BinWriter, opcode.PACK)
emit.String(w.BinWriter, "update") emit.AppCallNoArgs(w.BinWriter, mgmtHash, "update", callflag.All)
emit.AppCall(w.BinWriter, mgmtHash) emit.Opcodes(w.BinWriter, opcode.DROP)
emit.Opcodes(w.BinWriter, opcode.RET) emit.Opcodes(w.BinWriter, opcode.RET)
destroyOff := w.Len() destroyOff := w.Len()
emit.Opcodes(w.BinWriter, opcode.NEWARRAY0) emit.AppCall(w.BinWriter, mgmtHash, "destroy", callflag.All)
emit.String(w.BinWriter, "destroy") emit.Opcodes(w.BinWriter, opcode.DROP)
emit.AppCall(w.BinWriter, mgmtHash)
emit.Opcodes(w.BinWriter, opcode.RET) emit.Opcodes(w.BinWriter, opcode.RET)
invalidStackOff := w.Len() invalidStackOff := w.Len()
emit.Opcodes(w.BinWriter, opcode.NEWARRAY0, opcode.DUP, opcode.DUP, opcode.APPEND, opcode.NEWMAP) 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) { t.Run("Good", func(t *testing.T) {
loadScript(ic, currScript, 42) loadScript(ic, currScript, 42)
ic.VM.Estack().PushVal(addArgs) ic.VM.Estack().PushVal(addArgs)
ic.VM.Estack().PushVal(callflag.All)
ic.VM.Estack().PushVal("add") ic.VM.Estack().PushVal("add")
ic.VM.Estack().PushVal(h.BytesBE()) ic.VM.Estack().PushVal(h.BytesBE())
require.NoError(t, contract.Call(ic)) require.NoError(t, contract.Call(ic))
@ -696,11 +696,11 @@ func TestContractCall(t *testing.T) {
t.Run("CallExInvalidFlag", func(t *testing.T) { t.Run("CallExInvalidFlag", func(t *testing.T) {
loadScript(ic, currScript, 42) loadScript(ic, currScript, 42)
ic.VM.Estack().PushVal(byte(0xFF))
ic.VM.Estack().PushVal(addArgs) ic.VM.Estack().PushVal(addArgs)
ic.VM.Estack().PushVal(byte(0xFF))
ic.VM.Estack().PushVal("add") ic.VM.Estack().PushVal("add")
ic.VM.Estack().PushVal(h.BytesBE()) 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) { runInvalid := func(args ...interface{}) func(t *testing.T) {
@ -735,6 +735,7 @@ func TestContractCall(t *testing.T) {
t.Run("Many", func(t *testing.T) { t.Run("Many", func(t *testing.T) {
loadScript(ic, currScript, 42) loadScript(ic, currScript, 42)
ic.VM.Estack().PushVal(stackitem.NewArray(nil)) ic.VM.Estack().PushVal(stackitem.NewArray(nil))
ic.VM.Estack().PushVal(callflag.All)
ic.VM.Estack().PushVal("invalidReturn") ic.VM.Estack().PushVal("invalidReturn")
ic.VM.Estack().PushVal(h.BytesBE()) ic.VM.Estack().PushVal(h.BytesBE())
require.NoError(t, contract.Call(ic)) require.NoError(t, contract.Call(ic))
@ -743,6 +744,7 @@ func TestContractCall(t *testing.T) {
t.Run("Void", func(t *testing.T) { t.Run("Void", func(t *testing.T) {
loadScript(ic, currScript, 42) loadScript(ic, currScript, 42)
ic.VM.Estack().PushVal(stackitem.NewArray(nil)) ic.VM.Estack().PushVal(stackitem.NewArray(nil))
ic.VM.Estack().PushVal(callflag.All)
ic.VM.Estack().PushVal("justReturn") ic.VM.Estack().PushVal("justReturn")
ic.VM.Estack().PushVal(h.BytesBE()) ic.VM.Estack().PushVal(h.BytesBE())
require.NoError(t, contract.Call(ic)) require.NoError(t, contract.Call(ic))
@ -756,6 +758,7 @@ func TestContractCall(t *testing.T) {
t.Run("IsolatedStack", func(t *testing.T) { t.Run("IsolatedStack", func(t *testing.T) {
loadScript(ic, currScript, 42) loadScript(ic, currScript, 42)
ic.VM.Estack().PushVal(stackitem.NewArray(nil)) ic.VM.Estack().PushVal(stackitem.NewArray(nil))
ic.VM.Estack().PushVal(callflag.All)
ic.VM.Estack().PushVal("drop") ic.VM.Estack().PushVal("drop")
ic.VM.Estack().PushVal(h.BytesBE()) ic.VM.Estack().PushVal(h.BytesBE())
require.NoError(t, contract.Call(ic)) require.NoError(t, contract.Call(ic))
@ -767,6 +770,7 @@ func TestContractCall(t *testing.T) {
loadScript(ic, currScript, 42) loadScript(ic, currScript, 42)
ic.VM.Estack().PushVal(stackitem.NewArray([]stackitem.Item{stackitem.Make(5)})) 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("add3")
ic.VM.Estack().PushVal(h.BytesBE()) ic.VM.Estack().PushVal(h.BytesBE())
require.NoError(t, contract.Call(ic)) require.NoError(t, contract.Call(ic))

View file

@ -51,8 +51,6 @@ var systemInterops = []interop.Function{
{Name: interopnames.SystemBlockchainGetTransactionHeight, Func: bcGetTransactionHeight, Price: 1 << 15, {Name: interopnames.SystemBlockchainGetTransactionHeight, Func: bcGetTransactionHeight, Price: 1 << 15,
RequiredFlags: callflag.ReadStates, ParamCount: 1}, RequiredFlags: callflag.ReadStates, ParamCount: 1},
{Name: interopnames.SystemContractCall, Func: contract.Call, Price: 1 << 15, {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}, RequiredFlags: callflag.AllowCall, ParamCount: 4},
{Name: interopnames.SystemContractCallNative, Func: native.Call, Price: 0, ParamCount: 1}, {Name: interopnames.SystemContractCallNative, Func: native.Call, Price: 0, ParamCount: 1},
{Name: interopnames.SystemContractCreateStandardAccount, Func: contractCreateStandardAccount, Price: 1 << 8, 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/core/interop"
"github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
) )
// Call calls specified native contract method. // 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") 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() operation := ic.VM.Estack().Pop().String()
args := ic.VM.Estack().Pop().Array()
m, ok := c.Metadata().Methods[operation] m, ok := c.Metadata().Methods[operation]
if !ok { if !ok {
return fmt.Errorf("method %s not found", operation) return fmt.Errorf("method %s not found", operation)
@ -40,6 +40,10 @@ func Call(ic *interop.Context) error {
return errors.New("gas limit exceeded") return errors.New("gas limit exceeded")
} }
ctx := ic.VM.Context() 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) result := m.Func(ic, args)
if m.MD.ReturnType != smartcontract.VoidType { if m.MD.ReturnType != smartcontract.VoidType {
ctx.Estack().PushVal(result) 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/manifest"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef" "github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
"github.com/nspcc-dev/neo-go/pkg/util" "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" "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 { if err != nil {
panic(err) panic(err)
} }
callDeploy(ic, newcontract, false) m.callDeploy(ic, newcontract, false)
m.emitNotification(ic, contractDeployNotificationName, newcontract.Hash) m.emitNotification(ic, contractDeployNotificationName, newcontract.Hash)
return contractToStack(newcontract) return contractToStack(newcontract)
} }
@ -278,7 +277,7 @@ func (m *Management) update(ic *interop.Context, args []stackitem.Item) stackite
if err != nil { if err != nil {
panic(err) panic(err)
} }
callDeploy(ic, contract, true) m.callDeploy(ic, contract, true)
m.emitNotification(ic, contractUpdateNotificationName, contract.Hash) m.emitNotification(ic, contractUpdateNotificationName, contract.Hash)
return stackitem.Null{} return stackitem.Null{}
} }
@ -380,11 +379,11 @@ func (m *Management) setMinimumDeploymentFee(ic *interop.Context, args []stackit
return stackitem.NewBool(true) 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) md := cs.Manifest.ABI.GetMethod(manifest.MethodDeploy)
if md != nil { if md != nil {
err := contract.CallExInternal(ic, cs, manifest.MethodDeploy, err := contract.CallFromNative(ic, m.Hash, cs, manifest.MethodDeploy,
[]stackitem.Item{stackitem.NewBool(isUpdate)}, callflag.All, vm.EnsureIsEmpty) []stackitem.Item{stackitem.NewBool(isUpdate)}, false)
if err != nil { if err != nil {
panic(err) 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/callflag"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/util" "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" "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), stackitem.NewBigInteger(amount),
data, 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) 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/callflag"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/util" "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" "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)) 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{}} 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 { if err != nil {
panic(fmt.Errorf("failed to transfer GAS from Notary account: %w", err)) 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/callflag"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/util" "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/emit"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/nspcc-dev/neo-go/pkg/vm/opcode"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
@ -57,6 +56,7 @@ func init() {
w := io.NewBufBinWriter() w := io.NewBufBinWriter()
emit.Int(w.BinWriter, 0) emit.Int(w.BinWriter, 0)
emit.Opcodes(w.BinWriter, opcode.NEWARRAY) emit.Opcodes(w.BinWriter, opcode.NEWARRAY)
emit.Int(w.BinWriter, int64(callflag.All))
emit.String(w.BinWriter, "finish") emit.String(w.BinWriter, "finish")
emit.Bytes(w.BinWriter, h.BytesBE()) emit.Bytes(w.BinWriter, h.BytesBE())
emit.Syscall(w.BinWriter, interopnames.SystemContractCall) emit.Syscall(w.BinWriter, interopnames.SystemContractCall)
@ -239,7 +239,7 @@ func (o *Oracle) FinishInternal(ic *interop.Context) error {
if err != nil { if err != nil {
return err 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 { 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 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])) cs, err := ic.GetContract(toUint160(args[0]))
if err != nil { if err != nil {
panic(err) panic(err)
@ -147,19 +147,19 @@ func (tn *testNative) call(ic *interop.Context, args []stackitem.Item, checkRetu
if err != nil { if err != nil {
panic(err) 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 { if err != nil {
panic(err) panic(err)
} }
} }
func (tn *testNative) callOtherContractNoReturn(ic *interop.Context, args []stackitem.Item) stackitem.Item { 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{} return stackitem.Null{}
} }
func (tn *testNative) callOtherContractWithReturn(ic *interop.Context, args []stackitem.Item) stackitem.Item { 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() bi := ic.VM.Estack().Pop().BigInt()
return stackitem.Make(bi.Add(bi, big.NewInt(1))) 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 += 3 * fee.Opcode(chain.GetBaseExecFee(), opcode.PUSHINT8, opcode.PUSHDATA1)
price += 2 * fee.Opcode(chain.GetBaseExecFee(), opcode.SYSCALL) price += 2 * fee.Opcode(chain.GetBaseExecFee(), opcode.SYSCALL)
price += fee.Opcode(chain.GetBaseExecFee(), opcode.PACK) 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)) res, err := invokeContractMethod(chain, price, tn.Metadata().Hash, "sum", int64(14), int64(28))
require.NoError(t, err) require.NoError(t, err)
checkResult(t, res, stackitem.Make(42)) 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) { t.Run("fail, bad current script hash", func(t *testing.T) {
v.LoadScriptWithHash([]byte{1}, util.Uint160{1, 2, 3}, callflag.All) 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("sum")
v.Estack().PushVal(tn.Metadata().Name) v.Estack().PushVal(tn.Metadata().Name)
@ -232,7 +234,8 @@ func TestNativeContract_InvokeInternal(t *testing.T) {
t.Run("success", func(t *testing.T) { t.Run("success", func(t *testing.T) {
v.LoadScriptWithHash([]byte{1}, tn.Metadata().Hash, callflag.All) 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("sum")
v.Estack().PushVal(tn.Metadata().Name) 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{}{}) res, err := invokeContractMethod(chain, testSumPrice*4+10000, tn.Metadata().Hash, "callOtherContractNoReturn", cs.Hash, "justReturn", []interface{}{})
require.NoError(t, err) require.NoError(t, err)
drainTN(t) drainTN(t)
require.Equal(t, vm.HaltState, res.VMState, res.FaultException)
checkResult(t, res, stackitem.Null{}) // simple call is done with EnsureNotEmpty checkResult(t, res, stackitem.Null{}) // simple call is done with EnsureNotEmpty
}) })
t.Run("non-native, with return", func(t *testing.T) { 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/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/io" "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/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/vm" "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/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, int64(r))
emit.Int(w.BinWriter, 2) emit.Int(w.BinWriter, 2)
emit.Opcodes(w.BinWriter, opcode.PACK) emit.Opcodes(w.BinWriter, opcode.PACK)
emit.String(w.BinWriter, "designateAsRole") emit.AppCallNoArgs(w.BinWriter, bc.contracts.Designate.Hash, "designateAsRole", callflag.All)
emit.AppCall(w.BinWriter, bc.contracts.Designate.Hash)
require.NoError(t, w.Err) require.NoError(t, w.Err)
tx := transaction.New(netmode.UnitTestNet, w.Bytes(), 0) tx := transaction.New(netmode.UnitTestNet, w.Bytes(), 0)
tx.NetworkFee = 10_000_000 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/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/io" "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/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm"
@ -75,11 +76,11 @@ func TestNEO_Vote(t *testing.T) {
to := accs[i].Contract.ScriptHash() to := accs[i].Contract.ScriptHash()
w := io.NewBufBinWriter() 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(), neoOwner.BytesBE(), to.BytesBE(),
big.NewInt(int64(sz-i)*1000000).Int64(), nil) big.NewInt(int64(sz-i)*1000000).Int64(), nil)
emit.Opcodes(w.BinWriter, opcode.ASSERT) 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(), neoOwner.BytesBE(), to.BytesBE(),
int64(1_000_000_000), nil) int64(1_000_000_000), nil)
emit.Opcodes(w.BinWriter, opcode.ASSERT) emit.Opcodes(w.BinWriter, opcode.ASSERT)
@ -141,7 +142,7 @@ func TestNEO_Vote(t *testing.T) {
h := accs[i].PrivateKey().GetScriptHash() h := accs[i].PrivateKey().GetScriptHash()
gasBalance[i] = bc.GetUtilityTokenBalance(h) gasBalance[i] = bc.GetUtilityTokenBalance(h)
neoBalance[i], _ = bc.GetGoverningTokenBalance(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) h.BytesBE(), h.BytesBE(), int64(1), nil)
emit.Opcodes(w.BinWriter, opcode.ASSERT) emit.Opcodes(w.BinWriter, opcode.ASSERT)
require.NoError(t, w.Err) 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/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/encoding/bigint" "github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
"github.com/nspcc-dev/neo-go/pkg/io" "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/smartcontract/trigger"
"github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm"
@ -174,7 +175,8 @@ func TestNotaryContractPipeline(t *testing.T) {
// `withdraw`: bad witness // `withdraw`: bad witness
w := io.NewBufBinWriter() 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) require.NoError(t, w.Err)
script := w.Bytes() script := w.Bytes()
withdrawTx := transaction.New(chain.GetConfig().Magic, script, 10000000) withdrawTx := transaction.New(chain.GetConfig().Magic, script, 10000000)

View file

@ -33,9 +33,11 @@ func getOracleContractState(h util.Uint160) *state.Contract {
w := io.NewBufBinWriter() w := io.NewBufBinWriter()
emit.Int(w.BinWriter, 5) emit.Int(w.BinWriter, 5)
emit.Opcodes(w.BinWriter, opcode.PACK) emit.Opcodes(w.BinWriter, opcode.PACK)
emit.Int(w.BinWriter, int64(callflag.All))
emit.String(w.BinWriter, "request") emit.String(w.BinWriter, "request")
emit.Bytes(w.BinWriter, h.BytesBE()) emit.Bytes(w.BinWriter, h.BytesBE())
emit.Syscall(w.BinWriter, interopnames.SystemContractCall) emit.Syscall(w.BinWriter, interopnames.SystemContractCall)
emit.Opcodes(w.BinWriter, opcode.DROP)
emit.Opcodes(w.BinWriter, opcode.RET) emit.Opcodes(w.BinWriter, opcode.RET)
// `handle` method aborts if len(userData) == 2 // `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 // 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. // (20 bytes in BE form) using provided arguments and call flags.
// It returns whatever this contract returns. This function uses // It returns whatever this contract returns. This function uses
// `System.Contract.CallEx` syscall. // `System.Contract.Call` syscall.
func CallEx(f CallFlag, scriptHash interop.Hash160, method string, args ...interface{}) interface{} { func Call(scriptHash interop.Hash160, method string, f CallFlag, args ...interface{}) interface{} {
return nil return nil
} }

View file

@ -9,6 +9,7 @@ import (
"github.com/nspcc-dev/neo-go/pkg/io" "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/rpc/response/result"
"github.com/nspcc-dev/neo-go/pkg/smartcontract" "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/util"
"github.com/nspcc-dev/neo-go/pkg/vm/emit" "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/opcode"
@ -120,8 +121,8 @@ func (c *Client) CreateNEP17MultiTransferTx(acc *wallet.Account, gas int64, reci
} }
w := io.NewBufBinWriter() w := io.NewBufBinWriter()
for i := range recipients { for i := range recipients {
emit.AppCallWithOperationAndArgs(w.BinWriter, recipients[i].Token, "transfer", from, emit.AppCall(w.BinWriter, recipients[i].Token, "transfer",
recipients[i].Address, recipients[i].Amount, nil) callflag.WriteStates|callflag.AllowCall|callflag.AllowNotify, from, recipients[i].Address, recipients[i].Amount, nil)
emit.Opcodes(w.BinWriter, opcode.ASSERT) emit.Opcodes(w.BinWriter, opcode.ASSERT)
} }
accAddr, err := address.StringToUint160(acc.Address) 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/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/smartcontract" "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/util"
"github.com/nspcc-dev/neo-go/pkg/vm/emit" "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/opcode"
@ -101,7 +102,7 @@ func expandArrayIntoScript(script *io.BinWriter, slice []Param) error {
// CreateFunctionInvocationScript creates a script to invoke given contract with // CreateFunctionInvocationScript creates a script to invoke given contract with
// given parameters. // 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() script := io.NewBufBinWriter()
for i := len(params) - 1; i >= 0; i-- { for i := len(params) - 1; i >= 0; i-- {
switch params[i].Type { 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 return script.Bytes(), nil
} }

View file

@ -21,46 +21,44 @@ func TestInvocationScriptCreationGood(t *testing.T) {
ps Params ps Params
script string script string
}{{ }{{
script: "0c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52",
}, {
ps: Params{{Type: StringT, Value: "transfer"}}, ps: Params{{Type: StringT, Value: "transfer"}},
script: "0c087472616e736665720c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52", script: "1f0c087472616e736665720c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52",
}, { }, {
ps: Params{{Type: NumberT, Value: 42}}, ps: Params{{Type: NumberT, Value: 42}},
script: "0c0234320c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52", script: "1f0c0234320c146f459162ceeb248b071ec157d9e4f6fd26fdbe5041627d5b52",
}, { }, {
ps: Params{{Type: StringT, Value: "a"}, {Type: ArrayT, Value: []Param{}}}, 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"}}}}}}, 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"}}}}}}, 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"}}}}}}, 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"}}}}}}, 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"}}}}}}, 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"}}}}}}, 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}}}}}}, 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"}}}}}}, 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"}}}}}}, 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 { 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.Nil(t, err)
assert.Equal(t, ps.script, hex.EncodeToString(script)) 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{}}}}}}, {{Type: ArrayT, Value: []Param{{Type: FuncParamT, Value: FuncParam{Type: smartcontract.UnknownType, Value: Param{}}}}}},
} }
for _, ps := range testParams { for _, ps := range testParams {
_, err := CreateFunctionInvocationScript(contract, ps) _, err := CreateFunctionInvocationScript(contract, "", ps)
assert.NotNil(t, err) assert.NotNil(t, err)
} }
} }

View file

@ -1097,7 +1097,7 @@ func (s *Server) invokeFunction(reqParams request.Params) (interface{}, *respons
if len(tx.Signers) == 0 { if len(tx.Signers) == 0 {
tx.Signers = []transaction.Signer{{Account: util.Uint160{}, Scopes: transaction.None}} 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 { if err != nil {
return nil, response.NewInternalServerError("can't create invocation script", err) 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 return nil, responseErr
} }
args := make(request.Params, 1) args := reqParams[1:2]
args[0] = request.Param{
Type: request.StringT,
Value: manifest.MethodVerify,
}
if len(reqParams) > 1 {
args = append(args, reqParams[1])
}
var tx *transaction.Transaction var tx *transaction.Transaction
if len(reqParams) > 2 { if len(reqParams) > 2 {
signers, witnesses, err := reqParams[2].GetSignersWithWitnesses() signers, witnesses, err := reqParams[2].GetSignersWithWitnesses()
@ -1162,7 +1155,7 @@ func (s *Server) invokeContractVerify(reqParams request.Params) (interface{}, *r
if cs == nil { if cs == nil {
return nil, response.NewRPCError("unknown contract", scriptHash.StringBE(), 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 { if err != nil {
return nil, response.NewInternalServerError("can't create invocation script", err) return nil, response.NewInternalServerError("can't create invocation script", err)
} }

View file

@ -57,7 +57,7 @@ type rpcTestCase struct {
} }
const testContractHash = "743ed26f78e29ecd595535b74a943b1f9ccbc444" const testContractHash = "743ed26f78e29ecd595535b74a943b1f9ccbc444"
const deploymentTxHash = "a4644e08389ced59e875a8d5bdcb8fdded7507ed7dc3915a37e5be71736ac7b4" const deploymentTxHash = "2aef5684c6cf60884cc00400b78c65c5105ed4261a4f614bce74c74927a66bf3"
const genesisBlockHash = "0542f4350c6e236d0509bcd98188b0034bfbecc1a0c7fcdb8e4295310d468b70" const genesisBlockHash = "0542f4350c6e236d0509bcd98188b0034bfbecc1a0c7fcdb8e4295310d468b70"
const verifyContractHash = "a2eb22340979804cb10cc1add0b8822c201f4d8a" const verifyContractHash = "a2eb22340979804cb10cc1add0b8822c201f4d8a"
@ -1408,7 +1408,7 @@ func checkNep17Balances(t *testing.T, e *executor, acc interface{}) {
}, },
{ {
Asset: e.chain.UtilityTokenHash(), Asset: e.chain.UtilityTokenHash(),
Amount: "80006564770", Amount: "80006675650",
LastUpdated: 7, LastUpdated: 7,
}}, }},
Address: testchain.PrivateKeyByID(0).GetScriptHash().StringLE(), 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. // Call flags this context was created with.
callFlag callflag.CallFlag callFlag callflag.CallFlag
// CheckReturn specifies if amount of return values needs to be checked. // ParamCount specifies number of parameters.
CheckReturn CheckReturnState ParamCount int
// RetCount specifies number of return values.
RetCount int
} }
// CheckReturnState represents possible states of stack after opcode.RET was processed. // 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. // NewContext returns a new Context object.
func NewContext(b []byte) *Context { 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{ return &Context{
prog: b, prog: b,
breakPoints: []int{}, 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/core/interop/interopnames"
"github.com/nspcc-dev/neo-go/pkg/encoding/bigint" "github.com/nspcc-dev/neo-go/pkg/encoding/bigint"
"github.com/nspcc-dev/neo-go/pkg/io" "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/util"
"github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/nspcc-dev/neo-go/pkg/vm/opcode"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem" "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) Instruction(w, op, buf)
} }
// AppCall emits call to provided contract. // AppCallNoArgs emits call to provided contract.
func AppCall(w *io.BinWriter, scriptHash util.Uint160) { func AppCallNoArgs(w *io.BinWriter, scriptHash util.Uint160, operation string, f callflag.CallFlag) {
Int(w, int64(f))
String(w, operation)
Bytes(w, scriptHash.BytesBE()) Bytes(w, scriptHash.BytesBE())
Syscall(w, interopnames.SystemContractCall) Syscall(w, interopnames.SystemContractCall)
} }
// AppCallWithOperationAndArgs emits an APPCALL with the given operation and arguments. // AppCall emits an APPCALL with the default parameters given operation and arguments.
func AppCallWithOperationAndArgs(w *io.BinWriter, scriptHash util.Uint160, operation string, args ...interface{}) { func AppCall(w *io.BinWriter, scriptHash util.Uint160, operation string, f callflag.CallFlag, args ...interface{}) {
Array(w, args...) Array(w, args...)
String(w, operation) AppCallNoArgs(w, scriptHash, operation, f)
AppCall(w, scriptHash)
} }
func isInstructionJmp(op opcode.Opcode) bool { 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. // LoadScriptWithFlags loads script and sets call flag to f.
func (v *VM) LoadScriptWithFlags(b []byte, f callflag.CallFlag) { func (v *VM) LoadScriptWithFlags(b []byte, f callflag.CallFlag) {
v.checkInvocationStackSize() v.checkInvocationStackSize()
ctx := NewContext(b) ctx := NewContextWithParams(b, 0, -1, 0)
v.estack = v.newItemStack("estack") v.estack = v.newItemStack("estack")
ctx.estack = v.estack ctx.estack = v.estack
ctx.tryStack = NewStack("exception") ctx.tryStack = NewStack("exception")
@ -296,17 +296,24 @@ func (v *VM) LoadScriptWithFlags(b []byte, f callflag.CallFlag) {
// each other. // each other.
func (v *VM) LoadScriptWithHash(b []byte, hash util.Uint160, f callflag.CallFlag) { func (v *VM) LoadScriptWithHash(b []byte, hash util.Uint160, f callflag.CallFlag) {
shash := v.GetCurrentScriptHash() 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. // LoadScriptWithCallingHash is similar to LoadScriptWithHash but sets calling hash explicitly.
// It should be used for calling from native contracts. // 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) v.LoadScriptWithFlags(b, f)
ctx := v.Context() ctx := v.Context()
ctx.isDeployed = true ctx.isDeployed = true
ctx.scriptHash = hash ctx.scriptHash = hash
ctx.callingScriptHash = caller 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, // 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 newEstack := v.Context().estack
if oldEstack != newEstack { 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() rvcount := oldEstack.Len()
for i := rvcount; i > 0; i-- { for i := rvcount; i > 0; i-- {
elem := oldEstack.RemoveAt(i - 1) 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 { if ctx.static != nil && currCtx != nil && ctx.static != currCtx.static {
ctx.static.Clear() 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. // 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) { func (v *VM) call(ctx *Context, offset int) {
v.checkInvocationStackSize() v.checkInvocationStackSize()
newCtx := ctx.Copy() newCtx := ctx.Copy()
newCtx.CheckReturn = NoCheck newCtx.RetCount = -1
newCtx.local = nil newCtx.local = nil
newCtx.arguments = nil newCtx.arguments = nil
newCtx.tryStack = NewStack("exception") 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/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/io"
"github.com/nspcc-dev/neo-go/pkg/smartcontract" "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/emit"
"github.com/nspcc-dev/neo-go/pkg/wallet" "github.com/nspcc-dev/neo-go/pkg/wallet"
"go.uber.org/zap" "go.uber.org/zap"
@ -95,7 +96,7 @@ func main() {
rand.Read(value) rand.Read(value)
w := io.NewBufBinWriter() 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) handleError("can't create transaction", w.Err)
tx := transaction.New(netmode.UnitTestNet, w.Bytes(), 4_000_000) tx := transaction.New(netmode.UnitTestNet, w.Bytes(), 4_000_000)