From 971ab0646f4ec5327f907690c4f7b20703a9899c Mon Sep 17 00:00:00 2001 From: Evgenii Stratonikov Date: Wed, 10 Jun 2020 18:07:21 +0300 Subject: [PATCH] core: specify allowed triggers for interops Related #1026, #1027, #1028, #1031. --- pkg/core/interop/context.go | 2 ++ pkg/core/interop_neo.go | 4 --- pkg/core/interop_system.go | 10 ------ pkg/core/interops.go | 66 ++++++++++++++++++++++++------------- 4 files changed, 46 insertions(+), 36 deletions(-) diff --git a/pkg/core/interop/context.go b/pkg/core/interop/context.go index e3e15ecbe..10d2c376e 100644 --- a/pkg/core/interop/context.go +++ b/pkg/core/interop/context.go @@ -56,6 +56,8 @@ type Function struct { Name string Func func(*Context, *vm.VM) error Price int + // AllowedTriggers is a set of triggers which are allowed to initiate invocation. + AllowedTriggers trigger.Type // RequiredFlags is a set of flags which must be set during script invocations. // Default value is NoneFlag i.e. no flags are required. RequiredFlags smartcontract.CallFlag diff --git a/pkg/core/interop_neo.go b/pkg/core/interop_neo.go index 9b57250e6..c41258b7d 100644 --- a/pkg/core/interop_neo.go +++ b/pkg/core/interop_neo.go @@ -10,7 +10,6 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/state" "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" - "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" @@ -63,9 +62,6 @@ func storageFind(ic *interop.Context, v *vm.VM) error { // evaluation stack, does a lot of checks and returns Contract if it // succeeds. func createContractStateFromVM(ic *interop.Context, v *vm.VM) (*state.Contract, error) { - if ic.Trigger != trigger.Application { - return nil, errors.New("can't create contract when not triggered by an application") - } script := v.Estack().Pop().Bytes() if len(script) > MaxContractScriptSize { return nil, errors.New("the script is too big") diff --git a/pkg/core/interop_system.go b/pkg/core/interop_system.go index add63ebed..da634e51d 100644 --- a/pkg/core/interop_system.go +++ b/pkg/core/interop_system.go @@ -13,7 +13,6 @@ 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/smartcontract" - "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" @@ -272,9 +271,6 @@ func checkStorageContext(ic *interop.Context, stc *StorageContext) error { // storageDelete deletes stored key-value pair. func storageDelete(ic *interop.Context, v *vm.VM) error { - if ic.Trigger != trigger.Application { - return errors.New("can't delete when the trigger is not application") - } stcInterface := v.Estack().Pop().Value() stc, ok := stcInterface.(*StorageContext) if !ok { @@ -337,9 +333,6 @@ func storageGetReadOnlyContext(ic *interop.Context, v *vm.VM) error { } func putWithContextAndFlags(ic *interop.Context, stc *StorageContext, key []byte, value []byte, isConst bool) error { - if ic.Trigger != trigger.Application { - return errors.New("can't delete when the trigger is not application") - } if len(key) > MaxStorageKeyLen { return errors.New("key is too big") } @@ -450,9 +443,6 @@ func contractCallExInternal(ic *interop.Context, v *vm.VM, h []byte, method stac // contractDestroy destroys a contract. func contractDestroy(ic *interop.Context, v *vm.VM) error { - if ic.Trigger != trigger.Application { - return errors.New("can't destroy contract when not triggered by application") - } hash := v.GetCurrentScriptHash() cs, err := ic.DAO.GetContractState(hash) if err != nil { diff --git a/pkg/core/interops.go b/pkg/core/interops.go index 970bf53a0..8be1c16dc 100644 --- a/pkg/core/interops.go +++ b/pkg/core/interops.go @@ -17,6 +17,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/core/interop/runtime" "github.com/nspcc-dev/neo-go/pkg/core/native" "github.com/nspcc-dev/neo-go/pkg/smartcontract" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger" "github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm/emit" ) @@ -24,7 +25,7 @@ import ( // SpawnVM returns a VM with script getter and interop functions set // up for current blockchain. func SpawnVM(ic *interop.Context) *vm.VM { - vm := vm.New() + vm := vm.NewWithTrigger(ic.Trigger) vm.RegisterInteropGetter(getSystemInterop(ic)) vm.RegisterInteropGetter(getNeoInterop(ic)) if ic.Chain != nil { @@ -67,17 +68,28 @@ func getInteropFromSlice(ic *interop.Context, slice []interop.Function) func(uin // All lists are sorted, keep 'em this way, please. var systemInterops = []interop.Function{ - {Name: "System.Blockchain.GetBlock", Func: bcGetBlock, Price: 250, RequiredFlags: smartcontract.AllowStates}, - {Name: "System.Blockchain.GetContract", Func: bcGetContract, Price: 100, RequiredFlags: smartcontract.AllowStates}, - {Name: "System.Blockchain.GetHeight", Func: bcGetHeight, Price: 1, RequiredFlags: smartcontract.AllowStates}, - {Name: "System.Blockchain.GetTransaction", Func: bcGetTransaction, Price: 100, RequiredFlags: smartcontract.AllowStates}, - {Name: "System.Blockchain.GetTransactionFromBlock", Func: bcGetTransactionFromBlock, Price: 100, RequiredFlags: smartcontract.AllowStates}, - {Name: "System.Blockchain.GetTransactionHeight", Func: bcGetTransactionHeight, Price: 100, RequiredFlags: smartcontract.AllowStates}, - {Name: "System.Contract.Call", Func: contractCall, Price: 1, RequiredFlags: smartcontract.AllowCall}, - {Name: "System.Contract.CallEx", Func: contractCallEx, Price: 1, RequiredFlags: smartcontract.AllowCall}, - {Name: "System.Contract.Create", Func: contractCreate, Price: 0, RequiredFlags: smartcontract.AllowModifyStates}, - {Name: "System.Contract.Destroy", Func: contractDestroy, Price: 1, RequiredFlags: smartcontract.AllowModifyStates}, - {Name: "System.Contract.Update", Func: contractUpdate, Price: 0, RequiredFlags: smartcontract.AllowModifyStates}, + {Name: "System.Blockchain.GetBlock", Func: bcGetBlock, Price: 250, + AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowStates}, + {Name: "System.Blockchain.GetContract", Func: bcGetContract, Price: 100, + AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowStates}, + {Name: "System.Blockchain.GetHeight", Func: bcGetHeight, Price: 1, + AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowStates}, + {Name: "System.Blockchain.GetTransaction", Func: bcGetTransaction, Price: 100, + AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowStates}, + {Name: "System.Blockchain.GetTransactionFromBlock", Func: bcGetTransactionFromBlock, Price: 100, + AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowStates}, + {Name: "System.Blockchain.GetTransactionHeight", Func: bcGetTransactionHeight, Price: 100, + AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowStates}, + {Name: "System.Contract.Call", Func: contractCall, Price: 1, + AllowedTriggers: trigger.System | trigger.Application, RequiredFlags: smartcontract.AllowCall}, + {Name: "System.Contract.CallEx", Func: contractCallEx, Price: 1, + AllowedTriggers: trigger.System | trigger.Application, RequiredFlags: smartcontract.AllowCall}, + {Name: "System.Contract.Create", Func: contractCreate, Price: 0, + AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowModifyStates}, + {Name: "System.Contract.Destroy", Func: contractDestroy, Price: 1, + AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowModifyStates}, + {Name: "System.Contract.Update", Func: contractUpdate, Price: 0, + AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowModifyStates}, {Name: "System.Enumerator.Concat", Func: enumerator.Concat, Price: 1}, {Name: "System.Enumerator.Create", Func: enumerator.Create, Price: 1}, {Name: "System.Enumerator.Next", Func: enumerator.Next, Price: 1}, @@ -93,27 +105,37 @@ var systemInterops = []interop.Function{ {Name: "System.Iterator.Values", Func: iterator.Values, Price: 1}, {Name: "System.Runtime.CheckWitness", Func: runtime.CheckWitness, Price: 200, RequiredFlags: smartcontract.AllowStates}, {Name: "System.Runtime.Deserialize", Func: runtimeDeserialize, Price: 1}, - {Name: "System.Runtime.GetTime", Func: runtimeGetTime, Price: 1, RequiredFlags: smartcontract.AllowStates}, + {Name: "System.Runtime.GetTime", Func: runtimeGetTime, Price: 1, + AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowStates}, {Name: "System.Runtime.GetTrigger", Func: runtimeGetTrigger, Price: 1}, {Name: "System.Runtime.Log", Func: runtimeLog, Price: 1, RequiredFlags: smartcontract.AllowNotify}, {Name: "System.Runtime.Notify", Func: runtimeNotify, Price: 1, RequiredFlags: smartcontract.AllowNotify}, {Name: "System.Runtime.Platform", Func: runtimePlatform, Price: 1}, {Name: "System.Runtime.Serialize", Func: runtimeSerialize, Price: 1}, - {Name: "System.Storage.Delete", Func: storageDelete, Price: 100, RequiredFlags: smartcontract.AllowModifyStates}, - {Name: "System.Storage.Find", Func: storageFind, Price: 1, RequiredFlags: smartcontract.AllowStates}, - {Name: "System.Storage.Get", Func: storageGet, Price: 100, RequiredFlags: smartcontract.AllowStates}, - {Name: "System.Storage.GetContext", Func: storageGetContext, Price: 1, RequiredFlags: smartcontract.AllowStates}, - {Name: "System.Storage.GetReadOnlyContext", Func: storageGetReadOnlyContext, Price: 1, RequiredFlags: smartcontract.AllowStates}, - {Name: "System.Storage.Put", Func: storagePut, Price: 0, RequiredFlags: smartcontract.AllowModifyStates}, // These don't have static price in C# code. - {Name: "System.Storage.PutEx", Func: storagePutEx, Price: 0, RequiredFlags: smartcontract.AllowModifyStates}, - {Name: "System.Storage.AsReadOnly", Func: storageContextAsReadOnly, Price: 1, RequiredFlags: smartcontract.AllowStates}, + {Name: "System.Storage.Delete", Func: storageDelete, Price: 100, + AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowModifyStates}, + {Name: "System.Storage.Find", Func: storageFind, Price: 1, + AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowStates}, + {Name: "System.Storage.Get", Func: storageGet, Price: 100, + AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowStates}, + {Name: "System.Storage.GetContext", Func: storageGetContext, Price: 1, + AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowStates}, + {Name: "System.Storage.GetReadOnlyContext", Func: storageGetReadOnlyContext, Price: 1, + AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowStates}, + {Name: "System.Storage.Put", Func: storagePut, Price: 0, + AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowModifyStates}, // These don't have static price in C# code. + {Name: "System.Storage.PutEx", Func: storagePutEx, Price: 0, + AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowModifyStates}, + {Name: "System.Storage.AsReadOnly", Func: storageContextAsReadOnly, Price: 1, + AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowStates}, } var neoInterops = []interop.Function{ {Name: "Neo.Crypto.ECDsaVerify", Func: crypto.ECDSAVerify, Price: 1}, {Name: "Neo.Crypto.ECDsaCheckMultiSig", Func: crypto.ECDSACheckMultisig, Price: 1}, {Name: "Neo.Crypto.SHA256", Func: crypto.Sha256, Price: 1}, - {Name: "Neo.Native.Deploy", Func: native.Deploy, Price: 1, RequiredFlags: smartcontract.AllowModifyStates}, + {Name: "Neo.Native.Deploy", Func: native.Deploy, Price: 1, + AllowedTriggers: trigger.Application, RequiredFlags: smartcontract.AllowModifyStates}, } // initIDinInteropsSlice initializes IDs from names in one given