From b1f4201bd1e2f5fa94b70d1aa6bd69a719026761 Mon Sep 17 00:00:00 2001 From: Tasos Bitsios Date: Thu, 31 Oct 2024 17:35:20 +0100 Subject: [PATCH 1/2] goal: added "wallet rename [wallet name] [new wallet name]" --- cmd/goal/messages.go | 3 +++ cmd/goal/wallet.go | 48 +++++++++++++++++++++++++++++++++++ daemon/kmd/client/wrappers.go | 11 ++++++++ libgoal/wallets.go | 18 +++++++++++++ 4 files changed, 80 insertions(+) diff --git a/cmd/goal/messages.go b/cmd/goal/messages.go index 1bc78ce30a..b3cf284881 100644 --- a/cmd/goal/messages.go +++ b/cmd/goal/messages.go @@ -178,16 +178,19 @@ const ( infoPrintedBackupPhrase = "Your backup phrase is printed below.\nKeep this information safe -- never share it with anyone!" infoBackupPhrase = "\n%s" infoNoWallets = "No wallets found. You can create a wallet with `goal wallet new`" + infoRenamedWallet = "Renamed wallet '%s' to '%s'" errorCouldntCreateWallet = "Couldn't create wallet: %s" errorCouldntInitializeWallet = "Couldn't initialize wallet: %s" errorCouldntExportMDK = "Couldn't export master derivation key: %s" errorCouldntMakeMnemonic = "Couldn't make mnemonic: %s" errorCouldntListWallets = "Couldn't list wallets: %s" + errorCouldntFindWallet = "Couldn't find wallet: %s" errorPasswordConfirmation = "Password confirmation did not match" errorBadMnemonic = "Problem with mnemonic: %s" errorBadRecoveredKey = "Recovered invalid key" errorFailedToReadResponse = "Couldn't read response: %s" errorFailedToReadPassword = "Couldn't read password: %s" + errorCouldntRenameWallet = "Couldn't rename wallet: %s" // Commands infoPasswordPrompt = "Please enter the password for wallet '%s': " diff --git a/cmd/goal/wallet.go b/cmd/goal/wallet.go index e95aea62a6..a0a06e6cf4 100644 --- a/cmd/goal/wallet.go +++ b/cmd/goal/wallet.go @@ -39,6 +39,7 @@ var ( func init() { walletCmd.AddCommand(newWalletCmd) walletCmd.AddCommand(listWalletsCmd) + walletCmd.AddCommand(renameWalletCmd) // Default wallet to use when -w not specified walletCmd.Flags().StringVarP(&defaultWalletName, "default", "f", "", "Set the wallet with this name to be the default wallet") @@ -200,6 +201,53 @@ var listWalletsCmd = &cobra.Command{ }, } +var renameWalletCmd = &cobra.Command{ + Use: "rename [wallet name] [new wallet name]", + Short: "Rename wallet", + Args: cobra.ExactArgs(2), + Run: func(cmd *cobra.Command, args []string) { + dataDir := datadir.EnsureSingleDataDir() + + client := ensureKmdClient(dataDir) + + walletName := []byte(args[0]) + newWalletName := []byte(args[1]) + + wid, duplicate, err := client.FindWalletIDByName(walletName) + + if wid == nil { + reportErrorf(errorCouldntFindWallet, string(walletName)) + } + + if err != nil { + reportErrorf(errorCouldntRenameWallet, err) + } + + if duplicate { + reportErrorf(errorCouldntRenameWallet, "Multiple wallets by the same name are not supported") + } + + if bytes.Equal(walletName, newWalletName) { + reportErrorf(errorCouldntRenameWallet, "new name is identical to current name") + } + + walletPassword := []byte{} + + // if wallet is encrypted, fetch the password + if !client.WalletIsUnencrypted(wid) { + fmt.Printf(infoPasswordPrompt, walletName) + walletPassword = ensurePassword() + } + + err = client.RenameWallet(wid, newWalletName, walletPassword) + if err != nil { + reportErrorf(errorCouldntRenameWallet, err) + } + + reportInfof(infoRenamedWallet, walletName, newWalletName) + }, +} + func printWallets(dataDir string, wallets []kmdapi.APIV1Wallet) { accountList := makeAccountsList(dataDir) defaultWalletID := string(accountList.getDefaultWalletID()) diff --git a/daemon/kmd/client/wrappers.go b/daemon/kmd/client/wrappers.go index a7b5613aff..2167276c08 100644 --- a/daemon/kmd/client/wrappers.go +++ b/daemon/kmd/client/wrappers.go @@ -59,6 +59,17 @@ func (kcl KMDClient) CreateWallet(walletName []byte, walletDriverName string, wa return } +// RenameWallet wraps kmdapi.APIV1POSTWalletRenameRequest +func (kcl KMDClient) RenameWallet(walletID []byte, newWalletName []byte, walletPassword []byte) (resp kmdapi.APIV1POSTWalletRenameResponse, err error) { + req := kmdapi.APIV1POSTWalletRenameRequest{ + WalletID: string(walletID), + NewWalletName: string(newWalletName), + WalletPassword: string(walletPassword), + } + err = kcl.DoV1Request(req, &resp) + return +} + // InitWallet wraps kmdapi.APIV1POSTWalletInitRequest func (kcl KMDClient) InitWallet(walletID []byte, walletPassword []byte) (resp kmdapi.APIV1POSTWalletInitResponse, err error) { req := kmdapi.APIV1POSTWalletInitRequest{ diff --git a/libgoal/wallets.go b/libgoal/wallets.go index e175d62f77..c0db621bda 100644 --- a/libgoal/wallets.go +++ b/libgoal/wallets.go @@ -45,6 +45,24 @@ func (c *Client) CreateWallet(name []byte, password []byte, mdk crypto.MasterDer return []byte(resp.Wallet.ID), nil } +// RenameWallet renames a kmd wallet +func (c *Client) RenameWallet(wid []byte, name []byte, password []byte) error { + // Pull the list of all wallets from kmd + kmd, err := c.ensureKmdClient() + if err != nil { + return err + } + + // Rename the wallet + _, err = kmd.RenameWallet(wid, name, password) + + if err != nil { + return err + } + + return nil +} + // GetWalletHandleToken inits the wallet with the given id, returning a wallet handle token func (c *Client) GetWalletHandleToken(wid, pw []byte) ([]byte, error) { kmd, err := c.ensureKmdClient() From fe10c8e7a25cfca87a467b4ac4fc59107e490077 Mon Sep 17 00:00:00 2001 From: Pavel Zbitskiy Date: Fri, 21 Feb 2025 16:16:39 -0500 Subject: [PATCH 2/2] address CR comments + expect test --- cmd/goal/wallet.go | 16 ++++++++-------- test/e2e-go/cli/goal/expect/createWalletTest.exp | 6 ++++++ test/e2e-go/cli/goal/expect/goalExpectCommon.exp | 16 ++++++++++++++++ 3 files changed, 30 insertions(+), 8 deletions(-) diff --git a/cmd/goal/wallet.go b/cmd/goal/wallet.go index a0a06e6cf4..4edfa39e1c 100644 --- a/cmd/goal/wallet.go +++ b/cmd/goal/wallet.go @@ -213,22 +213,22 @@ var renameWalletCmd = &cobra.Command{ walletName := []byte(args[0]) newWalletName := []byte(args[1]) - wid, duplicate, err := client.FindWalletIDByName(walletName) - - if wid == nil { - reportErrorf(errorCouldntFindWallet, string(walletName)) + if bytes.Equal(walletName, newWalletName) { + reportErrorf(errorCouldntRenameWallet, "new name is identical to current name") } + wid, duplicate, err := client.FindWalletIDByName(walletName) + if err != nil { reportErrorf(errorCouldntRenameWallet, err) } - if duplicate { - reportErrorf(errorCouldntRenameWallet, "Multiple wallets by the same name are not supported") + if wid == nil { + reportErrorf(errorCouldntFindWallet, string(walletName)) } - if bytes.Equal(walletName, newWalletName) { - reportErrorf(errorCouldntRenameWallet, "new name is identical to current name") + if duplicate { + reportErrorf(errorCouldntRenameWallet, "Multiple wallets by the same name are not supported") } walletPassword := []byte{} diff --git a/test/e2e-go/cli/goal/expect/createWalletTest.exp b/test/e2e-go/cli/goal/expect/createWalletTest.exp index 5819a73220..af267f27b7 100644 --- a/test/e2e-go/cli/goal/expect/createWalletTest.exp +++ b/test/e2e-go/cli/goal/expect/createWalletTest.exp @@ -43,6 +43,12 @@ if { [catch { set NEW_PRIMARY_ACCOUNT_ADDRESS [::AlgorandGoal::CreateAccountForWallet $PRIMARY_WALLET_NAME $PRIMARY_WALLET_PASSWORD $TEST_PRIMARY_NODE_DIR] ::AlgorandGoal::VerifyAccount $PRIMARY_WALLET_NAME $PRIMARY_WALLET_PASSWORD $NEW_PRIMARY_ACCOUNT_ADDRESS $TEST_PRIMARY_NODE_DIR + # Rename the wallet + set SUFFIX "_renamed" + set NEW_PRIMARY_WALLET_NAME $PRIMARY_WALLET_NAME$SUFFIX + ::AlgorandGoal::RenameWallet $PRIMARY_WALLET_NAME $PRIMARY_WALLET_PASSWORD $TEST_PRIMARY_NODE_DIR $NEW_PRIMARY_WALLET_NAME + set PRIMARY_WALLET_NAME $NEW_PRIMARY_WALLET_NAME + set WALLET_NAME_PREFIX "TestWallet_" set WALLET_COUNT 3 set OUTPUT_FILE_NAME "$TEST_ALGO_DIR/walletlist.out" diff --git a/test/e2e-go/cli/goal/expect/goalExpectCommon.exp b/test/e2e-go/cli/goal/expect/goalExpectCommon.exp index 058694f684..e452fbfecc 100644 --- a/test/e2e-go/cli/goal/expect/goalExpectCommon.exp +++ b/test/e2e-go/cli/goal/expect/goalExpectCommon.exp @@ -403,6 +403,22 @@ proc ::AlgorandGoal::RecoverWallet { NEW_WALLET_NAME WALLET_PASSPHRASE NEW_WALLE return $RECOVERED_WALLET_NAME } +# Rename a wallet +proc ::AlgorandGoal::RenameWallet { WALLET_NAME WALLET_PASSWORD TEST_PRIMARY_NODE_DIR NEW_WALLET_NAME } { + set timeout 60 + if { [catch { + spawn goal wallet rename -d $TEST_PRIMARY_NODE_DIR $WALLET_NAME $NEW_WALLET_NAME + expect { + timeout { ::AlgorandGoal::Abort "Timed out seeing expected input for spawn goal wallet list" } + {Please type your recovery mnemonic below, and hit return when you are done:*} { send "$WALLET_PASSWORD\r" } + eof { ::AlgorandGoal::Abort "EOF seeing expected input for spawn goal wallet list" } + "*Renamed wallet*$WALLET_NAME*$NEW_WALLET_NAME" {close} + } + } EXCEPTION ] } { + ::AlgorandGoal::Abort "ERROR in RenameWallet: $EXCEPTION" + } +} + # Associate a new account with a specific wallet proc ::AlgorandGoal::CreateAccountForWallet { WALLET_NAME WALLET_PASSWORD TEST_PRIMARY_NODE_DIR } { set timeout 60