Merge pull request #1597 from nspcc-dev/fix/safemethods

manifest: add `Safe` flag to method descriptor
This commit is contained in:
Roman Khimov 2020-12-09 16:32:19 +03:00 committed by GitHub
commit 02457d9f77
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 82 additions and 85 deletions

View file

@ -251,9 +251,6 @@ func _deploy(isUpdate bool) {}
Trusts: manifest.WildUint160s{ Trusts: manifest.WildUint160s{
Value: []util.Uint160{}, Value: []util.Uint160{},
}, },
SafeMethods: manifest.WildStrings{
Value: []string{},
},
Extra: nil, Extra: nil,
} }
require.ElementsMatch(t, expected.ABI.Methods, actual.ABI.Methods) 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.Groups, actual.Groups)
require.Equal(t, expected.Permissions, actual.Permissions) require.Equal(t, expected.Permissions, actual.Permissions)
require.Equal(t, expected.Trusts, actual.Trusts) require.Equal(t, expected.Trusts, actual.Trusts)
require.Equal(t, expected.SafeMethods, actual.SafeMethods)
require.Equal(t, expected.Extra, actual.Extra) require.Equal(t, expected.Extra, actual.Extra)
}) })
} }

View file

@ -120,13 +120,11 @@ func NewContractMD(name string) *ContractMD {
} }
// AddMethod adds new method to a native contract. // 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) c.Manifest.ABI.Methods = append(c.Manifest.ABI.Methods, *desc)
md.MD = desc md.MD = desc
desc.Safe = (md.RequiredFlags & smartcontract.AllowModifyStates) == 0
c.Methods[desc.Name] = *md c.Methods[desc.Name] = *md
if safe {
c.Manifest.SafeMethods.Add(desc.Name)
}
} }
// AddEvent adds new event to a native contract. // AddEvent adds new event to a native contract.

View file

@ -46,8 +46,13 @@ func callExInternal(ic *interop.Context, h []byte, name string, args []stackitem
if strings.HasPrefix(name, "_") { if strings.HasPrefix(name, "_") {
return errors.New("invalid method name (starts with '_')") return errors.New("invalid method name (starts with '_')")
} }
ctx := ic.VM.Context() md := cs.Manifest.ABI.GetMethod(name)
if ctx != nil && ctx.IsDeployed() { 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()) curr, err := ic.DAO.GetContractState(ic.VM.GetCurrentScriptHash())
if err == nil { if err == nil {
if !curr.Manifest.CanCall(u, &cs.Manifest, name) { if !curr.Manifest.CanCall(u, &cs.Manifest, name) {

View file

@ -79,21 +79,21 @@ func newDesignate(p2pSigExtensionsEnabled bool) *Designate {
manifest.NewParameter("role", smartcontract.IntegerType), manifest.NewParameter("role", smartcontract.IntegerType),
manifest.NewParameter("index", smartcontract.IntegerType)) manifest.NewParameter("index", smartcontract.IntegerType))
md := newMethodAndPrice(s.getDesignatedByRole, 1000000, smartcontract.AllowStates) md := newMethodAndPrice(s.getDesignatedByRole, 1000000, smartcontract.AllowStates)
s.AddMethod(md, desc, false) s.AddMethod(md, desc)
desc = newDescriptor("designateAsRole", smartcontract.VoidType, desc = newDescriptor("designateAsRole", smartcontract.VoidType,
manifest.NewParameter("role", smartcontract.IntegerType), manifest.NewParameter("role", smartcontract.IntegerType),
manifest.NewParameter("nodes", smartcontract.ArrayType)) manifest.NewParameter("nodes", smartcontract.ArrayType))
md = newMethodAndPrice(s.designateAsRole, 0, smartcontract.AllowModifyStates) md = newMethodAndPrice(s.designateAsRole, 0, smartcontract.AllowModifyStates)
s.AddMethod(md, desc, false) s.AddMethod(md, desc)
desc = newDescriptor("onPersist", smartcontract.VoidType) desc = newDescriptor("onPersist", smartcontract.VoidType)
md = newMethodAndPrice(getOnPersistWrapper(onPersistBase), 0, smartcontract.AllowModifyStates) md = newMethodAndPrice(getOnPersistWrapper(onPersistBase), 0, smartcontract.AllowModifyStates)
s.AddMethod(md, desc, false) s.AddMethod(md, desc)
desc = newDescriptor("postPersist", smartcontract.VoidType) desc = newDescriptor("postPersist", smartcontract.VoidType)
md = newMethodAndPrice(getOnPersistWrapper(postPersistBase), 0, smartcontract.AllowModifyStates) md = newMethodAndPrice(getOnPersistWrapper(postPersistBase), 0, smartcontract.AllowModifyStates)
s.AddMethod(md, desc, false) s.AddMethod(md, desc)
return s return s
} }

View file

@ -121,44 +121,44 @@ func newNEO() *NEO {
manifest.NewParameter("account", smartcontract.Hash160Type), manifest.NewParameter("account", smartcontract.Hash160Type),
manifest.NewParameter("end", smartcontract.IntegerType)) manifest.NewParameter("end", smartcontract.IntegerType))
md := newMethodAndPrice(n.unclaimedGas, 3000000, smartcontract.AllowStates) md := newMethodAndPrice(n.unclaimedGas, 3000000, smartcontract.AllowStates)
n.AddMethod(md, desc, true) n.AddMethod(md, desc)
desc = newDescriptor("registerCandidate", smartcontract.BoolType, desc = newDescriptor("registerCandidate", smartcontract.BoolType,
manifest.NewParameter("pubkey", smartcontract.PublicKeyType)) manifest.NewParameter("pubkey", smartcontract.PublicKeyType))
md = newMethodAndPrice(n.registerCandidate, 5000000, smartcontract.AllowModifyStates) md = newMethodAndPrice(n.registerCandidate, 5000000, smartcontract.AllowModifyStates)
n.AddMethod(md, desc, false) n.AddMethod(md, desc)
desc = newDescriptor("unregisterCandidate", smartcontract.BoolType, desc = newDescriptor("unregisterCandidate", smartcontract.BoolType,
manifest.NewParameter("pubkey", smartcontract.PublicKeyType)) manifest.NewParameter("pubkey", smartcontract.PublicKeyType))
md = newMethodAndPrice(n.unregisterCandidate, 5000000, smartcontract.AllowModifyStates) md = newMethodAndPrice(n.unregisterCandidate, 5000000, smartcontract.AllowModifyStates)
n.AddMethod(md, desc, false) n.AddMethod(md, desc)
desc = newDescriptor("vote", smartcontract.BoolType, desc = newDescriptor("vote", smartcontract.BoolType,
manifest.NewParameter("account", smartcontract.Hash160Type), manifest.NewParameter("account", smartcontract.Hash160Type),
manifest.NewParameter("pubkey", smartcontract.PublicKeyType)) manifest.NewParameter("pubkey", smartcontract.PublicKeyType))
md = newMethodAndPrice(n.vote, 5000000, smartcontract.AllowModifyStates) md = newMethodAndPrice(n.vote, 5000000, smartcontract.AllowModifyStates)
n.AddMethod(md, desc, false) n.AddMethod(md, desc)
desc = newDescriptor("getCandidates", smartcontract.ArrayType) desc = newDescriptor("getCandidates", smartcontract.ArrayType)
md = newMethodAndPrice(n.getCandidatesCall, 100000000, smartcontract.AllowStates) md = newMethodAndPrice(n.getCandidatesCall, 100000000, smartcontract.AllowStates)
n.AddMethod(md, desc, true) n.AddMethod(md, desc)
desc = newDescriptor("getСommittee", smartcontract.ArrayType) desc = newDescriptor("getСommittee", smartcontract.ArrayType)
md = newMethodAndPrice(n.getCommittee, 100000000, smartcontract.AllowStates) md = newMethodAndPrice(n.getCommittee, 100000000, smartcontract.AllowStates)
n.AddMethod(md, desc, true) n.AddMethod(md, desc)
desc = newDescriptor("getNextBlockValidators", smartcontract.ArrayType) desc = newDescriptor("getNextBlockValidators", smartcontract.ArrayType)
md = newMethodAndPrice(n.getNextBlockValidators, 100000000, smartcontract.AllowStates) md = newMethodAndPrice(n.getNextBlockValidators, 100000000, smartcontract.AllowStates)
n.AddMethod(md, desc, true) n.AddMethod(md, desc)
desc = newDescriptor("getGasPerBlock", smartcontract.IntegerType) desc = newDescriptor("getGasPerBlock", smartcontract.IntegerType)
md = newMethodAndPrice(n.getGASPerBlock, 100_0000, smartcontract.AllowStates) md = newMethodAndPrice(n.getGASPerBlock, 100_0000, smartcontract.AllowStates)
n.AddMethod(md, desc, false) n.AddMethod(md, desc)
desc = newDescriptor("setGasPerBlock", smartcontract.BoolType, desc = newDescriptor("setGasPerBlock", smartcontract.BoolType,
manifest.NewParameter("gasPerBlock", smartcontract.IntegerType)) manifest.NewParameter("gasPerBlock", smartcontract.IntegerType))
md = newMethodAndPrice(n.setGASPerBlock, 500_0000, smartcontract.AllowModifyStates) md = newMethodAndPrice(n.setGASPerBlock, 500_0000, smartcontract.AllowModifyStates)
n.AddMethod(md, desc, false) n.AddMethod(md, desc)
return n return n
} }

View file

@ -56,20 +56,20 @@ func newNEP17Native(name string) *nep17TokenNative {
desc := newDescriptor("symbol", smartcontract.StringType) desc := newDescriptor("symbol", smartcontract.StringType)
md := newMethodAndPrice(n.Symbol, 0, smartcontract.NoneFlag) md := newMethodAndPrice(n.Symbol, 0, smartcontract.NoneFlag)
n.AddMethod(md, desc, true) n.AddMethod(md, desc)
desc = newDescriptor("decimals", smartcontract.IntegerType) desc = newDescriptor("decimals", smartcontract.IntegerType)
md = newMethodAndPrice(n.Decimals, 0, smartcontract.NoneFlag) md = newMethodAndPrice(n.Decimals, 0, smartcontract.NoneFlag)
n.AddMethod(md, desc, true) n.AddMethod(md, desc)
desc = newDescriptor("totalSupply", smartcontract.IntegerType) desc = newDescriptor("totalSupply", smartcontract.IntegerType)
md = newMethodAndPrice(n.TotalSupply, 1000000, smartcontract.AllowStates) md = newMethodAndPrice(n.TotalSupply, 1000000, smartcontract.AllowStates)
n.AddMethod(md, desc, true) n.AddMethod(md, desc)
desc = newDescriptor("balanceOf", smartcontract.IntegerType, desc = newDescriptor("balanceOf", smartcontract.IntegerType,
manifest.NewParameter("account", smartcontract.Hash160Type)) manifest.NewParameter("account", smartcontract.Hash160Type))
md = newMethodAndPrice(n.balanceOf, 1000000, smartcontract.AllowStates) md = newMethodAndPrice(n.balanceOf, 1000000, smartcontract.AllowStates)
n.AddMethod(md, desc, true) n.AddMethod(md, desc)
transferParams := []manifest.Parameter{ transferParams := []manifest.Parameter{
manifest.NewParameter("from", smartcontract.Hash160Type), manifest.NewParameter("from", smartcontract.Hash160Type),
@ -80,15 +80,15 @@ func newNEP17Native(name string) *nep17TokenNative {
append(transferParams, manifest.NewParameter("data", smartcontract.AnyType))..., append(transferParams, manifest.NewParameter("data", smartcontract.AnyType))...,
) )
md = newMethodAndPrice(n.Transfer, 8000000, smartcontract.AllowModifyStates) md = newMethodAndPrice(n.Transfer, 8000000, smartcontract.AllowModifyStates)
n.AddMethod(md, desc, false) n.AddMethod(md, desc)
desc = newDescriptor("onPersist", smartcontract.VoidType) desc = newDescriptor("onPersist", smartcontract.VoidType)
md = newMethodAndPrice(getOnPersistWrapper(onPersistBase), 0, smartcontract.AllowModifyStates) md = newMethodAndPrice(getOnPersistWrapper(onPersistBase), 0, smartcontract.AllowModifyStates)
n.AddMethod(md, desc, false) n.AddMethod(md, desc)
desc = newDescriptor("postPersist", smartcontract.VoidType) desc = newDescriptor("postPersist", smartcontract.VoidType)
md = newMethodAndPrice(getOnPersistWrapper(postPersistBase), 0, smartcontract.AllowModifyStates) md = newMethodAndPrice(getOnPersistWrapper(postPersistBase), 0, smartcontract.AllowModifyStates)
n.AddMethod(md, desc, false) n.AddMethod(md, desc)
n.AddEvent("Transfer", transferParams...) n.AddEvent("Transfer", transferParams...)

View file

@ -47,42 +47,42 @@ func newNotary() *Notary {
manifest.NewParameter("amount", smartcontract.IntegerType), manifest.NewParameter("amount", smartcontract.IntegerType),
manifest.NewParameter("data", smartcontract.AnyType)) manifest.NewParameter("data", smartcontract.AnyType))
md := newMethodAndPrice(n.onPayment, 100_0000, smartcontract.AllowModifyStates) md := newMethodAndPrice(n.onPayment, 100_0000, smartcontract.AllowModifyStates)
n.AddMethod(md, desc, true) n.AddMethod(md, desc)
desc = newDescriptor("lockDepositUntil", smartcontract.BoolType, desc = newDescriptor("lockDepositUntil", smartcontract.BoolType,
manifest.NewParameter("address", smartcontract.Hash160Type), manifest.NewParameter("address", smartcontract.Hash160Type),
manifest.NewParameter("till", smartcontract.IntegerType)) manifest.NewParameter("till", smartcontract.IntegerType))
md = newMethodAndPrice(n.lockDepositUntil, 100_0000, smartcontract.AllowModifyStates) md = newMethodAndPrice(n.lockDepositUntil, 100_0000, smartcontract.AllowModifyStates)
n.AddMethod(md, desc, true) n.AddMethod(md, desc)
desc = newDescriptor("withdraw", smartcontract.BoolType, desc = newDescriptor("withdraw", smartcontract.BoolType,
manifest.NewParameter("from", smartcontract.Hash160Type), manifest.NewParameter("from", smartcontract.Hash160Type),
manifest.NewParameter("to", smartcontract.Hash160Type)) manifest.NewParameter("to", smartcontract.Hash160Type))
md = newMethodAndPrice(n.withdraw, 100_0000, smartcontract.AllowModifyStates) md = newMethodAndPrice(n.withdraw, 100_0000, smartcontract.AllowModifyStates)
n.AddMethod(md, desc, true) n.AddMethod(md, desc)
desc = newDescriptor("balanceOf", smartcontract.IntegerType, desc = newDescriptor("balanceOf", smartcontract.IntegerType,
manifest.NewParameter("addr", smartcontract.Hash160Type)) manifest.NewParameter("addr", smartcontract.Hash160Type))
md = newMethodAndPrice(n.balanceOf, 100_0000, smartcontract.AllowStates) md = newMethodAndPrice(n.balanceOf, 100_0000, smartcontract.AllowStates)
n.AddMethod(md, desc, true) n.AddMethod(md, desc)
desc = newDescriptor("expirationOf", smartcontract.IntegerType, desc = newDescriptor("expirationOf", smartcontract.IntegerType,
manifest.NewParameter("addr", smartcontract.Hash160Type)) manifest.NewParameter("addr", smartcontract.Hash160Type))
md = newMethodAndPrice(n.expirationOf, 100_0000, smartcontract.AllowStates) md = newMethodAndPrice(n.expirationOf, 100_0000, smartcontract.AllowStates)
n.AddMethod(md, desc, true) n.AddMethod(md, desc)
desc = newDescriptor("verify", smartcontract.BoolType, desc = newDescriptor("verify", smartcontract.BoolType,
manifest.NewParameter("signature", smartcontract.SignatureType)) manifest.NewParameter("signature", smartcontract.SignatureType))
md = newMethodAndPrice(n.verify, 100_0000, smartcontract.AllowStates) md = newMethodAndPrice(n.verify, 100_0000, smartcontract.AllowStates)
n.AddMethod(md, desc, false) n.AddMethod(md, desc)
desc = newDescriptor("onPersist", smartcontract.VoidType) desc = newDescriptor("onPersist", smartcontract.VoidType)
md = newMethodAndPrice(getOnPersistWrapper(n.OnPersist), 0, smartcontract.AllowModifyStates) md = newMethodAndPrice(getOnPersistWrapper(n.OnPersist), 0, smartcontract.AllowModifyStates)
n.AddMethod(md, desc, false) n.AddMethod(md, desc)
desc = newDescriptor("postPersist", smartcontract.VoidType) desc = newDescriptor("postPersist", smartcontract.VoidType)
md = newMethodAndPrice(getOnPersistWrapper(postPersistBase), 0, smartcontract.AllowModifyStates) md = newMethodAndPrice(getOnPersistWrapper(postPersistBase), 0, smartcontract.AllowModifyStates)
n.AddMethod(md, desc, false) n.AddMethod(md, desc)
return n return n
} }

View file

@ -111,24 +111,24 @@ func newOracle() *Oracle {
manifest.NewParameter("userData", smartcontract.AnyType), manifest.NewParameter("userData", smartcontract.AnyType),
manifest.NewParameter("gasForResponse", smartcontract.IntegerType)) manifest.NewParameter("gasForResponse", smartcontract.IntegerType))
md := newMethodAndPrice(o.request, oracleRequestPrice, smartcontract.AllowModifyStates) md := newMethodAndPrice(o.request, oracleRequestPrice, smartcontract.AllowModifyStates)
o.AddMethod(md, desc, false) o.AddMethod(md, desc)
desc = newDescriptor("finish", smartcontract.VoidType) desc = newDescriptor("finish", smartcontract.VoidType)
md = newMethodAndPrice(o.finish, 0, smartcontract.AllowModifyStates) md = newMethodAndPrice(o.finish, 0, smartcontract.AllowModifyStates)
o.AddMethod(md, desc, false) o.AddMethod(md, desc)
desc = newDescriptor("verify", smartcontract.BoolType) desc = newDescriptor("verify", smartcontract.BoolType)
md = newMethodAndPrice(o.verify, 100_0000, smartcontract.NoneFlag) md = newMethodAndPrice(o.verify, 100_0000, smartcontract.NoneFlag)
o.AddMethod(md, desc, false) o.AddMethod(md, desc)
pp := chainOnPersist(postPersistBase, o.PostPersist) pp := chainOnPersist(postPersistBase, o.PostPersist)
desc = newDescriptor("postPersist", smartcontract.VoidType) desc = newDescriptor("postPersist", smartcontract.VoidType)
md = newMethodAndPrice(getOnPersistWrapper(pp), 0, smartcontract.AllowModifyStates) md = newMethodAndPrice(getOnPersistWrapper(pp), 0, smartcontract.AllowModifyStates)
o.AddMethod(md, desc, false) o.AddMethod(md, desc)
desc = newDescriptor("onPersist", smartcontract.VoidType) desc = newDescriptor("onPersist", smartcontract.VoidType)
md = newMethodAndPrice(getOnPersistWrapper(onPersistBase), 0, smartcontract.AllowModifyStates) md = newMethodAndPrice(getOnPersistWrapper(onPersistBase), 0, smartcontract.AllowModifyStates)
o.AddMethod(md, desc, false) o.AddMethod(md, desc)
o.AddEvent("OracleRequest", manifest.NewParameter("Id", smartcontract.IntegerType), o.AddEvent("OracleRequest", manifest.NewParameter("Id", smartcontract.IntegerType),
manifest.NewParameter("RequestContract", smartcontract.Hash160Type), manifest.NewParameter("RequestContract", smartcontract.Hash160Type),

View file

@ -77,62 +77,62 @@ func newPolicy() *Policy {
desc := newDescriptor("getMaxTransactionsPerBlock", smartcontract.IntegerType) desc := newDescriptor("getMaxTransactionsPerBlock", smartcontract.IntegerType)
md := newMethodAndPrice(p.getMaxTransactionsPerBlock, 1000000, smartcontract.AllowStates) md := newMethodAndPrice(p.getMaxTransactionsPerBlock, 1000000, smartcontract.AllowStates)
p.AddMethod(md, desc, true) p.AddMethod(md, desc)
desc = newDescriptor("getMaxBlockSize", smartcontract.IntegerType) desc = newDescriptor("getMaxBlockSize", smartcontract.IntegerType)
md = newMethodAndPrice(p.getMaxBlockSize, 1000000, smartcontract.AllowStates) md = newMethodAndPrice(p.getMaxBlockSize, 1000000, smartcontract.AllowStates)
p.AddMethod(md, desc, true) p.AddMethod(md, desc)
desc = newDescriptor("getFeePerByte", smartcontract.IntegerType) desc = newDescriptor("getFeePerByte", smartcontract.IntegerType)
md = newMethodAndPrice(p.getFeePerByte, 1000000, smartcontract.AllowStates) md = newMethodAndPrice(p.getFeePerByte, 1000000, smartcontract.AllowStates)
p.AddMethod(md, desc, true) p.AddMethod(md, desc)
desc = newDescriptor("isBlocked", smartcontract.BoolType, desc = newDescriptor("isBlocked", smartcontract.BoolType,
manifest.NewParameter("account", smartcontract.Hash160Type)) manifest.NewParameter("account", smartcontract.Hash160Type))
md = newMethodAndPrice(p.isBlocked, 1000000, smartcontract.AllowStates) md = newMethodAndPrice(p.isBlocked, 1000000, smartcontract.AllowStates)
p.AddMethod(md, desc, true) p.AddMethod(md, desc)
desc = newDescriptor("getMaxBlockSystemFee", smartcontract.IntegerType) desc = newDescriptor("getMaxBlockSystemFee", smartcontract.IntegerType)
md = newMethodAndPrice(p.getMaxBlockSystemFee, 1000000, smartcontract.AllowStates) md = newMethodAndPrice(p.getMaxBlockSystemFee, 1000000, smartcontract.AllowStates)
p.AddMethod(md, desc, true) p.AddMethod(md, desc)
desc = newDescriptor("setMaxBlockSize", smartcontract.BoolType, desc = newDescriptor("setMaxBlockSize", smartcontract.BoolType,
manifest.NewParameter("value", smartcontract.IntegerType)) manifest.NewParameter("value", smartcontract.IntegerType))
md = newMethodAndPrice(p.setMaxBlockSize, 3000000, smartcontract.AllowModifyStates) md = newMethodAndPrice(p.setMaxBlockSize, 3000000, smartcontract.AllowModifyStates)
p.AddMethod(md, desc, false) p.AddMethod(md, desc)
desc = newDescriptor("setMaxTransactionsPerBlock", smartcontract.BoolType, desc = newDescriptor("setMaxTransactionsPerBlock", smartcontract.BoolType,
manifest.NewParameter("value", smartcontract.IntegerType)) manifest.NewParameter("value", smartcontract.IntegerType))
md = newMethodAndPrice(p.setMaxTransactionsPerBlock, 3000000, smartcontract.AllowModifyStates) md = newMethodAndPrice(p.setMaxTransactionsPerBlock, 3000000, smartcontract.AllowModifyStates)
p.AddMethod(md, desc, false) p.AddMethod(md, desc)
desc = newDescriptor("setFeePerByte", smartcontract.BoolType, desc = newDescriptor("setFeePerByte", smartcontract.BoolType,
manifest.NewParameter("value", smartcontract.IntegerType)) manifest.NewParameter("value", smartcontract.IntegerType))
md = newMethodAndPrice(p.setFeePerByte, 3000000, smartcontract.AllowModifyStates) md = newMethodAndPrice(p.setFeePerByte, 3000000, smartcontract.AllowModifyStates)
p.AddMethod(md, desc, false) p.AddMethod(md, desc)
desc = newDescriptor("setMaxBlockSystemFee", smartcontract.BoolType, desc = newDescriptor("setMaxBlockSystemFee", smartcontract.BoolType,
manifest.NewParameter("value", smartcontract.IntegerType)) manifest.NewParameter("value", smartcontract.IntegerType))
md = newMethodAndPrice(p.setMaxBlockSystemFee, 3000000, smartcontract.AllowModifyStates) md = newMethodAndPrice(p.setMaxBlockSystemFee, 3000000, smartcontract.AllowModifyStates)
p.AddMethod(md, desc, false) p.AddMethod(md, desc)
desc = newDescriptor("blockAccount", smartcontract.BoolType, desc = newDescriptor("blockAccount", smartcontract.BoolType,
manifest.NewParameter("account", smartcontract.Hash160Type)) manifest.NewParameter("account", smartcontract.Hash160Type))
md = newMethodAndPrice(p.blockAccount, 3000000, smartcontract.AllowModifyStates) md = newMethodAndPrice(p.blockAccount, 3000000, smartcontract.AllowModifyStates)
p.AddMethod(md, desc, false) p.AddMethod(md, desc)
desc = newDescriptor("unblockAccount", smartcontract.BoolType, desc = newDescriptor("unblockAccount", smartcontract.BoolType,
manifest.NewParameter("account", smartcontract.Hash160Type)) manifest.NewParameter("account", smartcontract.Hash160Type))
md = newMethodAndPrice(p.unblockAccount, 3000000, smartcontract.AllowModifyStates) md = newMethodAndPrice(p.unblockAccount, 3000000, smartcontract.AllowModifyStates)
p.AddMethod(md, desc, false) p.AddMethod(md, desc)
desc = newDescriptor("onPersist", smartcontract.VoidType) desc = newDescriptor("onPersist", smartcontract.VoidType)
md = newMethodAndPrice(getOnPersistWrapper(p.OnPersist), 0, smartcontract.AllowModifyStates) md = newMethodAndPrice(getOnPersistWrapper(p.OnPersist), 0, smartcontract.AllowModifyStates)
p.AddMethod(md, desc, false) p.AddMethod(md, desc)
desc = newDescriptor("postPersist", smartcontract.VoidType) desc = newDescriptor("postPersist", smartcontract.VoidType)
md = newMethodAndPrice(getOnPersistWrapper(postPersistBase), 0, smartcontract.AllowModifyStates) md = newMethodAndPrice(getOnPersistWrapper(postPersistBase), 0, smartcontract.AllowModifyStates)
p.AddMethod(md, desc, false) p.AddMethod(md, desc)
return p return p
} }

View file

@ -66,13 +66,14 @@ func newTestNative() *testNative {
manifest.NewParameter("addend2", smartcontract.IntegerType), manifest.NewParameter("addend2", smartcontract.IntegerType),
}, },
ReturnType: smartcontract.IntegerType, ReturnType: smartcontract.IntegerType,
Safe: true,
} }
md := &interop.MethodAndPrice{ md := &interop.MethodAndPrice{
Func: tn.sum, Func: tn.sum,
Price: testSumPrice, Price: testSumPrice,
RequiredFlags: smartcontract.NoneFlag, RequiredFlags: smartcontract.NoneFlag,
} }
tn.meta.AddMethod(md, desc, true) tn.meta.AddMethod(md, desc)
desc = &manifest.Method{ desc = &manifest.Method{
Name: "callOtherContractNoReturn", Name: "callOtherContractNoReturn",
@ -82,16 +83,17 @@ func newTestNative() *testNative {
manifest.NewParameter("arg", smartcontract.ArrayType), manifest.NewParameter("arg", smartcontract.ArrayType),
}, },
ReturnType: smartcontract.VoidType, ReturnType: smartcontract.VoidType,
Safe: true,
} }
md = &interop.MethodAndPrice{ md = &interop.MethodAndPrice{
Func: tn.callOtherContractNoReturn, Func: tn.callOtherContractNoReturn,
Price: testSumPrice, Price: testSumPrice,
RequiredFlags: smartcontract.NoneFlag} RequiredFlags: smartcontract.NoneFlag}
tn.meta.AddMethod(md, desc, true) tn.meta.AddMethod(md, desc)
desc = &manifest.Method{Name: "onPersist", ReturnType: smartcontract.BoolType} desc = &manifest.Method{Name: "onPersist", ReturnType: smartcontract.BoolType}
md = &interop.MethodAndPrice{Func: tn.OnPersist, RequiredFlags: smartcontract.AllowModifyStates} md = &interop.MethodAndPrice{Func: tn.OnPersist, RequiredFlags: smartcontract.AllowModifyStates}
tn.meta.AddMethod(md, desc, false) tn.meta.AddMethod(md, desc)
return tn return tn
} }

View file

@ -49,8 +49,6 @@ type Manifest struct {
SupportedStandards []string `json:"supportedstandards"` SupportedStandards []string `json:"supportedstandards"`
// Trusts is a set of hashes to a which contract trusts. // Trusts is a set of hashes to a which contract trusts.
Trusts WildUint160s `json:"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 is an implementation-defined user data.
Extra interface{} `json:"extra"` Extra interface{} `json:"extra"`
} }
@ -67,7 +65,6 @@ func NewManifest(name string) *Manifest {
SupportedStandards: []string{}, SupportedStandards: []string{},
} }
m.Trusts.Restrict() m.Trusts.Restrict()
m.SafeMethods.Restrict()
return m return m
} }
@ -101,10 +98,6 @@ func (a *ABI) GetEvent(name string) *Event {
// CanCall returns true is current contract is allowed to call // CanCall returns true is current contract is allowed to call
// method of another contract with specified hash. // method of another contract with specified hash.
func (m *Manifest) CanCall(hash util.Uint160, toCall *Manifest, method string) bool { 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 { for i := range m.Permissions {
if m.Permissions[i].IsAllowed(hash, toCall, method) { if m.Permissions[i].IsAllowed(hash, toCall, method) {
return true return true

View file

@ -13,33 +13,33 @@ import (
// https://github.com/neo-project/neo/blob/master/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs#L10 // https://github.com/neo-project/neo/blob/master/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs#L10
func TestManifest_MarshalJSON(t *testing.T) { func TestManifest_MarshalJSON(t *testing.T) {
t.Run("default", func(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) m := testUnmarshalMarshalManifest(t, s)
require.Equal(t, DefaultManifest("Test"), m) require.Equal(t, DefaultManifest("Test"), m)
}) })
t.Run("permissions", func(t *testing.T) { 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) testUnmarshalMarshalManifest(t, s)
}) })
t.Run("safe methods", func(t *testing.T) { 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) testUnmarshalMarshalManifest(t, s)
}) })
t.Run("trust", func(t *testing.T) { 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) testUnmarshalMarshalManifest(t, s)
}) })
t.Run("groups", func(t *testing.T) { 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) testUnmarshalMarshalManifest(t, s)
}) })
t.Run("extra", func(t *testing.T) { 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) testUnmarshalMarshalManifest(t, s)
}) })
} }
@ -57,19 +57,9 @@ func testUnmarshalMarshalManifest(t *testing.T, s string) *Manifest {
} }
func TestManifest_CanCall(t *testing.T) { func TestManifest_CanCall(t *testing.T) {
t.Run("safe methods", func(t *testing.T) { man1 := DefaultManifest("Test1")
man1 := NewManifest("Test1") man2 := DefaultManifest("Test2")
man2 := DefaultManifest("Test2") require.True(t, man1.CanCall(util.Uint160{}, man2, "method1"))
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"))
})
} }
func TestPermission_IsAllowed(t *testing.T) { func TestPermission_IsAllowed(t *testing.T) {

View file

@ -41,6 +41,7 @@ type Method struct {
Offset int `json:"offset"` Offset int `json:"offset"`
Parameters []Parameter `json:"parameters"` Parameters []Parameter `json:"parameters"`
ReturnType smartcontract.ParamType `json:"returntype"` ReturnType smartcontract.ParamType `json:"returntype"`
Safe bool `json:"safe"`
} }
// NewParameter returns new parameter of specified name and type. // NewParameter returns new parameter of specified name and type.

View file

@ -14,6 +14,7 @@ var (
ErrInvalidReturnType = errors.New("invalid return type") ErrInvalidReturnType = errors.New("invalid return type")
ErrInvalidParameterCount = errors.New("invalid parameter count") ErrInvalidParameterCount = errors.New("invalid parameter count")
ErrInvalidParameterType = errors.New("invalid parameter type") ErrInvalidParameterType = errors.New("invalid parameter type")
ErrSafeMethodMismatch = errors.New("method has wrong safe flag")
) )
var checks = map[string]*manifest.Manifest{ 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) 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 { for _, ste := range st.ABI.Events {
name := ste.Name name := ste.Name

View file

@ -20,6 +20,7 @@ func fooMethodBarEvent() *manifest.Manifest {
{Type: smartcontract.PublicKeyType}, {Type: smartcontract.PublicKeyType},
}, },
ReturnType: smartcontract.IntegerType, ReturnType: smartcontract.IntegerType,
Safe: true,
}, },
}, },
Events: []manifest.Event{ Events: []manifest.Event{
@ -87,6 +88,13 @@ func TestMissingEvent(t *testing.T) {
require.True(t, errors.Is(err, ErrEventMissing)) 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) { func TestComplyValid(t *testing.T) {
m := fooMethodBarEvent() m := fooMethodBarEvent()
m.ABI.Methods = append(m.ABI.Methods, manifest.Method{ m.ABI.Methods = append(m.ABI.Methods, manifest.Method{