diff --git a/CHANGELOG.md b/CHANGELOG.md index 73ca8a6f30e7..eb0838cbc360 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -155,6 +155,7 @@ This patch release contains only minor dependency bumps. * (x/group) [#25917](https://github.com/cosmos/cosmos-sdk/pull/25917) Prevent creation of zero-weight groups. * (x/group) [#25919](https://github.com/cosmos/cosmos-sdk/pull/25919) add safer type assertions to group `DecisionPolicy` getter calls. * (x/group) [#25920](https://github.com/cosmos/cosmos-sdk/pull/25920) Expand voting period check to verify period is positive instead of nonzero. +* (types/address) [#25944] (https://github.com/cosmos/cosmos-sdk/pull/25944) correct sort comparator in Compose to satisfy strict weak ordering. * (baseapp) [#26063](https://github.com/cosmos/cosmos-sdk/pull/26063) Fixes an issue where values embedded in context during ante handling were wiped after the handlers returned. ### Deprecated diff --git a/types/address/hash.go b/types/address/hash.go index cb000e7f63c8..b6028bc07984 100644 --- a/types/address/hash.go +++ b/types/address/hash.go @@ -53,7 +53,7 @@ func Compose(typ string, subAddresses []Addressable) ([]byte, error) { totalLen += len(as[i]) } - sort.Slice(as, func(i, j int) bool { return bytes.Compare(as[i], as[j]) <= 0 }) + sort.Slice(as, func(i, j int) bool { return lessBytes(as[i], as[j]) }) key := make([]byte, totalLen) offset := 0 for i := range as { @@ -63,6 +63,10 @@ func Compose(typ string, subAddresses []Addressable) ([]byte, error) { return Hash(typ, key), nil } +func lessBytes(a, b []byte) bool { + return bytes.Compare(a, b) < 0 +} + // Module is a specialized version of a composed address for modules. Each module account // is constructed from a module name and a sequence of derivation keys (at least one // derivation key must be provided). The derivation keys must be unique diff --git a/types/address/hash_test.go b/types/address/hash_test.go index a52cd5d092d1..897096dfc7c5 100644 --- a/types/address/hash_test.go +++ b/types/address/hash_test.go @@ -61,6 +61,25 @@ func (suite *AddressSuite) TestComposed() { _, err = Compose(typ, []Addressable{a1, addrMock{make([]byte, 300)}}) assert.Error(err) assert.Contains(err.Error(), "should be max 255 bytes, got 300") + + // strict weak ordering: irreflexive and asymmetric + la := []byte{1, 2} + lb := []byte{3, 4} + assert.False(lessBytes(la, la), "lessBytes must be irreflexive") + assert.True(lessBytes(la, lb), "lessBytes(a,b) must be true when a < b") + assert.False(lessBytes(lb, la), "lessBytes must be asymmetric") + + // Compose must be order-independent even when sub-addresses include duplicates + da := addrMock{[]byte{1, 2, 3}} + db := addrMock{[]byte{4, 5, 6}} + aab, err := Compose("test", []Addressable{da, da, db}) + assert.NoError(err) + aba, err := Compose("test", []Addressable{da, db, da}) + assert.NoError(err) + baa, err := Compose("test", []Addressable{db, da, da}) + assert.NoError(err) + assert.Equal(aab, aba, "Compose must be order-independent with duplicates") + assert.Equal(aab, baa, "Compose must be order-independent with duplicates") } func (suite *AddressSuite) TestModule() {