mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-11-23 03:38:35 +00:00
compiler: add contract.CallEx
interop
This commit is contained in:
parent
ec58bec803
commit
37a8550215
5 changed files with 93 additions and 5 deletions
|
@ -850,8 +850,15 @@ 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 {
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||
cinterop "github.com/nspcc-dev/neo-go/pkg/interop"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm"
|
||||
|
@ -85,8 +86,26 @@ func spawnVM(t *testing.T, ic *interop.Context, src string) *vm.VM {
|
|||
}
|
||||
|
||||
func TestAppCall(t *testing.T) {
|
||||
srcInner := `
|
||||
package foo
|
||||
srcDeep := `package foo
|
||||
func Get42() int {
|
||||
return 42
|
||||
}`
|
||||
barCtr, di, err := compiler.CompileWithDebugInfo("bar.go", strings.NewReader(srcDeep))
|
||||
require.NoError(t, err)
|
||||
mBar, err := di.ConvertToManifest("Bar", nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
barH := hash.Hash160(barCtr)
|
||||
ic := interop.NewContext(trigger.Application, nil, dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet, false), nil, nil, nil, zaptest.NewLogger(t))
|
||||
require.NoError(t, ic.DAO.PutContractState(&state.Contract{
|
||||
Hash: barH,
|
||||
Script: barCtr,
|
||||
Manifest: *mBar,
|
||||
}))
|
||||
|
||||
srcInner := `package foo
|
||||
import "github.com/nspcc-dev/neo-go/pkg/interop/contract"
|
||||
import "github.com/nspcc-dev/neo-go/pkg/interop"
|
||||
var a int = 3
|
||||
func Main(a []byte, b []byte) []byte {
|
||||
panic("Main was called")
|
||||
|
@ -97,7 +116,11 @@ func TestAppCall(t *testing.T) {
|
|||
func Add3(n int) int {
|
||||
return a + n
|
||||
}
|
||||
`
|
||||
func CallInner() int {
|
||||
return contract.Call(%s, "get42").(int)
|
||||
}`
|
||||
srcInner = fmt.Sprintf(srcInner,
|
||||
fmt.Sprintf("%#v", cinterop.Hash160(barH.BytesBE())))
|
||||
|
||||
inner, di, err := compiler.CompileWithDebugInfo("foo.go", strings.NewReader(srcInner))
|
||||
require.NoError(t, err)
|
||||
|
@ -105,7 +128,6 @@ func TestAppCall(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
|
||||
ih := hash.Hash160(inner)
|
||||
ic := interop.NewContext(trigger.Application, nil, dao.NewSimple(storage.NewMemoryStore(), netmode.UnitTestNet, false), nil, nil, nil, zaptest.NewLogger(t))
|
||||
require.NoError(t, ic.DAO.PutContractState(&state.Contract{
|
||||
Hash: ih,
|
||||
Script: inner,
|
||||
|
@ -120,6 +142,19 @@ func TestAppCall(t *testing.T) {
|
|||
assertResult(t, v, []byte{1, 2, 3, 4})
|
||||
})
|
||||
|
||||
t.Run("callEx, valid", func(t *testing.T) {
|
||||
src := getCallExScript(fmt.Sprintf("%#v", ih.BytesBE()), "contract.AllowCall")
|
||||
v := spawnVM(t, ic, src)
|
||||
require.NoError(t, v.Run())
|
||||
|
||||
assertResult(t, v, big.NewInt(42))
|
||||
})
|
||||
t.Run("callEx, missing flags", func(t *testing.T) {
|
||||
src := getCallExScript(fmt.Sprintf("%#v", ih.BytesBE()), "contract.NoneFlag")
|
||||
v := spawnVM(t, ic, src)
|
||||
require.Error(t, v.Run())
|
||||
})
|
||||
|
||||
t.Run("missing script", func(t *testing.T) {
|
||||
h := ih
|
||||
h[0] = ^h[0]
|
||||
|
@ -209,6 +244,15 @@ func getAppCallScript(h string) string {
|
|||
`
|
||||
}
|
||||
|
||||
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")
|
||||
return result.(int)
|
||||
}`
|
||||
}
|
||||
|
||||
func TestBuiltinDoesNotCompile(t *testing.T) {
|
||||
src := `package foo
|
||||
import "github.com/nspcc-dev/neo-go/pkg/interop/util"
|
||||
|
|
|
@ -24,6 +24,7 @@ var syscalls = map[string]map[string]string{
|
|||
},
|
||||
"contract": {
|
||||
"Call": interopnames.SystemContractCall,
|
||||
"CallEx": interopnames.SystemContractCallEx,
|
||||
"Create": interopnames.SystemContractCreate,
|
||||
"CreateStandardAccount": interopnames.SystemContractCreateStandardAccount,
|
||||
"Destroy": interopnames.SystemContractDestroy,
|
||||
|
|
|
@ -4,11 +4,24 @@ import (
|
|||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/interop/contract"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// Checks that changes in `smartcontract` are reflected in compiler interop package.
|
||||
func TestCallFlags(t *testing.T) {
|
||||
require.EqualValues(t, contract.AllowStates, smartcontract.AllowStates)
|
||||
require.EqualValues(t, contract.AllowModifyStates, smartcontract.AllowModifyStates)
|
||||
require.EqualValues(t, contract.AllowCall, smartcontract.AllowCall)
|
||||
require.EqualValues(t, contract.AllowNotify, smartcontract.AllowNotify)
|
||||
require.EqualValues(t, contract.ReadOnly, smartcontract.ReadOnly)
|
||||
require.EqualValues(t, contract.All, smartcontract.All)
|
||||
require.EqualValues(t, contract.NoneFlag, smartcontract.NoneFlag)
|
||||
}
|
||||
|
||||
func TestStoragePutGet(t *testing.T) {
|
||||
src := `
|
||||
package foo
|
||||
|
|
|
@ -14,6 +14,21 @@ type Contract struct {
|
|||
Manifest []byte
|
||||
}
|
||||
|
||||
// CallFlag specifies valid call flags.
|
||||
type CallFlag byte
|
||||
|
||||
// Using `smartcontract` package from compiled contract requires moderate
|
||||
// compiler refactoring, thus all flags are mirrored here.
|
||||
const (
|
||||
AllowStates CallFlag = 1 << iota
|
||||
AllowModifyStates
|
||||
AllowCall
|
||||
AllowNotify
|
||||
ReadOnly = AllowStates | AllowCall | AllowNotify
|
||||
All = ReadOnly | AllowModifyStates
|
||||
NoneFlag CallFlag = 0
|
||||
)
|
||||
|
||||
// Create creates a new contract using a set of input parameters:
|
||||
// script contract's bytecode (limited in length by 1M)
|
||||
// manifest contract's manifest (limited in length by 2 KiB)
|
||||
|
@ -63,3 +78,11 @@ func GetCallFlags() int64 {
|
|||
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{} {
|
||||
return nil
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue