diff --git a/pkg/smartcontract/manifest/abi.go b/pkg/smartcontract/manifest/abi.go index 15211919e..67e28b073 100644 --- a/pkg/smartcontract/manifest/abi.go +++ b/pkg/smartcontract/manifest/abi.go @@ -4,7 +4,6 @@ import ( "cmp" "errors" "fmt" - "slices" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" ) @@ -63,23 +62,14 @@ func (a *ABI) IsValid() error { return fmt.Errorf("method %q/%d: %w", a.Methods[i].Name, len(a.Methods[i].Parameters), err) } } - if len(a.Methods) > 1 { - var methods = slices.Clone(a.Methods) - slices.SortFunc(methods, func(a, b Method) int { - return cmp.Or( - cmp.Compare(a.Name, b.Name), - cmp.Compare(len(a.Parameters), len(b.Parameters)), - ) - }) - for i := range methods { - if i == 0 { - continue - } - if methods[i].Name == methods[i-1].Name && - len(methods[i].Parameters) == len(methods[i-1].Parameters) { - return errors.New("duplicate method specifications") - } + if sliceHasDups(a.Methods, func(a, b Method) int { + res := cmp.Compare(a.Name, b.Name) + if res != 0 { + return res } + return cmp.Compare(len(a.Parameters), len(b.Parameters)) + }) { + return errors.New("duplicate method specifications") } for i := range a.Events { err := a.Events[i].IsValid() @@ -87,14 +77,10 @@ func (a *ABI) IsValid() error { return fmt.Errorf("event %q/%d: %w", a.Events[i].Name, len(a.Events[i].Parameters), err) } } - if len(a.Events) > 1 { - names := make([]string, len(a.Events)) - for i := range a.Events { - names[i] = a.Events[i].Name - } - if stringsHaveDups(names) { - return errors.New("duplicate event names") - } + if sliceHasDups(a.Events, func(a, b Event) int { + return cmp.Compare(a.Name, b.Name) + }) { + return errors.New("duplicate event names") } return nil } diff --git a/pkg/smartcontract/manifest/group.go b/pkg/smartcontract/manifest/group.go index d2473cbca..9c7b7bd8b 100644 --- a/pkg/smartcontract/manifest/group.go +++ b/pkg/smartcontract/manifest/group.go @@ -6,7 +6,6 @@ import ( "encoding/json" "errors" "slices" - "sort" "github.com/nspcc-dev/neo-go/pkg/crypto/hash" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" @@ -59,7 +58,7 @@ func (g Groups) AreValid(h util.Uint160) error { for i := range g { pkeys[i] = g[i].PublicKey } - sort.Sort(pkeys) + slices.SortFunc(pkeys, (*keys.PublicKey).Cmp) for i := range pkeys { if i == 0 { continue diff --git a/pkg/smartcontract/manifest/manifest.go b/pkg/smartcontract/manifest/manifest.go index 40cb168ce..f709c21ae 100644 --- a/pkg/smartcontract/manifest/manifest.go +++ b/pkg/smartcontract/manifest/manifest.go @@ -95,11 +95,8 @@ func (m *Manifest) IsValid(hash util.Uint160, checkSize bool) error { if slices.Contains(m.SupportedStandards, "") { return errors.New("invalid nameless supported standard") } - if len(m.SupportedStandards) > 1 { - names := slices.Clone(m.SupportedStandards) - if stringsHaveDups(names) { - return errors.New("duplicate supported standards") - } + if sliceHasDups(m.SupportedStandards, strings.Compare) { + return errors.New("duplicate supported standards") } err = m.ABI.IsValid() if err != nil { @@ -122,11 +119,8 @@ func (m *Manifest) IsValid(hash util.Uint160, checkSize bool) error { if m.Trusts.Value == nil && !m.Trusts.Wildcard { return errors.New("invalid (null?) trusts") } - if len(m.Trusts.Value) > 1 { - hashes := slices.Clone(m.Trusts.Value) - if permissionDescsHaveDups(hashes) { - return errors.New("duplicate trusted contracts") - } + if sliceHasDups(m.Trusts.Value, PermissionDesc.Compare) { + return errors.New("duplicate trusted contracts") } err = Permissions(m.Permissions).AreValid() if err != nil { diff --git a/pkg/smartcontract/manifest/parameter.go b/pkg/smartcontract/manifest/parameter.go index ee3381bba..7cccb2b22 100644 --- a/pkg/smartcontract/manifest/parameter.go +++ b/pkg/smartcontract/manifest/parameter.go @@ -1,6 +1,7 @@ package manifest import ( + "cmp" "errors" "fmt" "slices" @@ -79,42 +80,28 @@ func (p Parameters) AreValid() error { return fmt.Errorf("parameter #%d/%q: %w", i, p[i].Name, err) } } - if len(p) < 2 { - return nil - } - names := make([]string, len(p)) - for i := range p { - names[i] = p[i].Name - } - if stringsHaveDups(names) { + if sliceHasDups(p, func(a, b Parameter) int { + return cmp.Compare(a.Name, b.Name) + }) { return errors.New("duplicate parameter name") } return nil } -// stringsHaveDups checks the given set of strings for duplicates. It modifies the slice given! -func stringsHaveDups(strings []string) bool { - slices.Sort(strings) - for i := range strings { +// sliceHasDups checks the slice for duplicate elements. +func sliceHasDups[S ~[]E, E any](x S, cmp func(a, b E) int) bool { + if len(x) < 2 { + return false + } + if len(x) > 2 { + x = slices.Clone(x) + slices.SortFunc(x, cmp) + } + for i := range x { if i == 0 { continue } - if strings[i] == strings[i-1] { - return true - } - } - return false -} - -// permissionDescsHaveDups checks the given set of strings for duplicates. It modifies the slice given! -func permissionDescsHaveDups(descs []PermissionDesc) bool { - slices.SortFunc(descs, PermissionDesc.Compare) - for i := range descs { - if i == 0 { - continue - } - j := i - 1 - if descs[i].Compare(descs[j]) == 0 { + if cmp(x[i-1], x[i]) == 0 { return true } } diff --git a/pkg/smartcontract/manifest/permission.go b/pkg/smartcontract/manifest/permission.go index a560669e8..254391981 100644 --- a/pkg/smartcontract/manifest/permission.go +++ b/pkg/smartcontract/manifest/permission.go @@ -119,11 +119,7 @@ func (p *Permission) IsValid() error { if slices.Contains(p.Methods.Value, "") { return errors.New("empty method name") } - if len(p.Methods.Value) < 2 { - return nil - } - names := slices.Clone(p.Methods.Value) - if stringsHaveDups(names) { + if sliceHasDups(p.Methods.Value, cmp.Compare) { return errors.New("duplicate method names") } return nil @@ -137,14 +133,9 @@ func (ps Permissions) AreValid() error { return err } } - if len(ps) < 2 { - return nil - } - contracts := make([]PermissionDesc, 0, len(ps)) - for i := range ps { - contracts = append(contracts, ps[i].Contract) - } - if permissionDescsHaveDups(contracts) { + if sliceHasDups(ps, func(a, b Permission) int { + return a.Contract.Compare(b.Contract) + }) { return errors.New("contracts have duplicates") } return nil