Skip to content

Commit faf6e20

Browse files
authored
Merge pull request #97 from Ximik/mnemonic
Mnemonic wallet recovery
2 parents d64da07 + 41ed212 commit faf6e20

File tree

2 files changed

+40
-8
lines changed

2 files changed

+40
-8
lines changed

client/src/bin/space-cli.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ extern crate core;
22

33
use std::{
44
fs, io,
5-
io::{Cursor, IsTerminal},
5+
io::{Cursor, IsTerminal, Write},
66
path::PathBuf,
77
};
88

@@ -87,6 +87,9 @@ enum Commands {
8787
/// Generate a new wallet
8888
#[command(name = "createwallet")]
8989
CreateWallet,
90+
/// Recover wallet from mnemonic phrase
91+
#[command(name = "recoverwallet")]
92+
RecoverWallet,
9093
/// Load a wallet
9194
#[command(name = "loadwallet")]
9295
LoadWallet,
@@ -611,7 +614,17 @@ async fn handle_commands(cli: &SpaceCli, command: Commands) -> Result<(), Client
611614
print_list_wallets(result, cli.format);
612615
}
613616
Commands::CreateWallet => {
614-
cli.client.wallet_create(&cli.wallet).await?;
617+
let response = cli.client.wallet_create(&cli.wallet).await?;
618+
println!("⚠️ Write down your recovery phrase NOW!");
619+
println!("This is the ONLY time it will be shown:");
620+
println!("{}", &response);
621+
}
622+
Commands::RecoverWallet => {
623+
print!("Enter mnemonic phrase: ");
624+
io::stdout().flush().unwrap();
625+
let mut mnemonic = String::new();
626+
io::stdin().read_line(&mut mnemonic).unwrap();
627+
cli.client.wallet_recover(&cli.wallet, mnemonic).await?;
615628
}
616629
Commands::LoadWallet => {
617630
cli.client.wallet_load(&cli.wallet).await?;

client/src/rpc.rs

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,10 @@ pub trait Rpc {
242242
async fn wallet_export(&self, name: &str) -> Result<WalletExport, ErrorObjectOwned>;
243243

244244
#[method(name = "walletcreate")]
245-
async fn wallet_create(&self, name: &str) -> Result<(), ErrorObjectOwned>;
245+
async fn wallet_create(&self, name: &str) -> Result<String, ErrorObjectOwned>;
246+
247+
#[method(name = "walletrecover")]
248+
async fn wallet_recover(&self, name: &str, mnemonic: String) -> Result<(), ErrorObjectOwned>;
246249

247250
#[method(name = "walletsendrequest")]
248251
async fn wallet_send_request(
@@ -503,11 +506,18 @@ impl WalletManager {
503506
Ok(export)
504507
}
505508

506-
pub async fn create_wallet(&self, client: &reqwest::Client, name: &str) -> anyhow::Result<()> {
509+
pub async fn create_wallet(&self, client: &reqwest::Client, name: &str) -> anyhow::Result<String> {
507510
let mnemonic: GeneratedKey<_, Tap> =
508511
Mnemonic::generate((WordCount::Words12, Language::English))
509512
.map_err(|_| anyhow!("Mnemonic generation error"))?;
510513

514+
let start_block = self.get_wallet_start_block(client).await?;
515+
self.setup_new_wallet(name.to_string(), mnemonic.to_string(), start_block)?;
516+
self.load_wallet(name).await?;
517+
Ok(mnemonic.to_string())
518+
}
519+
520+
pub async fn recover_wallet(&self, client: &reqwest::Client, name: &str, mnemonic: &str) -> anyhow::Result<()> {
511521
let start_block = self.get_wallet_start_block(client).await?;
512522
self.setup_new_wallet(name.to_string(), mnemonic.to_string(), start_block)?;
513523
self.load_wallet(name).await?;
@@ -525,7 +535,7 @@ impl WalletManager {
525535
return Err(anyhow!(format!("Wallet `{}` already exists", name)));
526536
}
527537

528-
let export = self.wallet_from_mnemonic(name.clone(), mnemonic.to_string(), start_block)?;
538+
let export = self.wallet_from_mnemonic(name.clone(), mnemonic, start_block)?;
529539
fs::create_dir_all(&wallet_path)?;
530540
let wallet_export_path = wallet_path.join("wallet.json");
531541
let mut file = fs::File::create(wallet_export_path)?;
@@ -540,7 +550,7 @@ impl WalletManager {
540550
start_block: BlockId,
541551
) -> anyhow::Result<WalletExport> {
542552
let (network, _) = self.fallback_network();
543-
let xpriv = Self::descriptor_from_mnemonic(network, &mnemonic.to_string())?;
553+
let xpriv = Self::descriptor_from_mnemonic(network, &mnemonic)?;
544554

545555
let (external, internal) = Self::default_descriptors(xpriv);
546556
let tmp = bdk::Wallet::create(external, internal)
@@ -666,7 +676,7 @@ impl WalletManager {
666676
}
667677

668678
fn descriptor_from_mnemonic(network: Network, m: &str) -> anyhow::Result<Xpriv> {
669-
let mnemonic = Mnemonic::parse(m).unwrap();
679+
let mnemonic = Mnemonic::parse(m)?;
670680
let xkey: ExtendedKey = mnemonic.clone().into_extended_key()?;
671681
Ok(xkey.into_xprv(network).expect("xpriv"))
672682
}
@@ -924,7 +934,7 @@ impl RpcServer for RpcServerImpl {
924934
})
925935
}
926936

927-
async fn wallet_create(&self, name: &str) -> Result<(), ErrorObjectOwned> {
937+
async fn wallet_create(&self, name: &str) -> Result<String, ErrorObjectOwned> {
928938
self.wallet_manager
929939
.create_wallet(&self.client, name)
930940
.await
@@ -933,6 +943,15 @@ impl RpcServer for RpcServerImpl {
933943
})
934944
}
935945

946+
async fn wallet_recover(&self, name: &str, mnemonic: String) -> Result<(), ErrorObjectOwned> {
947+
self.wallet_manager
948+
.recover_wallet(&self.client, name, &mnemonic)
949+
.await
950+
.map_err(|error| {
951+
ErrorObjectOwned::owned(RPC_WALLET_NOT_LOADED, error.to_string(), None::<String>)
952+
})
953+
}
954+
936955
async fn wallet_send_request(
937956
&self,
938957
wallet: &str,

0 commit comments

Comments
 (0)