diff --git a/simapp/simd/cmd/root_di.go b/simapp/simd/cmd/root_di.go index 7182fe4c9bf9..ea6e70f37a99 100644 --- a/simapp/simd/cmd/root_di.go +++ b/simapp/simd/cmd/root_di.go @@ -16,6 +16,9 @@ import ( "cosmossdk.io/depinject" "cosmossdk.io/log" "cosmossdk.io/simapp" + basedepinject "cosmossdk.io/x/accounts/defaults/base/depinject" + lockupdepinject "cosmossdk.io/x/accounts/defaults/lockup/depinject" + multisigdepinject "cosmossdk.io/x/accounts/defaults/multisig/depinject" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/config" @@ -42,6 +45,12 @@ func NewRootCmd() *cobra.Command { depinject.Supply(log.NewNopLogger()), depinject.Provide( ProvideClientContext, + multisigdepinject.ProvideAccount, + basedepinject.ProvideAccount, + lockupdepinject.ProvideAllLockupAccounts, + + // provide base account options + basedepinject.ProvideSecp256K1PubKey, ), ), &autoCliOpts, diff --git a/simapp/v2/simdv2/cmd/root_di.go b/simapp/v2/simdv2/cmd/root_di.go index 6b13b7201342..a43ce3bca8c3 100644 --- a/simapp/v2/simdv2/cmd/root_di.go +++ b/simapp/v2/simdv2/cmd/root_di.go @@ -14,6 +14,9 @@ import ( "cosmossdk.io/log" "cosmossdk.io/runtime/v2" "cosmossdk.io/simapp/v2" + basedepinject "cosmossdk.io/x/accounts/defaults/base/depinject" + lockupdepinject "cosmossdk.io/x/accounts/defaults/lockup/depinject" + multisigdepinject "cosmossdk.io/x/accounts/defaults/multisig/depinject" "github.com/cosmos/cosmos-sdk/client" "github.com/cosmos/cosmos-sdk/client/config" @@ -36,7 +39,15 @@ func NewRootCmd[T transaction.Tx]() *cobra.Command { if err := depinject.Inject( depinject.Configs( simapp.AppConfig(), - depinject.Provide(ProvideClientContext), + depinject.Provide( + ProvideClientContext, + // inject desired account types: + multisigdepinject.ProvideAccount, + basedepinject.ProvideAccount, + lockupdepinject.ProvideAllLockupAccounts, + + // provide base account options + basedepinject.ProvideSecp256K1PubKey), depinject.Supply(log.NewNopLogger()), ), &autoCliOpts, diff --git a/tests/systemtests/account_test.go b/tests/systemtests/account_test.go index 5f3ee7e340ad..db4fed5c7126 100644 --- a/tests/systemtests/account_test.go +++ b/tests/systemtests/account_test.go @@ -1,6 +1,7 @@ package systemtests import ( + "fmt" "strings" "testing" @@ -51,3 +52,104 @@ func TestAccountCreation(t *testing.T) { rsp5 := cli.CustomQuery("q", "auth", "account", account1Addr) assert.Equal(t, "1", gjson.Get(rsp5, "account.value.sequence").String(), rsp5) } + +func TestAccountsMigration(t *testing.T) { + sut.ResetChain(t) + cli := NewCLIWrapper(t, sut, verbose) + + legacyAddress := cli.GetKeyAddr(defaultSrcAddr) + // Create a receiver account + receiverName := "receiver-account" + receiverAddress := cli.AddKey(receiverName) + require.NotEmpty(t, receiverAddress) + sut.ModifyGenesisCLI(t, + []string{"genesis", "add-genesis-account", receiverAddress, "1000000stake"}, + ) + + sut.StartChain(t) + + // Get pubkey + pubKeyValue := cli.GetPubKeyByCustomField(legacyAddress, "address") + require.NotEmpty(t, pubKeyValue, "Public key for legacy-account not found") + + // 1. Verify the account + rsp := cli.CustomQuery("q", "auth", "account", legacyAddress) + require.NotEmpty(t, rsp) + accountInfo := gjson.Parse(rsp) + addressFromResponse := accountInfo.Get("account.value.address").String() + require.Equal(t, legacyAddress, addressFromResponse, "The address in the response should match the legacy address") + + // 2. Migrate this account from x/auth to x/accounts + + // Verify the account not exist in account + rsp = cli.WithRunErrorsIgnored().CustomQuery("q", "accounts", "query", legacyAddress, "cosmos.accounts.defaults.base.v1.QuerySequence", "{}") + require.Contains(t, rsp, "not found: key") + + accountInitMsg := fmt.Sprintf(`{ + "@type": "/cosmos.accounts.defaults.base.v1.MsgInit", + "pub_key": { + "@type": "/cosmos.crypto.secp256k1.PubKey", + "key": "%s" + }, + "init_sequence": "0" + }`, pubKeyValue) + + rsp = cli.RunAndWait("tx", "auth", "migrate-account", + "--account-type=base", + fmt.Sprintf("--account-init-msg=%s", accountInitMsg), + fmt.Sprintf("--from=%s", legacyAddress), + "--fees=1stake") + RequireTxSuccess(t, rsp) + + // 3. Now the account should be existed, query the account Sequence + rsp = cli.CustomQuery("q", "accounts", "query", legacyAddress, "cosmos.accounts.defaults.base.v1.QuerySequence", "{}") + sequence := gjson.Get(rsp, "response.sequence").Exists() + require.True(t, sequence, "Sequence field should exist") + + // 4. Execute a transaction using the bank module + + // Check initial balances + legacyBalance := cli.QueryBalance(legacyAddress, "stake") + receiverBalance := cli.QueryBalance(receiverAddress, "stake") + require.Equal(t, int64(399999999), legacyBalance) // 20000000 - 1 (fee for migration) + require.Equal(t, int64(1000000), receiverBalance) + + transferAmount := "1000000" + rsp = cli.RunAndWait("tx", "bank", "send", + legacyAddress, + receiverAddress, + transferAmount+"stake", + fmt.Sprintf("--from=%s", legacyAddress), + "--fees=1stake") + RequireTxSuccess(t, rsp) + + // Verify the balances after the transaction + newLegacyBalance := cli.QueryBalance(legacyAddress, "stake") + newReceiverBalance := cli.QueryBalance(receiverAddress, "stake") + + expectedLegacyBalance := legacyBalance - 1000000 - 1 // Initial balance - transferred amount - fee + expectedReceiverBalance := receiverBalance + 1000000 // Initial balance + transferred amount + + require.Equal(t, expectedLegacyBalance, newLegacyBalance, "Legacy account balance is incorrect after transfer") + require.Equal(t, expectedReceiverBalance, newReceiverBalance, "Receiver account balance is incorrect after transfer") + + // 5. Test swapKey functionality + newKeyName := "new-key" + newKeyAddress := cli.AddKey(newKeyName) + require.NotEmpty(t, newKeyAddress) + + newPubKey := cli.GetPubKeyByCustomField(newKeyAddress, "address") + require.NotEmpty(t, newPubKey, "Public key for new-key not found") + + swapKeyMsg := fmt.Sprintf(`{ + "new_pub_key": { + "@type": "/cosmos.crypto.secp256k1.PubKey", + "key": "%s" + } + }`, newPubKey) + + rsp = cli.RunAndWait("tx", "accounts", "execute", legacyAddress, "cosmos.accounts.defaults.base.v1.MsgSwapPubKey", swapKeyMsg, + fmt.Sprintf("--from=%s", legacyAddress), + "--fees=1stake") + RequireTxSuccess(t, rsp) +} diff --git a/tests/systemtests/cli.go b/tests/systemtests/cli.go index a39cc0dd6d16..423b27dc57f5 100644 --- a/tests/systemtests/cli.go +++ b/tests/systemtests/cli.go @@ -331,6 +331,23 @@ func (c CLIWrapper) GetKeyAddrPrefix(name, prefix string) string { return addr } +// GetPubKeyByCustomField returns pubkey in base64 by custom field +func (c CLIWrapper) GetPubKeyByCustomField(addr, field string) string { + keysListOutput := c.Keys("keys", "list") + keysList := gjson.Parse(keysListOutput) + + var pubKeyValue string + keysList.ForEach(func(_, value gjson.Result) bool { + if value.Get(field).String() == addr { + pubKeyJSON := gjson.Parse(value.Get("pubkey").String()) + pubKeyValue = pubKeyJSON.Get("key").String() + return false + } + return true + }) + return pubKeyValue +} + const defaultSrcAddr = "node0" // FundAddress sends the token amount to the destination address diff --git a/x/accounts/keeper.go b/x/accounts/keeper.go index fee356a7736f..003e55715fb0 100644 --- a/x/accounts/keeper.go +++ b/x/accounts/keeper.go @@ -10,6 +10,7 @@ import ( gogoproto "github.com/cosmos/gogoproto/proto" + _ "cosmossdk.io/api/cosmos/accounts/defaults/base/v1" // import for side-effects "cosmossdk.io/collections" "cosmossdk.io/core/address" "cosmossdk.io/core/appmodule" diff --git a/x/accounts/module.go b/x/accounts/module.go index 1ed89d87d8dd..f7958240b7b5 100644 --- a/x/accounts/module.go +++ b/x/accounts/module.go @@ -42,21 +42,21 @@ type AppModule struct { k Keeper } -func (m AppModule) IsAppModule() {} +func (AppModule) IsAppModule() {} // Name returns the module's name. // Deprecated: kept for legacy reasons. -func (AppModule) Name() string { return ModuleName } +func (am AppModule) Name() string { return ModuleName } -func (m AppModule) RegisterInterfaces(registrar registry.InterfaceRegistrar) { +func (AppModule) RegisterInterfaces(registrar registry.InterfaceRegistrar) { msgservice.RegisterMsgServiceDesc(registrar, v1.MsgServiceDesc()) } // App module services -func (m AppModule) RegisterServices(registar grpc.ServiceRegistrar) error { - v1.RegisterQueryServer(registar, NewQueryServer(m.k)) - v1.RegisterMsgServer(registar, NewMsgServer(m.k)) +func (am AppModule) RegisterServices(registrar grpc.ServiceRegistrar) error { + v1.RegisterQueryServer(registrar, NewQueryServer(am.k)) + v1.RegisterMsgServer(registrar, NewMsgServer(am.k)) return nil }