]> Untitled Git - bdk-cli/commitdiff
fix(key): use password when restoring a key
authorIkeh Celestine Chukwudi <Chukwudi.ikeh.232330@unn.edu.ng>
Fri, 6 Nov 2026 15:33:47 +0000 (16:33 +0100)
committerIkeh Celestine Chukwudi <Chukwudi.ikeh.232330@unn.edu.ng>
Fri, 6 Nov 2026 15:33:47 +0000 (16:33 +0100)
RestoreKeyCommand passed only the mnemonic to into_extended_key,
dropping --password, so a key generated with a password could not be
restored. Pass the password as GenerateKeyCommand does, and add a test
that restores with and without it.

Fixes #372

CHANGELOG.md
src/handlers/key.rs
tests/integration/init.rs

index ccc29bdb284ce02afb8b9baf47d4f0f5733d6225..45cf4fcd7ce81189cf8d175640af61fd9b474867 100644 (file)
@@ -21,6 +21,7 @@ page. See [DEVELOPMENT_CYCLE.md](DEVELOPMENT_CYCLE.md) for more details.
 - Fixed `compile` panicking instead of returning an error when a key is valid on its own but the wrong shape for the requested script type (e.g. an x-only key under `sh`/`wsh`/`sh-wsh`, or an uncompressed key under `tr`)
 - Updated dependencies to resolve RUSTSEC advisories in rustls, rustls-webpki, webpki-roots, quinn-proto, h2 and zstd-sys; bumped async-hwi to 0.0.35.
 - Fixed `create_tx`, `create_dns_tx`, `create_sp_tx` and `bump_fee` building transactions over Bitcoin Core's 400,000 wu (100,000 vB) standard weight limit, which no default node relays
+- Fixed `key restore` ignoring `--password`, which restored passphrase-protected mnemonics to the wrong key
 - Fixed `key generate` silently producing a 24-word mnemonic for any `--entropy` value other than 12; values other than 12 and 24 are now rejected
 
 ## [4.0.0]
index 4abf3cdfe0f8f4bcbcdd525c68902cedeb4f46dc..648177e42ea0af949469bdb642e2784eca8ab9f3 100644 (file)
@@ -154,7 +154,7 @@ impl AppCommand<AppContext<Init>> for RestoreKeyCommand {
         let secp = Secp256k1::new();
 
         let mnemonic = Mnemonic::parse_in(Language::English, &self.mnemonic)?;
-        let xkey: ExtendedKey = (mnemonic.clone(), &self.password).0.into_extended_key()?;
+        let xkey: ExtendedKey = (mnemonic.clone(), self.password.clone()).into_extended_key()?;
         let xprv = xkey.into_xprv(ctx.network.into()).ok_or_else(|| {
             Error::Generic("Privatekey info not found (should not happen)".to_string())
         })?;
index 3454882829f106579581f97733fd529e60de5942..e1725e4f0588505abe92b06e76f5aa730f4c5749 100644 (file)
@@ -132,6 +132,43 @@ mod test_key {
             "The restored fingerprint does not match the generated fingerprint!"
         );
     }
+
+    #[test]
+    fn test_cli_key_restore_with_password() {
+        let temp_dir = TempDir::new().unwrap();
+        let cli = BdkCli::new("testnet", Some(temp_dir.path().to_path_buf()));
+
+        let generate_cmd = cli
+            .key_cmd(&["generate", "--password", "foo"])
+            .output()
+            .expect("Failed to execute generate command");
+        assert!(generate_cmd.status.success(), "Generate command failed");
+        let generate_json: Value =
+            serde_json::from_slice(&generate_cmd.stdout).expect("Failed to parse JSON");
+        let mnemonic = generate_json["mnemonic"].as_str().unwrap();
+
+        // Restoring with the same password must give back the same key
+        let restore_cmd = cli
+            .key_cmd(&["restore", "--mnemonic", mnemonic, "--password", "foo"])
+            .output()
+            .expect("Failed to execute restore command");
+        assert!(restore_cmd.status.success(), "Restore command failed");
+        let restore_json: Value =
+            serde_json::from_slice(&restore_cmd.stdout).expect("Failed to parse JSON");
+        assert_eq!(generate_json["xprv"], restore_json["xprv"]);
+        assert_eq!(generate_json["fingerprint"], restore_json["fingerprint"]);
+
+        // Restoring without the password must give a different key
+        let no_password_cmd = cli
+            .key_cmd(&["restore", "--mnemonic", mnemonic])
+            .env_remove("PASSWORD")
+            .output()
+            .expect("Failed to execute restore command");
+        assert!(no_password_cmd.status.success(), "Restore command failed");
+        let no_password_json: Value =
+            serde_json::from_slice(&no_password_cmd.stdout).expect("Failed to parse JSON");
+        assert_ne!(generate_json["xprv"], no_password_json["xprv"]);
+    }
 }
 
 // --- WALLETS COMMAND TESTS ---