diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e0ef7cc..030de58 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,6 @@ jobs: - all - debug,default - debug,esplora - - debug,snap steps: - name: checkout uses: actions/checkout@v4 diff --git a/Cargo.lock b/Cargo.lock index 2a5d8b3..da920d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -154,10 +154,8 @@ dependencies = [ "js-sys", "miniscript", "ring", - "rmp-serde", "serde", "serde-wasm-bindgen", - "thiserror 2.0.6", "wasm-bindgen", "wasm-bindgen-futures", "wasm-bindgen-test", @@ -927,15 +925,6 @@ dependencies = [ "tempfile", ] -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", -] - [[package]] name = "object" version = "0.36.5" @@ -995,12 +984,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "paste" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" - [[package]] name = "percent-encoding" version = "2.3.1" @@ -1138,28 +1121,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "rmp" -version = "0.8.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "228ed7c16fa39782c3b3468e974aec2795e9089153cd08ee2e9aefb3613334c4" -dependencies = [ - "byteorder", - "num-traits", - "paste", -] - -[[package]] -name = "rmp-serde" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52e599a477cf9840e92f2cde9a7189e67b42c57532749bf90aea6ec10facd4db" -dependencies = [ - "byteorder", - "rmp", - "serde", -] - [[package]] name = "rustc-demangle" version = "0.1.24" @@ -1428,16 +1389,7 @@ version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl 1.0.69", -] - -[[package]] -name = "thiserror" -version = "2.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec2a1820ebd077e2b90c4df007bebf344cd394098a13c563957d0afc83ea47" -dependencies = [ - "thiserror-impl 2.0.6", + "thiserror-impl", ] [[package]] @@ -1451,17 +1403,6 @@ dependencies = [ "syn", ] -[[package]] -name = "thiserror-impl" -version = "2.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d65750cab40f4ff1929fb1ba509e9914eb756131cef4210da8d5d700d26f6312" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "tinystr" version = "0.7.6" @@ -1520,7 +1461,7 @@ checksum = "0d4770b8024672c1101b3f6733eab95b18007dbe0847a8afe341fcf79e06043f" dependencies = [ "either", "futures-util", - "thiserror 1.0.69", + "thiserror", "tokio", ] diff --git a/Cargo.toml b/Cargo.toml index b4e7ad3..7cb23ae 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,17 +25,14 @@ crate-type = ["cdylib", "rlib"] default = [] esplora = ["bdk_esplora"] debug = ["console_error_panic_hook"] -snap = ["default"] [dependencies] wasm-bindgen = "0.2.99" wasm-bindgen-futures = "0.4.49" anyhow = "1.0.94" -thiserror = "2.0.6" serde = { version = "1.0.216", default-features = false, features = ["derive"] } js-sys = "0.3.76" serde-wasm-bindgen = "0.6.5" -rmp-serde = "1.3.0" # Compatibility to compile to WASM getrandom = { version = "0.2.15", features = ["js"] } diff --git a/README.md b/README.md index 9cbe3c9..8526863 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ CC = "/opt/homebrew/opt/llvm/bin/clang" wasm-pack build ``` -> Choose your desired features when building: `wasm-pack build --features "esplora default snap bitcoind"` +> Choose your desired features when building: `--features "esplora"` ### Test in Headless Browsers with `wasm-pack test` diff --git a/src/bitcoin/mod.rs b/src/bitcoin/mod.rs index c88853b..ae4e51c 100644 --- a/src/bitcoin/mod.rs +++ b/src/bitcoin/mod.rs @@ -7,11 +7,5 @@ pub use wallet::*; #[cfg(feature = "esplora")] mod esplora_client; -#[cfg(feature = "snap")] -mod snap_wallet; - #[cfg(feature = "esplora")] pub use esplora_client::EsploraClient; - -#[cfg(feature = "snap")] -pub use snap_wallet::SnapWallet; diff --git a/src/bitcoin/snap_wallet.rs b/src/bitcoin/snap_wallet.rs deleted file mode 100644 index aab9ab8..0000000 --- a/src/bitcoin/snap_wallet.rs +++ /dev/null @@ -1,167 +0,0 @@ -use std::str::FromStr; - -use bdk_wallet::{descriptor::IntoWalletDescriptor, PersistedWallet, Wallet as BdkWallet}; -use bitcoin::bip32::{Fingerprint, Xpriv, Xpub}; -use serde_wasm_bindgen::to_value; -use wasm_bindgen::{prelude::wasm_bindgen, JsError, JsValue}; - -use crate::{ - bitcoin::{seed_to_descriptor, xpriv_to_descriptor, xpub_to_descriptor}, - result::JsResult, - storage::SnapPersister, - types::{ - AddressInfo, AddressType, Balance, CheckPoint, FullScanRequest, KeychainKind, Network, SyncRequest, Update, - }, -}; - -const STORAGE_KEY: &str = "wallet"; - -#[wasm_bindgen] -pub struct SnapWallet { - wallet: PersistedWallet, - persister: SnapPersister, -} - -#[wasm_bindgen] -impl SnapWallet { - async fn create(network: Network, external_descriptor: D, internal_descriptor: D) -> JsResult - where - D: IntoWalletDescriptor + Send + Clone + 'static, - { - let mut persister = SnapPersister::new(STORAGE_KEY); - let wallet = BdkWallet::create(external_descriptor, internal_descriptor) - .network(network.into()) - .create_wallet_async(&mut persister) - .await?; - - Ok(SnapWallet { wallet, persister }) - } - - pub async fn load() -> JsResult { - let mut persister = SnapPersister::new(STORAGE_KEY); - let wallet_opt = BdkWallet::load().load_wallet_async(&mut persister).await?; - - let wallet = match wallet_opt { - Some(wallet) => wallet, - None => return Err(JsError::new("Failed to load wallet, check the changeset")), - }; - - Ok(SnapWallet { wallet, persister }) - } - - pub async fn from_descriptors( - network: Network, - external_descriptor: String, - internal_descriptor: String, - ) -> JsResult { - Self::create(network, external_descriptor, internal_descriptor).await - } - - pub async fn from_seed(seed: &[u8], network: Network, address_type: AddressType) -> JsResult { - let (external_descriptor, internal_descriptor) = - seed_to_descriptor(seed, network.into(), address_type.into()).map_err(|e| JsError::new(&e.to_string()))?; - - Self::create(network, external_descriptor, internal_descriptor).await - } - - pub async fn from_xpriv( - extended_privkey: &str, - fingerprint: &str, - network: Network, - address_type: AddressType, - ) -> JsResult { - let xprv = Xpriv::from_str(extended_privkey).map_err(|e| JsError::new(&e.to_string()))?; - let fingerprint = Fingerprint::from_hex(fingerprint)?; - - let (external_descriptor, internal_descriptor) = - xpriv_to_descriptor(xprv, fingerprint, network.into(), address_type.into()) - .map_err(|e| JsError::new(&e.to_string()))?; - - Self::create(network, external_descriptor, internal_descriptor).await - } - - pub async fn from_xpub( - extended_pubkey: &str, - fingerprint: &str, - network: Network, - address_type: AddressType, - ) -> JsResult { - let xpub = Xpub::from_str(extended_pubkey)?; - let fingerprint = Fingerprint::from_hex(fingerprint)?; - - let (external_descriptor, internal_descriptor) = - xpub_to_descriptor(xpub, fingerprint, network.into(), address_type.into()) - .map_err(|e| JsError::new(&e.to_string()))?; - - Self::create(network, external_descriptor, internal_descriptor).await - } - - pub fn start_full_scan(&self) -> FullScanRequest { - self.wallet.start_full_scan().build().into() - } - - pub fn start_sync_with_revealed_spks(&self) -> SyncRequest { - self.wallet.start_sync_with_revealed_spks().build().into() - } - - pub fn apply_update_at(&mut self, update: Update, seen_at: u64) -> JsResult<()> { - self.wallet.apply_update_at(update, seen_at)?; - Ok(()) - } - - pub fn network(&self) -> Network { - self.wallet.network().into() - } - - pub fn balance(&self) -> Balance { - self.wallet.balance().into() - } - - pub fn next_unused_address(&mut self, keychain: KeychainKind) -> AddressInfo { - self.wallet.next_unused_address(keychain.into()).into() - } - - pub fn peek_address(&self, keychain: KeychainKind, index: u32) -> AddressInfo { - self.wallet.peek_address(keychain.into(), index).into() - } - - pub fn reveal_next_address(&mut self, keychain: KeychainKind) -> AddressInfo { - self.wallet.reveal_next_address(keychain.into()).into() - } - - pub fn reveal_addresses_to(&mut self, keychain: KeychainKind, index: u32) -> Vec { - self.wallet - .reveal_addresses_to(keychain.into(), index) - .map(Into::into) - .collect() - } - - pub fn list_unused_addresses(&self, keychain: KeychainKind) -> Vec { - self.wallet - .list_unused_addresses(keychain.into()) - .map(Into::into) - .collect() - } - - pub fn list_unspent(&self) -> JsResult> { - self.wallet - .list_unspent() - .map(|output| to_value(&output).map_err(Into::into)) - .collect() - } - - pub fn transactions(&self) -> JsResult> { - self.wallet - .transactions() - .map(|tx| to_value(&tx.tx_node.tx).map_err(Into::into)) - .collect() - } - - pub fn latest_checkpoint(&self) -> CheckPoint { - self.wallet.latest_checkpoint().into() - } - - pub async fn persist(&mut self) -> JsResult { - self.wallet.persist_async(&mut self.persister).await.map_err(Into::into) - } -} diff --git a/src/lib.rs b/src/lib.rs index 8d6d889..6eed588 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,4 @@ pub mod bitcoin; -pub mod storage; pub mod types; mod utils; diff --git a/src/storage/mod.rs b/src/storage/mod.rs deleted file mode 100644 index 0a84d99..0000000 --- a/src/storage/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -#[cfg(feature = "snap")] -mod snap_persister; - -#[cfg(feature = "snap")] -pub use snap_persister::SnapPersister; diff --git a/src/storage/snap_persister.rs b/src/storage/snap_persister.rs deleted file mode 100644 index 343f657..0000000 --- a/src/storage/snap_persister.rs +++ /dev/null @@ -1,135 +0,0 @@ -use std::{collections::HashMap, future::Future, pin::Pin}; - -use crate::{types::SnapPersisterError, SendSyncWrapper}; -use bdk_wallet::{chain::Merge, AsyncWalletPersister, ChangeSet}; -use bitcoin::base64::{prelude::BASE64_STANDARD, Engine}; -use js_sys::Promise; -use serde::{Deserialize, Serialize}; -use serde_wasm_bindgen::{from_value, to_value, Serializer}; -use wasm_bindgen::prelude::{wasm_bindgen, JsValue}; -use wasm_bindgen_futures::JsFuture; - -type SnapState = HashMap; - -pub struct SnapPersister { - key: String, - serializer: Serializer, -} - -impl SnapPersister { - pub fn new(key: &str) -> Self { - Self { - key: key.to_string(), - serializer: Serializer::json_compatible(), - } - } - - async fn read_changeset(&self) -> Result { - let state = self.read_snap_state().await?; - self.extract_changeset(&state) - } - - async fn write_changeset(&self, new_changeset: &ChangeSet) -> Result<(), SnapPersisterError> { - let mut state = self.read_snap_state().await?; - let mut changeset = self.extract_changeset(&state)?; - changeset.merge(new_changeset.clone()); - - let state_bytes = rmp_serde::to_vec(&changeset).map_err(SnapPersisterError::EncodeMP)?; - let state_b64 = BASE64_STANDARD.encode(&state_bytes); - state.insert(self.key.clone(), state_b64); - - let args = RequestArguments { - method: "snap_manageState".to_string(), - params: RequestParams { - operation: "update".to_string(), - new_state: Some(state), - }, - }; - - let promise = snap_request(&args.serialize(&self.serializer).unwrap()); - JsFuture::from(promise) - .await - .map_err(SnapPersisterError::WriteSnapState)?; - - Ok(()) - } - - async fn read_snap_state(&self) -> Result { - let args = RequestArguments { - method: "snap_manageState".to_string(), - params: RequestParams { - operation: "get".to_string(), - new_state: None, - }, - }; - - let promise = snap_request(&to_value(&args).unwrap()); - let state = JsFuture::from(promise) - .await - .map_err(SnapPersisterError::ReadSnapState)?; - - if state.is_undefined() || state.is_null() { - Ok(SnapState::new()) - } else { - from_value(state).map_err(SnapPersisterError::Deserialize) - } - } - - fn extract_changeset(&self, state: &SnapState) -> Result { - if let Some(state_b64) = state.get(&self.key) { - let state_bytes = BASE64_STANDARD - .decode(state_b64) - .map_err(SnapPersisterError::DecodeBase64)?; - rmp_serde::from_slice(&state_bytes).map_err(SnapPersisterError::DecodeMP) - } else { - Ok(ChangeSet::default()) - } - } -} - -impl AsyncWalletPersister for SnapPersister { - type Error = SnapPersisterError; - - fn initialize<'a>( - persister: &'a mut Self, - ) -> Pin> + Send + 'a>> - where - Self: 'a, - { - let fut = async move { persister.read_changeset().await }; - let send_fut = SendSyncWrapper(fut); - Box::pin(send_fut) - } - - fn persist<'a>( - persister: &'a mut Self, - changeset: &'a ChangeSet, - ) -> Pin> + Send + 'a>> - where - Self: 'a, - { - let fut = async move { persister.write_changeset(changeset).await }; - let send_fut = SendSyncWrapper(fut); - Box::pin(send_fut) - } -} - -#[wasm_bindgen] -extern "C" { - #[wasm_bindgen(js_namespace = snap, js_name = request)] - fn snap_request(params: &JsValue) -> Promise; -} - -#[derive(Serialize, Deserialize, Clone)] -pub struct RequestArguments { - method: String, - params: RequestParams, -} - -#[derive(Serialize, Deserialize, Clone)] -#[serde(rename_all = "camelCase")] -pub struct RequestParams { - operation: String, - #[serde(skip_serializing_if = "Option::is_none")] - new_state: Option, -} diff --git a/src/types/error.rs b/src/types/error.rs deleted file mode 100644 index 1828ece..0000000 --- a/src/types/error.rs +++ /dev/null @@ -1,20 +0,0 @@ -use thiserror::Error; -use wasm_bindgen::JsValue; - -#[derive(Error, Debug)] -pub enum SnapPersisterError { - #[error("Failed to deserialize wallet state: {:?}", 0)] - Reflect(JsValue), - #[error("Failed to read snap state: {:?}", 0)] - ReadSnapState(JsValue), - #[error("Failed to write snap state: {:?}", 0)] - WriteSnapState(JsValue), - #[error("Failed to encode MessagePack: {0}")] - EncodeMP(#[source] rmp_serde::encode::Error), - #[error("Failed to decode MessagePack: {0}")] - DecodeMP(#[source] rmp_serde::decode::Error), - #[error("Failed to decode base64: {0}")] - DecodeBase64(#[source] bitcoin::base64::DecodeError), - #[error("Failed to deserialize: {0}")] - Deserialize(#[source] serde_wasm_bindgen::Error), -} diff --git a/src/types/mod.rs b/src/types/mod.rs index 5cb485f..d5c42e2 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -6,7 +6,6 @@ mod chain; mod changeset; mod checkpoint; mod descriptor; -mod error; mod keychain; mod network; mod slip10; @@ -19,7 +18,6 @@ pub use chain::*; pub use changeset::*; pub use checkpoint::*; pub use descriptor::*; -pub use error::*; pub use keychain::*; pub use network::*; pub use slip10::*;