Skip to content

Commit

Permalink
feat(auth): allow BaseAccounts to be migrated to x/accounts (#21820)
Browse files Browse the repository at this point in the history
Co-authored-by: Akhil Kumar P <[email protected]>
(cherry picked from commit ebbaa0e)

# Conflicts:
#	UPGRADING.md
#	api/cosmos/accounts/defaults/base/v1/base.pulsar.go
#	api/cosmos/auth/v1beta1/accounts.pulsar.go
#	api/cosmos/auth/v1beta1/tx.pulsar.go
#	api/cosmos/auth/v1beta1/tx_grpc.pb.go
  • Loading branch information
testinginprod authored and mergify[bot] committed Sep 23, 2024
1 parent 70e80ca commit 0b362ad
Show file tree
Hide file tree
Showing 18 changed files with 10,795 additions and 59 deletions.
969 changes: 969 additions & 0 deletions UPGRADING.md

Large diffs are not rendered by default.

3,686 changes: 3,686 additions & 0 deletions api/cosmos/accounts/defaults/base/v1/base.pulsar.go

Large diffs are not rendered by default.

1,095 changes: 1,095 additions & 0 deletions api/cosmos/auth/v1beta1/accounts.pulsar.go

Large diffs are not rendered by default.

3,993 changes: 3,993 additions & 0 deletions api/cosmos/auth/v1beta1/tx.pulsar.go

Large diffs are not rendered by default.

209 changes: 209 additions & 0 deletions api/cosmos/auth/v1beta1/tx_grpc.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion proto/cosmos/auth/v1beta1/accounts.proto
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ message QueryLegacyAccountResponse {
// the type wrapped by the any does not need to comply with the
// sdk.AccountI interface.
google.protobuf.Any account = 1;
// info represents the account as a BaseAccount, this can return
// base represents the account as a BaseAccount, this can return
// nil if the account cannot be represented as a BaseAccount.
// This is used in the gRPC QueryAccountInfo method.
BaseAccount base = 2;
Expand Down
22 changes: 22 additions & 0 deletions proto/cosmos/auth/v1beta1/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ service Msg {

// NonAtomicExec allows users to submit multiple messages for non-atomic execution.
rpc NonAtomicExec(MsgNonAtomicExec) returns (MsgNonAtomicExecResponse);

// MigrateAccount migrates the account to x/accounts.
rpc MigrateAccount(MsgMigrateAccount) returns (MsgMigrateAccountResponse);
}

// MsgUpdateParams is the Msg/UpdateParams request type.
Expand Down Expand Up @@ -64,3 +67,22 @@ message NonAtomicExecResult {
message MsgNonAtomicExecResponse {
repeated NonAtomicExecResult results = 1;
}

// MsgMigrateAccount defines a message which allows users to migrate from BaseAccount
// to other x/accounts types.
message MsgMigrateAccount {
option (amino.name) = "cosmos-sdk/x/auth/MsgMigrateAccount";
option (cosmos.msg.v1.signer) = "signer";

string signer = 1 [(cosmos_proto.scalar) = "cosmos.AddressString"];
string account_type = 2;
google.protobuf.Any account_init_msg = 3;
}

// MsgMigrateAccountResponse defines the response given when migrating to
// an x/accounts account.
message MsgMigrateAccountResponse {
// init_response defines the response returned by the x/account account
// initialization.
google.protobuf.Any init_response = 1;
}
100 changes: 100 additions & 0 deletions tests/integration/auth/keeper/migrate_x_accounts_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package keeper_test

import (
"testing"

"github.com/stretchr/testify/require"

basev1 "cosmossdk.io/x/accounts/defaults/base/v1"

codectypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
)

func TestMigrateToAccounts(t *testing.T) {
f := initFixture(t, nil)

// create a module account
modAcc := &authtypes.ModuleAccount{
BaseAccount: &authtypes.BaseAccount{
Address: f.mustAddr([]byte("cookies")),
PubKey: nil,
AccountNumber: 0,
Sequence: 0,
},
Name: "cookies",
Permissions: nil,
}
updatedMod := f.authKeeper.NewAccount(f.ctx, modAcc)
f.authKeeper.SetAccount(f.ctx, updatedMod)

// create account
msgSrv := authkeeper.NewMsgServerImpl(f.authKeeper)
privKey := secp256k1.GenPrivKey()
addr := sdk.AccAddress(privKey.PubKey().Address())

acc := f.authKeeper.NewAccountWithAddress(f.ctx, addr)
require.NoError(t, acc.SetPubKey(privKey.PubKey()))
f.authKeeper.SetAccount(f.ctx, acc)

t.Run("account does not exist", func(t *testing.T) {
resp, err := msgSrv.MigrateAccount(f.ctx, &authtypes.MsgMigrateAccount{
Signer: f.mustAddr([]byte("notexist")),
AccountType: "base",
AccountInitMsg: nil,
})
require.Nil(t, resp)
require.ErrorIs(t, err, sdkerrors.ErrUnknownAddress)
})

t.Run("invalid account type", func(t *testing.T) {
resp, err := msgSrv.MigrateAccount(f.ctx, &authtypes.MsgMigrateAccount{
Signer: f.mustAddr(updatedMod.GetAddress()),
AccountType: "base",
AccountInitMsg: nil,
})
require.Nil(t, resp)
require.ErrorContains(t, err, "only BaseAccount can be migrated")
})

t.Run("success", func(t *testing.T) {
pk, err := codectypes.NewAnyWithValue(privKey.PubKey())
require.NoError(t, err)

migrateMsg := &basev1.MsgInit{
PubKey: pk,
InitSequence: 100,
}

initMsgAny, err := codectypes.NewAnyWithValue(migrateMsg)
require.NoError(t, err)

resp, err := msgSrv.MigrateAccount(f.ctx, &authtypes.MsgMigrateAccount{
Signer: f.mustAddr(addr),
AccountType: "base",
AccountInitMsg: initMsgAny,
})
require.NoError(t, err)

// check response semantics.
require.Equal(t, resp.InitResponse.TypeUrl, "/cosmos.accounts.defaults.base.v1.MsgInitResponse")
require.NotNil(t, resp.InitResponse.Value)

// check the account was removed from x/auth and added to x/accounts
require.Nil(t, f.authKeeper.GetAccount(f.ctx, addr))
require.True(t, f.accountsKeeper.IsAccountsModuleAccount(f.ctx, addr))

// check the init information is correctly propagated.
seq, err := f.accountsKeeper.Query(f.ctx, addr, &basev1.QuerySequence{})
require.NoError(t, err)
require.Equal(t, migrateMsg.InitSequence, seq.(*basev1.QuerySequenceResponse).Sequence)

pkResp, err := f.accountsKeeper.Query(f.ctx, addr, &basev1.QueryPubKey{})
require.NoError(t, err)
require.Equal(t, migrateMsg.PubKey, pkResp.(*basev1.QueryPubKeyResponse).PubKey)
})
}
19 changes: 19 additions & 0 deletions x/accounts/defaults/base/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ type Account struct {
}

func (a Account) Init(ctx context.Context, msg *v1.MsgInit) (*v1.MsgInitResponse, error) {
if msg.InitSequence != 0 {
err := a.Sequence.Set(ctx, msg.InitSequence)
if err != nil {
return nil, err
}
}
return &v1.MsgInitResponse{}, a.savePubKey(ctx, msg.PubKey)
}

Expand Down Expand Up @@ -258,6 +264,18 @@ func (a Account) QuerySequence(ctx context.Context, _ *v1.QuerySequence) (*v1.Qu
return &v1.QuerySequenceResponse{Sequence: seq}, nil
}

func (a Account) QueryPubKey(ctx context.Context, _ *v1.QueryPubKey) (*v1.QueryPubKeyResponse, error) {
pubKey, err := a.loadPubKey(ctx)
if err != nil {
return nil, err
}
anyPubKey, err := codectypes.NewAnyWithValue(pubKey)
if err != nil {
return nil, err
}
return &v1.QueryPubKeyResponse{PubKey: anyPubKey}, nil
}

func (a Account) AuthRetroCompatibility(ctx context.Context, _ *authtypes.QueryLegacyAccount) (*authtypes.QueryLegacyAccountResponse, error) {
addr, err := a.addrCodec.BytesToString(accountstd.Whoami(ctx))
if err != nil {
Expand Down Expand Up @@ -311,5 +329,6 @@ func (a Account) RegisterExecuteHandlers(builder *accountstd.ExecuteBuilder) {

func (a Account) RegisterQueryHandlers(builder *accountstd.QueryBuilder) {
accountstd.RegisterQueryHandler(builder, a.QuerySequence)
accountstd.RegisterQueryHandler(builder, a.QueryPubKey)
accountstd.RegisterQueryHandler(builder, a.AuthRetroCompatibility)
}
Loading

0 comments on commit 0b362ad

Please sign in to comment.