manifest: make default trusts invalid

Refs. #3522. The core problem is the same as for groups/features: we can't
allow empty trusts when they're unmarshalled from JSON. But unlike others we
can't easily differentiate missing any value with other cases because the
default value for WildPermissionDescs is a valid thing. Adding an additional
field makes it invalid and we can build around it. Other options are
implementing custom UnmarshalJSON for Manifest (too much for this) or making
Trusts a pointer (an option, but can fail in too many ways).

Signed-off-by: Roman Khimov <roman@nspcc.ru>
This commit is contained in:
Roman Khimov 2024-07-26 12:32:49 +03:00
parent 58ab24efdb
commit b10af1ed31
7 changed files with 27 additions and 5 deletions

View file

@ -715,6 +715,7 @@ func TestSystemRuntimeNotify_HFBasilisk(t *testing.T) {
Name: "ctr", Name: "ctr",
Features: json.RawMessage("{}"), Features: json.RawMessage("{}"),
Groups: []manifest.Group{}, Groups: []manifest.Group{},
Trusts: manifest.WildPermissionDescs{Wildcard: true},
ABI: manifest.ABI{ ABI: manifest.ABI{
Methods: []manifest.Method{ Methods: []manifest.Method{
{ {

View file

@ -55,6 +55,7 @@ func TestManagement_DeployUpdate_HFBasilisk(t *testing.T) {
Name: "ctr", Name: "ctr",
Features: json.RawMessage("{}"), Features: json.RawMessage("{}"),
Groups: []manifest.Group{}, Groups: []manifest.Group{},
Trusts: manifest.WildPermissionDescs{Wildcard: true},
ABI: manifest.ABI{ ABI: manifest.ABI{
Methods: []manifest.Method{ Methods: []manifest.Method{
{ {
@ -92,6 +93,7 @@ func TestManagement_CallInTheSameBlock(t *testing.T) {
Name: "ctr", Name: "ctr",
Features: json.RawMessage("{}"), Features: json.RawMessage("{}"),
Groups: []manifest.Group{}, Groups: []manifest.Group{},
Trusts: manifest.WildPermissionDescs{Wildcard: true},
ABI: manifest.ABI{ ABI: manifest.ABI{
Methods: []manifest.Method{ Methods: []manifest.Method{
{ {

View file

@ -58,6 +58,7 @@ func TestContractStateToFromSI(t *testing.T) {
t.Run("preserve wildcard trusts", func(t *testing.T) { t.Run("preserve wildcard trusts", func(t *testing.T) {
contract.Manifest.Trusts.Value = nil contract.Manifest.Trusts.Value = nil
contract.Manifest.Trusts.Wildcard = true
require.True(t, contract.Manifest.Trusts.IsWildcard()) require.True(t, contract.Manifest.Trusts.IsWildcard())
actual := new(Contract) actual := new(Contract)
item, err := contract.ToStackItem() item, err := contract.ToStackItem()

View file

@ -17,6 +17,7 @@ type WildStrings struct {
// WildPermissionDescs represents a PermissionDescriptor set which can be a wildcard. // WildPermissionDescs represents a PermissionDescriptor set which can be a wildcard.
type WildPermissionDescs struct { type WildPermissionDescs struct {
Value []PermissionDesc Value []PermissionDesc
Wildcard bool
} }
// Contains checks if v is in the container. // Contains checks if v is in the container.
@ -49,13 +50,16 @@ func (c *WildPermissionDescs) Contains(v PermissionDesc) bool {
func (c *WildStrings) IsWildcard() bool { return c.Value == nil } func (c *WildStrings) IsWildcard() bool { return c.Value == nil }
// IsWildcard returns true iff the container is a wildcard. // IsWildcard returns true iff the container is a wildcard.
func (c *WildPermissionDescs) IsWildcard() bool { return c.Value == nil } func (c *WildPermissionDescs) IsWildcard() bool { return c.Wildcard }
// Restrict transforms the container into an empty one. // Restrict transforms the container into an empty one.
func (c *WildStrings) Restrict() { c.Value = []string{} } func (c *WildStrings) Restrict() { c.Value = []string{} }
// Restrict transforms the container into an empty one. // Restrict transforms the container into an empty one.
func (c *WildPermissionDescs) Restrict() { c.Value = []PermissionDesc{} } func (c *WildPermissionDescs) Restrict() {
c.Value = []PermissionDesc{}
c.Wildcard = false
}
// 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) }
@ -93,7 +97,8 @@ func (c *WildStrings) UnmarshalJSON(data []byte) error {
// UnmarshalJSON implements the json.Unmarshaler interface. // UnmarshalJSON implements the json.Unmarshaler interface.
func (c *WildPermissionDescs) UnmarshalJSON(data []byte) error { func (c *WildPermissionDescs) UnmarshalJSON(data []byte) error {
if !bytes.Equal(data, []byte(`"*"`)) { c.Wildcard = bytes.Equal(data, []byte(`"*"`))
if !c.Wildcard {
us := []PermissionDesc{} us := []PermissionDesc{}
if err := json.Unmarshal(data, &us); err != nil { if err := json.Unmarshal(data, &us); err != nil {
return err return err

View file

@ -24,6 +24,9 @@ func TestContainer_Restrict(t *testing.T) {
t.Run("PermissionDesc", func(t *testing.T) { t.Run("PermissionDesc", func(t *testing.T) {
check := func(t *testing.T, u PermissionDesc) { check := func(t *testing.T, u PermissionDesc) {
c := new(WildPermissionDescs) c := new(WildPermissionDescs)
require.False(t, c.IsWildcard())
require.False(t, c.Contains(u))
c.Wildcard = true
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()

View file

@ -124,6 +124,9 @@ func (m *Manifest) IsValid(hash util.Uint160, checkSize bool) error {
if err != nil { if err != nil {
return err return err
} }
if m.Trusts.Value == nil && !m.Trusts.Wildcard {
return errors.New("invalid (null?) trusts")
}
if len(m.Trusts.Value) > 1 { if len(m.Trusts.Value) > 1 {
hashes := make([]PermissionDesc, len(m.Trusts.Value)) hashes := make([]PermissionDesc, len(m.Trusts.Value))
copy(hashes, m.Trusts.Value) copy(hashes, m.Trusts.Value)
@ -278,7 +281,7 @@ func (m *Manifest) FromStackItem(item stackitem.Item) error {
m.Permissions[i] = *p m.Permissions[i] = *p
} }
if _, ok := str[6].(stackitem.Null); ok { if _, ok := str[6].(stackitem.Null); ok {
m.Trusts = WildPermissionDescs{Value: nil} // wildcard by default m.Trusts = WildPermissionDescs{Value: nil, Wildcard: true} // wildcard by default
} else { } else {
if str[6].Type() != stackitem.ArrayT { if str[6].Type() != stackitem.ArrayT {
return errors.New("invalid Trusts stackitem type") return errors.New("invalid Trusts stackitem type")

View file

@ -212,6 +212,13 @@ func TestIsValid(t *testing.T) {
}) })
m.SupportedStandards = m.SupportedStandards[:1] m.SupportedStandards = m.SupportedStandards[:1]
t.Run("invalid, no trusts", func(t *testing.T) {
m.Trusts.Value = nil
m.Trusts.Wildcard = false
require.Error(t, m.IsValid(contractHash, true))
})
m.Trusts.Restrict()
d := PermissionDesc{Type: PermissionHash, Value: util.Uint160{1, 2, 3}} d := PermissionDesc{Type: PermissionHash, Value: util.Uint160{1, 2, 3}}
m.Trusts.Add(d) m.Trusts.Add(d)
t.Run("valid, with trust", func(t *testing.T) { t.Run("valid, with trust", func(t *testing.T) {