diff --git a/api/metrics/multi_gatherer.go b/api/metrics/multi_gatherer.go index ce9af54936be..5e335b1d5f48 100644 --- a/api/metrics/multi_gatherer.go +++ b/api/metrics/multi_gatherer.go @@ -91,7 +91,14 @@ func (g *multiGatherer) Register(namespace string, gatherer prometheus.Gatherer) } func sortMetrics(m []*dto.MetricFamily) { - slices.SortFunc(m, func(i, j *dto.MetricFamily) bool { - return *i.Name < *j.Name + slices.SortFunc(m, func(i, j *dto.MetricFamily) int { + switch { + case *i.Name < *j.Name: + return -1 + case *i.Name > *j.Name: + return 1 + default: + return 0 + } }) } diff --git a/chains/atomic/state.go b/chains/atomic/state.go index aee269915ea9..402477299c21 100644 --- a/chains/atomic/state.go +++ b/chains/atomic/state.go @@ -8,11 +8,12 @@ import ( "errors" "fmt" + "golang.org/x/exp/slices" + "github.com/ava-labs/avalanchego/database" "github.com/ava-labs/avalanchego/database/linkeddb" "github.com/ava-labs/avalanchego/database/prefixdb" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/utils" "github.com/ava-labs/avalanchego/utils/hashing" "github.com/ava-labs/avalanchego/utils/set" ) @@ -207,7 +208,7 @@ func (s *state) getKeys(traits [][]byte, startTrait, startKey []byte, limit int) lastKey := startKey // Iterate over the traits in order appending all of the keys that possess // the given [traits]. - utils.SortBytes(traits) + slices.SortFunc(traits, bytes.Compare) for _, trait := range traits { switch bytes.Compare(trait, startTrait) { case -1: diff --git a/codec/reflectcodec/type_codec.go b/codec/reflectcodec/type_codec.go index 9f9037f43d4e..6f18f8500272 100644 --- a/codec/reflectcodec/type_codec.go +++ b/codec/reflectcodec/type_codec.go @@ -490,10 +490,10 @@ func (c *genericCodec) marshal( endOffset = p.Offset } - slices.SortFunc(sortedKeys, func(a, b keyTuple) bool { + slices.SortFunc(sortedKeys, func(a, b keyTuple) int { aBytes := p.Bytes[a.startIndex:a.endIndex] bBytes := p.Bytes[b.startIndex:b.endIndex] - return bytes.Compare(aBytes, bBytes) < 0 + return bytes.Compare(aBytes, bBytes) }) allKeyBytes := slices.Clone(p.Bytes[startOffset:p.Offset]) diff --git a/genesis/config.go b/genesis/config.go index a951e9e078fc..a55cbcd1ec1f 100644 --- a/genesis/config.go +++ b/genesis/config.go @@ -53,9 +53,15 @@ func (a Allocation) Unparse(networkID uint32) (UnparsedAllocation, error) { return ua, err } -func (a Allocation) Less(other Allocation) bool { - return a.InitialAmount < other.InitialAmount || - (a.InitialAmount == other.InitialAmount && a.AVAXAddr.Less(other.AVAXAddr)) +func (a Allocation) Compare(other Allocation) int { + switch { + case a.InitialAmount < other.InitialAmount: + return -1 + case a.InitialAmount > other.InitialAmount: + return 1 + default: + return a.AVAXAddr.Compare(other.AVAXAddr) + } } type Staker struct { diff --git a/genesis/config_test.go b/genesis/config_test.go index 455045dad70b..d83815cebfba 100644 --- a/genesis/config_test.go +++ b/genesis/config_test.go @@ -11,56 +11,43 @@ import ( "github.com/ava-labs/avalanchego/ids" ) -func TestAllocationLess(t *testing.T) { +func TestAllocationCompare(t *testing.T) { type test struct { name string alloc1 Allocation alloc2 Allocation - expected bool + expected int } tests := []test{ { name: "equal", alloc1: Allocation{}, alloc2: Allocation{}, - expected: false, + expected: 0, }, { - name: "first initial amount smaller", + name: "initial amount smaller", alloc1: Allocation{}, alloc2: Allocation{ InitialAmount: 1, }, - expected: true, + expected: -1, }, { - name: "first initial amount larger", - alloc1: Allocation{ - InitialAmount: 1, - }, - alloc2: Allocation{}, - expected: false, - }, - { - name: "first bytes smaller", + name: "bytes smaller", alloc1: Allocation{}, alloc2: Allocation{ AVAXAddr: ids.ShortID{1}, }, - expected: true, - }, - { - name: "first bytes larger", - alloc1: Allocation{ - AVAXAddr: ids.ShortID{1}, - }, - alloc2: Allocation{}, - expected: false, + expected: -1, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - require.Equal(t, tt.expected, tt.alloc1.Less(tt.alloc2)) + require := require.New(t) + + require.Equal(tt.expected, tt.alloc1.Compare(tt.alloc2)) + require.Equal(-tt.expected, tt.alloc2.Compare(tt.alloc1)) }) } } diff --git a/go.mod b/go.mod index 3547ac3dc558..8475d36cf162 100644 --- a/go.mod +++ b/go.mod @@ -58,11 +58,11 @@ require ( go.uber.org/goleak v1.2.1 go.uber.org/mock v0.2.0 go.uber.org/zap v1.26.0 - golang.org/x/crypto v0.14.0 - golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df - golang.org/x/net v0.17.0 - golang.org/x/sync v0.4.0 - golang.org/x/term v0.13.0 + golang.org/x/crypto v0.16.0 + golang.org/x/exp v0.0.0-20231127185646-65229373498e + golang.org/x/net v0.19.0 + golang.org/x/sync v0.5.0 + golang.org/x/term v0.15.0 golang.org/x/time v0.0.0-20220922220347-f3bd1da661af gonum.org/v1/gonum v0.11.0 google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 @@ -146,9 +146,9 @@ require ( go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.11.0 // indirect go.opentelemetry.io/proto/otlp v0.19.0 // indirect go.uber.org/multierr v1.10.0 // indirect - golang.org/x/sys v0.14.0 // indirect - golang.org/x/text v0.13.0 // indirect - golang.org/x/tools v0.14.0 // indirect + golang.org/x/sys v0.15.0 // indirect + golang.org/x/text v0.14.0 // indirect + golang.org/x/tools v0.16.0 // indirect google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 // indirect gopkg.in/ini.v1 v1.67.0 // indirect diff --git a/go.sum b/go.sum index 50f6bf7f3245..dc186944e492 100644 --- a/go.sum +++ b/go.sum @@ -696,8 +696,8 @@ golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY= +golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -708,8 +708,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME= -golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= +golang.org/x/exp v0.0.0-20231127185646-65229373498e h1:Gvh4YaCaXNs6dKTlfgismwWZKyjVZXwOPfIyUaqU3No= +golang.org/x/exp v0.0.0-20231127185646-65229373498e/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI= golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -736,7 +736,7 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.13.0 h1:I/DsJXRlw/8l/0c24sM9yb0T4z9liZTduXvdAWYiysY= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -783,8 +783,8 @@ golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= +golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -807,8 +807,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ= -golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= +golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -878,12 +878,12 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= -golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= +golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -894,8 +894,8 @@ golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -958,8 +958,8 @@ golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.14.0 h1:jvNa2pY0M4r62jkRQ6RwEZZyPcymeL9XZMLBbV7U2nc= -golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= +golang.org/x/tools v0.16.0 h1:GO788SKMRunPIBCXiQyo2AaexLstOrVhuAL5YwsckQM= +golang.org/x/tools v0.16.0/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/ids/id.go b/ids/id.go index 68148018b078..6e5aed7876fd 100644 --- a/ids/id.go +++ b/ids/id.go @@ -145,6 +145,6 @@ func (id ID) MarshalText() ([]byte, error) { return []byte(id.String()), nil } -func (id ID) Less(other ID) bool { - return bytes.Compare(id[:], other[:]) < 0 +func (id ID) Compare(other ID) int { + return bytes.Compare(id[:], other[:]) } diff --git a/ids/id_test.go b/ids/id_test.go index 3424b17633e6..701d9613441f 100644 --- a/ids/id_test.go +++ b/ids/id_test.go @@ -5,6 +5,7 @@ package ids import ( "encoding/json" + "fmt" "testing" "github.com/stretchr/testify/require" @@ -200,26 +201,39 @@ func TestIDMapMarshalling(t *testing.T) { require.Equal(originalMap, unmarshalledMap) } -func TestIDLess(t *testing.T) { - require := require.New(t) +func TestIDCompare(t *testing.T) { + tests := []struct { + a ID + b ID + expected int + }{ + { + a: ID{}, + b: ID{}, + expected: 0, + }, + { + a: ID{1}, + b: ID{0}, + expected: 1, + }, + { + a: ID{1}, + b: ID{1}, + expected: 0, + }, + { + a: ID{1, 0}, + b: ID{1, 2}, + expected: -1, + }, + } + for _, test := range tests { + t.Run(fmt.Sprintf("%s_%s_%d", test.a, test.b, test.expected), func(t *testing.T) { + require := require.New(t) - id1 := ID{} - id2 := ID{} - require.False(id1.Less(id2)) - require.False(id2.Less(id1)) - - id1 = ID{1} - id2 = ID{0} - require.False(id1.Less(id2)) - require.True(id2.Less(id1)) - - id1 = ID{1} - id2 = ID{1} - require.False(id1.Less(id2)) - require.False(id2.Less(id1)) - - id1 = ID{1, 0} - id2 = ID{1, 2} - require.True(id1.Less(id2)) - require.False(id2.Less(id1)) + require.Equal(test.expected, test.a.Compare(test.b)) + require.Equal(-test.expected, test.b.Compare(test.a)) + }) + } } diff --git a/ids/node_id.go b/ids/node_id.go index 57d0c0b5bc69..a20b00d24462 100644 --- a/ids/node_id.go +++ b/ids/node_id.go @@ -66,8 +66,8 @@ func (id *NodeID) UnmarshalText(text []byte) error { return id.UnmarshalJSON(text) } -func (id NodeID) Less(other NodeID) bool { - return bytes.Compare(id[:], other[:]) == -1 +func (id NodeID) Compare(other NodeID) int { + return bytes.Compare(id[:], other[:]) } // ToNodeID attempt to convert a byte slice into a node id diff --git a/ids/node_id_test.go b/ids/node_id_test.go index b92fb6e19053..bd4ba24cdf4e 100644 --- a/ids/node_id_test.go +++ b/ids/node_id_test.go @@ -5,6 +5,7 @@ package ids import ( "encoding/json" + "fmt" "testing" "github.com/stretchr/testify/require" @@ -174,26 +175,39 @@ func TestNodeIDMapMarshalling(t *testing.T) { require.Equal(originalMap, unmarshalledMap) } -func TestNodeIDLess(t *testing.T) { - require := require.New(t) +func TestNodeIDCompare(t *testing.T) { + tests := []struct { + a NodeID + b NodeID + expected int + }{ + { + a: NodeID{}, + b: NodeID{}, + expected: 0, + }, + { + a: NodeID{1}, + b: NodeID{0}, + expected: 1, + }, + { + a: NodeID{1}, + b: NodeID{1}, + expected: 0, + }, + { + a: NodeID{1, 0}, + b: NodeID{1, 2}, + expected: -1, + }, + } + for _, test := range tests { + t.Run(fmt.Sprintf("%s_%s_%d", test.a, test.b, test.expected), func(t *testing.T) { + require := require.New(t) - id1 := NodeID{} - id2 := NodeID{} - require.False(id1.Less(id2)) - require.False(id2.Less(id1)) - - id1 = NodeID{1} - id2 = NodeID{} - require.False(id1.Less(id2)) - require.True(id2.Less(id1)) - - id1 = NodeID{1} - id2 = NodeID{1} - require.False(id1.Less(id2)) - require.False(id2.Less(id1)) - - id1 = NodeID{1} - id2 = NodeID{1, 2} - require.True(id1.Less(id2)) - require.False(id2.Less(id1)) + require.Equal(test.expected, test.a.Compare(test.b)) + require.Equal(-test.expected, test.b.Compare(test.a)) + }) + } } diff --git a/ids/short.go b/ids/short.go index 25b96f1755b0..b19e0420690d 100644 --- a/ids/short.go +++ b/ids/short.go @@ -110,8 +110,8 @@ func (id ShortID) MarshalText() ([]byte, error) { return []byte(id.String()), nil } -func (id ShortID) Less(other ShortID) bool { - return bytes.Compare(id[:], other[:]) == -1 +func (id ShortID) Compare(other ShortID) int { + return bytes.Compare(id[:], other[:]) } // ShortIDsToStrings converts an array of shortIDs to an array of their string diff --git a/snow/consensus/snowman/test_block.go b/snow/consensus/snowman/test_block.go index a02bf31787c1..b0802027c4d0 100644 --- a/snow/consensus/snowman/test_block.go +++ b/snow/consensus/snowman/test_block.go @@ -48,6 +48,13 @@ func (b *TestBlock) Bytes() []byte { return b.BytesV } -func (b *TestBlock) Less(other *TestBlock) bool { - return b.HeightV < other.HeightV +func (b *TestBlock) Compare(other *TestBlock) int { + switch { + case b.HeightV < other.HeightV: + return -1 + case b.HeightV > other.HeightV: + return 1 + default: + return 0 + } } diff --git a/utils/sampler/weighted_array.go b/utils/sampler/weighted_array.go index 0db1dda17af9..26f6ebe46bdf 100644 --- a/utils/sampler/weighted_array.go +++ b/utils/sampler/weighted_array.go @@ -19,8 +19,15 @@ type weightedArrayElement struct { } // Note that this sorts in order of decreasing weight. -func (e weightedArrayElement) Less(other weightedArrayElement) bool { - return e.cumulativeWeight > other.cumulativeWeight +func (e weightedArrayElement) Compare(other weightedArrayElement) int { + switch { + case e.cumulativeWeight > other.cumulativeWeight: + return -1 + case e.cumulativeWeight < other.cumulativeWeight: + return 1 + default: + return 0 + } } // Sampling is performed by executing a modified binary search over the provided diff --git a/utils/sampler/weighted_array_test.go b/utils/sampler/weighted_array_test.go index e10583633436..0f44869b7dee 100644 --- a/utils/sampler/weighted_array_test.go +++ b/utils/sampler/weighted_array_test.go @@ -4,24 +4,39 @@ package sampler import ( + "fmt" "testing" "github.com/stretchr/testify/require" ) -func TestWeightedArrayElementLess(t *testing.T) { - require := require.New(t) - - var elt1, elt2 weightedArrayElement - require.False(elt1.Less(elt2)) - require.False(elt2.Less(elt1)) - - elt1 = weightedArrayElement{ - cumulativeWeight: 1, +func TestWeightedArrayElementCompare(t *testing.T) { + tests := []struct { + a weightedArrayElement + b weightedArrayElement + expected int + }{ + { + a: weightedArrayElement{}, + b: weightedArrayElement{}, + expected: 0, + }, + { + a: weightedArrayElement{ + cumulativeWeight: 1, + }, + b: weightedArrayElement{ + cumulativeWeight: 2, + }, + expected: 1, + }, } - elt2 = weightedArrayElement{ - cumulativeWeight: 2, + for _, test := range tests { + t.Run(fmt.Sprintf("%d_%d_%d", test.a.cumulativeWeight, test.b.cumulativeWeight, test.expected), func(t *testing.T) { + require := require.New(t) + + require.Equal(test.expected, test.a.Compare(test.b)) + require.Equal(-test.expected, test.b.Compare(test.a)) + }) } - require.False(elt1.Less(elt2)) - require.True(elt2.Less(elt1)) } diff --git a/utils/sampler/weighted_heap.go b/utils/sampler/weighted_heap.go index 4b7fb84df482..dc8d717b6048 100644 --- a/utils/sampler/weighted_heap.go +++ b/utils/sampler/weighted_heap.go @@ -19,17 +19,22 @@ type weightedHeapElement struct { index int } -func (e weightedHeapElement) Less(other weightedHeapElement) bool { +func (e weightedHeapElement) Compare(other weightedHeapElement) int { // By accounting for the initial index of the weights, this results in a // stable sort. We do this rather than using `sort.Stable` because of the // reported change in performance of the sort used. - if e.weight > other.weight { - return true + switch { + case e.weight > other.weight: + return -1 + case e.weight < other.weight: + return 1 + case e.index < other.index: + return -1 + case e.index > other.index: + return 1 + default: + return 0 } - if e.weight < other.weight { - return false - } - return e.index < other.index } // Sampling is performed by executing a search over a tree of elements in the diff --git a/utils/sampler/weighted_heap_test.go b/utils/sampler/weighted_heap_test.go index 3187c14fa10a..eb9ff46ab276 100644 --- a/utils/sampler/weighted_heap_test.go +++ b/utils/sampler/weighted_heap_test.go @@ -23,57 +23,44 @@ func TestWeightedHeapInitialize(t *testing.T) { } } -func TestWeightedHeapElementLess(t *testing.T) { +func TestWeightedHeapElementCompare(t *testing.T) { type test struct { name string elt1 weightedHeapElement elt2 weightedHeapElement - expected bool + expected int } tests := []test{ { name: "all same", elt1: weightedHeapElement{}, elt2: weightedHeapElement{}, - expected: false, + expected: 0, }, { - name: "first lower weight", + name: "lower weight", elt1: weightedHeapElement{}, elt2: weightedHeapElement{ weight: 1, }, - expected: false, + expected: 1, }, { - name: "first higher weight", - elt1: weightedHeapElement{ - weight: 1, - }, - elt2: weightedHeapElement{}, - expected: true, - }, - { - name: "first higher index", + name: "higher index", elt1: weightedHeapElement{ index: 1, }, elt2: weightedHeapElement{}, - expected: false, - }, - { - name: "second higher index", - elt1: weightedHeapElement{}, - elt2: weightedHeapElement{ - index: 1, - }, - expected: true, + expected: 1, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - require.Equal(t, tt.expected, tt.elt1.Less(tt.elt2)) + require := require.New(t) + + require.Equal(tt.expected, tt.elt1.Compare(tt.elt2)) + require.Equal(-tt.expected, tt.elt2.Compare(tt.elt1)) }) } } diff --git a/utils/sampler/weighted_linear.go b/utils/sampler/weighted_linear.go index a5d0e3b16711..dfb2f93e18ae 100644 --- a/utils/sampler/weighted_linear.go +++ b/utils/sampler/weighted_linear.go @@ -19,8 +19,15 @@ type weightedLinearElement struct { } // Note that this sorts in order of decreasing cumulative weight. -func (e weightedLinearElement) Less(other weightedLinearElement) bool { - return e.cumulativeWeight > other.cumulativeWeight +func (e weightedLinearElement) Compare(other weightedLinearElement) int { + switch { + case e.cumulativeWeight > other.cumulativeWeight: + return -1 + case e.cumulativeWeight < other.cumulativeWeight: + return 1 + default: + return 0 + } } // Sampling is performed by executing a linear search over the provided elements diff --git a/utils/sampler/weighted_linear_test.go b/utils/sampler/weighted_linear_test.go index b34035017b4a..6757158ed8e6 100644 --- a/utils/sampler/weighted_linear_test.go +++ b/utils/sampler/weighted_linear_test.go @@ -4,24 +4,39 @@ package sampler import ( + "fmt" "testing" "github.com/stretchr/testify/require" ) -func TestWeightedLinearElementLess(t *testing.T) { - require := require.New(t) - - var elt1, elt2 weightedLinearElement - require.False(elt1.Less(elt2)) - require.False(elt2.Less(elt1)) - - elt1 = weightedLinearElement{ - cumulativeWeight: 1, +func TestWeightedLinearElementCompare(t *testing.T) { + tests := []struct { + a weightedLinearElement + b weightedLinearElement + expected int + }{ + { + a: weightedLinearElement{}, + b: weightedLinearElement{}, + expected: 0, + }, + { + a: weightedLinearElement{ + cumulativeWeight: 1, + }, + b: weightedLinearElement{ + cumulativeWeight: 2, + }, + expected: 1, + }, } - elt2 = weightedLinearElement{ - cumulativeWeight: 2, + for _, test := range tests { + t.Run(fmt.Sprintf("%d_%d_%d", test.a.cumulativeWeight, test.b.cumulativeWeight, test.expected), func(t *testing.T) { + require := require.New(t) + + require.Equal(test.expected, test.a.Compare(test.b)) + require.Equal(-test.expected, test.b.Compare(test.a)) + }) } - require.False(elt1.Less(elt2)) - require.True(elt2.Less(elt1)) } diff --git a/utils/set/sampleable_set.go b/utils/set/sampleable_set.go index 0d22f40fbf8a..8c4c02461bd9 100644 --- a/utils/set/sampleable_set.go +++ b/utils/set/sampleable_set.go @@ -176,7 +176,7 @@ func (s *SampleableSet[_]) MarshalJSON() ([]byte, error) { } } // Sort for determinism - utils.SortBytes(elementBytes) + slices.SortFunc(elementBytes, bytes.Compare) // Build the JSON var ( diff --git a/utils/set/set.go b/utils/set/set.go index fd6525b6b127..41d8bb712f10 100644 --- a/utils/set/set.go +++ b/utils/set/set.go @@ -9,6 +9,7 @@ import ( stdjson "encoding/json" "golang.org/x/exp/maps" + "golang.org/x/exp/slices" "github.com/ava-labs/avalanchego/utils" "github.com/ava-labs/avalanchego/utils/json" @@ -161,7 +162,7 @@ func (s Set[_]) MarshalJSON() ([]byte, error) { i++ } // Sort for determinism - utils.SortBytes(eltBytes) + slices.SortFunc(eltBytes, bytes.Compare) // Build the JSON var ( diff --git a/utils/sorting.go b/utils/sorting.go index 74f24abeb69f..a05a3f4c12c2 100644 --- a/utils/sorting.go +++ b/utils/sorting.go @@ -12,32 +12,23 @@ import ( "github.com/ava-labs/avalanchego/utils/hashing" ) -// TODO can we handle sorting where the Less function relies on a codec? +// TODO can we handle sorting where the Compare function relies on a codec? type Sortable[T any] interface { - Less(T) bool + Compare(T) int } // Sorts the elements of [s]. func Sort[T Sortable[T]](s []T) { - slices.SortFunc(s, T.Less) + slices.SortFunc(s, T.Compare) } // Sorts the elements of [s] based on their hashes. func SortByHash[T ~[]byte](s []T) { - slices.SortFunc(s, func(i, j T) bool { + slices.SortFunc(s, func(i, j T) int { iHash := hashing.ComputeHash256(i) jHash := hashing.ComputeHash256(j) - return bytes.Compare(iHash, jHash) == -1 - }) -} - -// Sorts a 2D byte slice. -// Each byte slice is not sorted internally; the byte slices are sorted relative -// to one another. -func SortBytes[T ~[]byte](s []T) { - slices.SortFunc(s, func(i, j T) bool { - return bytes.Compare(i, j) == -1 + return bytes.Compare(iHash, jHash) }) } @@ -54,7 +45,7 @@ func IsSortedBytes[T ~[]byte](s []T) bool { // Returns true iff the elements in [s] are unique and sorted. func IsSortedAndUnique[T Sortable[T]](s []T) bool { for i := 0; i < len(s)-1; i++ { - if !s[i].Less(s[i+1]) { + if s[i].Compare(s[i+1]) >= 0 { return false } } diff --git a/utils/sorting_test.go b/utils/sorting_test.go index 464959dd9588..019834907686 100644 --- a/utils/sorting_test.go +++ b/utils/sorting_test.go @@ -4,9 +4,7 @@ package utils import ( - "math/rand" "testing" - "time" "github.com/stretchr/testify/require" ) @@ -15,8 +13,15 @@ var _ Sortable[sortable] = sortable(0) type sortable int -func (s sortable) Less(other sortable) bool { - return s < other +func (s sortable) Compare(other sortable) int { + switch { + case s < other: + return -1 + case s > other: + return 1 + default: + return 0 + } } func TestSortSliceSortable(t *testing.T) { @@ -59,23 +64,6 @@ func TestSortSliceSortable(t *testing.T) { require.Equal([]sortable{1, 2, 3}, s) } -func TestSortBytesIsSortedBytes(t *testing.T) { - require := require.New(t) - - seed := time.Now().UnixNano() - t.Log("Seed: ", seed) - rand := rand.New(rand.NewSource(seed)) //#nosec G404 - - slices := make([][]byte, 1024) - for j := 0; j < len(slices); j++ { - slices[j] = make([]byte, 32) - _, _ = rand.Read(slices[j]) - } - require.False(IsSortedBytes(slices)) - SortBytes(slices) - require.True(IsSortedBytes(slices)) -} - func TestIsSortedAndUniqueSortable(t *testing.T) { require := require.New(t) diff --git a/vms/avm/genesis.go b/vms/avm/genesis.go index 506d2465d691..f2ea7d3e2903 100644 --- a/vms/avm/genesis.go +++ b/vms/avm/genesis.go @@ -19,6 +19,13 @@ type GenesisAsset struct { txs.CreateAssetTx `serialize:"true"` } -func (g *GenesisAsset) Less(other *GenesisAsset) bool { - return g.Alias < other.Alias +func (g *GenesisAsset) Compare(other *GenesisAsset) int { + switch { + case g.Alias < other.Alias: + return -1 + case g.Alias > other.Alias: + return 1 + default: + return 0 + } } diff --git a/vms/avm/genesis_test.go b/vms/avm/genesis_test.go index 10c7aac40295..3e3743bbfdb5 100644 --- a/vms/avm/genesis_test.go +++ b/vms/avm/genesis_test.go @@ -4,24 +4,39 @@ package avm import ( + "fmt" "testing" "github.com/stretchr/testify/require" ) -func TestGenesisAssetLess(t *testing.T) { - require := require.New(t) - - var g1, g2 GenesisAsset - require.False(g1.Less(&g2)) - require.False(g2.Less(&g1)) - - g1 = GenesisAsset{ - Alias: "a", +func TestGenesisAssetCompare(t *testing.T) { + tests := []struct { + a GenesisAsset + b GenesisAsset + expected int + }{ + { + a: GenesisAsset{}, + b: GenesisAsset{}, + expected: 0, + }, + { + a: GenesisAsset{ + Alias: "a", + }, + b: GenesisAsset{ + Alias: "aa", + }, + expected: -1, + }, } - g2 = GenesisAsset{ - Alias: "aa", + for _, test := range tests { + t.Run(fmt.Sprintf("%s_%s_%d", test.a.Alias, test.b.Alias, test.expected), func(t *testing.T) { + require := require.New(t) + + require.Equal(test.expected, test.a.Compare(&test.b)) + require.Equal(-test.expected, test.b.Compare(&test.a)) + }) } - require.True(g1.Less(&g2)) - require.False(g2.Less(&g1)) } diff --git a/vms/avm/txs/initial_state.go b/vms/avm/txs/initial_state.go index a50c88c0a294..aca0d5135e55 100644 --- a/vms/avm/txs/initial_state.go +++ b/vms/avm/txs/initial_state.go @@ -59,8 +59,15 @@ func (is *InitialState) Verify(c codec.Manager, numFxs int) error { return nil } -func (is *InitialState) Less(other *InitialState) bool { - return is.FxIndex < other.FxIndex +func (is *InitialState) Compare(other *InitialState) int { + switch { + case is.FxIndex < other.FxIndex: + return -1 + case is.FxIndex > other.FxIndex: + return 1 + default: + return 0 + } } func (is *InitialState) Sort(c codec.Manager) { diff --git a/vms/avm/txs/initial_state_test.go b/vms/avm/txs/initial_state_test.go index f54f54b3b9ed..a60f6db6dded 100644 --- a/vms/avm/txs/initial_state_test.go +++ b/vms/avm/txs/initial_state_test.go @@ -5,6 +5,7 @@ package txs import ( "errors" + "fmt" "testing" "github.com/stretchr/testify/require" @@ -159,14 +160,31 @@ func TestInitialStateVerifyUnsortedOutputs(t *testing.T) { require.NoError(is.Verify(m, numFxs)) } -func TestInitialStateLess(t *testing.T) { - require := require.New(t) - - var is1, is2 InitialState - require.False(is1.Less(&is2)) - require.False(is2.Less(&is1)) +func TestInitialStateCompare(t *testing.T) { + tests := []struct { + a InitialState + b InitialState + expected int + }{ + { + a: InitialState{}, + b: InitialState{}, + expected: 0, + }, + { + a: InitialState{ + FxIndex: 1, + }, + b: InitialState{}, + expected: 1, + }, + } + for _, test := range tests { + t.Run(fmt.Sprintf("%d_%d_%d", test.a.FxIndex, test.b.FxIndex, test.expected), func(t *testing.T) { + require := require.New(t) - is1.FxIndex = 1 - require.False(is1.Less(&is2)) - require.True(is2.Less(&is1)) + require.Equal(test.expected, test.a.Compare(&test.b)) + require.Equal(-test.expected, test.b.Compare(&test.a)) + }) + } } diff --git a/vms/avm/txs/operation.go b/vms/avm/txs/operation.go index 4b4cb27aa46b..ded7671e618a 100644 --- a/vms/avm/txs/operation.go +++ b/vms/avm/txs/operation.go @@ -48,16 +48,16 @@ type operationAndCodec struct { codec codec.Manager } -func (o *operationAndCodec) Less(other *operationAndCodec) bool { +func (o *operationAndCodec) Compare(other *operationAndCodec) int { oBytes, err := o.codec.Marshal(CodecVersion, o.op) if err != nil { - return false + return 0 } otherBytes, err := o.codec.Marshal(CodecVersion, other.op) if err != nil { - return false + return 0 } - return bytes.Compare(oBytes, otherBytes) == -1 + return bytes.Compare(oBytes, otherBytes) } func SortOperations(ops []*Operation, c codec.Manager) { diff --git a/vms/components/avax/transferables.go b/vms/components/avax/transferables.go index 3134ac68ff4a..44bafeef8d6c 100644 --- a/vms/components/avax/transferables.go +++ b/vms/components/avax/transferables.go @@ -161,8 +161,8 @@ func (in *TransferableInput) Verify() error { } } -func (in *TransferableInput) Less(other *TransferableInput) bool { - return in.UTXOID.Less(&other.UTXOID) +func (in *TransferableInput) Compare(other *TransferableInput) int { + return in.UTXOID.Compare(&other.UTXOID) } type innerSortTransferableInputsWithSigners struct { diff --git a/vms/components/avax/utxo_id.go b/vms/components/avax/utxo_id.go index 26fe8b83fb98..e094ea299628 100644 --- a/vms/components/avax/utxo_id.go +++ b/vms/components/avax/utxo_id.go @@ -91,16 +91,23 @@ func (utxo *UTXOID) Verify() error { } } -func (utxo *UTXOID) Less(other *UTXOID) bool { +func (utxo *UTXOID) Compare(other *UTXOID) int { utxoID, utxoIndex := utxo.InputSource() otherID, otherIndex := other.InputSource() switch bytes.Compare(utxoID[:], otherID[:]) { case -1: - return true - case 0: - return utxoIndex < otherIndex + return -1 + case 1: + return 1 + } + + switch { + case utxoIndex < otherIndex: + return -1 + case utxoIndex > otherIndex: + return 1 default: - return false + return 0 } } diff --git a/vms/components/avax/utxo_id_test.go b/vms/components/avax/utxo_id_test.go index 5652fa1afa69..e21ef620428b 100644 --- a/vms/components/avax/utxo_id_test.go +++ b/vms/components/avax/utxo_id_test.go @@ -50,56 +50,43 @@ func TestUTXOID(t *testing.T) { require.Equal(utxoID.InputID(), newUTXOID.InputID()) } -func TestUTXOIDLess(t *testing.T) { +func TestUTXOIDCompare(t *testing.T) { type test struct { name string id1 UTXOID id2 UTXOID - expected bool + expected int } tests := []*test{ { name: "same", id1: UTXOID{}, id2: UTXOID{}, - expected: false, + expected: 0, }, { - name: "first id smaller", + name: "id smaller", id1: UTXOID{}, id2: UTXOID{ TxID: ids.ID{1}, }, - expected: true, + expected: -1, }, { - name: "first id larger", - id1: UTXOID{ - TxID: ids.ID{1}, - }, - id2: UTXOID{}, - expected: false, - }, - { - name: "first index smaller", + name: "index smaller", id1: UTXOID{}, id2: UTXOID{ OutputIndex: 1, }, - expected: true, - }, - { - name: "first index larger", - id1: UTXOID{ - OutputIndex: 1, - }, - id2: UTXOID{}, - expected: false, + expected: -1, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - require.Equal(t, tt.expected, tt.id1.Less(&tt.id2)) + require := require.New(t) + + require.Equal(tt.expected, tt.id1.Compare(&tt.id2)) + require.Equal(-tt.expected, tt.id2.Compare(&tt.id1)) }) } } diff --git a/vms/platformvm/api/static_service.go b/vms/platformvm/api/static_service.go index ea1cfcb2d63f..7e97ff172112 100644 --- a/vms/platformvm/api/static_service.go +++ b/vms/platformvm/api/static_service.go @@ -50,30 +50,30 @@ type UTXO struct { } // TODO can we define this on *UTXO? -func (utxo UTXO) Less(other UTXO) bool { +func (utxo UTXO) Compare(other UTXO) int { if utxo.Locktime < other.Locktime { - return true + return -1 } else if utxo.Locktime > other.Locktime { - return false + return 1 } if utxo.Amount < other.Amount { - return true + return -1 } else if utxo.Amount > other.Amount { - return false + return 1 } utxoAddr, err := bech32ToID(utxo.Address) if err != nil { - return false + return 0 } otherAddr, err := bech32ToID(other.Address) if err != nil { - return false + return 0 } - return utxoAddr.Less(otherAddr) + return utxoAddr.Compare(otherAddr) } // TODO: Refactor APIStaker, APIValidators and merge them together for diff --git a/vms/platformvm/api/static_service_test.go b/vms/platformvm/api/static_service_test.go index 8bcbf4e766db..70140e4311d9 100644 --- a/vms/platformvm/api/static_service_test.go +++ b/vms/platformvm/api/static_service_test.go @@ -237,7 +237,7 @@ func TestBuildGenesisReturnsSortedValidators(t *testing.T) { require.Len(validators, 3) } -func TestUTXOLess(t *testing.T) { +func TestUTXOCompare(t *testing.T) { var ( smallerAddr = ids.ShortID{} largerAddr = ids.ShortID{1} @@ -251,72 +251,49 @@ func TestUTXOLess(t *testing.T) { name string utxo1 UTXO utxo2 UTXO - expected bool + expected int } tests := []test{ { name: "both empty", utxo1: UTXO{}, utxo2: UTXO{}, - expected: false, + expected: -1, }, { - name: "first locktime smaller", + name: "locktime smaller", utxo1: UTXO{}, utxo2: UTXO{ Locktime: 1, }, - expected: true, + expected: -1, }, { - name: "first locktime larger", - utxo1: UTXO{ - Locktime: 1, - }, - utxo2: UTXO{}, - expected: false, - }, - { - name: "first amount smaller", + name: "amount smaller", utxo1: UTXO{}, utxo2: UTXO{ Amount: 1, }, - expected: true, + expected: -1, }, { - name: "first amount larger", - utxo1: UTXO{ - Amount: 1, - }, - utxo2: UTXO{}, - expected: false, - }, - { - name: "first address smaller", + name: "address smaller", utxo1: UTXO{ Address: smallerAddrStr, }, utxo2: UTXO{ Address: largerAddrStr, }, - expected: true, - }, - { - name: "first address larger", - utxo1: UTXO{ - Address: largerAddrStr, - }, - utxo2: UTXO{ - Address: smallerAddrStr, - }, - expected: false, + expected: -1, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - require.Equal(t, tt.expected, tt.utxo1.Less(tt.utxo2)) + require := require.New(t) + + require.Equal(tt.expected, tt.utxo1.Compare(tt.utxo2)) + require.Equal(-tt.expected, tt.utxo2.Compare(tt.utxo1)) }) } } diff --git a/vms/platformvm/warp/signature_test.go b/vms/platformvm/warp/signature_test.go index b3eaa88bbfe8..c721eb62938d 100644 --- a/vms/platformvm/warp/signature_test.go +++ b/vms/platformvm/warp/signature_test.go @@ -39,8 +39,8 @@ type testValidator struct { vdr *Validator } -func (v *testValidator) Less(o *testValidator) bool { - return v.vdr.Less(o.vdr) +func (v *testValidator) Compare(o *testValidator) int { + return v.vdr.Compare(o.vdr) } func newTestValidator() *testValidator { diff --git a/vms/platformvm/warp/validator.go b/vms/platformvm/warp/validator.go index 42ff34e7cb5e..5e193ae1815e 100644 --- a/vms/platformvm/warp/validator.go +++ b/vms/platformvm/warp/validator.go @@ -39,8 +39,8 @@ type Validator struct { NodeIDs []ids.NodeID } -func (v *Validator) Less(o *Validator) bool { - return bytes.Compare(v.PublicKeyBytes, o.PublicKeyBytes) < 0 +func (v *Validator) Compare(o *Validator) int { + return bytes.Compare(v.PublicKeyBytes, o.PublicKeyBytes) } // GetCanonicalValidatorSet returns the validator set of [subnetID] at diff --git a/vms/proposervm/proposer/validators.go b/vms/proposervm/proposer/validators.go index ba60a088003a..89ed964d5983 100644 --- a/vms/proposervm/proposer/validators.go +++ b/vms/proposervm/proposer/validators.go @@ -15,6 +15,6 @@ type validatorData struct { weight uint64 } -func (d validatorData) Less(other validatorData) bool { - return d.id.Less(other.id) +func (d validatorData) Compare(other validatorData) int { + return d.id.Compare(other.id) } diff --git a/vms/proposervm/proposer/validators_test.go b/vms/proposervm/proposer/validators_test.go index 2f7913d01e2e..8be1f4c23d99 100644 --- a/vms/proposervm/proposer/validators_test.go +++ b/vms/proposervm/proposer/validators_test.go @@ -4,6 +4,7 @@ package proposer import ( + "fmt" "testing" "github.com/stretchr/testify/require" @@ -11,16 +12,31 @@ import ( "github.com/ava-labs/avalanchego/ids" ) -func TestValidatorDataLess(t *testing.T) { - require := require.New(t) - - var v1, v2 validatorData - require.False(v1.Less(v2)) - require.False(v2.Less(v1)) +func TestValidatorDataCompare(t *testing.T) { + tests := []struct { + a validatorData + b validatorData + expected int + }{ + { + a: validatorData{}, + b: validatorData{}, + expected: 0, + }, + { + a: validatorData{ + id: ids.BuildTestNodeID([]byte{1}), + }, + b: validatorData{}, + expected: 1, + }, + } + for _, test := range tests { + t.Run(fmt.Sprintf("%s_%s_%d", test.a.id, test.b.id, test.expected), func(t *testing.T) { + require := require.New(t) - v1 = validatorData{ - id: ids.BuildTestNodeID([]byte{1}), + require.Equal(test.expected, test.a.Compare(test.b)) + require.Equal(-test.expected, test.b.Compare(test.a)) + }) } - require.False(v1.Less(v2)) - require.True(v2.Less(v1)) } diff --git a/x/archivedb/key_test.go b/x/archivedb/key_test.go index d56dca5f37fc..18343e725bc0 100644 --- a/x/archivedb/key_test.go +++ b/x/archivedb/key_test.go @@ -21,9 +21,7 @@ func TestNaturalDescSortingForSameKey(t *testing.T) { entry := [][]byte{key0, key1, key2, key3} expected := [][]byte{key3, key2, key1, key0} - slices.SortFunc(entry, func(i, j []byte) bool { - return bytes.Compare(i, j) < 0 - }) + slices.SortFunc(entry, bytes.Compare) require.Equal(t, expected, entry) } @@ -37,9 +35,7 @@ func TestSortingDifferentPrefix(t *testing.T) { entry := [][]byte{key0, key1, key2, key3} expected := [][]byte{key1, key0, key3, key2} - slices.SortFunc(entry, func(i, j []byte) bool { - return bytes.Compare(i, j) < 0 - }) + slices.SortFunc(entry, bytes.Compare) require.Equal(t, expected, entry) } diff --git a/x/merkledb/history.go b/x/merkledb/history.go index c52385445cd2..fae7e59718df 100644 --- a/x/merkledb/history.go +++ b/x/merkledb/history.go @@ -180,8 +180,8 @@ func (th *trieHistory) getValueChanges( // Add the changes from this commit to [combinedChanges]. for key, valueChange := range changes.values { // The key is outside the range [start, end]. - if (startKey.HasValue() && key.Less(startKey.Value())) || - (end.HasValue() && key.Greater(endKey.Value())) { + if (startKey.HasValue() && key.Compare(startKey.Value()) == -1) || + (end.HasValue() && key.Compare(endKey.Value()) == 1) { continue } @@ -256,8 +256,8 @@ func (th *trieHistory) getChangesToGetToRoot(rootID ids.ID, start maybe.Maybe[[] } for key, valueChange := range changes.values { - if (startKey.IsNothing() || !key.Less(startKey.Value())) && - (endKey.IsNothing() || !key.Greater(endKey.Value())) { + if (startKey.IsNothing() || key.Compare(startKey.Value()) >= 0) && + (endKey.IsNothing() || key.Compare(endKey.Value()) <= 0) { if existing, ok := combinedChanges.values[key]; ok { existing.after = valueChange.before } else { diff --git a/x/merkledb/key.go b/x/merkledb/key.go index d65d9b74a0a6..4dad698f9280 100644 --- a/x/merkledb/key.go +++ b/x/merkledb/key.go @@ -162,14 +162,19 @@ func (k Key) Length() int { return k.length } -// Greater returns true if current Key is greater than other Key -func (k Key) Greater(other Key) bool { - return k.value > other.value || (k.value == other.value && k.length > other.length) -} - -// Less will return true if current Key is less than other Key -func (k Key) Less(other Key) bool { - return k.value < other.value || (k.value == other.value && k.length < other.length) +func (k Key) Compare(other Key) int { + switch { + case k.value < other.value: + return -1 + case k.value > other.value: + return 1 + case k.length < other.length: + return -1 + case k.length > other.length: + return 1 + default: + return 0 + } } // Extend returns a new Key that is the in-order aggregation of Key [k] with [keys] diff --git a/x/merkledb/proof.go b/x/merkledb/proof.go index 39ceff3d3157..e9d0758d2474 100644 --- a/x/merkledb/proof.go +++ b/x/merkledb/proof.go @@ -467,7 +467,7 @@ func verifyAllRangeProofKeyValuesPresent(proof []ProofNode, start maybe.Maybe[Ke ) // Skip keys that cannot have a value (enforced by [verifyProofPath]). - if !nodeKey.hasPartialByte() && (start.IsNothing() || !nodeKey.Less(start.Value())) && (end.IsNothing() || !nodeKey.Greater(end.Value())) { + if !nodeKey.hasPartialByte() && (start.IsNothing() || nodeKey.Compare(start.Value()) >= 0) && (end.IsNothing() || nodeKey.Compare(end.Value()) <= 0) { value, ok := keysValues[nodeKey] if !ok && node.ValueOrHash.HasValue() { // We didn't get a key-value pair for this key, but the proof node has a value. @@ -640,7 +640,7 @@ func verifyAllChangeProofKeyValuesPresent( // Check the value of any node with a key that is within the range. // Skip keys that cannot have a value (enforced by [verifyProofPath]). - if !nodeKey.hasPartialByte() && (start.IsNothing() || !nodeKey.Less(start.Value())) && (end.IsNothing() || !nodeKey.Greater(end.Value())) { + if !nodeKey.hasPartialByte() && (start.IsNothing() || nodeKey.Compare(start.Value()) >= 0) && (end.IsNothing() || nodeKey.Compare(end.Value()) <= 0) { value, ok := keysValues[nodeKey] if !ok { // This value isn't in the list of key-value pairs we got. @@ -841,8 +841,8 @@ func addPathInfo( compressedKey = existingChild.compressedKey } childKey := key.Extend(ToToken(index, t.tokenSize), compressedKey) - if (shouldInsertLeftChildren && childKey.Less(insertChildrenLessThan.Value())) || - (shouldInsertRightChildren && childKey.Greater(insertChildrenGreaterThan.Value())) { + if (shouldInsertLeftChildren && childKey.Compare(insertChildrenLessThan.Value()) == -1) || + (shouldInsertRightChildren && childKey.Compare(insertChildrenGreaterThan.Value()) == 1) { // We didn't set the other values on the child entry, but it doesn't matter. // We only need the IDs to be correct so that the calculated hash is correct. n.setChildEntry( diff --git a/x/merkledb/view_iterator.go b/x/merkledb/view_iterator.go index fac213bf350b..b8097d5d1db0 100644 --- a/x/merkledb/view_iterator.go +++ b/x/merkledb/view_iterator.go @@ -31,7 +31,7 @@ func (t *trieView) NewIteratorWithStartAndPrefix(start, prefix []byte) database. ) for key, change := range t.changes.values { - if len(start) > 0 && startKey.Greater(key) || !key.HasPrefix(prefixKey) { + if len(start) > 0 && startKey.Compare(key) == 1 || !key.HasPrefix(prefixKey) { continue } changes = append(changes, KeyChange{ @@ -41,8 +41,8 @@ func (t *trieView) NewIteratorWithStartAndPrefix(start, prefix []byte) database. } // sort [changes] so they can be merged with the parent trie's state - slices.SortFunc(changes, func(a, b KeyChange) bool { - return bytes.Compare(a.Key, b.Key) == -1 + slices.SortFunc(changes, func(a, b KeyChange) int { + return bytes.Compare(a.Key, b.Key) }) return &viewIterator{ diff --git a/x/sync/sync_test.go b/x/sync/sync_test.go index af908c9d941c..71871e95db56 100644 --- a/x/sync/sync_test.go +++ b/x/sync/sync_test.go @@ -696,11 +696,11 @@ func TestFindNextKeyRandom(t *testing.T) { } // Sort in ascending order by key prefix. - serializedPathLess := func(i, j keyAndID) bool { - return i.key.Less(j.key) + serializedPathCompare := func(i, j keyAndID) int { + return i.key.Compare(j.key) } - slices.SortFunc(remoteKeyIDs, serializedPathLess) - slices.SortFunc(localKeyIDs, serializedPathLess) + slices.SortFunc(remoteKeyIDs, serializedPathCompare) + slices.SortFunc(localKeyIDs, serializedPathCompare) // Filter out keys that are before the last received key findBounds := func(keyIDs []keyAndID) (int, int) { @@ -738,7 +738,7 @@ func TestFindNextKeyRandom(t *testing.T) { for i := 0; i < len(remoteKeyIDs) && i < len(localKeyIDs); i++ { // See if the keys are different. smaller, bigger := remoteKeyIDs[i], localKeyIDs[i] - if serializedPathLess(localKeyIDs[i], remoteKeyIDs[i]) { + if serializedPathCompare(localKeyIDs[i], remoteKeyIDs[i]) == -1 { smaller, bigger = localKeyIDs[i], remoteKeyIDs[i] } @@ -1194,8 +1194,6 @@ func generateTrieWithMinKeyLen(t *testing.T, r *rand.Rand, count int, minKeyLen } i++ } - slices.SortFunc(allKeys, func(a, b []byte) bool { - return bytes.Compare(a, b) < 0 - }) + slices.SortFunc(allKeys, bytes.Compare) return db, allKeys, batch.Write() } diff --git a/x/sync/workheap_test.go b/x/sync/workheap_test.go index 0a3262a9310f..826011dbdebd 100644 --- a/x/sync/workheap_test.go +++ b/x/sync/workheap_test.go @@ -4,6 +4,7 @@ package sync import ( + "bytes" "math/rand" "testing" "time" @@ -13,7 +14,6 @@ import ( "golang.org/x/exp/slices" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/avalanchego/utils" "github.com/ava-labs/avalanchego/utils/maybe" ) @@ -199,7 +199,7 @@ func TestWorkHeapMergeInsertRandom(t *testing.T) { _, _ = rand.Read(bound) bounds = append(bounds, bound) } - utils.SortBytes(bounds) + slices.SortFunc(bounds, bytes.Compare) // Note that start < end for all ranges. // It is possible but extremely unlikely that