[wasm] Fix SystemTime for wasm and refactor the cli part

This commit is contained in:
Alekos Filini 2020-05-08 23:30:45 +02:00
parent e5ff696e73
commit 1ff9852cff
No known key found for this signature in database
GPG Key ID: 5E8AFC3034FDFA4F
5 changed files with 325 additions and 291 deletions

View File

@ -18,6 +18,7 @@ sled = { version = "0.31.0", optional = true }
electrum-client = { git = "https://github.com/MagicalBitcoin/rust-electrum-client.git", optional = true } electrum-client = { git = "https://github.com/MagicalBitcoin/rust-electrum-client.git", optional = true }
reqwest = { version = "0.10", optional = true, features = ["json"] } reqwest = { version = "0.10", optional = true, features = ["json"] }
futures = { version = "0.3", optional = true } futures = { version = "0.3", optional = true }
clap = { version = "2.33", optional = true }
[features] [features]
minimal = [] minimal = []
@ -26,18 +27,19 @@ default = ["key-value-db", "electrum"]
electrum = ["electrum-client"] electrum = ["electrum-client"]
esplora = ["reqwest", "futures"] esplora = ["reqwest", "futures"]
key-value-db = ["sled"] key-value-db = ["sled"]
cli-utils = ["clap"]
[dev-dependencies] [dev-dependencies]
tokio = { version = "0.2", features = ["macros"] } tokio = { version = "0.2", features = ["macros"] }
lazy_static = "1.4" lazy_static = "1.4"
rustyline = "5.0" # newer version requires 2018 edition rustyline = "6.0"
clap = "2.33"
dirs = "2.0" dirs = "2.0"
env_logger = "0.7" env_logger = "0.7"
rand = "0.7" rand = "0.7"
[[example]] [[example]]
name = "repl" name = "repl"
required-features = ["cli-utils"]
[[example]] [[example]]
name = "psbt" name = "psbt"
[[example]] [[example]]
@ -52,4 +54,5 @@ required-features = ["compiler"]
[[example]] [[example]]
name = "magic" name = "magic"
path = "examples/repl.rs" path = "examples/repl.rs"
required-features = ["cli-utils"]

View File

@ -1,32 +1,21 @@
extern crate base64;
extern crate clap;
extern crate dirs;
extern crate env_logger;
extern crate log;
extern crate magical_bitcoin_wallet;
extern crate rustyline;
use std::fs; use std::fs;
use std::path::PathBuf; use std::path::PathBuf;
use std::str::FromStr;
use std::sync::Arc; use std::sync::Arc;
use clap::{App, AppSettings, Arg, ArgMatches, SubCommand};
use rustyline::error::ReadlineError; use rustyline::error::ReadlineError;
use rustyline::Editor; use rustyline::Editor;
use clap::AppSettings;
#[allow(unused_imports)] #[allow(unused_imports)]
use log::{debug, error, info, trace, LevelFilter}; use log::{debug, error, info, trace, LevelFilter};
use bitcoin::consensus::encode::{deserialize, serialize, serialize_hex}; use bitcoin::Network;
use bitcoin::util::psbt::PartiallySignedTransaction;
use bitcoin::{Address, Network, OutPoint};
use magical_bitcoin_wallet::bitcoin; use magical_bitcoin_wallet::bitcoin;
use magical_bitcoin_wallet::blockchain::ElectrumBlockchain; use magical_bitcoin_wallet::blockchain::ElectrumBlockchain;
use magical_bitcoin_wallet::cli;
use magical_bitcoin_wallet::sled; use magical_bitcoin_wallet::sled;
use magical_bitcoin_wallet::types::ScriptType;
use magical_bitcoin_wallet::{Client, Wallet}; use magical_bitcoin_wallet::{Client, Wallet};
fn prepare_home_dir() -> PathBuf { fn prepare_home_dir() -> PathBuf {
@ -43,204 +32,14 @@ fn prepare_home_dir() -> PathBuf {
dir dir
} }
fn parse_addressee(s: &str) -> Result<(Address, u64), String> {
let parts: Vec<_> = s.split(":").collect();
if parts.len() != 2 {
return Err("Invalid format".to_string());
}
let addr = Address::from_str(&parts[0]);
if let Err(e) = addr {
return Err(format!("{:?}", e));
}
let val = u64::from_str(&parts[1]);
if let Err(e) = val {
return Err(format!("{:?}", e));
}
Ok((addr.unwrap(), val.unwrap()))
}
fn parse_outpoint(s: &str) -> Result<OutPoint, String> {
OutPoint::from_str(s).map_err(|e| format!("{:?}", e))
}
fn addressee_validator(s: String) -> Result<(), String> {
parse_addressee(&s).map(|_| ())
}
fn outpoint_validator(s: String) -> Result<(), String> {
parse_outpoint(&s).map(|_| ())
}
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
env_logger::init(); env_logger::init();
let app = App::new("Magical Bitcoin Wallet") let app = cli::make_cli_subcommands();
.version(option_env!("CARGO_PKG_VERSION").unwrap_or("unknown"))
.author(option_env!("CARGO_PKG_AUTHORS").unwrap_or(""))
.about("A modern, lightweight, descriptor-based wallet")
.subcommand(
SubCommand::with_name("get_new_address").about("Generates a new external address"),
)
.subcommand(SubCommand::with_name("sync").about("Syncs with the chosen Electrum server"))
.subcommand(
SubCommand::with_name("list_unspent").about("Lists the available spendable UTXOs"),
)
.subcommand(
SubCommand::with_name("get_balance").about("Returns the current wallet balance"),
)
.subcommand(
SubCommand::with_name("create_tx")
.about("Creates a new unsigned tranasaction")
.arg(
Arg::with_name("to")
.long("to")
.value_name("ADDRESS:SAT")
.help("Adds an addressee to the transaction")
.takes_value(true)
.number_of_values(1)
.required(true)
.multiple(true)
.validator(addressee_validator),
)
.arg(
Arg::with_name("send_all")
.short("all")
.long("send_all")
.help("Sends all the funds (or all the selected utxos). Requires only one addressees of value 0"),
)
.arg(
Arg::with_name("utxos")
.long("utxos")
.value_name("TXID:VOUT")
.help("Selects which utxos *must* be spent")
.takes_value(true)
.number_of_values(1)
.multiple(true)
.validator(outpoint_validator),
)
.arg(
Arg::with_name("unspendable")
.long("unspendable")
.value_name("TXID:VOUT")
.help("Marks an utxo as unspendable")
.takes_value(true)
.number_of_values(1)
.multiple(true)
.validator(outpoint_validator),
)
.arg(
Arg::with_name("fee_rate")
.short("fee")
.long("fee_rate")
.value_name("SATS_VBYTE")
.help("Fee rate to use in sat/vbyte")
.takes_value(true),
)
.arg(
Arg::with_name("policy")
.long("policy")
.value_name("POLICY")
.help("Selects which policy will be used to satisfy the descriptor")
.takes_value(true)
.number_of_values(1),
),
)
.subcommand(
SubCommand::with_name("policies")
.about("Returns the available spending policies for the descriptor")
)
.subcommand(
SubCommand::with_name("sign")
.about("Signs and tries to finalize a PSBT")
.arg(
Arg::with_name("psbt")
.long("psbt")
.value_name("BASE64_PSBT")
.help("Sets the PSBT to sign")
.takes_value(true)
.number_of_values(1)
.required(true),
)
.arg(
Arg::with_name("assume_height")
.long("assume_height")
.value_name("HEIGHT")
.help("Assume the blockchain has reached a specific height. This affects the transaction finalization, if there are timelocks in the descriptor")
.takes_value(true)
.number_of_values(1)
.required(false),
))
.subcommand(
SubCommand::with_name("broadcast")
.about("Extracts the finalized transaction from a PSBT and broadcasts it to the network")
.arg(
Arg::with_name("psbt")
.long("psbt")
.value_name("BASE64_PSBT")
.help("Sets the PSBT to broadcast")
.takes_value(true)
.number_of_values(1)
.required(true),
));
let mut repl_app = app.clone().setting(AppSettings::NoBinaryName); let mut repl_app = app.clone().setting(AppSettings::NoBinaryName);
let app = app let app = cli::add_global_flags(app);
.arg(
Arg::with_name("network")
.short("n")
.long("network")
.value_name("NETWORK")
.help("Sets the network")
.takes_value(true)
.default_value("testnet")
.possible_values(&["testnet", "regtest"]),
)
.arg(
Arg::with_name("wallet")
.short("w")
.long("wallet")
.value_name("WALLET_NAME")
.help("Selects the wallet to use")
.takes_value(true)
.default_value("main"),
)
.arg(
Arg::with_name("server")
.short("s")
.long("server")
.value_name("SERVER:PORT")
.help("Sets the Electrum server to use")
.takes_value(true)
.default_value("tn.not.fyi:55001"),
)
.arg(
Arg::with_name("descriptor")
.short("d")
.long("descriptor")
.value_name("DESCRIPTOR")
.help("Sets the descriptor to use for the external addresses")
.required(true)
.takes_value(true),
)
.arg(
Arg::with_name("change_descriptor")
.short("c")
.long("change_descriptor")
.value_name("DESCRIPTOR")
.help("Sets the descriptor to use for internal addresses")
.takes_value(true),
)
.arg(
Arg::with_name("v")
.short("v")
.multiple(true)
.help("Sets the level of verbosity"),
)
.subcommand(SubCommand::with_name("repl").about("Opens an interactive shell"));
let matches = app.get_matches(); let matches = app.get_matches();
@ -280,86 +79,6 @@ async fn main() {
.unwrap(); .unwrap();
let wallet = Arc::new(wallet); let wallet = Arc::new(wallet);
// TODO: print errors in a nice way
async fn handle_matches<C, D>(wallet: Arc<Wallet<C, D>>, matches: ArgMatches<'_>)
where
C: magical_bitcoin_wallet::blockchain::OnlineBlockchain,
D: magical_bitcoin_wallet::database::BatchDatabase,
{
if let Some(_sub_matches) = matches.subcommand_matches("get_new_address") {
println!("{}", wallet.get_new_address().unwrap().to_string());
} else if let Some(_sub_matches) = matches.subcommand_matches("sync") {
wallet.sync(None, None).await.unwrap();
} else if let Some(_sub_matches) = matches.subcommand_matches("list_unspent") {
for utxo in wallet.list_unspent().unwrap() {
println!("{} value {} SAT", utxo.outpoint, utxo.txout.value);
}
} else if let Some(_sub_matches) = matches.subcommand_matches("get_balance") {
println!("{} SAT", wallet.get_balance().unwrap());
} else if let Some(sub_matches) = matches.subcommand_matches("create_tx") {
let addressees = sub_matches
.values_of("to")
.unwrap()
.map(|s| parse_addressee(s).unwrap())
.collect();
let send_all = sub_matches.is_present("send_all");
let fee_rate = sub_matches
.value_of("fee_rate")
.map(|s| f32::from_str(s).unwrap())
.unwrap_or(1.0);
let utxos = sub_matches
.values_of("utxos")
.map(|s| s.map(|i| parse_outpoint(i).unwrap()).collect());
let unspendable = sub_matches
.values_of("unspendable")
.map(|s| s.map(|i| parse_outpoint(i).unwrap()).collect());
let policy: Option<Vec<_>> = sub_matches
.value_of("policy")
.map(|s| serde_json::from_str::<Vec<Vec<usize>>>(&s).unwrap());
let result = wallet
.create_tx(
addressees,
send_all,
fee_rate * 1e-5,
policy,
utxos,
unspendable,
)
.unwrap();
println!("{:#?}", result.1);
println!("PSBT: {}", base64::encode(&serialize(&result.0)));
} else if let Some(_sub_matches) = matches.subcommand_matches("policies") {
println!(
"External: {}",
serde_json::to_string(&wallet.policies(ScriptType::External).unwrap()).unwrap()
);
println!(
"Internal: {}",
serde_json::to_string(&wallet.policies(ScriptType::Internal).unwrap()).unwrap()
);
} else if let Some(sub_matches) = matches.subcommand_matches("sign") {
let psbt = base64::decode(sub_matches.value_of("psbt").unwrap()).unwrap();
let psbt: PartiallySignedTransaction = deserialize(&psbt).unwrap();
let assume_height = sub_matches
.value_of("assume_height")
.and_then(|s| Some(s.parse().unwrap()));
let (psbt, finalized) = wallet.sign(psbt, assume_height).unwrap();
println!("PSBT: {}", base64::encode(&serialize(&psbt)));
println!("Finalized: {}", finalized);
if finalized {
println!("Extracted: {}", serialize_hex(&psbt.extract_tx()));
}
} else if let Some(sub_matches) = matches.subcommand_matches("broadcast") {
let psbt = base64::decode(sub_matches.value_of("psbt").unwrap()).unwrap();
let psbt: PartiallySignedTransaction = deserialize(&psbt).unwrap();
let (txid, _) = wallet.broadcast(psbt).await.unwrap();
println!("TXID: {}", txid);
}
};
if let Some(_sub_matches) = matches.subcommand_matches("repl") { if let Some(_sub_matches) = matches.subcommand_matches("repl") {
let mut rl = Editor::<()>::new(); let mut rl = Editor::<()>::new();
@ -382,7 +101,7 @@ async fn main() {
continue; continue;
} }
handle_matches(Arc::clone(&wallet), matches.unwrap()).await; cli::handle_matches(&Arc::clone(&wallet), matches.unwrap()).await;
} }
Err(ReadlineError::Interrupted) => continue, Err(ReadlineError::Interrupted) => continue,
Err(ReadlineError::Eof) => break, Err(ReadlineError::Eof) => break,
@ -395,6 +114,6 @@ async fn main() {
// rl.save_history("history.txt").unwrap(); // rl.save_history("history.txt").unwrap();
} else { } else {
handle_matches(wallet, matches).await; cli::handle_matches(&wallet, matches).await;
} }
} }

300
src/cli.rs Normal file
View File

@ -0,0 +1,300 @@
use std::str::FromStr;
use clap::{App, Arg, ArgMatches, SubCommand};
#[allow(unused_imports)]
use log::{debug, error, info, trace, LevelFilter};
use bitcoin::consensus::encode::{deserialize, serialize, serialize_hex};
use bitcoin::util::psbt::PartiallySignedTransaction;
use bitcoin::{Address, OutPoint};
use crate::error::Error;
use crate::types::ScriptType;
use crate::Wallet;
fn parse_addressee(s: &str) -> Result<(Address, u64), String> {
let parts: Vec<_> = s.split(":").collect();
if parts.len() != 2 {
return Err("Invalid format".to_string());
}
let addr = Address::from_str(&parts[0]);
if let Err(e) = addr {
return Err(format!("{:?}", e));
}
let val = u64::from_str(&parts[1]);
if let Err(e) = val {
return Err(format!("{:?}", e));
}
Ok((addr.unwrap(), val.unwrap()))
}
fn parse_outpoint(s: &str) -> Result<OutPoint, String> {
OutPoint::from_str(s).map_err(|e| format!("{:?}", e))
}
fn addressee_validator(s: String) -> Result<(), String> {
parse_addressee(&s).map(|_| ())
}
fn outpoint_validator(s: String) -> Result<(), String> {
parse_outpoint(&s).map(|_| ())
}
pub fn make_cli_subcommands<'a, 'b>() -> App<'a, 'b> {
App::new("Magical Bitcoin Wallet")
.version(option_env!("CARGO_PKG_VERSION").unwrap_or("unknown"))
.author(option_env!("CARGO_PKG_AUTHORS").unwrap_or(""))
.about("A modern, lightweight, descriptor-based wallet")
.subcommand(
SubCommand::with_name("get_new_address").about("Generates a new external address"),
)
.subcommand(SubCommand::with_name("sync").about("Syncs with the chosen Electrum server"))
.subcommand(
SubCommand::with_name("list_unspent").about("Lists the available spendable UTXOs"),
)
.subcommand(
SubCommand::with_name("get_balance").about("Returns the current wallet balance"),
)
.subcommand(
SubCommand::with_name("create_tx")
.about("Creates a new unsigned tranasaction")
.arg(
Arg::with_name("to")
.long("to")
.value_name("ADDRESS:SAT")
.help("Adds an addressee to the transaction")
.takes_value(true)
.number_of_values(1)
.required(true)
.multiple(true)
.validator(addressee_validator),
)
.arg(
Arg::with_name("send_all")
.short("all")
.long("send_all")
.help("Sends all the funds (or all the selected utxos). Requires only one addressees of value 0"),
)
.arg(
Arg::with_name("utxos")
.long("utxos")
.value_name("TXID:VOUT")
.help("Selects which utxos *must* be spent")
.takes_value(true)
.number_of_values(1)
.multiple(true)
.validator(outpoint_validator),
)
.arg(
Arg::with_name("unspendable")
.long("unspendable")
.value_name("TXID:VOUT")
.help("Marks an utxo as unspendable")
.takes_value(true)
.number_of_values(1)
.multiple(true)
.validator(outpoint_validator),
)
.arg(
Arg::with_name("fee_rate")
.short("fee")
.long("fee_rate")
.value_name("SATS_VBYTE")
.help("Fee rate to use in sat/vbyte")
.takes_value(true),
)
.arg(
Arg::with_name("policy")
.long("policy")
.value_name("POLICY")
.help("Selects which policy will be used to satisfy the descriptor")
.takes_value(true)
.number_of_values(1),
),
)
.subcommand(
SubCommand::with_name("policies")
.about("Returns the available spending policies for the descriptor")
)
.subcommand(
SubCommand::with_name("sign")
.about("Signs and tries to finalize a PSBT")
.arg(
Arg::with_name("psbt")
.long("psbt")
.value_name("BASE64_PSBT")
.help("Sets the PSBT to sign")
.takes_value(true)
.number_of_values(1)
.required(true),
)
.arg(
Arg::with_name("assume_height")
.long("assume_height")
.value_name("HEIGHT")
.help("Assume the blockchain has reached a specific height. This affects the transaction finalization, if there are timelocks in the descriptor")
.takes_value(true)
.number_of_values(1)
.required(false),
))
.subcommand(
SubCommand::with_name("broadcast")
.about("Extracts the finalized transaction from a PSBT and broadcasts it to the network")
.arg(
Arg::with_name("psbt")
.long("psbt")
.value_name("BASE64_PSBT")
.help("Sets the PSBT to broadcast")
.takes_value(true)
.number_of_values(1)
.required(true),
))
}
pub fn add_global_flags<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> {
app.arg(
Arg::with_name("network")
.short("n")
.long("network")
.value_name("NETWORK")
.help("Sets the network")
.takes_value(true)
.default_value("testnet")
.possible_values(&["testnet", "regtest"]),
)
.arg(
Arg::with_name("wallet")
.short("w")
.long("wallet")
.value_name("WALLET_NAME")
.help("Selects the wallet to use")
.takes_value(true)
.default_value("main"),
)
.arg(
Arg::with_name("server")
.short("s")
.long("server")
.value_name("SERVER:PORT")
.help("Sets the Electrum server to use")
.takes_value(true)
.default_value("tn.not.fyi:55001"),
)
.arg(
Arg::with_name("descriptor")
.short("d")
.long("descriptor")
.value_name("DESCRIPTOR")
.help("Sets the descriptor to use for the external addresses")
.required(true)
.takes_value(true),
)
.arg(
Arg::with_name("change_descriptor")
.short("c")
.long("change_descriptor")
.value_name("DESCRIPTOR")
.help("Sets the descriptor to use for internal addresses")
.takes_value(true),
)
.arg(
Arg::with_name("v")
.short("v")
.multiple(true)
.help("Sets the level of verbosity"),
)
.subcommand(SubCommand::with_name("repl").about("Opens an interactive shell"))
}
pub async fn handle_matches<C, D>(
wallet: &Wallet<C, D>,
matches: ArgMatches<'_>,
) -> Result<Option<String>, Error>
where
C: crate::blockchain::OnlineBlockchain,
D: crate::database::BatchDatabase,
{
if let Some(_sub_matches) = matches.subcommand_matches("get_new_address") {
Ok(Some(format!("{}", wallet.get_new_address()?)))
} else if let Some(_sub_matches) = matches.subcommand_matches("sync") {
wallet.sync(None, None).await?;
Ok(None)
} else if let Some(_sub_matches) = matches.subcommand_matches("list_unspent") {
let mut res = String::new();
for utxo in wallet.list_unspent()? {
res += &format!("{} value {} SAT\n", utxo.outpoint, utxo.txout.value);
}
Ok(Some(res))
} else if let Some(_sub_matches) = matches.subcommand_matches("get_balance") {
Ok(Some(format!("{} SAT", wallet.get_balance()?)))
} else if let Some(sub_matches) = matches.subcommand_matches("create_tx") {
let addressees = sub_matches
.values_of("to")
.unwrap()
.map(|s| parse_addressee(s).unwrap())
.collect();
let send_all = sub_matches.is_present("send_all");
let fee_rate = sub_matches
.value_of("fee_rate")
.map(|s| f32::from_str(s).unwrap())
.unwrap_or(1.0);
let utxos = sub_matches
.values_of("utxos")
.map(|s| s.map(|i| parse_outpoint(i).unwrap()).collect());
let unspendable = sub_matches
.values_of("unspendable")
.map(|s| s.map(|i| parse_outpoint(i).unwrap()).collect());
let policy: Option<Vec<_>> = sub_matches
.value_of("policy")
.map(|s| serde_json::from_str::<Vec<Vec<usize>>>(&s).unwrap());
let result = wallet.create_tx(
addressees,
send_all,
fee_rate * 1e-5,
policy,
utxos,
unspendable,
)?;
Ok(Some(format!(
"{:#?}\nPSBT: {}",
result.1,
base64::encode(&serialize(&result.0))
)))
} else if let Some(_sub_matches) = matches.subcommand_matches("policies") {
Ok(Some(format!(
"External: {}\nInternal:{}",
serde_json::to_string(&wallet.policies(ScriptType::External)?).unwrap(),
serde_json::to_string(&wallet.policies(ScriptType::Internal)?).unwrap(),
)))
} else if let Some(sub_matches) = matches.subcommand_matches("sign") {
let psbt = base64::decode(sub_matches.value_of("psbt").unwrap()).unwrap();
let psbt: PartiallySignedTransaction = deserialize(&psbt).unwrap();
let assume_height = sub_matches
.value_of("assume_height")
.and_then(|s| Some(s.parse().unwrap()));
let (psbt, finalized) = wallet.sign(psbt, assume_height)?;
let mut res = String::new();
res += &format!("PSBT: {}\n", base64::encode(&serialize(&psbt)));
res += &format!("Finalized: {}", finalized);
if finalized {
res += &format!("\nExtracted: {}", serialize_hex(&psbt.extract_tx()));
}
Ok(Some(res))
} else if let Some(sub_matches) = matches.subcommand_matches("broadcast") {
let psbt = base64::decode(sub_matches.value_of("psbt").unwrap()).unwrap();
let psbt: PartiallySignedTransaction = deserialize(&psbt).unwrap();
let (txid, _) = wallet.broadcast(psbt).await?;
Ok(Some(format!("TXID: {}", txid)))
} else {
Ok(None)
}
}

View File

@ -25,6 +25,9 @@ pub use blockchain::esplora::EsploraBlockchain;
#[cfg(feature = "key-value-db")] #[cfg(feature = "key-value-db")]
pub extern crate sled; pub extern crate sled;
#[cfg(feature = "cli-utils")]
pub mod cli;
#[macro_use] #[macro_use]
pub mod error; pub mod error;
pub mod blockchain; pub mod blockchain;

View File

@ -498,6 +498,7 @@ where
// Internals // Internals
#[cfg(not(target_arch = "wasm32"))]
fn get_timestamp() -> u64 { fn get_timestamp() -> u64 {
SystemTime::now() SystemTime::now()
.duration_since(UNIX_EPOCH) .duration_since(UNIX_EPOCH)
@ -505,6 +506,11 @@ where
.as_secs() .as_secs()
} }
#[cfg(target_arch = "wasm32")]
fn get_timestamp() -> u64 {
0
}
fn get_descriptor_for(&self, script_type: ScriptType) -> &ExtendedDescriptor { fn get_descriptor_for(&self, script_type: ScriptType) -> &ExtendedDescriptor {
let desc = match script_type { let desc = match script_type {
ScriptType::External => &self.descriptor, ScriptType::External => &self.descriptor,
@ -646,6 +652,7 @@ where
// safe to run only on the descriptor because we assume the change descriptor also has // safe to run only on the descriptor because we assume the change descriptor also has
// the same structure // the same structure
let desc = self.descriptor.derive_from_psbt_input(psbt, n); let desc = self.descriptor.derive_from_psbt_input(psbt, n);
debug!("{:?}", psbt.inputs[n].hd_keypaths);
debug!("reconstructed descriptor is {:?}", desc); debug!("reconstructed descriptor is {:?}", desc);
let desc = match desc { let desc = match desc {
@ -765,6 +772,7 @@ where
// cache a few of our addresses // cache a few of our addresses
if last_addr.is_none() { if last_addr.is_none() {
let mut address_batch = self.database.borrow().begin_batch(); let mut address_batch = self.database.borrow().begin_batch();
#[cfg(not(target_arch = "wasm32"))]
let start = Instant::now(); let start = Instant::now();
for i in 0..=max_address { for i in 0..=max_address {
@ -790,6 +798,7 @@ where
} }
} }
#[cfg(not(target_arch = "wasm32"))]
info!( info!(
"derivation of {} addresses, took {} ms", "derivation of {} addresses, took {} ms",
max_address, max_address,