Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(auth): use collections for GlobalAccountNumber #15830

Merged
merged 14 commits into from
Apr 17, 2023
25 changes: 18 additions & 7 deletions collections/colltest/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,30 @@ import (
db "github.com/cosmos/cosmos-db"
)

type contextStoreKey struct{}

// MockStore returns a mock store.KVStoreService and a mock context.Context.
// They can be used to test collections.
func MockStore() (store.KVStoreService, context.Context) {
// They can be used to test collections. The StoreService.NewStoreContext
// can be used to instantiate a new empty KVStore.
func MockStore() (*StoreService, context.Context) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

changes here allow the MockStore to properly match the design of the KVStoreService.

kv := db.NewMemDB()
return &testStore{kv}, context.Background()
ctx := context.WithValue(context.Background(), contextStoreKey{}, &testStore{kv})
return &StoreService{}, ctx
}

type testStore struct {
db db.DB
type StoreService struct{}

func (s StoreService) OpenKVStore(ctx context.Context) store.KVStore {
return ctx.Value(contextStoreKey{}).(store.KVStore)
}

func (t testStore) OpenKVStore(ctx context.Context) store.KVStore {
return t
func (s StoreService) NewStoreContext() context.Context {
kv := db.NewMemDB()
return context.WithValue(context.Background(), contextStoreKey{}, &testStore{kv})
}

type testStore struct {
db db.DB
}

func (t testStore) Get(key []byte) ([]byte, error) {
Expand Down
2 changes: 1 addition & 1 deletion collections/sequence.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import (
)

// DefaultSequenceStart defines the default starting number of a sequence.
const DefaultSequenceStart uint64 = 1
const DefaultSequenceStart uint64 = 0
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

modified this to make it match the account number semantics, which start from zero.


// Sequence builds on top of an Item, and represents a monotonically increasing number.
type Sequence Item[uint64]
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ module github.com/cosmos/cosmos-sdk

require (
cosmossdk.io/api v0.4.0
cosmossdk.io/collections v0.0.0-20230411101845-3d1a0b8840e4
cosmossdk.io/collections v0.0.0-20230413101615-b81cd0ebb86e
cosmossdk.io/core v0.6.1
cosmossdk.io/depinject v1.0.0-alpha.3
cosmossdk.io/errors v1.0.0-beta.7
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
cosmossdk.io/api v0.4.0 h1:x90DmdidP6EhzktAa/6/IofSHidDnPjahdlrUvyQZQw=
cosmossdk.io/api v0.4.0/go.mod h1:TWDzBhUBhI1LhSf2XSYpfIBf6D4mbLu/fvzvDfhcaYM=
cosmossdk.io/collections v0.0.0-20230411101845-3d1a0b8840e4 h1:QQZ0Qz8Gy/EmUNMRiHkUPG3BMA6OqEBp67IsfKETXIU=
cosmossdk.io/collections v0.0.0-20230411101845-3d1a0b8840e4/go.mod h1:/vS4ugR7ad3IciUd5TQuP2Ldz3NukHK2u/l5xTxXbbE=
cosmossdk.io/collections v0.0.0-20230413101615-b81cd0ebb86e h1:3gV/RSkCybtvU7unzH/Tt7yI7W8O6q22Lg7FSwSP1K8=
cosmossdk.io/collections v0.0.0-20230413101615-b81cd0ebb86e/go.mod h1:/vS4ugR7ad3IciUd5TQuP2Ldz3NukHK2u/l5xTxXbbE=
cosmossdk.io/core v0.6.1 h1:OBy7TI2W+/gyn2z40vVvruK3di+cAluinA6cybFbE7s=
cosmossdk.io/core v0.6.1/go.mod h1:g3MMBCBXtxbDWBURDVnJE7XML4BG5qENhs0gzkcpuFA=
cosmossdk.io/depinject v1.0.0-alpha.3 h1:6evFIgj//Y3w09bqOUOzEpFj5tsxBqdc5CfkO7z+zfw=
Expand Down
2 changes: 1 addition & 1 deletion simapp/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ require (
cloud.google.com/go/compute/metadata v0.2.3 // indirect
cloud.google.com/go/iam v0.13.0 // indirect
cloud.google.com/go/storage v1.30.0 // indirect
cosmossdk.io/collections v0.0.0-20230411101845-3d1a0b8840e4 // indirect
cosmossdk.io/collections v0.0.0-20230413101615-b81cd0ebb86e // indirect
cosmossdk.io/errors v1.0.0-beta.7 // indirect
cosmossdk.io/x/tx v0.5.1-0.20230407182919-057d2e09bd63 // indirect
filippo.io/edwards25519 v1.0.0 // indirect
Expand Down
4 changes: 2 additions & 2 deletions simapp/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -190,8 +190,8 @@ cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1V
cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M=
cosmossdk.io/client/v2 v2.0.0-20230309163709-87da587416ba h1:LuPHCncU2KLMNPItFECs709uo46I9wSu2fAWYVCx+/U=
cosmossdk.io/client/v2 v2.0.0-20230309163709-87da587416ba/go.mod h1:SXdwqO7cN5htalh/lhXWP8V4zKtBrhhcSTU+ytuEtmM=
cosmossdk.io/collections v0.0.0-20230411101845-3d1a0b8840e4 h1:QQZ0Qz8Gy/EmUNMRiHkUPG3BMA6OqEBp67IsfKETXIU=
cosmossdk.io/collections v0.0.0-20230411101845-3d1a0b8840e4/go.mod h1:/vS4ugR7ad3IciUd5TQuP2Ldz3NukHK2u/l5xTxXbbE=
cosmossdk.io/collections v0.0.0-20230413101615-b81cd0ebb86e h1:3gV/RSkCybtvU7unzH/Tt7yI7W8O6q22Lg7FSwSP1K8=
cosmossdk.io/collections v0.0.0-20230413101615-b81cd0ebb86e/go.mod h1:/vS4ugR7ad3IciUd5TQuP2Ldz3NukHK2u/l5xTxXbbE=
cosmossdk.io/core v0.6.1 h1:OBy7TI2W+/gyn2z40vVvruK3di+cAluinA6cybFbE7s=
cosmossdk.io/core v0.6.1/go.mod h1:g3MMBCBXtxbDWBURDVnJE7XML4BG5qENhs0gzkcpuFA=
cosmossdk.io/depinject v1.0.0-alpha.3 h1:6evFIgj//Y3w09bqOUOzEpFj5tsxBqdc5CfkO7z+zfw=
Expand Down
2 changes: 1 addition & 1 deletion tests/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ require (
cloud.google.com/go/iam v0.13.0 // indirect
cloud.google.com/go/storage v1.30.0 // indirect
cosmossdk.io/client/v2 v2.0.0-20230309163709-87da587416ba // indirect
cosmossdk.io/collections v0.0.0-20230411101845-3d1a0b8840e4 // indirect
cosmossdk.io/collections v0.0.0-20230413101615-b81cd0ebb86e // indirect
cosmossdk.io/core v0.6.1 // indirect
filippo.io/edwards25519 v1.0.0 // indirect
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect
Expand Down
4 changes: 2 additions & 2 deletions tests/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -190,8 +190,8 @@ cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1V
cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M=
cosmossdk.io/client/v2 v2.0.0-20230309163709-87da587416ba h1:LuPHCncU2KLMNPItFECs709uo46I9wSu2fAWYVCx+/U=
cosmossdk.io/client/v2 v2.0.0-20230309163709-87da587416ba/go.mod h1:SXdwqO7cN5htalh/lhXWP8V4zKtBrhhcSTU+ytuEtmM=
cosmossdk.io/collections v0.0.0-20230411101845-3d1a0b8840e4 h1:QQZ0Qz8Gy/EmUNMRiHkUPG3BMA6OqEBp67IsfKETXIU=
cosmossdk.io/collections v0.0.0-20230411101845-3d1a0b8840e4/go.mod h1:/vS4ugR7ad3IciUd5TQuP2Ldz3NukHK2u/l5xTxXbbE=
cosmossdk.io/collections v0.0.0-20230413101615-b81cd0ebb86e h1:3gV/RSkCybtvU7unzH/Tt7yI7W8O6q22Lg7FSwSP1K8=
cosmossdk.io/collections v0.0.0-20230413101615-b81cd0ebb86e/go.mod h1:/vS4ugR7ad3IciUd5TQuP2Ldz3NukHK2u/l5xTxXbbE=
cosmossdk.io/core v0.6.1 h1:OBy7TI2W+/gyn2z40vVvruK3di+cAluinA6cybFbE7s=
cosmossdk.io/core v0.6.1/go.mod h1:g3MMBCBXtxbDWBURDVnJE7XML4BG5qENhs0gzkcpuFA=
cosmossdk.io/depinject v1.0.0-alpha.3 h1:6evFIgj//Y3w09bqOUOzEpFj5tsxBqdc5CfkO7z+zfw=
Expand Down
47 changes: 13 additions & 34 deletions x/auth/keeper/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,10 @@ import (

"cosmossdk.io/collections"

"cosmossdk.io/log"
gogotypes "github.com/cosmos/gogoproto/types"

"cosmossdk.io/core/address"
"cosmossdk.io/core/store"
errorsmod "cosmossdk.io/errors"
"cosmossdk.io/log"

"github.com/cosmos/cosmos-sdk/codec"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
Expand Down Expand Up @@ -74,7 +72,8 @@ type AccountKeeper struct {

// State

ParamsState collections.Item[types.Params] // NOTE: name is this because it conflicts with the Params gRPC method impl
ParamsState collections.Item[types.Params] // NOTE: name is this because it conflicts with the Params gRPC method impl
AccountNumber collections.Sequence
}

var _ AccountKeeperI = &AccountKeeper{}
Expand All @@ -99,13 +98,14 @@ func NewAccountKeeper(
sb := collections.NewSchemaBuilder(storeService)

return AccountKeeper{
storeService: storeService,
proto: proto,
cdc: cdc,
permAddrs: permAddrs,
addressCdc: bech32Codec,
authority: authority,
ParamsState: collections.NewItem(sb, types.ParamsKey, "params", codec.CollValue[types.Params](cdc)),
storeService: storeService,
proto: proto,
cdc: cdc,
permAddrs: permAddrs,
addressCdc: bech32Codec,
authority: authority,
ParamsState: collections.NewItem(sb, types.ParamsKey, "params", codec.CollValue[types.Params](cdc)),
AccountNumber: collections.NewSequence(sb, types.GlobalAccountNumberKey, "account_number"),
}
}

Expand Down Expand Up @@ -148,32 +148,11 @@ func (ak AccountKeeper) GetSequence(ctx context.Context, addr sdk.AccAddress) (u
// NextAccountNumber returns and increments the global account number counter.
// If the global account number is not set, it initializes it with value 0.
func (ak AccountKeeper) NextAccountNumber(ctx context.Context) uint64 {
var accNumber uint64
store := ak.storeService.OpenKVStore(ctx)

bz, err := store.Get(types.GlobalAccountNumberKey)
n, err := ak.AccountNumber.Next(ctx)
if err != nil {
// panics only on nil key, which should not be possible
panic(err)
}
if bz == nil {
// initialize the account numbers
accNumber = 0
} else {
val := gogotypes.UInt64Value{}

err := ak.cdc.Unmarshal(bz, &val)
if err != nil {
panic(err)
}

accNumber = val.GetValue()
}

bz = ak.cdc.MustMarshal(&gogotypes.UInt64Value{Value: accNumber + 1})
store.Set(types.GlobalAccountNumberKey, bz)

return accNumber
return n
}

// GetModulePermissions fetches per-module account permissions.
Expand Down
8 changes: 8 additions & 0 deletions x/auth/keeper/migrations.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package keeper

import (
v6 "github.com/cosmos/cosmos-sdk/x/auth/migrations/v6"
"github.com/cosmos/gogoproto/grpc"

sdk "github.com/cosmos/cosmos-sdk/types"
Expand Down Expand Up @@ -59,6 +60,13 @@ func (m Migrator) Migrate3to4(ctx sdk.Context) error {
return v4.Migrate(ctx, m.keeper.storeService, m.legacySubspace, m.keeper.cdc)
}

// Migrate5to6 migrates the x/auth module state from the consensus version 5 to 6.
// It migrates the GlobalAccountNumber from being a protobuf defined value to a
// big-endian encoded uint64, it also migrates it to use a more canonical prefix.
func (m Migrator) Migrate5to6(ctx sdk.Context) error {
return v6.Migrate(ctx, m.keeper.storeService, m.keeper.AccountNumber)
}

// V45_SetAccount implements V45_SetAccount
// set the account without map to accAddr to accNumber.
//
Expand Down
45 changes: 45 additions & 0 deletions x/auth/migrations/v6/migrate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package v6
julienrbrt marked this conversation as resolved.
Show resolved Hide resolved

import (
"context"

"cosmossdk.io/collections"
storetypes "cosmossdk.io/core/store"
"github.com/cosmos/gogoproto/types"
)

var LegacyGlobalAccountNumberKey = []byte("globalAccountNumber")

func Migrate(ctx context.Context, storeService storetypes.KVStoreService, sequence collections.Sequence) error {
store := storeService.OpenKVStore(ctx)
b, err := store.Get(LegacyGlobalAccountNumberKey)
if err != nil {
return err
}
if b == nil {
// this would mean no account was ever created in this chain which is being migrated?
// we're doing nothing as the collections.Sequence already handles the non-existing value.
return nil
}

// get old value
v := new(types.UInt64Value)
err = v.Unmarshal(b)
if err != nil {
return err
}

// set the old value in the collection
err = sequence.Set(ctx, v.Value)
if err != nil {
return err
}

// remove the value from the old prefix.
err = store.Delete(LegacyGlobalAccountNumberKey)
if err != nil {
return err
}

return nil
}
44 changes: 44 additions & 0 deletions x/auth/migrations/v6/migrate_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package v6

import (
"testing"

"cosmossdk.io/collections"
"cosmossdk.io/collections/colltest"
"github.com/cosmos/gogoproto/types"
"github.com/stretchr/testify/require"
)

func TestMigrate(t *testing.T) {
kv, ctx := colltest.MockStore()
sb := collections.NewSchemaBuilder(kv)
seq := collections.NewSequence(sb, collections.NewPrefix(0), "seq")

wantValue := uint64(100)

// set old sequence to wanted value
legacySeqBytes, err := (&types.UInt64Value{Value: wantValue}).Marshal()
require.NoError(t, err)

err = kv.OpenKVStore(ctx).Set(LegacyGlobalAccountNumberKey, legacySeqBytes)
require.NoError(t, err)

err = Migrate(ctx, kv, seq)
require.NoError(t, err)

// check that after migration the sequence is what we want it to be
gotValue, err := seq.Peek(ctx)
require.NoError(t, err)
require.Equal(t, wantValue, gotValue)

// case the global account number was not set
ctx = kv.NewStoreContext() // this resets the store to zero
wantValue = collections.DefaultSequenceStart

err = Migrate(ctx, kv, seq)
require.NoError(t, err)

gotValue, err = seq.Next(ctx)
require.NoError(t, err)
require.Equal(t, wantValue, gotValue)
}
6 changes: 5 additions & 1 deletion x/auth/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import (
)

// ConsensusVersion defines the current x/auth module consensus version.
const ConsensusVersion = 4
const ConsensusVersion = 5

var (
_ module.AppModule = AppModule{}
Expand Down Expand Up @@ -151,6 +151,10 @@ func (am AppModule) RegisterServices(cfg module.Configurator) {
if err := cfg.RegisterMigration(types.ModuleName, 4, func(ctx sdk.Context) error { return nil }); err != nil {
julienrbrt marked this conversation as resolved.
Show resolved Hide resolved
panic(fmt.Sprintf("failed to migrate x/%s from version 4 to 5: %v", types.ModuleName, err))
}

if err := cfg.RegisterMigration(types.ModuleName, 5, m.Migrate5to6); err != nil {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is actually migration 4 to 5, as migration 4 was for v0.47. You can as well delete the placeholder for migration to 5, which wasn't necessary (#14483 (comment))

panic(fmt.Sprintf("failed to migrate x/%s from version 5 to 6", types.ModuleName))
}
}

// InitGenesis performs genesis initialization for the auth module. It returns
Expand Down
5 changes: 3 additions & 2 deletions x/auth/types/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ var (
// AddressStoreKeyPrefix prefix for account-by-address store
AddressStoreKeyPrefix = []byte{0x01}

// param key for global account number
GlobalAccountNumberKey = []byte("globalAccountNumber")
// GlobalAccountNumberKey identifies the prefix where the monotonically increasing
// account number is stored.
GlobalAccountNumberKey = collections.NewPrefix(2)

// AccountNumberStoreKeyPrefix prefix for account-by-id store
AccountNumberStoreKeyPrefix = []byte("accountNumber")
Expand Down