forked from TrueCloudLab/neoneo-go
compiler: extend permission check to runtime hashes
If a method is known at compile time we can still check if it is present in the list of methods of at least one contract. Signed-off-by: Evgeniy Stratonikov <evgeniy@nspcc.ru>
This commit is contained in:
parent
4249674ddc
commit
aa76383fa7
4 changed files with 39 additions and 32 deletions
|
@ -16,6 +16,7 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest/standard"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest/standard"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"golang.org/x/tools/go/loader"
|
"golang.org/x/tools/go/loader"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -304,11 +305,13 @@ func CreateManifest(di *DebugInfo, o *Options) (*manifest.Manifest, error) {
|
||||||
// Thus only basic checks are performed.
|
// Thus only basic checks are performed.
|
||||||
|
|
||||||
for h, methods := range di.InvokedContracts {
|
for h, methods := range di.InvokedContracts {
|
||||||
|
knownHash := !h.Equals(util.Uint160{})
|
||||||
|
|
||||||
methodLoop:
|
methodLoop:
|
||||||
for _, m := range methods {
|
for _, m := range methods {
|
||||||
for _, p := range o.Permissions {
|
for _, p := range o.Permissions {
|
||||||
// Group or wildcard permission is ok to try.
|
// Group or wildcard permission is ok to try.
|
||||||
if p.Contract.Type == manifest.PermissionHash && !p.Contract.Hash().Equals(h) {
|
if knownHash && p.Contract.Type == manifest.PermissionHash && !p.Contract.Hash().Equals(h) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -317,8 +320,12 @@ func CreateManifest(di *DebugInfo, o *Options) (*manifest.Manifest, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, fmt.Errorf("method '%s' of contract %s is invoked but"+
|
if knownHash {
|
||||||
" corresponding permission is missing", m, h.StringLE())
|
return nil, fmt.Errorf("method '%s' of contract %s is invoked but"+
|
||||||
|
" corresponding permission is missing", m, h.StringLE())
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("method '%s' is invoked but"+
|
||||||
|
" corresponding permission is missing", m)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/nspcc-dev/neo-go/internal/random"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/compiler"
|
"github.com/nspcc-dev/neo-go/pkg/compiler"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/config"
|
"github.com/nspcc-dev/neo-go/pkg/config"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
|
@ -263,9 +264,7 @@ func TestInvokedContractsPermissons(t *testing.T) {
|
||||||
contract.Call(interop.Hash160(hash), runtimeMethod, contract.All)
|
contract.Call(interop.Hash160(hash), runtimeMethod, contract.All)
|
||||||
contract.Call(runtimeHash, "someMethod", contract.All)
|
contract.Call(runtimeHash, "someMethod", contract.All)
|
||||||
contract.Call(interop.Hash160(runtimeHash), "someMethod", contract.All)
|
contract.Call(interop.Hash160(runtimeHash), "someMethod", contract.All)
|
||||||
contract.Call(runh.RuntimeHash(), "method3", contract.All)
|
contract.Call(runh.RuntimeHash(), "method4", contract.All)
|
||||||
contract.Call(runh.RuntimeHashArgs(hash), "method3", contract.All)
|
|
||||||
contract.Call(invoke(hash), "method3", contract.All)
|
|
||||||
}`, hashStr)
|
}`, hashStr)
|
||||||
|
|
||||||
_, di, err := compiler.CompileWithOptions("permissionTest", strings.NewReader(src), &compiler.Options{})
|
_, di, err := compiler.CompileWithOptions("permissionTest", strings.NewReader(src), &compiler.Options{})
|
||||||
|
@ -281,12 +280,17 @@ func TestInvokedContractsPermissons(t *testing.T) {
|
||||||
require.Error(t, testCompile(t, di, false, *p))
|
require.Error(t, testCompile(t, di, false, *p))
|
||||||
require.NoError(t, testCompile(t, di, true, *p))
|
require.NoError(t, testCompile(t, di, true, *p))
|
||||||
|
|
||||||
|
pr := manifest.NewPermission(manifest.PermissionHash, random.Uint160())
|
||||||
|
pr.Methods.Add("someMethod")
|
||||||
|
pr.Methods.Add("method4")
|
||||||
|
|
||||||
t.Run("wildcard", func(t *testing.T) {
|
t.Run("wildcard", func(t *testing.T) {
|
||||||
pw := manifest.NewPermission(manifest.PermissionWildcard)
|
pw := manifest.NewPermission(manifest.PermissionWildcard)
|
||||||
require.NoError(t, testCompile(t, di, false, *p, *pw))
|
require.NoError(t, testCompile(t, di, false, *p, *pw))
|
||||||
|
|
||||||
pw.Methods.Add("method2")
|
pw.Methods.Add("method2")
|
||||||
require.NoError(t, testCompile(t, di, false, *p, *pw))
|
require.Error(t, testCompile(t, di, false, *p, *pw))
|
||||||
|
require.NoError(t, testCompile(t, di, false, *p, *pw, *pr))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("group", func(t *testing.T) {
|
t.Run("group", func(t *testing.T) {
|
||||||
|
@ -295,10 +299,11 @@ func TestInvokedContractsPermissons(t *testing.T) {
|
||||||
require.NoError(t, testCompile(t, di, false, *p, *pw))
|
require.NoError(t, testCompile(t, di, false, *p, *pw))
|
||||||
|
|
||||||
pw.Methods.Add("invalid")
|
pw.Methods.Add("invalid")
|
||||||
require.Error(t, testCompile(t, di, false, *p, *pw))
|
require.Error(t, testCompile(t, di, false, *p, *pw, *pr))
|
||||||
|
|
||||||
pw.Methods.Add("method2")
|
pw.Methods.Add("method2")
|
||||||
require.NoError(t, testCompile(t, di, false, *p, *pw))
|
require.Error(t, testCompile(t, di, false, *p, *pw))
|
||||||
|
require.NoError(t, testCompile(t, di, false, *p, *pw, *pr))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -159,34 +159,27 @@ func (c *codegen) processNotify(f *funcScope, args []ast.Expr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *codegen) processContractCall(f *funcScope, args []ast.Expr) {
|
func (c *codegen) processContractCall(f *funcScope, args []ast.Expr) {
|
||||||
|
var u util.Uint160
|
||||||
|
|
||||||
// For stdlib calls it is `interop.Hash160(constHash)`
|
// For stdlib calls it is `interop.Hash160(constHash)`
|
||||||
// so we can determine hash at compile-time.
|
// so we can determine hash at compile-time.
|
||||||
ce, ok := args[0].(*ast.CallExpr)
|
ce, ok := args[0].(*ast.CallExpr)
|
||||||
if !ok || len(ce.Args) != 1 {
|
if ok && len(ce.Args) == 1 {
|
||||||
return
|
// Ensure this is a type conversion, not a simple invoke.
|
||||||
|
se, ok := ce.Fun.(*ast.SelectorExpr)
|
||||||
|
if ok {
|
||||||
|
name, _ := c.getFuncNameFromSelector(se)
|
||||||
|
if _, ok := c.funcs[name]; !ok {
|
||||||
|
value := c.typeAndValueOf(ce.Args[0]).Value
|
||||||
|
if value != nil {
|
||||||
|
s := constant.StringVal(value)
|
||||||
|
copy(u[:], s) // constant must be big-endian
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure this is a type conversion, not a simple invoke.
|
value := c.typeAndValueOf(args[1]).Value
|
||||||
se, ok := ce.Fun.(*ast.SelectorExpr)
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
name, _ := c.getFuncNameFromSelector(se)
|
|
||||||
if _, ok := c.funcs[name]; ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
value := c.typeAndValueOf(ce.Args[0]).Value
|
|
||||||
if value == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
s := constant.StringVal(value)
|
|
||||||
var u util.Uint160
|
|
||||||
copy(u[:], s) // constant must be big-endian
|
|
||||||
|
|
||||||
value = c.typeAndValueOf(args[1]).Value
|
|
||||||
if value == nil {
|
if value == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
2
pkg/rpc/server/testdata/test_contract.yml
vendored
2
pkg/rpc/server/testdata/test_contract.yml
vendored
|
@ -10,3 +10,5 @@ events:
|
||||||
type: Hash160
|
type: Hash160
|
||||||
- name: amount
|
- name: amount
|
||||||
type: Integer
|
type: Integer
|
||||||
|
permissions:
|
||||||
|
- methods: ["onNEP17Payment"]
|
Loading…
Add table
Reference in a new issue