diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index 982886bcc..c1fcb4385 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -20,6 +20,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/encoding/bigint" "github.com/nspcc-dev/neo-go/pkg/io" + "github.com/nspcc-dev/neo-go/pkg/smartcontract" "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" @@ -562,7 +563,7 @@ func (bc *Blockchain) storeBlock(block *block.Block) error { systemInterop := bc.newInteropContext(trigger.Application, cache, block, tx) v := SpawnVM(systemInterop) - v.LoadScript(tx.Script) + v.LoadScriptWithFlags(tx.Script, smartcontract.All) v.SetPriceGetter(getPrice) if bc.config.FreeGasLimit > 0 { v.SetGasLimit(bc.config.FreeGasLimit + tx.SystemFee) @@ -1276,7 +1277,7 @@ func (bc *Blockchain) verifyHashAgainstScript(hash util.Uint160, witness *transa } vm := SpawnVM(interopCtx) - vm.LoadScript(verification) + vm.LoadScriptWithFlags(verification, smartcontract.ReadOnly) vm.LoadScript(witness.InvocationScript) if useKeys { bc.keyCacheLock.RLock() diff --git a/pkg/core/interop/context.go b/pkg/core/interop/context.go index a6ac1f393..e3e15ecbe 100644 --- a/pkg/core/interop/context.go +++ b/pkg/core/interop/context.go @@ -56,6 +56,9 @@ type Function struct { Name string Func func(*Context, *vm.VM) error Price int + // 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 } // Method is a signature for a native method. diff --git a/pkg/core/interop_system.go b/pkg/core/interop_system.go index 51daacde2..31077ce53 100644 --- a/pkg/core/interop_system.go +++ b/pkg/core/interop_system.go @@ -442,7 +442,7 @@ func contractCallExInternal(ic *interop.Context, v *vm.VM, h []byte, method stac return errors.New("disallowed method call") } } - v.LoadScriptWithHash(cs.Script, u, f) + v.LoadScriptWithHash(cs.Script, u, v.Context().GetCallFlags()&f) v.Estack().PushVal(args) v.Estack().PushVal(method) return nil diff --git a/pkg/core/interops.go b/pkg/core/interops.go index 819be06ab..970bf53a0 100644 --- a/pkg/core/interops.go +++ b/pkg/core/interops.go @@ -16,6 +16,7 @@ import ( "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/native" + "github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/vm" "github.com/nspcc-dev/neo-go/pkg/vm/emit" ) @@ -52,9 +53,13 @@ func getInteropFromSlice(ic *interop.Context, slice []interop.Function) func(uin return slice[i].ID >= id }) if n < len(slice) && slice[n].ID == id { - return &vm.InteropFuncPrice{Func: func(v *vm.VM) error { - return slice[n].Func(ic, v) - }, Price: slice[n].Price} + return &vm.InteropFuncPrice{ + Func: func(v *vm.VM) error { + return slice[n].Func(ic, v) + }, + Price: slice[n].Price, + RequiredFlags: slice[n].RequiredFlags, + } } return nil } @@ -62,17 +67,17 @@ 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}, - {Name: "System.Blockchain.GetContract", Func: bcGetContract, Price: 100}, - {Name: "System.Blockchain.GetHeight", Func: bcGetHeight, Price: 1}, - {Name: "System.Blockchain.GetTransaction", Func: bcGetTransaction, Price: 100}, - {Name: "System.Blockchain.GetTransactionFromBlock", Func: bcGetTransactionFromBlock, 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.Create", Func: contractCreate, Price: 0}, - {Name: "System.Contract.Destroy", Func: contractDestroy, Price: 1}, - {Name: "System.Contract.Update", Func: contractUpdate, Price: 0}, + {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.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}, @@ -86,29 +91,29 @@ var systemInterops = []interop.Function{ {Name: "System.Iterator.Key", Func: iterator.Key, Price: 1}, {Name: "System.Iterator.Keys", Func: iterator.Keys, Price: 1}, {Name: "System.Iterator.Values", Func: iterator.Values, Price: 1}, - {Name: "System.Runtime.CheckWitness", Func: runtime.CheckWitness, Price: 200}, + {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}, + {Name: "System.Runtime.GetTime", Func: runtimeGetTime, Price: 1, RequiredFlags: smartcontract.AllowStates}, {Name: "System.Runtime.GetTrigger", Func: runtimeGetTrigger, Price: 1}, - {Name: "System.Runtime.Log", Func: runtimeLog, Price: 1}, - {Name: "System.Runtime.Notify", Func: runtimeNotify, 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}, - {Name: "System.Storage.Find", Func: storageFind, Price: 1}, - {Name: "System.Storage.Get", Func: storageGet, Price: 100}, - {Name: "System.Storage.GetContext", Func: storageGetContext, Price: 1}, - {Name: "System.Storage.GetReadOnlyContext", Func: storageGetReadOnlyContext, Price: 1}, - {Name: "System.Storage.Put", Func: storagePut, Price: 0}, // These don't have static price in C# code. - {Name: "System.Storage.PutEx", Func: storagePutEx, Price: 0}, - {Name: "System.Storage.AsReadOnly", Func: storageContextAsReadOnly, 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}, } 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}, + {Name: "Neo.Native.Deploy", Func: native.Deploy, Price: 1, RequiredFlags: smartcontract.AllowModifyStates}, } // initIDinInteropsSlice initializes IDs from names in one given diff --git a/pkg/core/native/contract.go b/pkg/core/native/contract.go index a16df5af8..34b5cf578 100644 --- a/pkg/core/native/contract.go +++ b/pkg/core/native/contract.go @@ -79,6 +79,9 @@ func getNativeInterop(ic *interop.Context, c interop.Contract) func(v *vm.VM) er if !ok { return fmt.Errorf("method %s not found", name) } + if !v.Context().GetCallFlags().Has(m.RequiredFlags) { + return errors.New("missing call flags") + } result := m.Func(ic, args) v.Estack().PushVal(result) return nil diff --git a/pkg/rpc/server/server.go b/pkg/rpc/server/server.go index c75af6fad..009f097af 100644 --- a/pkg/rpc/server/server.go +++ b/pkg/rpc/server/server.go @@ -915,7 +915,7 @@ func (s *Server) invokescript(reqParams request.Params) (interface{}, *response. func (s *Server) runScriptInVM(script []byte) *result.Invoke { vm := s.chain.GetTestVM() vm.SetGasLimit(s.config.MaxGasInvoke) - vm.LoadScript(script) + vm.LoadScriptWithFlags(script, smartcontract.All) _ = vm.Run() result := &result.Invoke{ State: vm.State(), diff --git a/pkg/vm/context.go b/pkg/vm/context.go index f984ce1e5..49ba04353 100644 --- a/pkg/vm/context.go +++ b/pkg/vm/context.go @@ -158,6 +158,11 @@ func (c *Context) Copy() *Context { return ctx } +// GetCallFlags returns calling flags context was created with. +func (c *Context) GetCallFlags() smartcontract.CallFlag { + return c.callFlag +} + // Program returns the loaded program. func (c *Context) Program() []byte { return c.prog