diff --git a/pkg/compiler/debug_test.go b/pkg/compiler/debug_test.go index 858af186c..e49708ba1 100644 --- a/pkg/compiler/debug_test.go +++ b/pkg/compiler/debug_test.go @@ -251,9 +251,6 @@ func _deploy(isUpdate bool) {} Trusts: manifest.WildUint160s{ Value: []util.Uint160{}, }, - SafeMethods: manifest.WildStrings{ - Value: []string{}, - }, Extra: nil, } require.ElementsMatch(t, expected.ABI.Methods, actual.ABI.Methods) @@ -261,7 +258,6 @@ func _deploy(isUpdate bool) {} require.Equal(t, expected.Groups, actual.Groups) require.Equal(t, expected.Permissions, actual.Permissions) require.Equal(t, expected.Trusts, actual.Trusts) - require.Equal(t, expected.SafeMethods, actual.SafeMethods) require.Equal(t, expected.Extra, actual.Extra) }) } diff --git a/pkg/core/interop/context.go b/pkg/core/interop/context.go index b388e5d22..d56e5cc64 100644 --- a/pkg/core/interop/context.go +++ b/pkg/core/interop/context.go @@ -120,13 +120,11 @@ func NewContractMD(name string) *ContractMD { } // AddMethod adds new method to a native contract. -func (c *ContractMD) AddMethod(md *MethodAndPrice, desc *manifest.Method, safe bool) { +func (c *ContractMD) AddMethod(md *MethodAndPrice, desc *manifest.Method) { c.Manifest.ABI.Methods = append(c.Manifest.ABI.Methods, *desc) md.MD = desc + desc.Safe = (md.RequiredFlags & smartcontract.AllowModifyStates) == 0 c.Methods[desc.Name] = *md - if safe { - c.Manifest.SafeMethods.Add(desc.Name) - } } // AddEvent adds new event to a native contract. diff --git a/pkg/core/interop/contract/call.go b/pkg/core/interop/contract/call.go index 1a327e608..fe2925771 100644 --- a/pkg/core/interop/contract/call.go +++ b/pkg/core/interop/contract/call.go @@ -46,8 +46,13 @@ func callExInternal(ic *interop.Context, h []byte, name string, args []stackitem if strings.HasPrefix(name, "_") { return errors.New("invalid method name (starts with '_')") } - ctx := ic.VM.Context() - if ctx != nil && ctx.IsDeployed() { + md := cs.Manifest.ABI.GetMethod(name) + if md == nil { + return errors.New("method not found") + } + if md.Safe { + f &^= smartcontract.AllowModifyStates + } else if ctx := ic.VM.Context(); ctx != nil && ctx.IsDeployed() { curr, err := ic.DAO.GetContractState(ic.VM.GetCurrentScriptHash()) if err == nil { if !curr.Manifest.CanCall(u, &cs.Manifest, name) { diff --git a/pkg/core/native/designate.go b/pkg/core/native/designate.go index ba3ce7cf1..559fed452 100644 --- a/pkg/core/native/designate.go +++ b/pkg/core/native/designate.go @@ -79,21 +79,21 @@ func newDesignate(p2pSigExtensionsEnabled bool) *Designate { manifest.NewParameter("role", smartcontract.IntegerType), manifest.NewParameter("index", smartcontract.IntegerType)) md := newMethodAndPrice(s.getDesignatedByRole, 1000000, smartcontract.AllowStates) - s.AddMethod(md, desc, false) + s.AddMethod(md, desc) desc = newDescriptor("designateAsRole", smartcontract.VoidType, manifest.NewParameter("role", smartcontract.IntegerType), manifest.NewParameter("nodes", smartcontract.ArrayType)) md = newMethodAndPrice(s.designateAsRole, 0, smartcontract.AllowModifyStates) - s.AddMethod(md, desc, false) + s.AddMethod(md, desc) desc = newDescriptor("onPersist", smartcontract.VoidType) md = newMethodAndPrice(getOnPersistWrapper(onPersistBase), 0, smartcontract.AllowModifyStates) - s.AddMethod(md, desc, false) + s.AddMethod(md, desc) desc = newDescriptor("postPersist", smartcontract.VoidType) md = newMethodAndPrice(getOnPersistWrapper(postPersistBase), 0, smartcontract.AllowModifyStates) - s.AddMethod(md, desc, false) + s.AddMethod(md, desc) return s } diff --git a/pkg/core/native/native_neo.go b/pkg/core/native/native_neo.go index d211accca..2a250f65d 100644 --- a/pkg/core/native/native_neo.go +++ b/pkg/core/native/native_neo.go @@ -121,44 +121,44 @@ func newNEO() *NEO { manifest.NewParameter("account", smartcontract.Hash160Type), manifest.NewParameter("end", smartcontract.IntegerType)) md := newMethodAndPrice(n.unclaimedGas, 3000000, smartcontract.AllowStates) - n.AddMethod(md, desc, true) + n.AddMethod(md, desc) desc = newDescriptor("registerCandidate", smartcontract.BoolType, manifest.NewParameter("pubkey", smartcontract.PublicKeyType)) md = newMethodAndPrice(n.registerCandidate, 5000000, smartcontract.AllowModifyStates) - n.AddMethod(md, desc, false) + n.AddMethod(md, desc) desc = newDescriptor("unregisterCandidate", smartcontract.BoolType, manifest.NewParameter("pubkey", smartcontract.PublicKeyType)) md = newMethodAndPrice(n.unregisterCandidate, 5000000, smartcontract.AllowModifyStates) - n.AddMethod(md, desc, false) + n.AddMethod(md, desc) desc = newDescriptor("vote", smartcontract.BoolType, manifest.NewParameter("account", smartcontract.Hash160Type), manifest.NewParameter("pubkey", smartcontract.PublicKeyType)) md = newMethodAndPrice(n.vote, 5000000, smartcontract.AllowModifyStates) - n.AddMethod(md, desc, false) + n.AddMethod(md, desc) desc = newDescriptor("getCandidates", smartcontract.ArrayType) md = newMethodAndPrice(n.getCandidatesCall, 100000000, smartcontract.AllowStates) - n.AddMethod(md, desc, true) + n.AddMethod(md, desc) desc = newDescriptor("getŠ”ommittee", smartcontract.ArrayType) md = newMethodAndPrice(n.getCommittee, 100000000, smartcontract.AllowStates) - n.AddMethod(md, desc, true) + n.AddMethod(md, desc) desc = newDescriptor("getNextBlockValidators", smartcontract.ArrayType) md = newMethodAndPrice(n.getNextBlockValidators, 100000000, smartcontract.AllowStates) - n.AddMethod(md, desc, true) + n.AddMethod(md, desc) desc = newDescriptor("getGasPerBlock", smartcontract.IntegerType) md = newMethodAndPrice(n.getGASPerBlock, 100_0000, smartcontract.AllowStates) - n.AddMethod(md, desc, false) + n.AddMethod(md, desc) desc = newDescriptor("setGasPerBlock", smartcontract.BoolType, manifest.NewParameter("gasPerBlock", smartcontract.IntegerType)) md = newMethodAndPrice(n.setGASPerBlock, 500_0000, smartcontract.AllowModifyStates) - n.AddMethod(md, desc, false) + n.AddMethod(md, desc) return n } diff --git a/pkg/core/native/native_nep17.go b/pkg/core/native/native_nep17.go index 981d85e03..8fd519c01 100644 --- a/pkg/core/native/native_nep17.go +++ b/pkg/core/native/native_nep17.go @@ -56,20 +56,20 @@ func newNEP17Native(name string) *nep17TokenNative { desc := newDescriptor("symbol", smartcontract.StringType) md := newMethodAndPrice(n.Symbol, 0, smartcontract.NoneFlag) - n.AddMethod(md, desc, true) + n.AddMethod(md, desc) desc = newDescriptor("decimals", smartcontract.IntegerType) md = newMethodAndPrice(n.Decimals, 0, smartcontract.NoneFlag) - n.AddMethod(md, desc, true) + n.AddMethod(md, desc) desc = newDescriptor("totalSupply", smartcontract.IntegerType) md = newMethodAndPrice(n.TotalSupply, 1000000, smartcontract.AllowStates) - n.AddMethod(md, desc, true) + n.AddMethod(md, desc) desc = newDescriptor("balanceOf", smartcontract.IntegerType, manifest.NewParameter("account", smartcontract.Hash160Type)) md = newMethodAndPrice(n.balanceOf, 1000000, smartcontract.AllowStates) - n.AddMethod(md, desc, true) + n.AddMethod(md, desc) transferParams := []manifest.Parameter{ manifest.NewParameter("from", smartcontract.Hash160Type), @@ -80,15 +80,15 @@ func newNEP17Native(name string) *nep17TokenNative { append(transferParams, manifest.NewParameter("data", smartcontract.AnyType))..., ) md = newMethodAndPrice(n.Transfer, 8000000, smartcontract.AllowModifyStates) - n.AddMethod(md, desc, false) + n.AddMethod(md, desc) desc = newDescriptor("onPersist", smartcontract.VoidType) md = newMethodAndPrice(getOnPersistWrapper(onPersistBase), 0, smartcontract.AllowModifyStates) - n.AddMethod(md, desc, false) + n.AddMethod(md, desc) desc = newDescriptor("postPersist", smartcontract.VoidType) md = newMethodAndPrice(getOnPersistWrapper(postPersistBase), 0, smartcontract.AllowModifyStates) - n.AddMethod(md, desc, false) + n.AddMethod(md, desc) n.AddEvent("Transfer", transferParams...) diff --git a/pkg/core/native/notary.go b/pkg/core/native/notary.go index 0d692670c..5d84d03e9 100644 --- a/pkg/core/native/notary.go +++ b/pkg/core/native/notary.go @@ -47,42 +47,42 @@ func newNotary() *Notary { manifest.NewParameter("amount", smartcontract.IntegerType), manifest.NewParameter("data", smartcontract.AnyType)) md := newMethodAndPrice(n.onPayment, 100_0000, smartcontract.AllowModifyStates) - n.AddMethod(md, desc, true) + n.AddMethod(md, desc) desc = newDescriptor("lockDepositUntil", smartcontract.BoolType, manifest.NewParameter("address", smartcontract.Hash160Type), manifest.NewParameter("till", smartcontract.IntegerType)) md = newMethodAndPrice(n.lockDepositUntil, 100_0000, smartcontract.AllowModifyStates) - n.AddMethod(md, desc, true) + n.AddMethod(md, desc) desc = newDescriptor("withdraw", smartcontract.BoolType, manifest.NewParameter("from", smartcontract.Hash160Type), manifest.NewParameter("to", smartcontract.Hash160Type)) md = newMethodAndPrice(n.withdraw, 100_0000, smartcontract.AllowModifyStates) - n.AddMethod(md, desc, true) + n.AddMethod(md, desc) desc = newDescriptor("balanceOf", smartcontract.IntegerType, manifest.NewParameter("addr", smartcontract.Hash160Type)) md = newMethodAndPrice(n.balanceOf, 100_0000, smartcontract.AllowStates) - n.AddMethod(md, desc, true) + n.AddMethod(md, desc) desc = newDescriptor("expirationOf", smartcontract.IntegerType, manifest.NewParameter("addr", smartcontract.Hash160Type)) md = newMethodAndPrice(n.expirationOf, 100_0000, smartcontract.AllowStates) - n.AddMethod(md, desc, true) + n.AddMethod(md, desc) desc = newDescriptor("verify", smartcontract.BoolType, manifest.NewParameter("signature", smartcontract.SignatureType)) md = newMethodAndPrice(n.verify, 100_0000, smartcontract.AllowStates) - n.AddMethod(md, desc, false) + n.AddMethod(md, desc) desc = newDescriptor("onPersist", smartcontract.VoidType) md = newMethodAndPrice(getOnPersistWrapper(n.OnPersist), 0, smartcontract.AllowModifyStates) - n.AddMethod(md, desc, false) + n.AddMethod(md, desc) desc = newDescriptor("postPersist", smartcontract.VoidType) md = newMethodAndPrice(getOnPersistWrapper(postPersistBase), 0, smartcontract.AllowModifyStates) - n.AddMethod(md, desc, false) + n.AddMethod(md, desc) return n } diff --git a/pkg/core/native/oracle.go b/pkg/core/native/oracle.go index 39bc4713c..35d405d62 100644 --- a/pkg/core/native/oracle.go +++ b/pkg/core/native/oracle.go @@ -111,24 +111,24 @@ func newOracle() *Oracle { manifest.NewParameter("userData", smartcontract.AnyType), manifest.NewParameter("gasForResponse", smartcontract.IntegerType)) md := newMethodAndPrice(o.request, oracleRequestPrice, smartcontract.AllowModifyStates) - o.AddMethod(md, desc, false) + o.AddMethod(md, desc) desc = newDescriptor("finish", smartcontract.VoidType) md = newMethodAndPrice(o.finish, 0, smartcontract.AllowModifyStates) - o.AddMethod(md, desc, false) + o.AddMethod(md, desc) desc = newDescriptor("verify", smartcontract.BoolType) md = newMethodAndPrice(o.verify, 100_0000, smartcontract.NoneFlag) - o.AddMethod(md, desc, false) + o.AddMethod(md, desc) pp := chainOnPersist(postPersistBase, o.PostPersist) desc = newDescriptor("postPersist", smartcontract.VoidType) md = newMethodAndPrice(getOnPersistWrapper(pp), 0, smartcontract.AllowModifyStates) - o.AddMethod(md, desc, false) + o.AddMethod(md, desc) desc = newDescriptor("onPersist", smartcontract.VoidType) md = newMethodAndPrice(getOnPersistWrapper(onPersistBase), 0, smartcontract.AllowModifyStates) - o.AddMethod(md, desc, false) + o.AddMethod(md, desc) o.AddEvent("OracleRequest", manifest.NewParameter("Id", smartcontract.IntegerType), manifest.NewParameter("RequestContract", smartcontract.Hash160Type), diff --git a/pkg/core/native/policy.go b/pkg/core/native/policy.go index bcab99498..f9d38e088 100644 --- a/pkg/core/native/policy.go +++ b/pkg/core/native/policy.go @@ -77,62 +77,62 @@ func newPolicy() *Policy { desc := newDescriptor("getMaxTransactionsPerBlock", smartcontract.IntegerType) md := newMethodAndPrice(p.getMaxTransactionsPerBlock, 1000000, smartcontract.AllowStates) - p.AddMethod(md, desc, true) + p.AddMethod(md, desc) desc = newDescriptor("getMaxBlockSize", smartcontract.IntegerType) md = newMethodAndPrice(p.getMaxBlockSize, 1000000, smartcontract.AllowStates) - p.AddMethod(md, desc, true) + p.AddMethod(md, desc) desc = newDescriptor("getFeePerByte", smartcontract.IntegerType) md = newMethodAndPrice(p.getFeePerByte, 1000000, smartcontract.AllowStates) - p.AddMethod(md, desc, true) + p.AddMethod(md, desc) desc = newDescriptor("isBlocked", smartcontract.BoolType, manifest.NewParameter("account", smartcontract.Hash160Type)) md = newMethodAndPrice(p.isBlocked, 1000000, smartcontract.AllowStates) - p.AddMethod(md, desc, true) + p.AddMethod(md, desc) desc = newDescriptor("getMaxBlockSystemFee", smartcontract.IntegerType) md = newMethodAndPrice(p.getMaxBlockSystemFee, 1000000, smartcontract.AllowStates) - p.AddMethod(md, desc, true) + p.AddMethod(md, desc) desc = newDescriptor("setMaxBlockSize", smartcontract.BoolType, manifest.NewParameter("value", smartcontract.IntegerType)) md = newMethodAndPrice(p.setMaxBlockSize, 3000000, smartcontract.AllowModifyStates) - p.AddMethod(md, desc, false) + p.AddMethod(md, desc) desc = newDescriptor("setMaxTransactionsPerBlock", smartcontract.BoolType, manifest.NewParameter("value", smartcontract.IntegerType)) md = newMethodAndPrice(p.setMaxTransactionsPerBlock, 3000000, smartcontract.AllowModifyStates) - p.AddMethod(md, desc, false) + p.AddMethod(md, desc) desc = newDescriptor("setFeePerByte", smartcontract.BoolType, manifest.NewParameter("value", smartcontract.IntegerType)) md = newMethodAndPrice(p.setFeePerByte, 3000000, smartcontract.AllowModifyStates) - p.AddMethod(md, desc, false) + p.AddMethod(md, desc) desc = newDescriptor("setMaxBlockSystemFee", smartcontract.BoolType, manifest.NewParameter("value", smartcontract.IntegerType)) md = newMethodAndPrice(p.setMaxBlockSystemFee, 3000000, smartcontract.AllowModifyStates) - p.AddMethod(md, desc, false) + p.AddMethod(md, desc) desc = newDescriptor("blockAccount", smartcontract.BoolType, manifest.NewParameter("account", smartcontract.Hash160Type)) md = newMethodAndPrice(p.blockAccount, 3000000, smartcontract.AllowModifyStates) - p.AddMethod(md, desc, false) + p.AddMethod(md, desc) desc = newDescriptor("unblockAccount", smartcontract.BoolType, manifest.NewParameter("account", smartcontract.Hash160Type)) md = newMethodAndPrice(p.unblockAccount, 3000000, smartcontract.AllowModifyStates) - p.AddMethod(md, desc, false) + p.AddMethod(md, desc) desc = newDescriptor("onPersist", smartcontract.VoidType) md = newMethodAndPrice(getOnPersistWrapper(p.OnPersist), 0, smartcontract.AllowModifyStates) - p.AddMethod(md, desc, false) + p.AddMethod(md, desc) desc = newDescriptor("postPersist", smartcontract.VoidType) md = newMethodAndPrice(getOnPersistWrapper(postPersistBase), 0, smartcontract.AllowModifyStates) - p.AddMethod(md, desc, false) + p.AddMethod(md, desc) return p } diff --git a/pkg/core/native_contract_test.go b/pkg/core/native_contract_test.go index 2f23752bb..19f1236ce 100644 --- a/pkg/core/native_contract_test.go +++ b/pkg/core/native_contract_test.go @@ -66,13 +66,14 @@ func newTestNative() *testNative { manifest.NewParameter("addend2", smartcontract.IntegerType), }, ReturnType: smartcontract.IntegerType, + Safe: true, } md := &interop.MethodAndPrice{ Func: tn.sum, Price: testSumPrice, RequiredFlags: smartcontract.NoneFlag, } - tn.meta.AddMethod(md, desc, true) + tn.meta.AddMethod(md, desc) desc = &manifest.Method{ Name: "callOtherContractNoReturn", @@ -82,16 +83,17 @@ func newTestNative() *testNative { manifest.NewParameter("arg", smartcontract.ArrayType), }, ReturnType: smartcontract.VoidType, + Safe: true, } md = &interop.MethodAndPrice{ Func: tn.callOtherContractNoReturn, Price: testSumPrice, RequiredFlags: smartcontract.NoneFlag} - tn.meta.AddMethod(md, desc, true) + tn.meta.AddMethod(md, desc) desc = &manifest.Method{Name: "onPersist", ReturnType: smartcontract.BoolType} md = &interop.MethodAndPrice{Func: tn.OnPersist, RequiredFlags: smartcontract.AllowModifyStates} - tn.meta.AddMethod(md, desc, false) + tn.meta.AddMethod(md, desc) return tn } diff --git a/pkg/smartcontract/manifest/manifest.go b/pkg/smartcontract/manifest/manifest.go index 78de6bf24..c095fa4a8 100644 --- a/pkg/smartcontract/manifest/manifest.go +++ b/pkg/smartcontract/manifest/manifest.go @@ -49,8 +49,6 @@ type Manifest struct { SupportedStandards []string `json:"supportedstandards"` // Trusts is a set of hashes to a which contract trusts. Trusts WildUint160s `json:"trusts"` - // SafeMethods is a set of names of safe methods. - SafeMethods WildStrings `json:"safemethods"` // Extra is an implementation-defined user data. Extra interface{} `json:"extra"` } @@ -67,7 +65,6 @@ func NewManifest(name string) *Manifest { SupportedStandards: []string{}, } m.Trusts.Restrict() - m.SafeMethods.Restrict() return m } @@ -101,10 +98,6 @@ func (a *ABI) GetEvent(name string) *Event { // CanCall returns true is current contract is allowed to call // method of another contract with specified hash. func (m *Manifest) CanCall(hash util.Uint160, toCall *Manifest, method string) bool { - // this if is not present in the original code but should probably be here - if toCall.SafeMethods.Contains(method) { - return true - } for i := range m.Permissions { if m.Permissions[i].IsAllowed(hash, toCall, method) { return true diff --git a/pkg/smartcontract/manifest/manifest_test.go b/pkg/smartcontract/manifest/manifest_test.go index 9fd1ca2e4..667542fba 100644 --- a/pkg/smartcontract/manifest/manifest_test.go +++ b/pkg/smartcontract/manifest/manifest_test.go @@ -13,33 +13,33 @@ import ( // https://github.com/neo-project/neo/blob/master/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs#L10 func TestManifest_MarshalJSON(t *testing.T) { t.Run("default", func(t *testing.T) { - s := `{"groups":[],"supportedstandards":[],"name":"Test","abi":{"methods":[],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"safemethods":[],"extra":null}` + s := `{"groups":[],"supportedstandards":[],"name":"Test","abi":{"methods":[],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"extra":null}` m := testUnmarshalMarshalManifest(t, s) require.Equal(t, DefaultManifest("Test"), m) }) t.Run("permissions", func(t *testing.T) { - s := `{"groups":[],"supportedstandards":[],"name":"Test","abi":{"methods":[],"events":[]},"permissions":[{"contract":"0x0000000000000000000000000000000000000000","methods":["method1","method2"]}],"trusts":[],"safemethods":[],"extra":null}` + s := `{"groups":[],"supportedstandards":[],"name":"Test","abi":{"methods":[],"events":[]},"permissions":[{"contract":"0x0000000000000000000000000000000000000000","methods":["method1","method2"]}],"trusts":[],"extra":null}` testUnmarshalMarshalManifest(t, s) }) t.Run("safe methods", func(t *testing.T) { - s := `{"groups":[],"supportedstandards":[],"name":"Test","abi":{"methods":[],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"safemethods":["balanceOf"],"extra":null}` + s := `{"groups":[],"supportedstandards":[],"name":"Test","abi":{"methods":[{"name":"safeMet","offset":123,"parameters":[],"returntype":"Integer","safe":true}],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"extra":null}` testUnmarshalMarshalManifest(t, s) }) t.Run("trust", func(t *testing.T) { - s := `{"groups":[],"supportedstandards":[],"name":"Test","abi":{"methods":[],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":["0x0000000000000000000000000000000000000001"],"safemethods":[],"extra":null}` + s := `{"groups":[],"supportedstandards":[],"name":"Test","abi":{"methods":[],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":["0x0000000000000000000000000000000000000001"],"extra":null}` testUnmarshalMarshalManifest(t, s) }) t.Run("groups", func(t *testing.T) { - s := `{"groups":[{"pubkey":"03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c","signature":"QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQQ=="}],"supportedstandards":[],"name":"Test","abi":{"methods":[],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"safemethods":[],"extra":null}` + s := `{"groups":[{"pubkey":"03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c","signature":"QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQQ=="}],"supportedstandards":[],"name":"Test","abi":{"methods":[],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"extra":null}` testUnmarshalMarshalManifest(t, s) }) t.Run("extra", func(t *testing.T) { - s := `{"groups":[],"supportedstandards":[],"name":"Test","abi":{"methods":[],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"safemethods":[],"extra":{"key":"value"}}` + s := `{"groups":[],"supportedstandards":[],"name":"Test","abi":{"methods":[],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"extra":{"key":"value"}}` testUnmarshalMarshalManifest(t, s) }) } @@ -57,19 +57,9 @@ func testUnmarshalMarshalManifest(t *testing.T, s string) *Manifest { } func TestManifest_CanCall(t *testing.T) { - t.Run("safe methods", func(t *testing.T) { - man1 := NewManifest("Test1") - man2 := DefaultManifest("Test2") - require.False(t, man1.CanCall(util.Uint160{}, man2, "method1")) - man2.SafeMethods.Add("method1") - require.True(t, man1.CanCall(util.Uint160{}, man2, "method1")) - }) - - t.Run("wildcard permission", func(t *testing.T) { - man1 := DefaultManifest("Test1") - man2 := DefaultManifest("Test2") - require.True(t, man1.CanCall(util.Uint160{}, man2, "method1")) - }) + man1 := DefaultManifest("Test1") + man2 := DefaultManifest("Test2") + require.True(t, man1.CanCall(util.Uint160{}, man2, "method1")) } func TestPermission_IsAllowed(t *testing.T) { diff --git a/pkg/smartcontract/manifest/method.go b/pkg/smartcontract/manifest/method.go index 1c9a2584c..def5f14a3 100644 --- a/pkg/smartcontract/manifest/method.go +++ b/pkg/smartcontract/manifest/method.go @@ -41,6 +41,7 @@ type Method struct { Offset int `json:"offset"` Parameters []Parameter `json:"parameters"` ReturnType smartcontract.ParamType `json:"returntype"` + Safe bool `json:"safe"` } // NewParameter returns new parameter of specified name and type. diff --git a/pkg/smartcontract/manifest/standard/comply.go b/pkg/smartcontract/manifest/standard/comply.go index afd6e4000..b629723bc 100644 --- a/pkg/smartcontract/manifest/standard/comply.go +++ b/pkg/smartcontract/manifest/standard/comply.go @@ -14,6 +14,7 @@ var ( ErrInvalidReturnType = errors.New("invalid return type") ErrInvalidParameterCount = errors.New("invalid parameter count") ErrInvalidParameterType = errors.New("invalid parameter type") + ErrSafeMethodMismatch = errors.New("method has wrong safe flag") ) var checks = map[string]*manifest.Manifest{ @@ -55,6 +56,9 @@ func Comply(m, st *manifest.Manifest) error { name, i, stm.Parameters[i].Type, md.Parameters[i].Type) } } + if stm.Safe != md.Safe { + return fmt.Errorf("%w: expected %t", ErrSafeMethodMismatch, stm.Safe) + } } for _, ste := range st.ABI.Events { name := ste.Name diff --git a/pkg/smartcontract/manifest/standard/comply_test.go b/pkg/smartcontract/manifest/standard/comply_test.go index 34e0b52fd..8a1851d42 100644 --- a/pkg/smartcontract/manifest/standard/comply_test.go +++ b/pkg/smartcontract/manifest/standard/comply_test.go @@ -20,6 +20,7 @@ func fooMethodBarEvent() *manifest.Manifest { {Type: smartcontract.PublicKeyType}, }, ReturnType: smartcontract.IntegerType, + Safe: true, }, }, Events: []manifest.Event{ @@ -87,6 +88,13 @@ func TestMissingEvent(t *testing.T) { require.True(t, errors.Is(err, ErrEventMissing)) } +func TestSafeFlag(t *testing.T) { + m := fooMethodBarEvent() + m.ABI.GetMethod("foo").Safe = false + err := Comply(m, fooMethodBarEvent()) + require.True(t, errors.Is(err, ErrSafeMethodMismatch)) +} + func TestComplyValid(t *testing.T) { m := fooMethodBarEvent() m.ABI.Methods = append(m.ABI.Methods, manifest.Method{