core: implement System.Contract.Call interop

This commit is contained in:
Evgenii Stratonikov 2020-05-07 14:11:59 +03:00
parent 301c1b7601
commit ec900c7ff7
4 changed files with 51 additions and 14 deletions

View file

@ -84,6 +84,16 @@ type ContractMD struct {
Methods map[string]MethodAndPrice Methods map[string]MethodAndPrice
} }
// GetContract returns script of the contract with the specified hash.
func (ic *Context) GetContract(h util.Uint160) ([]byte, bool) {
cs, err := ic.DAO.GetContractState(h)
if err != nil {
return nil, false
}
hasDynamicInvoke := (cs.Properties & smartcontract.HasDynamicInvoke) != 0
return cs.Script, hasDynamicInvoke
}
// NewContractMD returns Contract with the specified list of methods. // NewContractMD returns Contract with the specified list of methods.
func NewContractMD(name string) *ContractMD { func NewContractMD(name string) *ContractMD {
c := &ContractMD{ c := &ContractMD{

View file

@ -11,6 +11,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/core/transaction" "github.com/nspcc-dev/neo-go/pkg/core/transaction"
"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/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"
@ -470,6 +471,39 @@ func storageContextAsReadOnly(ic *interop.Context, v *vm.VM) error {
return nil return nil
} }
// contractCall calls a contract.
func contractCall(ic *interop.Context, v *vm.VM) error {
h := v.Estack().Pop().Bytes()
method := v.Estack().Pop().Item()
args := v.Estack().Pop().Item()
return contractCallExInternal(ic, v, h, method, args, smartcontract.All)
}
// contractCallEx calls a contract with flags.
func contractCallEx(ic *interop.Context, v *vm.VM) error {
h := v.Estack().Pop().Bytes()
method := v.Estack().Pop().Item()
args := v.Estack().Pop().Item()
flags := smartcontract.CallFlag(int32(v.Estack().Pop().BigInt().Int64()))
return contractCallExInternal(ic, v, h, method, args, flags)
}
func contractCallExInternal(ic *interop.Context, v *vm.VM, h []byte, method vm.StackItem, args vm.StackItem, _ smartcontract.CallFlag) error {
u, err := util.Uint160DecodeBytesBE(h)
if err != nil {
return errors.New("invalid contract hash")
}
script, _ := ic.GetContract(u)
if script == nil {
return errors.New("contract not found")
}
// TODO perform flags checking after #923
v.LoadScript(script)
v.Estack().PushVal(args)
v.Estack().PushVal(method)
return nil
}
// contractDestroy destroys a contract. // contractDestroy destroys a contract.
func contractDestroy(ic *interop.Context, v *vm.VM) error { func contractDestroy(ic *interop.Context, v *vm.VM) error {
if ic.Trigger != trigger.Application { if ic.Trigger != trigger.Application {

View file

@ -16,8 +16,6 @@ import (
"github.com/nspcc-dev/neo-go/pkg/core/interop/iterator" "github.com/nspcc-dev/neo-go/pkg/core/interop/iterator"
"github.com/nspcc-dev/neo-go/pkg/core/interop/runtime" "github.com/nspcc-dev/neo-go/pkg/core/interop/runtime"
"github.com/nspcc-dev/neo-go/pkg/core/native" "github.com/nspcc-dev/neo-go/pkg/core/native"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"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"
) )
@ -27,18 +25,7 @@ import (
func SpawnVM(ic *interop.Context) *vm.VM { func SpawnVM(ic *interop.Context) *vm.VM {
vm := vm.New() vm := vm.New()
bc := ic.Chain.(*Blockchain) bc := ic.Chain.(*Blockchain)
vm.SetScriptGetter(func(hash util.Uint160) ([]byte, bool) { vm.SetScriptGetter(ic.GetContract)
if c := bc.contracts.ByHash(hash); c != nil {
meta := c.Metadata()
return meta.Script, (meta.Manifest.Features&smartcontract.HasDynamicInvoke != 0)
}
cs, err := ic.DAO.GetContractState(hash)
if err != nil {
return nil, false
}
hasDynamicInvoke := (cs.Properties & smartcontract.HasDynamicInvoke) != 0
return cs.Script, hasDynamicInvoke
})
vm.RegisterInteropGetter(getSystemInterop(ic)) vm.RegisterInteropGetter(getSystemInterop(ic))
vm.RegisterInteropGetter(getNeoInterop(ic)) vm.RegisterInteropGetter(getNeoInterop(ic))
vm.RegisterInteropGetter(bc.contracts.GetNativeInterop(ic)) vm.RegisterInteropGetter(bc.contracts.GetNativeInterop(ic))
@ -84,6 +71,8 @@ var systemInterops = []interop.Function{
{Name: "System.Blockchain.GetHeight", Func: bcGetHeight, Price: 1}, {Name: "System.Blockchain.GetHeight", Func: bcGetHeight, Price: 1},
{Name: "System.Blockchain.GetTransaction", Func: bcGetTransaction, Price: 200}, {Name: "System.Blockchain.GetTransaction", Func: bcGetTransaction, Price: 200},
{Name: "System.Blockchain.GetTransactionHeight", Func: bcGetTransactionHeight, Price: 100}, {Name: "System.Blockchain.GetTransactionHeight", Func: bcGetTransactionHeight, Price: 100},
{Name: "System.Contract.Call", Func: contractCall, Price: 1},
{Name: "System.Contract.CallEx", Func: contractCallEx, Price: 1},
{Name: "System.Contract.Destroy", Func: contractDestroy, Price: 1}, {Name: "System.Contract.Destroy", Func: contractDestroy, Price: 1},
{Name: "System.Contract.GetStorageContext", Func: contractGetStorageContext, Price: 1}, {Name: "System.Contract.GetStorageContext", Func: contractGetStorageContext, Price: 1},
{Name: "System.ExecutionEngine.GetCallingScriptHash", Func: engineGetCallingScriptHash, Price: 1}, {Name: "System.ExecutionEngine.GetCallingScriptHash", Func: engineGetCallingScriptHash, Price: 1},

View file

@ -5,6 +5,7 @@ import (
"testing" "testing"
"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/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"
@ -85,6 +86,9 @@ func TestNativeContract_Invoke(t *testing.T) {
tn := newTestNative() tn := newTestNative()
chain.registerNative(tn) chain.registerNative(tn)
err := chain.dao.PutContractState(&state.Contract{Script: tn.meta.Script})
require.NoError(t, err)
w := io.NewBufBinWriter() w := io.NewBufBinWriter()
emit.AppCallWithOperationAndArgs(w.BinWriter, tn.Metadata().Hash, "sum", int64(14), int64(28)) emit.AppCallWithOperationAndArgs(w.BinWriter, tn.Metadata().Hash, "sum", int64(14), int64(28))
script := w.Bytes() script := w.Bytes()