Merge pull request #1988 from nspcc-dev/cli-permission
cli,compiler: allow to specify manifest permissions
This commit is contained in:
commit
352450d25a
11 changed files with 241 additions and 19 deletions
129
cli/smartcontract/permission.go
Normal file
129
cli/smartcontract/permission.go
Normal file
|
@ -0,0 +1,129 @@
|
|||
package smartcontract
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
type permission manifest.Permission
|
||||
|
||||
const (
|
||||
permHashKey = "hash"
|
||||
permGroupKey = "group"
|
||||
permMethodKey = "methods"
|
||||
)
|
||||
|
||||
func (p permission) MarshalYAML() (interface{}, error) {
|
||||
m := make(yaml.MapSlice, 0, 2)
|
||||
switch p.Contract.Type {
|
||||
case manifest.PermissionWildcard:
|
||||
case manifest.PermissionHash:
|
||||
m = append(m, yaml.MapItem{
|
||||
Key: permHashKey,
|
||||
Value: p.Contract.Value.(util.Uint160).StringLE(),
|
||||
})
|
||||
case manifest.PermissionGroup:
|
||||
bs := p.Contract.Value.(*keys.PublicKey).Bytes()
|
||||
m = append(m, yaml.MapItem{
|
||||
Key: permGroupKey,
|
||||
Value: hex.EncodeToString(bs),
|
||||
})
|
||||
default:
|
||||
return nil, fmt.Errorf("invalid permission type: %d", p.Contract.Type)
|
||||
}
|
||||
|
||||
var val interface{} = "*"
|
||||
if !p.Methods.IsWildcard() {
|
||||
val = p.Methods.Value
|
||||
}
|
||||
|
||||
m = append(m, yaml.MapItem{
|
||||
Key: permMethodKey,
|
||||
Value: val,
|
||||
})
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (p *permission) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
var m map[string]interface{}
|
||||
if err := unmarshal(&m); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := p.fillType(m); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return p.fillMethods(m)
|
||||
}
|
||||
|
||||
func (p *permission) fillType(m map[string]interface{}) error {
|
||||
vh, ok1 := m[permHashKey]
|
||||
vg, ok2 := m[permGroupKey]
|
||||
switch {
|
||||
case ok1 && ok2:
|
||||
return errors.New("permission must have either 'hash' or 'group' field")
|
||||
case ok1:
|
||||
s, ok := vh.(string)
|
||||
if !ok {
|
||||
return errors.New("invalid 'hash' type")
|
||||
}
|
||||
|
||||
u, err := util.Uint160DecodeStringLE(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.Contract.Type = manifest.PermissionHash
|
||||
p.Contract.Value = u
|
||||
case ok2:
|
||||
s, ok := vg.(string)
|
||||
if !ok {
|
||||
return errors.New("invalid 'hash' type")
|
||||
}
|
||||
|
||||
pub, err := keys.NewPublicKeyFromString(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.Contract.Type = manifest.PermissionGroup
|
||||
p.Contract.Value = pub
|
||||
default:
|
||||
p.Contract.Type = manifest.PermissionWildcard
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *permission) fillMethods(m map[string]interface{}) error {
|
||||
methods, ok := m[permMethodKey]
|
||||
if !ok {
|
||||
return errors.New("'methods' field is missing from permission")
|
||||
}
|
||||
|
||||
switch mt := methods.(type) {
|
||||
case string:
|
||||
if mt == "*" {
|
||||
p.Methods.Value = nil
|
||||
return nil
|
||||
}
|
||||
case []interface{}:
|
||||
ms := make([]string, len(mt))
|
||||
for i := range mt {
|
||||
ms[i], ok = mt[i].(string)
|
||||
if !ok {
|
||||
return errors.New("invalid permission method name")
|
||||
}
|
||||
}
|
||||
p.Methods.Value = ms
|
||||
return nil
|
||||
default:
|
||||
}
|
||||
return errors.New("'methods' field is invalid")
|
||||
}
|
|
@ -401,6 +401,7 @@ func initSmartContract(ctx *cli.Context) error {
|
|||
},
|
||||
},
|
||||
},
|
||||
Permissions: []permission{permission(*manifest.NewPermission(manifest.PermissionWildcard))},
|
||||
}
|
||||
b, err := yaml.Marshal(m)
|
||||
if err != nil {
|
||||
|
@ -450,6 +451,10 @@ func contractCompile(ctx *cli.Context) error {
|
|||
o.Name = conf.Name
|
||||
o.ContractEvents = conf.Events
|
||||
o.ContractSupportedStandards = conf.SupportedStandards
|
||||
o.Permissions = make([]manifest.Permission, len(conf.Permissions))
|
||||
for i := range conf.Permissions {
|
||||
o.Permissions[i] = manifest.Permission(conf.Permissions[i])
|
||||
}
|
||||
o.SafeMethods = conf.SafeMethods
|
||||
}
|
||||
|
||||
|
@ -674,6 +679,7 @@ type ProjectConfig struct {
|
|||
SafeMethods []string
|
||||
SupportedStandards []string
|
||||
Events []manifest.Event
|
||||
Permissions []permission
|
||||
}
|
||||
|
||||
func inspect(ctx *cli.Context) error {
|
||||
|
|
|
@ -1,13 +1,18 @@
|
|||
package smartcontract
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"flag"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/internal/random"
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/urfave/cli"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
func TestInitSmartContract(t *testing.T) {
|
||||
|
@ -64,5 +69,73 @@ events:
|
|||
parameters:
|
||||
- name: args
|
||||
type: Array
|
||||
permissions:
|
||||
- methods: '*'
|
||||
`, string(manifest))
|
||||
}
|
||||
|
||||
func testPermissionMarshal(t *testing.T, p *manifest.Permission, expected string) {
|
||||
out, err := yaml.Marshal((*permission)(p))
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, expected, string(out))
|
||||
|
||||
t.Run("Unmarshal", func(t *testing.T) {
|
||||
actual := new(permission)
|
||||
require.NoError(t, yaml.Unmarshal(out, actual))
|
||||
require.Equal(t, p, (*manifest.Permission)(actual))
|
||||
})
|
||||
}
|
||||
|
||||
func TestPermissionMarshal(t *testing.T) {
|
||||
t.Run("Wildcard", func(t *testing.T) {
|
||||
p := manifest.NewPermission(manifest.PermissionWildcard)
|
||||
testPermissionMarshal(t, p, "methods: '*'\n")
|
||||
})
|
||||
t.Run("no allowed methods", func(t *testing.T) {
|
||||
p := manifest.NewPermission(manifest.PermissionWildcard)
|
||||
p.Methods.Restrict()
|
||||
testPermissionMarshal(t, p, "methods: []\n")
|
||||
})
|
||||
t.Run("hash", func(t *testing.T) {
|
||||
h := random.Uint160()
|
||||
p := manifest.NewPermission(manifest.PermissionHash, h)
|
||||
testPermissionMarshal(t, p,
|
||||
"hash: "+h.StringLE()+"\n"+
|
||||
"methods: '*'\n")
|
||||
})
|
||||
t.Run("group with some methods", func(t *testing.T) {
|
||||
priv, err := keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
|
||||
p := manifest.NewPermission(manifest.PermissionGroup, priv.PublicKey())
|
||||
p.Methods.Add("abc")
|
||||
p.Methods.Add("lamao")
|
||||
testPermissionMarshal(t, p,
|
||||
"group: "+hex.EncodeToString(priv.PublicKey().Bytes())+"\n"+
|
||||
"methods:\n- abc\n- lamao\n")
|
||||
})
|
||||
}
|
||||
|
||||
func TestPermissionUnmarshalInvalid(t *testing.T) {
|
||||
priv, err := keys.NewPrivateKey()
|
||||
require.NoError(t, err)
|
||||
|
||||
pub := hex.EncodeToString(priv.PublicKey().Bytes())
|
||||
u160 := random.Uint160().StringLE()
|
||||
testCases := []string{
|
||||
"hash: []\nmethods: '*'\n", // invalid hash type
|
||||
"hash: notahex\nmethods: '*'\n", // invalid hash
|
||||
"group: []\nmethods: '*'\n", // invalid group type
|
||||
"group: notahex\nmethods: '*'\n", // invalid group
|
||||
"hash: " + u160 + "\n", // missing methods
|
||||
"group: " + pub + "\nhash: " + u160 + "\nmethods: '*'", // hash/group conflict
|
||||
"hash: " + u160 + "\nmethods:\n a: b\n", // invalid methods type
|
||||
"hash: " + u160 + "\nmethods:\n- []\n", // methods array, invalid single
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc, func(t *testing.T) {
|
||||
require.Error(t, yaml.Unmarshal([]byte(tc), new(permission)))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
3
cli/testdata/deploy/neo-go.yml
vendored
3
cli/testdata/deploy/neo-go.yml
vendored
|
@ -1 +1,4 @@
|
|||
name: Test deploy
|
||||
permissions:
|
||||
- hash: fffdc93764dbaddd97c48f252a53ea4643faa3fd
|
||||
methods: ["update"]
|
||||
|
|
|
@ -13,3 +13,5 @@ events:
|
|||
type: Integer
|
||||
- name: tokenId
|
||||
type: ByteArray
|
||||
permissions:
|
||||
- methods: ["onNEP11Transfer"]
|
||||
|
|
|
@ -12,3 +12,5 @@ events:
|
|||
type: Integer
|
||||
- name: tokenId
|
||||
type: ByteArray
|
||||
permissions:
|
||||
- methods: ["onNEP11Transfer"]
|
|
@ -16,6 +16,7 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
||||
"github.com/nspcc-dev/neo-go/pkg/io"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||
|
@ -78,6 +79,10 @@ func NewDeployTx(bc blockchainer.Blockchainer, name string, sender util.Uint160,
|
|||
o.Name = conf.Name
|
||||
o.ContractEvents = conf.Events
|
||||
o.ContractSupportedStandards = conf.SupportedStandards
|
||||
o.Permissions = make([]manifest.Permission, len(conf.Permissions))
|
||||
for i := range conf.Permissions {
|
||||
o.Permissions[i] = manifest.Permission(conf.Permissions[i])
|
||||
}
|
||||
o.SafeMethods = conf.SafeMethods
|
||||
}
|
||||
m, err := compiler.CreateManifest(di, o)
|
||||
|
|
|
@ -54,6 +54,9 @@ type Options struct {
|
|||
|
||||
// SafeMethods contains list of methods which will be marked as safe in manifest.
|
||||
SafeMethods []string
|
||||
|
||||
// Permissions is a list of permissions for every contract method.
|
||||
Permissions []manifest.Permission
|
||||
}
|
||||
|
||||
type buildInfo struct {
|
||||
|
|
|
@ -441,13 +441,6 @@ func (di *DebugInfo) ConvertToManifest(o *Options) (*manifest.Manifest, error) {
|
|||
if result.ABI.Events == nil {
|
||||
result.ABI.Events = make([]manifest.Event, 0)
|
||||
}
|
||||
result.Permissions = []manifest.Permission{
|
||||
{
|
||||
Contract: manifest.PermissionDesc{
|
||||
Type: manifest.PermissionWildcard,
|
||||
},
|
||||
Methods: manifest.WildStrings{},
|
||||
},
|
||||
}
|
||||
result.Permissions = o.Permissions
|
||||
return result, nil
|
||||
}
|
||||
|
|
|
@ -175,7 +175,14 @@ func _deploy(data interface{}, isUpdate bool) { x := 1; _ = x }
|
|||
}
|
||||
|
||||
t.Run("convert to Manifest", func(t *testing.T) {
|
||||
actual, err := d.ConvertToManifest(&Options{Name: "MyCTR", SafeMethods: []string{"methodInt", "methodString"}})
|
||||
p := manifest.NewPermission(manifest.PermissionWildcard)
|
||||
p.Methods.Add("randomMethod")
|
||||
|
||||
actual, err := d.ConvertToManifest(&Options{
|
||||
Name: "MyCTR",
|
||||
SafeMethods: []string{"methodInt", "methodString"},
|
||||
Permissions: []manifest.Permission{*p},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
expected := &manifest.Manifest{
|
||||
Name: "MyCTR",
|
||||
|
@ -267,15 +274,8 @@ func _deploy(data interface{}, isUpdate bool) { x := 1; _ = x }
|
|||
},
|
||||
Events: []manifest.Event{},
|
||||
},
|
||||
Groups: []manifest.Group{},
|
||||
Permissions: []manifest.Permission{
|
||||
{
|
||||
Contract: manifest.PermissionDesc{
|
||||
Type: manifest.PermissionWildcard,
|
||||
},
|
||||
Methods: manifest.WildStrings{},
|
||||
},
|
||||
},
|
||||
Groups: []manifest.Group{},
|
||||
Permissions: []manifest.Permission{*p},
|
||||
Trusts: manifest.WildPermissionDescs{
|
||||
Value: []manifest.PermissionDesc{},
|
||||
},
|
||||
|
|
|
@ -18,6 +18,7 @@ import (
|
|||
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
|
||||
cinterop "github.com/nspcc-dev/neo-go/pkg/interop"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/nef"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
|
@ -157,7 +158,12 @@ func TestAppCall(t *testing.T) {
|
|||
|
||||
inner, di, err := compiler.CompileWithDebugInfo("foo.go", strings.NewReader(srcInner))
|
||||
require.NoError(t, err)
|
||||
m, err := di.ConvertToManifest(&compiler.Options{Name: "Foo"})
|
||||
m, err := di.ConvertToManifest(&compiler.Options{
|
||||
Name: "Foo",
|
||||
Permissions: []manifest.Permission{
|
||||
*manifest.NewPermission(manifest.PermissionWildcard),
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
ih := hash.Hash160(inner)
|
||||
|
|
Loading…
Reference in a new issue