mirror of
https://github.com/Qortal/piratewallet-light-cli.git
synced 2025-07-31 12:21:25 +00:00
Improve recovery
This commit is contained in:
@@ -27,6 +27,10 @@ macro_rules! configure_clapapp {
|
|||||||
.long("recover")
|
.long("recover")
|
||||||
.help("Attempt to recover the seed from the wallet")
|
.help("Attempt to recover the seed from the wallet")
|
||||||
.takes_value(false))
|
.takes_value(false))
|
||||||
|
.arg(Arg::with_name("password")
|
||||||
|
.long("password")
|
||||||
|
.help("When recovering seed, specify a password for the encrypted wallet")
|
||||||
|
.takes_value(true))
|
||||||
.arg(Arg::with_name("seed")
|
.arg(Arg::with_name("seed")
|
||||||
.short("s")
|
.short("s")
|
||||||
.long("seed")
|
.long("seed")
|
||||||
@@ -234,7 +238,7 @@ pub fn command_loop(lightclient: Arc<LightClient>) -> (Sender<(String, Vec<Strin
|
|||||||
(command_tx, resp_rx)
|
(command_tx, resp_rx)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn attempt_recover_seed() {
|
pub fn attempt_recover_seed(password: Option<String>) {
|
||||||
// Create a Light Client Config in an attempt to recover the file.
|
// Create a Light Client Config in an attempt to recover the file.
|
||||||
let config = LightClientConfig {
|
let config = LightClientConfig {
|
||||||
server: "0.0.0.0:0".parse().unwrap(),
|
server: "0.0.0.0:0".parse().unwrap(),
|
||||||
@@ -246,7 +250,7 @@ pub fn attempt_recover_seed() {
|
|||||||
data_dir: None,
|
data_dir: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
match LightClient::attempt_recover_seed(&config) {
|
match LightClient::attempt_recover_seed(&config, password) {
|
||||||
Ok(seed) => println!("Recovered seed: '{}'", seed),
|
Ok(seed) => println!("Recovered seed: '{}'", seed),
|
||||||
Err(e) => eprintln!("Failed to recover seed. Error: {}", e)
|
Err(e) => eprintln!("Failed to recover seed. Error: {}", e)
|
||||||
};
|
};
|
||||||
|
@@ -13,9 +13,10 @@ pub fn main() {
|
|||||||
let fresh_app = App::new("Zecwallet CLI");
|
let fresh_app = App::new("Zecwallet CLI");
|
||||||
let configured_app = configure_clapapp!(fresh_app);
|
let configured_app = configure_clapapp!(fresh_app);
|
||||||
let matches = configured_app.get_matches();
|
let matches = configured_app.get_matches();
|
||||||
|
|
||||||
if matches.is_present("recover") {
|
if matches.is_present("recover") {
|
||||||
// Create a Light Client Config in an attempt to recover the file.
|
// Create a Light Client Config in an attempt to recover the file.
|
||||||
attempt_recover_seed();
|
attempt_recover_seed(matches.value_of("password").map(|s| s.to_string()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,8 +56,9 @@ pub fn main() {
|
|||||||
let (command_tx, resp_rx) = match startup(server, dangerous, seed, birthday, !nosync, command.is_none()) {
|
let (command_tx, resp_rx) = match startup(server, dangerous, seed, birthday, !nosync, command.is_none()) {
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("Error during startup: {}", e);
|
let emsg = format!("Error during startup:{}\nIf you repeatedly run into this issue, you might have to restore your wallet from your seed phrase.", e);
|
||||||
error!("Error during startup: {}", e);
|
eprintln!("{}", emsg);
|
||||||
|
error!("{}", emsg);
|
||||||
if cfg!(target_os = "unix" ) {
|
if cfg!(target_os = "unix" ) {
|
||||||
match e.raw_os_error() {
|
match e.raw_os_error() {
|
||||||
Some(13) => report_permission_error(),
|
Some(13) => report_permission_error(),
|
||||||
|
@@ -418,7 +418,7 @@ impl LightClient {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn attempt_recover_seed(config: &LightClientConfig) -> Result<String, String> {
|
pub fn attempt_recover_seed(config: &LightClientConfig, password: Option<String>) -> Result<String, String> {
|
||||||
use std::io::prelude::*;
|
use std::io::prelude::*;
|
||||||
use byteorder::{LittleEndian, ReadBytesExt};
|
use byteorder::{LittleEndian, ReadBytesExt};
|
||||||
use libflate::gzip::Decoder;
|
use libflate::gzip::Decoder;
|
||||||
@@ -442,8 +442,8 @@ impl LightClient {
|
|||||||
false
|
false
|
||||||
};
|
};
|
||||||
|
|
||||||
if encrypted {
|
if encrypted && password.is_none() {
|
||||||
return Err("The wallet is encrypted!".to_string());
|
return Err("The wallet is encrypted and a password was not specified. Please specify the password with '--password'!".to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut enc_seed = [0u8; 48];
|
let mut enc_seed = [0u8; 48];
|
||||||
@@ -451,19 +451,35 @@ impl LightClient {
|
|||||||
reader.read_exact(&mut enc_seed).unwrap();
|
reader.read_exact(&mut enc_seed).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
let _nonce = if version >= 4 {
|
let nonce = if version >= 4 {
|
||||||
Vector::read(&mut reader, |r| r.read_u8()).unwrap()
|
Vector::read(&mut reader, |r| r.read_u8()).unwrap()
|
||||||
} else {
|
} else {
|
||||||
vec![]
|
vec![]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let phrase = if encrypted {
|
||||||
|
use sodiumoxide::crypto::secretbox;
|
||||||
|
use crate::lightwallet::double_sha256;
|
||||||
|
|
||||||
|
// Get the doublesha256 of the password, which is the right length
|
||||||
|
let key = secretbox::Key::from_slice(&double_sha256(password.unwrap().as_bytes())).unwrap();
|
||||||
|
let nonce = secretbox::Nonce::from_slice(&nonce).unwrap();
|
||||||
|
|
||||||
|
let seed = match secretbox::open(&enc_seed, &nonce, &key) {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(_) => return Err("Decryption failed. Is your password correct?".to_string())
|
||||||
|
};
|
||||||
|
|
||||||
|
Mnemonic::from_entropy(&seed, Language::English)
|
||||||
|
} else {
|
||||||
// Seed
|
// Seed
|
||||||
let mut seed_bytes = [0u8; 32];
|
let mut seed_bytes = [0u8; 32];
|
||||||
reader.read_exact(&mut seed_bytes).unwrap();
|
reader.read_exact(&mut seed_bytes).unwrap();
|
||||||
|
|
||||||
let phrase = Mnemonic::from_entropy(&seed_bytes, Language::English,).unwrap().phrase().to_string();
|
Mnemonic::from_entropy(&seed_bytes, Language::English)
|
||||||
|
}.map_err(|e| format!("Failed to read seed. {:?}", e));
|
||||||
|
|
||||||
Ok(phrase)
|
phrase.map(|m| m.phrase().to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1268,13 +1284,14 @@ pub mod tests {
|
|||||||
let seed = lc.do_seed_phrase().unwrap()["seed"].as_str().unwrap().to_string();
|
let seed = lc.do_seed_phrase().unwrap()["seed"].as_str().unwrap().to_string();
|
||||||
lc.do_save().unwrap();
|
lc.do_save().unwrap();
|
||||||
|
|
||||||
assert_eq!(seed, LightClient::attempt_recover_seed(&config).unwrap());
|
assert_eq!(seed, LightClient::attempt_recover_seed(&config, None).unwrap());
|
||||||
|
|
||||||
// Now encrypt and save the file
|
// Now encrypt and save the file
|
||||||
lc.wallet.write().unwrap().encrypt("password".to_string()).unwrap();
|
let pwd = "password".to_string();
|
||||||
|
lc.wallet.write().unwrap().encrypt(pwd.clone()).unwrap();
|
||||||
lc.do_save().unwrap();
|
lc.do_save().unwrap();
|
||||||
|
|
||||||
assert!(LightClient::attempt_recover_seed(&config).is_err());
|
assert_eq!(seed, LightClient::attempt_recover_seed(&config, Some(pwd)).unwrap());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -239,12 +239,15 @@ impl LightWallet {
|
|||||||
return Err(io::Error::new(ErrorKind::InvalidData, e));
|
return Err(io::Error::new(ErrorKind::InvalidData, e));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
println!("Reading wallet version {}", version);
|
||||||
info!("Reading wallet version {}", version);
|
info!("Reading wallet version {}", version);
|
||||||
|
|
||||||
// After version 5, we're writing the rest of the file as a compressed stream (gzip)
|
// After version 5, we're writing the rest of the file as a compressed stream (gzip)
|
||||||
let mut reader: Box<dyn Read> = if version <= 4 {
|
let mut reader: Box<dyn Read> = if version <= 4 {
|
||||||
|
info!("Reading direct");
|
||||||
Box::new(inp)
|
Box::new(inp)
|
||||||
} else {
|
} else {
|
||||||
|
info!("Reading libflat");
|
||||||
Box::new(Decoder::new(inp).unwrap())
|
Box::new(Decoder::new(inp).unwrap())
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -253,6 +256,7 @@ impl LightWallet {
|
|||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
};
|
};
|
||||||
|
info!("Wallet Encryption {:?}", encrypted);
|
||||||
|
|
||||||
let mut enc_seed = [0u8; 48];
|
let mut enc_seed = [0u8; 48];
|
||||||
if version >= 4 {
|
if version >= 4 {
|
||||||
@@ -271,7 +275,6 @@ impl LightWallet {
|
|||||||
|
|
||||||
// Read the spending keys
|
// Read the spending keys
|
||||||
let extsks = Vector::read(&mut reader, |r| ExtendedSpendingKey::read(r))?;
|
let extsks = Vector::read(&mut reader, |r| ExtendedSpendingKey::read(r))?;
|
||||||
println!("reading version {}", version);
|
|
||||||
|
|
||||||
let extfvks = if version >= 4 {
|
let extfvks = if version >= 4 {
|
||||||
// Read the viewing keys
|
// Read the viewing keys
|
||||||
@@ -363,7 +366,7 @@ impl LightWallet {
|
|||||||
writer.write_all(&self.seed)?;
|
writer.write_all(&self.seed)?;
|
||||||
|
|
||||||
// Flush after writing the seed, so in case of a disaster, we can still recover the seed.
|
// Flush after writing the seed, so in case of a disaster, we can still recover the seed.
|
||||||
//writer.flush()?;
|
writer.flush()?;
|
||||||
|
|
||||||
// Write all the spending keys
|
// Write all the spending keys
|
||||||
Vector::write(&mut writer, &self.extsks.read().unwrap(),
|
Vector::write(&mut writer, &self.extsks.read().unwrap(),
|
||||||
|
Reference in New Issue
Block a user