forked from TrueCloudLab/neoneo-go
Merge pull request #1946 from nspcc-dev/manifest/trusts
smartcontract: use permission descriptors for manifest's trusts
This commit is contained in:
commit
218938200a
7 changed files with 227 additions and 192 deletions
|
@ -7,7 +7,6 @@ import (
|
||||||
"github.com/nspcc-dev/neo-go/internal/testserdes"
|
"github.com/nspcc-dev/neo-go/internal/testserdes"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||||
"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/util"
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
"github.com/nspcc-dev/neo-go/pkg/vm/opcode"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
@ -263,8 +262,8 @@ func _deploy(data interface{}, isUpdate bool) {}
|
||||||
Methods: manifest.WildStrings{},
|
Methods: manifest.WildStrings{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Trusts: manifest.WildUint160s{
|
Trusts: manifest.WildPermissionDescs{
|
||||||
Value: []util.Uint160{},
|
Value: []manifest.PermissionDesc{},
|
||||||
},
|
},
|
||||||
Extra: json.RawMessage("null"),
|
Extra: json.RawMessage("null"),
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,8 +7,6 @@ package manifest
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// WildStrings represents string set which can be wildcard.
|
// WildStrings represents string set which can be wildcard.
|
||||||
|
@ -16,9 +14,9 @@ type WildStrings struct {
|
||||||
Value []string
|
Value []string
|
||||||
}
|
}
|
||||||
|
|
||||||
// WildUint160s represents Uint160 set which can be wildcard.
|
// WildPermissionDescs represents PermissionDescriptor set which can be wildcard.
|
||||||
type WildUint160s struct {
|
type WildPermissionDescs struct {
|
||||||
Value []util.Uint160
|
Value []PermissionDesc
|
||||||
}
|
}
|
||||||
|
|
||||||
// Contains checks if v is in the container.
|
// Contains checks if v is in the container.
|
||||||
|
@ -35,7 +33,7 @@ func (c *WildStrings) Contains(v string) bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Contains checks if v is in the container.
|
// Contains checks if v is in the container.
|
||||||
func (c *WildUint160s) Contains(v util.Uint160) bool {
|
func (c *WildPermissionDescs) Contains(v PermissionDesc) bool {
|
||||||
if c.IsWildcard() {
|
if c.IsWildcard() {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -51,19 +49,19 @@ func (c *WildUint160s) Contains(v util.Uint160) bool {
|
||||||
func (c *WildStrings) IsWildcard() bool { return c.Value == nil }
|
func (c *WildStrings) IsWildcard() bool { return c.Value == nil }
|
||||||
|
|
||||||
// IsWildcard returns true iff container is wildcard.
|
// IsWildcard returns true iff container is wildcard.
|
||||||
func (c *WildUint160s) IsWildcard() bool { return c.Value == nil }
|
func (c *WildPermissionDescs) IsWildcard() bool { return c.Value == nil }
|
||||||
|
|
||||||
// Restrict transforms container into an empty one.
|
// Restrict transforms container into an empty one.
|
||||||
func (c *WildStrings) Restrict() { c.Value = []string{} }
|
func (c *WildStrings) Restrict() { c.Value = []string{} }
|
||||||
|
|
||||||
// Restrict transforms container into an empty one.
|
// Restrict transforms container into an empty one.
|
||||||
func (c *WildUint160s) Restrict() { c.Value = []util.Uint160{} }
|
func (c *WildPermissionDescs) Restrict() { c.Value = []PermissionDesc{} }
|
||||||
|
|
||||||
// Add adds v to the container.
|
// Add adds v to the container.
|
||||||
func (c *WildStrings) Add(v string) { c.Value = append(c.Value, v) }
|
func (c *WildStrings) Add(v string) { c.Value = append(c.Value, v) }
|
||||||
|
|
||||||
// Add adds v to the container.
|
// Add adds v to the container.
|
||||||
func (c *WildUint160s) Add(v util.Uint160) { c.Value = append(c.Value, v) }
|
func (c *WildPermissionDescs) Add(v PermissionDesc) { c.Value = append(c.Value, v) }
|
||||||
|
|
||||||
// MarshalJSON implements json.Marshaler interface.
|
// MarshalJSON implements json.Marshaler interface.
|
||||||
func (c WildStrings) MarshalJSON() ([]byte, error) {
|
func (c WildStrings) MarshalJSON() ([]byte, error) {
|
||||||
|
@ -74,7 +72,7 @@ func (c WildStrings) MarshalJSON() ([]byte, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// MarshalJSON implements json.Marshaler interface.
|
// MarshalJSON implements json.Marshaler interface.
|
||||||
func (c WildUint160s) MarshalJSON() ([]byte, error) {
|
func (c WildPermissionDescs) MarshalJSON() ([]byte, error) {
|
||||||
if c.IsWildcard() {
|
if c.IsWildcard() {
|
||||||
return []byte(`"*"`), nil
|
return []byte(`"*"`), nil
|
||||||
}
|
}
|
||||||
|
@ -94,9 +92,9 @@ func (c *WildStrings) UnmarshalJSON(data []byte) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalJSON implements json.Unmarshaler interface.
|
// UnmarshalJSON implements json.Unmarshaler interface.
|
||||||
func (c *WildUint160s) UnmarshalJSON(data []byte) error {
|
func (c *WildPermissionDescs) UnmarshalJSON(data []byte) error {
|
||||||
if !bytes.Equal(data, []byte(`"*"`)) {
|
if !bytes.Equal(data, []byte(`"*"`)) {
|
||||||
us := []util.Uint160{}
|
us := []PermissionDesc{}
|
||||||
if err := json.Unmarshal(data, &us); err != nil {
|
if err := json.Unmarshal(data, &us); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ import (
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/internal/random"
|
"github.com/nspcc-dev/neo-go/internal/random"
|
||||||
"github.com/nspcc-dev/neo-go/internal/testserdes"
|
"github.com/nspcc-dev/neo-go/internal/testserdes"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -21,15 +21,30 @@ func TestContainer_Restrict(t *testing.T) {
|
||||||
require.Equal(t, 0, len(c.Value))
|
require.Equal(t, 0, len(c.Value))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("uint160", func(t *testing.T) {
|
t.Run("PermissionDesc", func(t *testing.T) {
|
||||||
c := new(WildUint160s)
|
check := func(t *testing.T, u PermissionDesc) {
|
||||||
u := random.Uint160()
|
c := new(WildPermissionDescs)
|
||||||
require.True(t, c.IsWildcard())
|
require.True(t, c.IsWildcard())
|
||||||
require.True(t, c.Contains(u))
|
require.True(t, c.Contains(u))
|
||||||
c.Restrict()
|
c.Restrict()
|
||||||
require.False(t, c.IsWildcard())
|
require.False(t, c.IsWildcard())
|
||||||
require.False(t, c.Contains(u))
|
require.False(t, c.Contains(u))
|
||||||
require.Equal(t, 0, len(c.Value))
|
require.Equal(t, 0, len(c.Value))
|
||||||
|
}
|
||||||
|
t.Run("Hash", func(t *testing.T) {
|
||||||
|
check(t, PermissionDesc{
|
||||||
|
Type: PermissionHash,
|
||||||
|
Value: random.Uint160(),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
t.Run("Group", func(t *testing.T) {
|
||||||
|
pk, err := keys.NewPrivateKey()
|
||||||
|
require.NoError(t, err)
|
||||||
|
check(t, PermissionDesc{
|
||||||
|
Type: PermissionGroup,
|
||||||
|
Value: pk.PublicKey(),
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,17 +59,24 @@ func TestContainer_Add(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("uint160", func(t *testing.T) {
|
t.Run("uint160", func(t *testing.T) {
|
||||||
c := new(WildUint160s)
|
c := new(WildPermissionDescs)
|
||||||
require.Equal(t, []util.Uint160(nil), c.Value)
|
require.Equal(t, []PermissionDesc(nil), c.Value)
|
||||||
|
pk, err := keys.NewPrivateKey()
|
||||||
exp := []util.Uint160{random.Uint160(), random.Uint160()}
|
require.NoError(t, err)
|
||||||
|
exp := []PermissionDesc{
|
||||||
|
{Type: PermissionHash, Value: random.Uint160()},
|
||||||
|
{Type: PermissionGroup, Value: pk.PublicKey()},
|
||||||
|
}
|
||||||
for i := range exp {
|
for i := range exp {
|
||||||
c.Add(exp[i])
|
c.Add(exp[i])
|
||||||
}
|
}
|
||||||
for i := range exp {
|
for i := range exp {
|
||||||
require.True(t, c.Contains(exp[i]))
|
require.True(t, c.Contains(exp[i]))
|
||||||
}
|
}
|
||||||
require.False(t, c.Contains(random.Uint160()))
|
pkRand, err := keys.NewPrivateKey()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.False(t, c.Contains(PermissionDesc{Type: PermissionHash, Value: random.Uint160()}))
|
||||||
|
require.False(t, c.Contains(PermissionDesc{Type: PermissionGroup, Value: pkRand.PublicKey()}))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,27 +107,30 @@ func TestContainer_MarshalJSON(t *testing.T) {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("uint160", func(t *testing.T) {
|
t.Run("PermissionDesc", func(t *testing.T) {
|
||||||
t.Run("wildcard", func(t *testing.T) {
|
t.Run("wildcard", func(t *testing.T) {
|
||||||
expected := new(WildUint160s)
|
expected := new(WildPermissionDescs)
|
||||||
testserdes.MarshalUnmarshalJSON(t, expected, new(WildUint160s))
|
testserdes.MarshalUnmarshalJSON(t, expected, new(WildPermissionDescs))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("empty", func(t *testing.T) {
|
t.Run("empty", func(t *testing.T) {
|
||||||
expected := new(WildUint160s)
|
expected := new(WildPermissionDescs)
|
||||||
expected.Restrict()
|
expected.Restrict()
|
||||||
testserdes.MarshalUnmarshalJSON(t, expected, new(WildUint160s))
|
testserdes.MarshalUnmarshalJSON(t, expected, new(WildPermissionDescs))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("non-empty", func(t *testing.T) {
|
t.Run("non-empty", func(t *testing.T) {
|
||||||
expected := new(WildUint160s)
|
expected := new(WildPermissionDescs)
|
||||||
expected.Add(random.Uint160())
|
expected.Add(PermissionDesc{
|
||||||
testserdes.MarshalUnmarshalJSON(t, expected, new(WildUint160s))
|
Type: PermissionHash,
|
||||||
|
Value: random.Uint160(),
|
||||||
|
})
|
||||||
|
testserdes.MarshalUnmarshalJSON(t, expected, new(WildPermissionDescs))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("invalid", func(t *testing.T) {
|
t.Run("invalid", func(t *testing.T) {
|
||||||
js := []byte(`["notahex"]`)
|
js := []byte(`["notahex"]`)
|
||||||
c := new(WildUint160s)
|
c := new(WildPermissionDescs)
|
||||||
require.Error(t, json.Unmarshal(js, c))
|
require.Error(t, json.Unmarshal(js, c))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"math"
|
"math"
|
||||||
"sort"
|
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
|
@ -38,7 +37,7 @@ type Manifest struct {
|
||||||
// SupportedStandards is a list of standards supported by the contract.
|
// SupportedStandards is a list of standards supported by the contract.
|
||||||
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 WildPermissionDescs `json:"trusts"`
|
||||||
// Extra is an implementation-defined user data.
|
// Extra is an implementation-defined user data.
|
||||||
Extra json.RawMessage `json:"extra"`
|
Extra json.RawMessage `json:"extra"`
|
||||||
}
|
}
|
||||||
|
@ -109,18 +108,10 @@ func (m *Manifest) IsValid(hash util.Uint160) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if len(m.Trusts.Value) > 1 {
|
if len(m.Trusts.Value) > 1 {
|
||||||
hashes := make([]util.Uint160, len(m.Trusts.Value))
|
hashes := make([]PermissionDesc, len(m.Trusts.Value))
|
||||||
copy(hashes, m.Trusts.Value)
|
copy(hashes, m.Trusts.Value)
|
||||||
sort.Slice(hashes, func(i, j int) bool {
|
if permissionDescsHaveDups(hashes) {
|
||||||
return hashes[i].Less(hashes[j])
|
return errors.New("duplicate trusted contracts")
|
||||||
})
|
|
||||||
for i := range hashes {
|
|
||||||
if i == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if hashes[i] == hashes[i-1] {
|
|
||||||
return errors.New("duplicate trusted contracts")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Permissions(m.Permissions).AreValid()
|
return Permissions(m.Permissions).AreValid()
|
||||||
|
@ -144,8 +135,8 @@ func (m *Manifest) ToStackItem() (stackitem.Item, error) {
|
||||||
trusts := stackitem.Item(stackitem.Null{})
|
trusts := stackitem.Item(stackitem.Null{})
|
||||||
if !m.Trusts.IsWildcard() {
|
if !m.Trusts.IsWildcard() {
|
||||||
tItems := make([]stackitem.Item, len(m.Trusts.Value))
|
tItems := make([]stackitem.Item, len(m.Trusts.Value))
|
||||||
for i := range m.Trusts.Value {
|
for i, v := range m.Trusts.Value {
|
||||||
tItems[i] = stackitem.NewByteArray(m.Trusts.Value[i].BytesBE())
|
tItems[i] = v.ToStackItem()
|
||||||
}
|
}
|
||||||
trusts = stackitem.Make(tItems)
|
trusts = stackitem.Make(tItems)
|
||||||
}
|
}
|
||||||
|
@ -231,16 +222,14 @@ func (m *Manifest) FromStackItem(item stackitem.Item) error {
|
||||||
return errors.New("invalid Trusts stackitem type")
|
return errors.New("invalid Trusts stackitem type")
|
||||||
}
|
}
|
||||||
trusts := str[6].Value().([]stackitem.Item)
|
trusts := str[6].Value().([]stackitem.Item)
|
||||||
m.Trusts = WildUint160s{Value: make([]util.Uint160, len(trusts))}
|
m.Trusts = WildPermissionDescs{Value: make([]PermissionDesc, len(trusts))}
|
||||||
for i := range trusts {
|
for i := range trusts {
|
||||||
bytes, err := trusts[i].TryBytes()
|
v := new(PermissionDesc)
|
||||||
if err != nil {
|
err = v.FromStackItem(trusts[i])
|
||||||
return err
|
|
||||||
}
|
|
||||||
m.Trusts.Value[i], err = util.Uint160DecodeBytesBE(bytes)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
m.Trusts.Value[i] = *v
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
extra, err := str[7].TryBytes()
|
extra, err := str[7].TryBytes()
|
||||||
|
|
|
@ -181,17 +181,18 @@ func TestIsValid(t *testing.T) {
|
||||||
})
|
})
|
||||||
m.SupportedStandards = m.SupportedStandards[:1]
|
m.SupportedStandards = m.SupportedStandards[:1]
|
||||||
|
|
||||||
m.Trusts.Add(util.Uint160{1, 2, 3})
|
d := PermissionDesc{Type: PermissionHash, Value: util.Uint160{1, 2, 3}}
|
||||||
|
m.Trusts.Add(d)
|
||||||
t.Run("valid, with trust", func(t *testing.T) {
|
t.Run("valid, with trust", func(t *testing.T) {
|
||||||
require.NoError(t, m.IsValid(contractHash))
|
require.NoError(t, m.IsValid(contractHash))
|
||||||
})
|
})
|
||||||
|
|
||||||
m.Trusts.Add(util.Uint160{3, 2, 1})
|
m.Trusts.Add(PermissionDesc{Type: PermissionHash, Value: util.Uint160{3, 2, 1}})
|
||||||
t.Run("valid, with trusts", func(t *testing.T) {
|
t.Run("valid, with trusts", func(t *testing.T) {
|
||||||
require.NoError(t, m.IsValid(contractHash))
|
require.NoError(t, m.IsValid(contractHash))
|
||||||
})
|
})
|
||||||
|
|
||||||
m.Trusts.Add(util.Uint160{1, 2, 3})
|
m.Trusts.Add(d)
|
||||||
t.Run("invalid, with trusts", func(t *testing.T) {
|
t.Run("invalid, with trusts", func(t *testing.T) {
|
||||||
require.Error(t, m.IsValid(contractHash))
|
require.Error(t, m.IsValid(contractHash))
|
||||||
})
|
})
|
||||||
|
@ -276,8 +277,17 @@ func TestManifestToStackItem(t *testing.T) {
|
||||||
}},
|
}},
|
||||||
Permissions: []Permission{*NewPermission(PermissionWildcard)},
|
Permissions: []Permission{*NewPermission(PermissionWildcard)},
|
||||||
SupportedStandards: []string{"NEP-17"},
|
SupportedStandards: []string{"NEP-17"},
|
||||||
Trusts: WildUint160s{
|
Trusts: WildPermissionDescs{
|
||||||
Value: []util.Uint160{{1, 2, 3}},
|
Value: []PermissionDesc{
|
||||||
|
{
|
||||||
|
Type: PermissionHash,
|
||||||
|
Value: util.Uint160{1, 2, 3},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: PermissionGroup,
|
||||||
|
Value: pk.PublicKey(),
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Extra: []byte(`even not a json allowed`),
|
Extra: []byte(`even not a json allowed`),
|
||||||
}
|
}
|
||||||
|
@ -286,56 +296,36 @@ func TestManifestToStackItem(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestManifest_FromStackItemErrors(t *testing.T) {
|
func TestManifest_FromStackItemErrors(t *testing.T) {
|
||||||
|
name := stackitem.NewByteArray([]byte{})
|
||||||
|
groups := stackitem.NewArray([]stackitem.Item{})
|
||||||
|
features := stackitem.NewMap()
|
||||||
|
sStandards := stackitem.NewArray([]stackitem.Item{})
|
||||||
|
abi := stackitem.NewStruct([]stackitem.Item{stackitem.NewArray([]stackitem.Item{}), stackitem.NewArray([]stackitem.Item{})})
|
||||||
|
permissions := stackitem.NewArray([]stackitem.Item{})
|
||||||
|
trusts := stackitem.NewArray([]stackitem.Item{})
|
||||||
|
extra := stackitem.NewByteArray([]byte{})
|
||||||
|
|
||||||
|
// check OK
|
||||||
|
goodSI := []stackitem.Item{name, groups, features, sStandards, abi, permissions, trusts, extra}
|
||||||
|
m := new(Manifest)
|
||||||
|
require.NoError(t, m.FromStackItem(stackitem.NewStruct(goodSI)))
|
||||||
|
|
||||||
errCases := map[string]stackitem.Item{
|
errCases := map[string]stackitem.Item{
|
||||||
"not a struct": stackitem.NewArray([]stackitem.Item{}),
|
"not a struct": stackitem.NewArray([]stackitem.Item{}),
|
||||||
"invalid length": stackitem.NewStruct([]stackitem.Item{}),
|
"invalid length": stackitem.NewStruct([]stackitem.Item{}),
|
||||||
"invalid name type": stackitem.NewStruct([]stackitem.Item{stackitem.NewInterop(nil), stackitem.Null{}, stackitem.Null{}, stackitem.Null{}, stackitem.Null{}, stackitem.Null{}, stackitem.Null{}}),
|
"invalid name type": stackitem.NewStruct(append([]stackitem.Item{stackitem.NewInterop(nil)}, goodSI[1:]...)),
|
||||||
"invalid groups type": stackitem.NewStruct([]stackitem.Item{stackitem.NewByteArray([]byte{}), stackitem.Null{}, stackitem.Null{}, stackitem.Null{}, stackitem.Null{}, stackitem.Null{}, stackitem.Null{}}),
|
"invalid Groups type": stackitem.NewStruct(append([]stackitem.Item{name, stackitem.Null{}}, goodSI[2:]...)),
|
||||||
"invalid group": stackitem.NewStruct([]stackitem.Item{stackitem.NewByteArray([]byte{}), stackitem.NewArray([]stackitem.Item{stackitem.Null{}}), stackitem.Null{}, stackitem.Null{}, stackitem.Null{}, stackitem.Null{}, stackitem.Null{}}),
|
"invalid group": stackitem.NewStruct(append([]stackitem.Item{name, stackitem.NewArray([]stackitem.Item{stackitem.Null{}})}, goodSI[2:]...)),
|
||||||
"invalid supported standards type": stackitem.NewStruct([]stackitem.Item{stackitem.NewByteArray([]byte{}), stackitem.NewArray([]stackitem.Item{}), stackitem.Null{}, stackitem.Null{}, stackitem.Null{}, stackitem.Null{}, stackitem.Null{}}),
|
"invalid Features type": stackitem.NewStruct(append([]stackitem.Item{name, groups, stackitem.Null{}}, goodSI[3:]...)),
|
||||||
"invalid supported standard": stackitem.NewStruct([]stackitem.Item{stackitem.NewByteArray([]byte{}), stackitem.NewArray([]stackitem.Item{}), stackitem.NewArray([]stackitem.Item{stackitem.Null{}}), stackitem.Null{}, stackitem.Null{}, stackitem.Null{}, stackitem.Null{}}),
|
"invalid supported standards type": stackitem.NewStruct(append([]stackitem.Item{name, groups, features, stackitem.Null{}}, goodSI[4:]...)),
|
||||||
"invalid ABI": stackitem.NewStruct([]stackitem.Item{stackitem.NewByteArray([]byte{}), stackitem.NewArray([]stackitem.Item{}), stackitem.NewArray([]stackitem.Item{}), stackitem.Null{}, stackitem.Null{}, stackitem.Null{}, stackitem.Null{}}),
|
"invalid supported standard": stackitem.NewStruct(append([]stackitem.Item{name, groups, features, stackitem.NewArray([]stackitem.Item{stackitem.Null{}})}, goodSI[4:]...)),
|
||||||
"invalid Permissions type": stackitem.NewStruct([]stackitem.Item{
|
"invalid ABI": stackitem.NewStruct(append([]stackitem.Item{name, groups, features, sStandards, stackitem.Null{}}, goodSI[5:]...)),
|
||||||
stackitem.NewByteArray([]byte{}),
|
"invalid Permissions type": stackitem.NewStruct(append([]stackitem.Item{name, groups, features, sStandards, abi, stackitem.Null{}}, goodSI[6:]...)),
|
||||||
stackitem.NewArray([]stackitem.Item{}),
|
"invalid permission": stackitem.NewStruct(append([]stackitem.Item{name, groups, features, sStandards, abi, stackitem.NewArray([]stackitem.Item{stackitem.Null{}})}, goodSI[6:]...)),
|
||||||
stackitem.NewArray([]stackitem.Item{}),
|
"invalid Trusts type": stackitem.NewStruct(append([]stackitem.Item{name, groups, features, sStandards, abi, permissions, stackitem.NewInterop(nil)}, goodSI[7:]...)),
|
||||||
stackitem.NewStruct([]stackitem.Item{stackitem.NewArray([]stackitem.Item{}), stackitem.NewArray([]stackitem.Item{})}),
|
"invalid trust": stackitem.NewStruct(append([]stackitem.Item{name, groups, features, sStandards, abi, permissions, stackitem.NewArray([]stackitem.Item{stackitem.NewInterop(nil)})}, goodSI[7:]...)),
|
||||||
stackitem.Null{}, stackitem.Null{}, stackitem.Null{}}),
|
"invalid Uint160 trust": stackitem.NewStruct(append([]stackitem.Item{name, groups, features, sStandards, abi, permissions, stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray([]byte{1, 2, 3})})}, goodSI[7:]...)),
|
||||||
"invalid permission": stackitem.NewStruct([]stackitem.Item{
|
"invalid extra type": stackitem.NewStruct([]stackitem.Item{name, groups, features, sStandards, abi, permissions, trusts, stackitem.Null{}}),
|
||||||
stackitem.NewByteArray([]byte{}),
|
|
||||||
stackitem.NewArray([]stackitem.Item{}),
|
|
||||||
stackitem.NewArray([]stackitem.Item{}),
|
|
||||||
stackitem.NewStruct([]stackitem.Item{stackitem.NewArray([]stackitem.Item{}), stackitem.NewArray([]stackitem.Item{})}),
|
|
||||||
stackitem.NewArray([]stackitem.Item{stackitem.Null{}}), stackitem.Null{}, stackitem.Null{}}),
|
|
||||||
"invalid Trusts type": stackitem.NewStruct([]stackitem.Item{
|
|
||||||
stackitem.NewByteArray([]byte{}),
|
|
||||||
stackitem.NewArray([]stackitem.Item{}),
|
|
||||||
stackitem.NewArray([]stackitem.Item{}),
|
|
||||||
stackitem.NewStruct([]stackitem.Item{stackitem.NewArray([]stackitem.Item{}), stackitem.NewArray([]stackitem.Item{})}),
|
|
||||||
stackitem.NewArray([]stackitem.Item{}),
|
|
||||||
stackitem.NewInterop(nil), stackitem.Null{}}),
|
|
||||||
"invalid trust": stackitem.NewStruct([]stackitem.Item{
|
|
||||||
stackitem.NewByteArray([]byte{}),
|
|
||||||
stackitem.NewArray([]stackitem.Item{}),
|
|
||||||
stackitem.NewArray([]stackitem.Item{}),
|
|
||||||
stackitem.NewStruct([]stackitem.Item{stackitem.NewArray([]stackitem.Item{}), stackitem.NewArray([]stackitem.Item{})}),
|
|
||||||
stackitem.NewArray([]stackitem.Item{}),
|
|
||||||
stackitem.NewArray([]stackitem.Item{stackitem.Null{}}), stackitem.Null{}}),
|
|
||||||
"invalid Uint160 trust": stackitem.NewStruct([]stackitem.Item{
|
|
||||||
stackitem.NewByteArray([]byte{}),
|
|
||||||
stackitem.NewArray([]stackitem.Item{}),
|
|
||||||
stackitem.NewArray([]stackitem.Item{}),
|
|
||||||
stackitem.NewStruct([]stackitem.Item{stackitem.NewArray([]stackitem.Item{}), stackitem.NewArray([]stackitem.Item{})}),
|
|
||||||
stackitem.NewArray([]stackitem.Item{}),
|
|
||||||
stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray([]byte{1, 2, 3})}), stackitem.Null{}}),
|
|
||||||
"invalid extra type": stackitem.NewStruct([]stackitem.Item{
|
|
||||||
stackitem.NewByteArray([]byte{}),
|
|
||||||
stackitem.NewArray([]stackitem.Item{}),
|
|
||||||
stackitem.NewArray([]stackitem.Item{}),
|
|
||||||
stackitem.NewStruct([]stackitem.Item{stackitem.NewArray([]stackitem.Item{}), stackitem.NewArray([]stackitem.Item{})}),
|
|
||||||
stackitem.NewArray([]stackitem.Item{}),
|
|
||||||
stackitem.NewArray([]stackitem.Item{}),
|
|
||||||
stackitem.Null{}}),
|
|
||||||
}
|
}
|
||||||
for name, errCase := range errCases {
|
for name, errCase := range errCases {
|
||||||
t.Run(name, func(t *testing.T) {
|
t.Run(name, func(t *testing.T) {
|
||||||
|
|
|
@ -104,3 +104,32 @@ func stringsHaveDups(strings []string) bool {
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// permissionDescsHaveDups checks given set of strings for duplicates. It modifies the slice given!
|
||||||
|
func permissionDescsHaveDups(descs []PermissionDesc) bool {
|
||||||
|
sort.Slice(descs, func(i, j int) bool {
|
||||||
|
return descs[i].Less(descs[j])
|
||||||
|
})
|
||||||
|
for i := range descs {
|
||||||
|
if i == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
j := i - 1
|
||||||
|
if descs[i].Type != descs[j].Type {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch descs[i].Type {
|
||||||
|
case PermissionWildcard:
|
||||||
|
return true
|
||||||
|
case PermissionHash:
|
||||||
|
if descs[i].Hash() == descs[j].Hash() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
case PermissionGroup:
|
||||||
|
if descs[i].Group().Cmp(descs[j].Group()) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
|
@ -89,6 +88,37 @@ func (d *PermissionDesc) Group() *keys.PublicKey {
|
||||||
return d.Value.(*keys.PublicKey)
|
return d.Value.(*keys.PublicKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Less returns true if this value is less than given PermissionDesc value.
|
||||||
|
func (d *PermissionDesc) Less(d1 PermissionDesc) bool {
|
||||||
|
if d.Type < d1.Type {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if d.Type != d1.Type {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
switch d.Type {
|
||||||
|
case PermissionHash:
|
||||||
|
return d.Hash().Less(d1.Hash())
|
||||||
|
case PermissionGroup:
|
||||||
|
return d.Group().Cmp(d1.Group()) < 0
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equals returns true if both PermissionDesc values are the same.
|
||||||
|
func (d *PermissionDesc) Equals(v PermissionDesc) bool {
|
||||||
|
if d.Type != v.Type {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
switch d.Type {
|
||||||
|
case PermissionHash:
|
||||||
|
return d.Hash().Equals(v.Hash())
|
||||||
|
case PermissionGroup:
|
||||||
|
return d.Group().Cmp(v.Group()) == 0
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// IsValid checks if Permission is correct.
|
// IsValid checks if Permission is correct.
|
||||||
func (p *Permission) IsValid() error {
|
func (p *Permission) IsValid() error {
|
||||||
for i := range p.Methods.Value {
|
for i := range p.Methods.Value {
|
||||||
|
@ -122,45 +152,8 @@ func (ps Permissions) AreValid() error {
|
||||||
for i := range ps {
|
for i := range ps {
|
||||||
contracts = append(contracts, ps[i].Contract)
|
contracts = append(contracts, ps[i].Contract)
|
||||||
}
|
}
|
||||||
sort.Slice(contracts, func(i, j int) bool {
|
if permissionDescsHaveDups(contracts) {
|
||||||
if contracts[i].Type < contracts[j].Type {
|
return errors.New("contracts have duplicates")
|
||||||
return true
|
|
||||||
}
|
|
||||||
if contracts[i].Type != contracts[j].Type {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
switch contracts[i].Type {
|
|
||||||
case PermissionHash:
|
|
||||||
return contracts[i].Hash().Less(contracts[j].Hash())
|
|
||||||
case PermissionGroup:
|
|
||||||
return contracts[i].Group().Cmp(contracts[j].Group()) < 0
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
})
|
|
||||||
for i := range contracts {
|
|
||||||
if i == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
j := i - 1
|
|
||||||
if contracts[i].Type != contracts[j].Type {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
var bad bool
|
|
||||||
switch contracts[i].Type {
|
|
||||||
case PermissionWildcard:
|
|
||||||
bad = true
|
|
||||||
case PermissionHash:
|
|
||||||
if contracts[i].Hash() == contracts[j].Hash() {
|
|
||||||
bad = true
|
|
||||||
}
|
|
||||||
case PermissionGroup:
|
|
||||||
if contracts[i].Group().Cmp(contracts[j].Group()) == 0 {
|
|
||||||
bad = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if bad {
|
|
||||||
return errors.New("duplicate contracts")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -254,20 +247,55 @@ func (d *PermissionDesc) UnmarshalJSON(data []byte) error {
|
||||||
return errors.New("unknown permission")
|
return errors.New("unknown permission")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToStackItem converts PermissionDesc to stackitem.Item.
|
||||||
|
func (d *PermissionDesc) ToStackItem() stackitem.Item {
|
||||||
|
switch d.Type {
|
||||||
|
case PermissionWildcard:
|
||||||
|
return stackitem.Null{}
|
||||||
|
case PermissionHash:
|
||||||
|
return stackitem.NewByteArray(d.Hash().BytesBE())
|
||||||
|
case PermissionGroup:
|
||||||
|
return stackitem.NewByteArray(d.Group().Bytes())
|
||||||
|
default:
|
||||||
|
panic("unsupported PermissionDesc type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromStackItem converts stackitem.Item to PermissionDesc.
|
||||||
|
func (d *PermissionDesc) FromStackItem(item stackitem.Item) error {
|
||||||
|
if _, ok := item.(stackitem.Null); ok {
|
||||||
|
d.Type = PermissionWildcard
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if item.Type() != stackitem.ByteArrayT {
|
||||||
|
return fmt.Errorf("unsupported permission descriptor type: %s", item.Type())
|
||||||
|
}
|
||||||
|
byteArr, err := item.TryBytes()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch len(byteArr) {
|
||||||
|
case util.Uint160Size:
|
||||||
|
hash, _ := util.Uint160DecodeBytesBE(byteArr)
|
||||||
|
d.Type = PermissionHash
|
||||||
|
d.Value = hash
|
||||||
|
case 33:
|
||||||
|
pKey, err := keys.NewPublicKeyFromBytes(byteArr, elliptic.P256())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
d.Type = PermissionGroup
|
||||||
|
d.Value = pKey
|
||||||
|
default:
|
||||||
|
return errors.New("invalid ByteArray length")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// ToStackItem converts Permission to stackitem.Item.
|
// ToStackItem converts Permission to stackitem.Item.
|
||||||
func (p *Permission) ToStackItem() stackitem.Item {
|
func (p *Permission) ToStackItem() stackitem.Item {
|
||||||
var (
|
var methods stackitem.Item
|
||||||
contract stackitem.Item
|
contract := p.Contract.ToStackItem()
|
||||||
methods stackitem.Item
|
|
||||||
)
|
|
||||||
switch p.Contract.Type {
|
|
||||||
case PermissionWildcard:
|
|
||||||
contract = stackitem.Null{}
|
|
||||||
case PermissionHash:
|
|
||||||
contract = stackitem.NewByteArray(p.Contract.Hash().BytesBE())
|
|
||||||
case PermissionGroup:
|
|
||||||
contract = stackitem.NewByteArray(p.Contract.Group().Bytes())
|
|
||||||
}
|
|
||||||
if p.Methods.IsWildcard() {
|
if p.Methods.IsWildcard() {
|
||||||
methods = stackitem.Null{}
|
methods = stackitem.Null{}
|
||||||
} else {
|
} else {
|
||||||
|
@ -293,35 +321,12 @@ func (p *Permission) FromStackItem(item stackitem.Item) error {
|
||||||
if len(str) != 2 {
|
if len(str) != 2 {
|
||||||
return errors.New("invalid Permission stackitem length")
|
return errors.New("invalid Permission stackitem length")
|
||||||
}
|
}
|
||||||
if _, ok := str[0].(stackitem.Null); ok {
|
desc := new(PermissionDesc)
|
||||||
p.Contract = PermissionDesc{
|
err = desc.FromStackItem(str[0])
|
||||||
Type: PermissionWildcard,
|
if err != nil {
|
||||||
}
|
return fmt.Errorf("invalid Contract stackitem: %w", err)
|
||||||
} else {
|
|
||||||
byteArr, err := str[0].TryBytes()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
switch len(byteArr) {
|
|
||||||
case util.Uint160Size:
|
|
||||||
hash, _ := util.Uint160DecodeBytesBE(byteArr)
|
|
||||||
p.Contract = PermissionDesc{
|
|
||||||
Type: PermissionHash,
|
|
||||||
Value: hash,
|
|
||||||
}
|
|
||||||
case 33:
|
|
||||||
pKey, err := keys.NewPublicKeyFromBytes(byteArr, elliptic.P256())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
p.Contract = PermissionDesc{
|
|
||||||
Type: PermissionGroup,
|
|
||||||
Value: pKey,
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return errors.New("invalid Contract ByteArray length")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
p.Contract = *desc
|
||||||
if _, ok := str[1].(stackitem.Null); ok {
|
if _, ok := str[1].(stackitem.Null); ok {
|
||||||
p.Methods = WildStrings{Value: nil}
|
p.Methods = WildStrings{Value: nil}
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Reference in a new issue