"electrsd",
"env_logger",
"fd-lock",
+ "js-sys",
"log",
+ "rand 0.6.5",
"regex",
"rustyline",
+ "secp256k1",
+ "serde",
"serde_json",
"structopt",
"tokio",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "wasm-logger",
"zeroize",
]
[[package]]
name = "js-sys"
-version = "0.3.59"
+version = "0.3.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "258451ab10b34f8af53416d1fdab72c22e805f0c92a1136d59470ec0b11138b2"
+checksum = "a38fc24e30fd564ce974c02bf1d337caddff65be6cc4735a1f7eab22a7440f04"
dependencies = [
"wasm-bindgen",
]
"libc",
"rand_core 0.4.2",
"rdrand",
+ "wasm-bindgen",
"winapi",
]
[[package]]
name = "wasm-bindgen"
-version = "0.2.82"
+version = "0.2.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fc7652e3f6c4706c8d9cd54832c4a4ccb9b5336e2c3bd154d5cccfbf1c1f5f7d"
+checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06"
dependencies = [
"cfg-if",
+ "serde",
+ "serde_json",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
-version = "0.2.82"
+version = "0.2.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "662cd44805586bd52971b9586b1df85cdbbd9112e4ef4d8f41559c334dc6ac3f"
+checksum = "8b21c0df030f5a177f3cba22e9bc4322695ec43e7257d865302900290bcdedca"
dependencies = [
"bumpalo",
+ "lazy_static",
"log",
- "once_cell",
"proc-macro2",
"quote",
"syn",
[[package]]
name = "wasm-bindgen-futures"
-version = "0.4.32"
+version = "0.4.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa76fb221a1f8acddf5b54ace85912606980ad661ac7a503b4570ffd3a624dad"
+checksum = "2eb6ec270a31b1d3c7e266b999739109abce8b6c87e4b31fcfcd788b65267395"
dependencies = [
"cfg-if",
"js-sys",
[[package]]
name = "wasm-bindgen-macro"
-version = "0.2.82"
+version = "0.2.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b260f13d3012071dfb1512849c033b1925038373aea48ced3012c09df952c602"
+checksum = "2f4203d69e40a52ee523b2529a773d5ffc1dc0071801c87b3d270b471b80ed01"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
[[package]]
name = "wasm-bindgen-macro-support"
-version = "0.2.82"
+version = "0.2.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5be8e654bdd9b79216c2929ab90721aa82faf65c48cdf08bdc4e7f51357b80da"
+checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc"
dependencies = [
"proc-macro2",
"quote",
[[package]]
name = "wasm-bindgen-shared"
-version = "0.2.82"
+version = "0.2.79"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3d958d035c4438e28c70e4321a2911302f10135ce78a9c7834c0cab4123d06a2"
+
+[[package]]
+name = "wasm-logger"
+version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6598dd0bd3c7d51095ff6531a5b23e02acdc81804e30d8f07afb77b7215a140a"
+checksum = "074649a66bb306c8f2068c9016395fa65d8e08d2affcbf95acf3c24c3ab19718"
+dependencies = [
+ "log",
+ "wasm-bindgen",
+ "web-sys",
+]
[[package]]
name = "web-sys"
-version = "0.3.59"
+version = "0.3.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed055ab27f941423197eb86b2035720b1a3ce40504df082cac2ecc6ed73335a1"
+checksum = "c060b319f29dd25724f09a2ba1418f142f539b2be99fbf4d2d5a8f7330afb8eb"
dependencies = [
"js-sys",
"wasm-bindgen",
regex = { version = "1", optional = true }
bdk-reserves = { version = "0.22", optional = true }
electrsd = { version= "0.19", features = ["bitcoind_22_0"], optional = true}
-tokio = { version = "1", features = ["rt", "macros", "rt-multi-thread"], optional = true }
+
+# Platform-specific dependencies
+[target.'cfg(target_arch = "wasm32")'.dependencies]
+wasm-bindgen = { version = "=0.2.79", features = ["serde-serialize"] }
+wasm-bindgen-futures = { version = "0.4" }
+js-sys = "=0.3.56"
+wasm-logger = "0.2.0"
+secp256k1 = { version = "0.22.0", default-features = false }
+rand = { version = "^0.6", features = ["wasm-bindgen"] }
+serde = { version = "^1.0", features = ["derive"] }
+regex = { version = "1" }
+
+[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
+tokio = { version = "1", features = ["rt", "macros", "rt-multi-thread"] }
[features]
default = ["repl", "sqlite-db"]
compact_filters = ["bdk/compact_filters"]
esplora = []
esplora-ureq = ["esplora", "bdk/use-esplora-ureq"]
-async-interface = ["bdk/async-interface", "tokio"]
+async-interface = ["bdk/async-interface"]
esplora-reqwest = ["esplora", "bdk/use-esplora-reqwest", "bdk/reqwest-default-tls", "async-interface"]
# Use this to consensus verify transactions at sync time
regtest-electrum = ["regtest-node", "electrum", "electrsd/electrs_0_8_10"]
#TODO: Check why esplora in electrsd isn't working.
#regtest-esplora-ureq = ["regtest-node", "esplora-ureq", "electrsd/esplora_a33e97e1"]
-#regtest-esplora-reqwest = ["regtest-node", "esplora-reqwest", "electrsd/esplora_a33e97e1"]
\ No newline at end of file
+#regtest-esplora-reqwest = ["regtest-node", "esplora-reqwest", "electrsd/esplora_a33e97e1"]
--- /dev/null
+use crate::commands::*;
+use crate::handlers::*;
+use crate::nodes::Nodes;
+use crate::utils::*;
+use bdk::*;
+
+use bitcoin::*;
+
+use bdk::blockchain::AnyBlockchain;
+use bdk::database::AnyDatabase;
+use js_sys::Promise;
+use regex::Regex;
+use std::error::Error;
+use std::ops::Deref;
+use std::path::PathBuf;
+use std::rc::Rc;
+use std::str::FromStr;
+use structopt::StructOpt;
+use wasm_bindgen::prelude::*;
+use wasm_bindgen_futures::future_to_promise;
+
+#[cfg(feature = "compiler")]
+use bdk::keys::{GeneratableDefaultOptions, GeneratedKey};
+#[cfg(feature = "compiler")]
+use bdk::miniscript::{self, policy::Concrete, Descriptor, TranslatePk};
+#[cfg(feature = "compiler")]
+use serde::Deserialize;
+
+#[wasm_bindgen]
+pub struct WasmWallet {
+ wallet: Rc<Wallet<AnyDatabase>>,
+ wallet_opts: Rc<WalletOpts>,
+ blockchain: Rc<AnyBlockchain>,
+ network: Network,
+}
+
+#[wasm_bindgen]
+pub fn log_init() {
+ wasm_logger::init(wasm_logger::Config::default());
+}
+
+#[wasm_bindgen]
+impl WasmWallet {
+ #[wasm_bindgen(constructor)]
+ pub fn new(network: String, wallet_opts: Vec<JsValue>) -> Result<WasmWallet, String> {
+ fn new_inner(
+ network: String,
+ wallet_opts: Vec<JsValue>,
+ ) -> Result<WasmWallet, Box<dyn Error>> {
+ // Both open_database and new_blockchain need a home path to be passed
+ // in, even tho it won't be used
+ let dummy_home_dir = PathBuf::new();
+ let wallet_opts = wallet_opts
+ .into_iter()
+ .map(|a| a.as_string().expect("Invalid type"));
+ let wallet_opts: WalletOpts = WalletOpts::from_iter_safe(wallet_opts)?;
+ let network = Network::from_str(&network)?;
+ let wallet_opts = maybe_descriptor_wallet_name(wallet_opts, network)?;
+ let database = open_database(&wallet_opts, &dummy_home_dir)?;
+ let wallet = new_wallet(network, &wallet_opts, database)?;
+ let blockchain = new_blockchain(network, &wallet_opts, &Nodes::None, &dummy_home_dir)?;
+ Ok(WasmWallet {
+ wallet: Rc::new(wallet),
+ wallet_opts: Rc::new(wallet_opts),
+ blockchain: Rc::new(blockchain),
+ network,
+ })
+ }
+
+ new_inner(network, wallet_opts).map_err(|e| e.to_string().into())
+ }
+
+ pub fn run_command(&self, command: String) -> Promise {
+ let wallet = Rc::clone(&self.wallet);
+ let wallet_opts = Rc::clone(&self.wallet_opts);
+ let blockchain = Rc::clone(&self.blockchain);
+ let network = self.network;
+
+ async fn run_command_inner(
+ command: String,
+ wallet: Rc<Wallet<AnyDatabase>>,
+ wallet_opts: Rc<WalletOpts>,
+ blockchain: Rc<AnyBlockchain>,
+ network: Network,
+ ) -> Result<serde_json::Value, Box<dyn Error>> {
+ let split_regex = Regex::new(crate::REPL_LINE_SPLIT_REGEX)?;
+ let split_line: Vec<&str> = split_regex
+ .captures_iter(&command)
+ .map(|c| {
+ Ok(c.get(1)
+ .or_else(|| c.get(2))
+ .or_else(|| c.get(3))
+ .ok_or_else(|| "Invalid commands".to_string())?
+ .as_str())
+ })
+ .collect::<Result<Vec<_>, String>>()?;
+ let repl_subcommand = ReplSubCommand::from_iter_safe(split_line)?;
+ log::debug!("repl_subcommand = {:?}", repl_subcommand);
+
+ let result = match repl_subcommand {
+ ReplSubCommand::Wallet {
+ subcommand: WalletSubCommand::OnlineWalletSubCommand(online_subcommand),
+ } => {
+ handle_online_wallet_subcommand(&wallet, blockchain.deref(), online_subcommand)
+ .await?
+ }
+ ReplSubCommand::Wallet {
+ subcommand: WalletSubCommand::OfflineWalletSubCommand(offline_subcommand),
+ } => handle_offline_wallet_subcommand(&wallet, &wallet_opts, offline_subcommand)?,
+ ReplSubCommand::Key { subcommand } => handle_key_subcommand(network, subcommand)?,
+ ReplSubCommand::Exit => return Ok(serde_json::Value::Null),
+ };
+
+ Ok(result)
+ }
+
+ future_to_promise(async move {
+ run_command_inner(command, wallet, wallet_opts, blockchain, network)
+ .await
+ .map(|v| JsValue::from_serde(&v).expect("Serde serialization failed"))
+ .map_err(|e| e.to_string().into())
+ })
+ }
+}
+
+#[wasm_bindgen]
+#[cfg(feature = "compiler")]
+pub fn compile(policy: String, aliases: String, script_type: String) -> Result<JsValue, String> {
+ fn compile_inner(
+ policy: String,
+ aliases: String,
+ script_type: String,
+ ) -> Result<String, Box<dyn Error>> {
+ use std::collections::HashMap;
+ let aliases: HashMap<String, Alias> = serde_json::from_str(&aliases)?;
+ let aliases: HashMap<String, String> = aliases
+ .into_iter()
+ .map(|(k, v)| (k, v.into_key()))
+ .collect();
+
+ let policy = Concrete::<String>::from_str(&policy)?;
+
+ let descriptor = match script_type.as_str() {
+ "sh" => Descriptor::new_sh(policy.compile()?)?,
+ "wsh" => Descriptor::new_wsh(policy.compile()?)?,
+ "sh-wsh" => Descriptor::new_sh_wsh(policy.compile()?)?,
+ _ => return Err(Box::<dyn Error>::from("InvalidScriptType")),
+ };
+
+ let descriptor: Result<Descriptor<String>, bdk::Error> = descriptor.translate_pk(
+ |key| Ok(aliases.get(key).unwrap_or(key).into()),
+ |key| Ok(aliases.get(key).unwrap_or(key).into()),
+ );
+ let descriptor = descriptor?;
+
+ Ok(descriptor.to_string().into())
+ }
+
+ compile_inner(policy, aliases, script_type)
+ .map(|v| JsValue::from_serde(&v).expect("Serde serialization failed"))
+ .map_err(|e| e.to_string().into())
+}
+
+#[derive(Debug, Deserialize)]
+#[serde(tag = "type", rename_all = "snake_case")]
+#[cfg(feature = "compiler")]
+enum Alias {
+ GenWif,
+ GenExt { extra: String },
+ Existing { extra: String },
+}
+
+#[cfg(feature = "compiler")]
+impl Alias {
+ fn into_key(self) -> String {
+ match self {
+ Alias::GenWif => {
+ let generated: GeneratedKey<bitcoin::PrivateKey, miniscript::Legacy> =
+ GeneratableDefaultOptions::generate_default().unwrap();
+
+ let mut key = generated.into_key();
+ key.network = Network::Testnet;
+
+ key.to_wif()
+ }
+ Alias::GenExt { extra: path } => {
+ let generated: GeneratedKey<
+ bitcoin::util::bip32::ExtendedPrivKey,
+ miniscript::Legacy,
+ > = GeneratableDefaultOptions::generate_default().unwrap();
+
+ let mut xprv = generated.into_key();
+ xprv.network = Network::Testnet;
+
+ format!("{}{}", xprv, path)
+ }
+ Alias::Existing { extra } => extra,
+ }
+ }
+}