manifest: add manifest validity checks

Refs. #1699.
This commit is contained in:
Roman Khimov 2021-02-08 22:58:22 +03:00
parent 284476c070
commit dc3967ae7b
5 changed files with 131 additions and 5 deletions

View file

@ -0,0 +1 @@
name: Test deploy

View file

@ -5,6 +5,7 @@ import (
"encoding/hex"
"encoding/json"
"errors"
"sort"
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
@ -20,6 +21,9 @@ type Group struct {
Signature []byte `json:"signature"`
}
// Groups is just an array of Group.
type Groups []Group
type groupAux struct {
PublicKey string `json:"pubkey"`
Signature []byte `json:"signature"`
@ -33,6 +37,33 @@ func (g *Group) IsValid(h util.Uint160) error {
return nil
}
// AreValid checks for groups correctness and uniqueness.
func (g Groups) AreValid(h util.Uint160) error {
for i := range g {
err := g[i].IsValid(h)
if err != nil {
return err
}
}
if len(g) < 2 {
return nil
}
pkeys := make(keys.PublicKeys, len(g))
for i := range g {
pkeys[i] = g[i].PublicKey
}
sort.Sort(pkeys)
for i := range pkeys {
if i == 0 {
continue
}
if pkeys[i].Cmp(pkeys[i-1]) == 0 {
return errors.New("duplicate group keys")
}
}
return nil
}
// MarshalJSON implements json.Marshaler interface.
func (g *Group) MarshalJSON() ([]byte, error) {
aux := &groupAux{

View file

@ -5,6 +5,7 @@ import (
"github.com/nspcc-dev/neo-go/internal/testserdes"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/stretchr/testify/require"
)
@ -16,3 +17,27 @@ func TestGroupJSONInOut(t *testing.T) {
g := Group{pub, sig}
testserdes.MarshalUnmarshalJSON(t, &g, new(Group))
}
func TestGroupsAreValid(t *testing.T) {
h := util.Uint160{42, 42, 42}
priv, err := keys.NewPrivateKey()
require.NoError(t, err)
priv2, err := keys.NewPrivateKey()
require.NoError(t, err)
pub := priv.PublicKey()
pub2 := priv2.PublicKey()
gcorrect := Group{pub, priv.Sign(h.BytesBE())}
gcorrect2 := Group{pub2, priv2.Sign(h.BytesBE())}
gincorrect := Group{pub, priv.Sign(h.BytesLE())}
gps := Groups{gcorrect}
require.NoError(t, gps.AreValid(h))
gps = Groups{gincorrect}
require.Error(t, gps.AreValid(h))
gps = Groups{gcorrect, gcorrect2}
require.NoError(t, gps.AreValid(h))
gps = Groups{gcorrect, gcorrect}
require.Error(t, gps.AreValid(h))
}

View file

@ -4,6 +4,7 @@ import (
"encoding/json"
"errors"
"math"
"sort"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
@ -75,15 +76,44 @@ func (m *Manifest) CanCall(hash util.Uint160, toCall *Manifest, method string) b
func (m *Manifest) IsValid(hash util.Uint160) error {
var err error
if m.Name == "" {
return errors.New("no name")
}
for i := range m.SupportedStandards {
if m.SupportedStandards[i] == "" {
return errors.New("invalid nameless supported standard")
}
}
if len(m.SupportedStandards) > 1 {
names := make([]string, len(m.SupportedStandards))
copy(names, m.SupportedStandards)
if stringsHaveDups(names) {
return errors.New("duplicate supported standards")
}
}
err = m.ABI.IsValid()
if err != nil {
return err
}
for _, g := range m.Groups {
err = g.IsValid(hash)
err = Groups(m.Groups).AreValid(hash)
if err != nil {
return err
}
if len(m.Trusts.Value) > 1 {
hashes := make([]util.Uint160, len(m.Trusts.Value))
copy(hashes, m.Trusts.Value)
sort.Slice(hashes, func(i, j int) bool {
return hashes[i].Less(hashes[j])
})
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()
}

View file

@ -109,7 +109,13 @@ func TestPermission_IsAllowed(t *testing.T) {
func TestIsValid(t *testing.T) {
contractHash := util.Uint160{1, 2, 3}
m := NewManifest("Test")
m := &Manifest{}
t.Run("invalid, no name", func(t *testing.T) {
require.Error(t, m.IsValid(contractHash))
})
m = NewManifest("Test")
t.Run("invalid, no ABI methods", func(t *testing.T) {
require.Error(t, m.IsValid(contractHash))
@ -158,6 +164,39 @@ func TestIsValid(t *testing.T) {
})
m.Permissions = m.Permissions[:1]
m.SupportedStandards = append(m.SupportedStandards, "NEP-17")
t.Run("valid, with standards", func(t *testing.T) {
require.NoError(t, m.IsValid(contractHash))
})
m.SupportedStandards = append(m.SupportedStandards, "")
t.Run("invalid, with nameless standard", func(t *testing.T) {
require.Error(t, m.IsValid(contractHash))
})
m.SupportedStandards = m.SupportedStandards[:1]
m.SupportedStandards = append(m.SupportedStandards, "NEP-17")
t.Run("invalid, with duplicate standards", func(t *testing.T) {
require.Error(t, m.IsValid(contractHash))
})
m.SupportedStandards = m.SupportedStandards[:1]
m.Trusts.Add(util.Uint160{1, 2, 3})
t.Run("valid, with trust", func(t *testing.T) {
require.NoError(t, m.IsValid(contractHash))
})
m.Trusts.Add(util.Uint160{3, 2, 1})
t.Run("valid, with trusts", func(t *testing.T) {
require.NoError(t, m.IsValid(contractHash))
})
m.Trusts.Add(util.Uint160{1, 2, 3})
t.Run("invalid, with trusts", func(t *testing.T) {
require.Error(t, m.IsValid(contractHash))
})
m.Trusts.Restrict()
t.Run("with groups", func(t *testing.T) {
m.Groups = make([]Group, 3)
pks := make([]*keys.PrivateKey, 3)