Skip to content

Commit c1b567f

Browse files
amaury1093robert-zarembafdymyljaaaroncmergify[bot]
authored
Refactor store keys for variable-length addresses (#8363)
* Change account store key in x/bank * Fix pagination test * Fix merge master * Fix staking keys.go * Use bech32 in val state change map * Fix sortNoLongerBonded * Use length-prefix function * Use length prefix function * Fix test accountStore * Fix ExamplePaginate * Fix staking keys * Use shorter balances prefix * Do slashing keys * Fix gov keys * Fix x/gov tests * Fix x/distrib * Address reviews * add change log entry * Add changelog * Fix failing tests * Fix sim tests * fix after-export sim * Fix lint * Address review * Fix x/authz * Fix global config in test * Update x/staking/keeper/val_state_change.go Co-authored-by: Robert Zaremba <[email protected]> * Address comments * Fix comments * Address review * Fix authz test * Update comment * Rename to LengthPrefixedAddressStoreKey * Use variable * Rename function * Fix test build * chore: update rosetta CI (#8453) * Rename again * Rename yet again * Update feegrant keys * Add function to create prefix Co-authored-by: Robert Zaremba <[email protected]> Co-authored-by: Frojdi Dymylja <[email protected]> Co-authored-by: Aaron Craelius <[email protected]> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
1 parent 17d7e9a commit c1b567f

File tree

28 files changed

+411
-271
lines changed

28 files changed

+411
-271
lines changed

CHANGELOG.md

+5
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,13 @@ Ref: https://keepachangelog.com/en/1.0.0/
3636

3737
## [Unreleased]
3838

39+
### Client Breaking Changes
40+
41+
* [\#8363](https://github.com/cosmos/cosmos-sdk/issues/8363) Addresses no longer have a fixed 20-byte length. From the SDK modules' point of view, any 1-255 bytes-long byte array is a valid address.
42+
3943
### State Machine Breaking
4044

45+
* (x/{bank,distrib,gov,slashing,staking}) [\#8363](https://github.com/cosmos/cosmos-sdk/issues/8363) Store keys have been modified to allow for variable-length addresses.
4146
* (x/ibc) [\#8266](https://github.com/cosmos/cosmos-sdk/issues/8266) Add amino JSON for IBC messages in order to support Ledger text signing.
4247

4348
### Improvements
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
[
22
{
33
"account_identifier": {
4-
"address":"cosmos1hdmjfmqmf8ck4pv4evu0s3up0ucm0yjjqfl87e"
4+
"address":"cosmos158nkd0l9tyemv2crp579rmj8dg37qty8lzff88"
55
},
66
"currency":{
77
"symbol":"stake",
88
"decimals":0
99
},
10-
"value": "999900000000"
10+
"value": "999990000000"
1111
}
1212
]

contrib/rosetta/configuration/data.sh

+3-2
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,13 @@ simd init simd --chain-id testing
1616
simd keys add fd --keyring-backend=test
1717

1818
addr=$(simd keys show fd -a --keyring-backend=test)
19+
val_addr=$(simd keys show fd --keyring-backend=test --bech val -a)
1920

2021
# give the accounts some money
2122
simd add-genesis-account "$addr" 1000000000000stake --keyring-backend=test
2223

2324
# save configs for the daemon
24-
simd gentx fd --chain-id testing --keyring-backend=test
25+
simd gentx fd 10000000stake --chain-id testing --keyring-backend=test
2526

2627
# input genTx to the genesis file
2728
simd collect-gentxs
@@ -55,4 +56,4 @@ echo zipping data dir and saving to /tmp/data.tar.gz
5556

5657
tar -czvf /tmp/data.tar.gz /root/.simapp
5758

58-
echo new address for bootstrap.json "$addr"
59+
echo new address for bootstrap.json "$addr" "$val_addr"

contrib/rosetta/configuration/staking.ros

+2-2
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ staking(1){
9494
"account": {
9595
"address": "staking_account",
9696
"sub_account": {
97-
"address" : "cosmosvaloper1hdmjfmqmf8ck4pv4evu0s3up0ucm0yjj9atjj2"
97+
"address" : "cosmosvaloper158nkd0l9tyemv2crp579rmj8dg37qty86kaut5"
9898
}
9999
},
100100
"amount":{
@@ -134,7 +134,7 @@ staking(1){
134134
"account": {
135135
"address": "staking_account",
136136
"sub_account": {
137-
"address" : "cosmosvaloper1hdmjfmqmf8ck4pv4evu0s3up0ucm0yjj9atjj2"
137+
"address" : "cosmosvaloper158nkd0l9tyemv2crp579rmj8dg37qty86kaut5"
138138
}
139139
},
140140
"amount":{

contrib/rosetta/node/data.tar.gz

1.89 KB
Binary file not shown.

simapp/export.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ func (app *SimApp) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddrs []
157157
counter := int16(0)
158158

159159
for ; iter.Valid(); iter.Next() {
160-
addr := sdk.ValAddress(iter.Key()[1:])
160+
addr := sdk.ValAddress(stakingtypes.AddressFromValidatorsKey(iter.Key()))
161161
validator, found := app.StakingKeeper.GetValidator(ctx, addr)
162162
if !found {
163163
panic("expected validator, not found")

types/address.go

+10-4
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ import (
1212

1313
"github.com/cosmos/cosmos-sdk/codec/legacy"
1414
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
15+
"github.com/cosmos/cosmos-sdk/types/address"
1516
"github.com/cosmos/cosmos-sdk/types/bech32"
17+
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
1618
)
1719

1820
const (
@@ -28,8 +30,6 @@ const (
2830
// config.SetFullFundraiserPath(yourFullFundraiserPath)
2931
// config.Seal()
3032

31-
// AddrLen defines a valid address length
32-
AddrLen = 20
3333
// Bech32MainPrefix defines the main SDK Bech32 prefix of an account's address
3434
Bech32MainPrefix = "cosmos"
3535

@@ -110,9 +110,15 @@ func VerifyAddressFormat(bz []byte) error {
110110
if verifier != nil {
111111
return verifier(bz)
112112
}
113-
if len(bz) != AddrLen {
114-
return fmt.Errorf("incorrect address length (expected: %d, actual: %d)", AddrLen, len(bz))
113+
114+
if len(bz) == 0 {
115+
return sdkerrors.Wrap(sdkerrors.ErrUnknownAddress, "addresses cannot be empty")
116+
}
117+
118+
if len(bz) > address.MaxAddrLen {
119+
return sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "address max length is %d, got %d", address.MaxAddrLen, len(bz))
115120
}
121+
116122
return nil
117123
}
118124

types/address/store_key.go

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package address
2+
3+
import (
4+
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
5+
)
6+
7+
// MaxAddrLen is the maximum allowed length (in bytes) for an address.
8+
const MaxAddrLen = 255
9+
10+
// LengthPrefix prefixes the address bytes with its length, this is used
11+
// for example for variable-length components in store keys.
12+
func LengthPrefix(bz []byte) ([]byte, error) {
13+
bzLen := len(bz)
14+
if bzLen == 0 {
15+
return bz, nil
16+
}
17+
18+
if bzLen > MaxAddrLen {
19+
return nil, sdkerrors.Wrapf(sdkerrors.ErrUnknownAddress, "address length should be max %d bytes, got %d", MaxAddrLen, bzLen)
20+
}
21+
22+
return append([]byte{byte(bzLen)}, bz...), nil
23+
}
24+
25+
// MustLengthPrefix is LengthPrefix with panic on error.
26+
func MustLengthPrefix(bz []byte) []byte {
27+
res, err := LengthPrefix(bz)
28+
if err != nil {
29+
panic(err)
30+
}
31+
32+
return res
33+
}

types/address/store_key_test.go

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package address_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/require"
7+
8+
"github.com/cosmos/cosmos-sdk/types/address"
9+
)
10+
11+
func TestLengthPrefixedAddressStoreKey(t *testing.T) {
12+
addr10byte := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
13+
addr20byte := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}
14+
addr256byte := make([]byte, 256)
15+
16+
tests := []struct {
17+
name string
18+
addr []byte
19+
expStoreKey []byte
20+
expErr bool
21+
}{
22+
{"10-byte address", addr10byte, append([]byte{byte(10)}, addr10byte...), false},
23+
{"20-byte address", addr20byte, append([]byte{byte(20)}, addr20byte...), false},
24+
{"256-byte address (too long)", addr256byte, nil, true},
25+
}
26+
for _, tt := range tests {
27+
tt := tt
28+
t.Run(tt.name, func(t *testing.T) {
29+
storeKey, err := address.LengthPrefix(tt.addr)
30+
if tt.expErr {
31+
require.Error(t, err)
32+
} else {
33+
require.NoError(t, err)
34+
require.Equal(t, tt.expStoreKey, storeKey)
35+
}
36+
})
37+
}
38+
}

types/address_test.go

+24-16
Original file line numberDiff line numberDiff line change
@@ -347,15 +347,18 @@ func (s *addressTestSuite) TestVerifyAddressFormat() {
347347
addr5 := make([]byte, 5)
348348
addr20 := make([]byte, 20)
349349
addr32 := make([]byte, 32)
350+
addr256 := make([]byte, 256)
350351

351352
err := types.VerifyAddressFormat(addr0)
352-
s.Require().EqualError(err, "incorrect address length 0")
353+
s.Require().EqualError(err, "addresses cannot be empty: unknown address")
353354
err = types.VerifyAddressFormat(addr5)
354-
s.Require().EqualError(err, "incorrect address length 5")
355+
s.Require().NoError(err)
355356
err = types.VerifyAddressFormat(addr20)
356-
s.Require().Nil(err)
357+
s.Require().NoError(err)
357358
err = types.VerifyAddressFormat(addr32)
358-
s.Require().EqualError(err, "incorrect address length 32")
359+
s.Require().NoError(err)
360+
err = types.VerifyAddressFormat(addr256)
361+
s.Require().EqualError(err, "address max length is 255, got 256: unknown address")
359362
}
360363

361364
func (s *addressTestSuite) TestCustomAddressVerifier() {
@@ -364,34 +367,39 @@ func (s *addressTestSuite) TestCustomAddressVerifier() {
364367
accBech := types.AccAddress(addr).String()
365368
valBech := types.ValAddress(addr).String()
366369
consBech := types.ConsAddress(addr).String()
367-
// Verifiy that the default logic rejects this 10 byte address
370+
// Verify that the default logic doesn't reject this 10 byte address
371+
// The default verifier is nil, we're only checking address length is
372+
// between 1-255 bytes.
368373
err := types.VerifyAddressFormat(addr)
369-
s.Require().NotNil(err)
374+
s.Require().Nil(err)
370375
_, err = types.AccAddressFromBech32(accBech)
371-
s.Require().NotNil(err)
376+
s.Require().Nil(err)
372377
_, err = types.ValAddressFromBech32(valBech)
373-
s.Require().NotNil(err)
378+
s.Require().Nil(err)
374379
_, err = types.ConsAddressFromBech32(consBech)
375-
s.Require().NotNil(err)
380+
s.Require().Nil(err)
376381

377-
// Set a custom address verifier that accepts 10 or 20 byte addresses
382+
// Set a custom address verifier only accepts 20 byte addresses
378383
types.GetConfig().SetAddressVerifier(func(bz []byte) error {
379384
n := len(bz)
380-
if n == 10 || n == types.AddrLen {
385+
if n == 20 {
381386
return nil
382387
}
383388
return fmt.Errorf("incorrect address length %d", n)
384389
})
385390

386-
// Verifiy that the custom logic accepts this 10 byte address
391+
// Verifiy that the custom logic rejects this 10 byte address
387392
err = types.VerifyAddressFormat(addr)
388-
s.Require().Nil(err)
393+
s.Require().NotNil(err)
389394
_, err = types.AccAddressFromBech32(accBech)
390-
s.Require().Nil(err)
395+
s.Require().NotNil(err)
391396
_, err = types.ValAddressFromBech32(valBech)
392-
s.Require().Nil(err)
397+
s.Require().NotNil(err)
393398
_, err = types.ConsAddressFromBech32(consBech)
394-
s.Require().Nil(err)
399+
s.Require().NotNil(err)
400+
401+
// Reinitialize the global config to default address verifier (nil)
402+
types.GetConfig().SetAddressVerifier(nil)
395403
}
396404

397405
func (s *addressTestSuite) TestBech32ifyAddressBytes() {

types/query/filtered_pagination_test.go

+3-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"github.com/cosmos/cosmos-sdk/codec"
77
"github.com/cosmos/cosmos-sdk/store/prefix"
88
sdk "github.com/cosmos/cosmos-sdk/types"
9+
"github.com/cosmos/cosmos-sdk/types/address"
910
"github.com/cosmos/cosmos-sdk/types/query"
1011
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
1112
"github.com/cosmos/cosmos-sdk/x/bank/types"
@@ -111,7 +112,7 @@ func ExampleFilteredPaginate() {
111112
pageReq := &query.PageRequest{Key: nil, Limit: 1, CountTotal: true}
112113
store := ctx.KVStore(app.GetKey(authtypes.StoreKey))
113114
balancesStore := prefix.NewStore(store, types.BalancesPrefix)
114-
accountStore := prefix.NewStore(balancesStore, addr1.Bytes())
115+
accountStore := prefix.NewStore(balancesStore, address.MustLengthPrefix(addr1))
115116

116117
var balResult sdk.Coins
117118
pageRes, err := query.FilteredPaginate(accountStore, pageReq, func(key []byte, value []byte, accumulate bool) (bool, error) {
@@ -143,7 +144,7 @@ func ExampleFilteredPaginate() {
143144

144145
func execFilterPaginate(store sdk.KVStore, pageReq *query.PageRequest, appCodec codec.Marshaler) (balances sdk.Coins, res *query.PageResponse, err error) {
145146
balancesStore := prefix.NewStore(store, types.BalancesPrefix)
146-
accountStore := prefix.NewStore(balancesStore, addr1.Bytes())
147+
accountStore := prefix.NewStore(balancesStore, address.MustLengthPrefix(addr1))
147148

148149
var balResult sdk.Coins
149150
res, err = query.FilteredPaginate(accountStore, pageReq, func(key []byte, value []byte, accumulate bool) (bool, error) {

types/query/pagination_test.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"github.com/cosmos/cosmos-sdk/store"
1818
"github.com/cosmos/cosmos-sdk/store/prefix"
1919
sdk "github.com/cosmos/cosmos-sdk/types"
20+
"github.com/cosmos/cosmos-sdk/types/address"
2021
"github.com/cosmos/cosmos-sdk/types/query"
2122
authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"
2223
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
@@ -193,7 +194,7 @@ func ExamplePaginate() {
193194
balResult := sdk.NewCoins()
194195
authStore := ctx.KVStore(app.GetKey(authtypes.StoreKey))
195196
balancesStore := prefix.NewStore(authStore, types.BalancesPrefix)
196-
accountStore := prefix.NewStore(balancesStore, addr1.Bytes())
197+
accountStore := prefix.NewStore(balancesStore, address.MustLengthPrefix(addr1))
197198
pageRes, err := query.Paginate(accountStore, request.Pagination, func(key []byte, value []byte) error {
198199
var tempRes sdk.Coin
199200
err := app.AppCodec().UnmarshalBinaryBare(value, &tempRes)

x/authz/types/keys.go

+15-4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package types
22

33
import (
44
sdk "github.com/cosmos/cosmos-sdk/types"
5+
"github.com/cosmos/cosmos-sdk/types/address"
56
)
67

78
const (
@@ -21,7 +22,7 @@ const (
2122
// Keys for authz store
2223
// Items are stored with the following key: values
2324
//
24-
// - 0x01<granterAddress_Bytes><granteeAddress_Bytes><msgType_Bytes>: Grant
25+
// - 0x01<granterAddressLen (1 Byte)><granterAddress_Bytes><granteeAddressLen (1 Byte)><granteeAddress_Bytes><msgType_Bytes>: Grant
2526

2627
var (
2728
// Keys for store prefixes
@@ -30,12 +31,22 @@ var (
3031

3132
// GetAuthorizationStoreKey - return authorization store key
3233
func GetAuthorizationStoreKey(grantee sdk.AccAddress, granter sdk.AccAddress, msgType string) []byte {
33-
return append(append(append(GrantKey, granter.Bytes()...), grantee.Bytes()...), []byte(msgType)...)
34+
return append(append(append(
35+
GrantKey,
36+
address.MustLengthPrefix(granter)...),
37+
address.MustLengthPrefix(grantee)...),
38+
[]byte(msgType)...,
39+
)
3440
}
3541

3642
// ExtractAddressesFromGrantKey - split granter & grantee address from the authorization key
3743
func ExtractAddressesFromGrantKey(key []byte) (granterAddr, granteeAddr sdk.AccAddress) {
38-
granterAddr = sdk.AccAddress(key[1 : sdk.AddrLen+1])
39-
granteeAddr = sdk.AccAddress(key[sdk.AddrLen+1 : sdk.AddrLen*2+1])
44+
// key if of format:
45+
// 0x01<granterAddressLen (1 Byte)><granterAddress_Bytes><granteeAddressLen (1 Byte)><granteeAddress_Bytes><msgType_Bytes>
46+
granterAddrLen := key[1] // remove prefix key
47+
granterAddr = sdk.AccAddress(key[2 : 2+granterAddrLen])
48+
granteeAddrLen := int(key[2+granterAddrLen])
49+
granteeAddr = sdk.AccAddress(key[3+granterAddrLen : 3+granterAddrLen+byte(granteeAddrLen)])
50+
4051
return granterAddr, granteeAddr
4152
}

x/bank/keeper/grpc_query.go

+1-3
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,7 @@ func (k BaseKeeper) AllBalances(ctx context.Context, req *types.QueryAllBalances
5757
sdkCtx := sdk.UnwrapSDKContext(ctx)
5858

5959
balances := sdk.NewCoins()
60-
store := sdkCtx.KVStore(k.storeKey)
61-
balancesStore := prefix.NewStore(store, types.BalancesPrefix)
62-
accountStore := prefix.NewStore(balancesStore, addr.Bytes())
60+
accountStore := k.getAccountStore(sdkCtx, addr)
6361

6462
pageRes, err := query.Paginate(accountStore, req.Pagination, func(_, value []byte) error {
6563
var result sdk.Coin

x/bank/keeper/send.go

+2-7
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package keeper
22

33
import (
44
"github.com/cosmos/cosmos-sdk/codec"
5-
"github.com/cosmos/cosmos-sdk/store/prefix"
65
"github.com/cosmos/cosmos-sdk/telemetry"
76
sdk "github.com/cosmos/cosmos-sdk/types"
87
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
@@ -233,9 +232,7 @@ func (k BaseSendKeeper) ClearBalances(ctx sdk.Context, addr sdk.AccAddress) {
233232
return false
234233
})
235234

236-
store := ctx.KVStore(k.storeKey)
237-
balancesStore := prefix.NewStore(store, types.BalancesPrefix)
238-
accountStore := prefix.NewStore(balancesStore, addr.Bytes())
235+
accountStore := k.getAccountStore(ctx, addr)
239236

240237
for _, key := range keys {
241238
accountStore.Delete(key)
@@ -264,9 +261,7 @@ func (k BaseSendKeeper) SetBalance(ctx sdk.Context, addr sdk.AccAddress, balance
264261
return sdkerrors.Wrap(sdkerrors.ErrInvalidCoins, balance.String())
265262
}
266263

267-
store := ctx.KVStore(k.storeKey)
268-
balancesStore := prefix.NewStore(store, types.BalancesPrefix)
269-
accountStore := prefix.NewStore(balancesStore, addr.Bytes())
264+
accountStore := k.getAccountStore(ctx, addr)
270265

271266
bz := k.cdc.MustMarshalBinaryBare(&balance)
272267
accountStore.Set([]byte(balance.Denom), bz)

0 commit comments

Comments
 (0)