From c833789b480e9ce399f9a7c4466782a3efc49e97 Mon Sep 17 00:00:00 2001 From: rustaceanrob Date: Wed, 31 Jul 2024 18:59:15 -0500 Subject: [PATCH] deps: bump bdk to beta-2 temp 2 impl wallet no persist enable default constructor on wallet type test: fix jvm tests add tx methods new errors feat: expose correct errors remove macros for in-memory connection tests(python): add in-memory connection tests(swift, kotlin): add tests with swift failures lib: bump to bdk-wallet 1.0.0-beta2 various fixes --- bdk-ffi/Cargo.lock | 157 +++---- bdk-ffi/Cargo.toml | 13 +- bdk-ffi/src/bdk.udl | 86 ++-- bdk-ffi/src/electrum.rs | 31 +- bdk-ffi/src/error.rs | 168 ++++---- bdk-ffi/src/esplora.rs | 14 +- bdk-ffi/src/lib.rs | 17 +- bdk-ffi/src/store.rs | 39 +- bdk-ffi/src/types.rs | 153 ++++--- bdk-ffi/src/wallet.rs | 382 +++--------------- .../bitcoindevkit/LiveElectrumClientTest.kt | 5 +- .../org/bitcoindevkit/LiveMemoryWalletTest.kt | 10 +- .../org/bitcoindevkit/LiveTransactionTests.kt | 9 +- .../org/bitcoindevkit/LiveTxBuilderTest.kt | 12 +- .../org/bitcoindevkit/LiveWalletTest.kt | 12 +- .../bitcoindevkit/OfflinePersistenceTest.kt | 9 +- .../org/bitcoindevkit/OfflineWalletTest.kt | 24 +- ...re_existing_wallet_persistence_test.sqlite | Bin 57344 -> 53248 bytes bdk-jvm/scripts/build-macos-aarch64.sh | 13 + bdk-python/tests/test_live_tx_builder.py | 10 +- bdk-python/tests/test_live_wallet.py | 10 +- bdk-python/tests/test_offline_wallet.py | 10 +- .../LiveElectrumClientTests.swift | 4 +- .../LiveMemoryWalletTests.swift | 8 +- .../LiveTransactionTests.swift | 4 +- .../LiveTxBuilderTests.swift | 8 +- .../BitcoinDevKitTests/LiveWalletTests.swift | 8 +- .../OfflinePersistenceTests.swift | 10 +- .../OfflineWalletTests.swift | 8 +- 29 files changed, 488 insertions(+), 746 deletions(-) create mode 100644 bdk-jvm/scripts/build-macos-aarch64.sh diff --git a/bdk-ffi/Cargo.lock b/bdk-ffi/Cargo.lock index a789c7c3..edc1a1b0 100644 --- a/bdk-ffi/Cargo.lock +++ b/bdk-ffi/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "ahash" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0453232ace82dee0dd0b4c87a59bd90f7b53b314f3e0f61fe2ee7c8a16482289" + [[package]] name = "ahash" version = "0.8.11" @@ -171,9 +177,9 @@ version = "1.0.0-alpha.11" dependencies = [ "assert_matches", "bdk_bitcoind_rpc", + "bdk_core", "bdk_electrum", "bdk_esplora", - "bdk_sqlite", "bdk_wallet", "bitcoin-internals 0.2.0", "thiserror", @@ -182,71 +188,72 @@ dependencies = [ [[package]] name = "bdk_bitcoind_rpc" -version = "0.12.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae8b85b7f47af08bb41d6fb9301c9de8ad937a4d506ba0d9920b094fd48d8fbb" +checksum = "21067332a1d532f093d1d4c634f55be24a7f85a9d7d642cbad16ad16196b8e15" dependencies = [ - "bdk_chain", + "bdk_core", "bitcoin", "bitcoincore-rpc", ] [[package]] name = "bdk_chain" -version = "0.16.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "163b064557cee078e8ee5dd2c88944204506f7b2b1524f78e8fcba38c346da7b" +checksum = "4658f922245892aafb2e9cdfd6674b10b8057b81f0cc2cc5920d4c3a434f4311" dependencies = [ + "bdk_core", "bitcoin", "miniscript", + "rusqlite", "serde", + "serde_json", ] [[package]] -name = "bdk_electrum" -version = "0.15.0" -source = "git+https://github.com/thunderbiscuit/bdk/?branch=feature/electrum-client-ring-ffi-alpha13#1d745e492d53937c6c405e5564798dbd8ef1c1ed" +name = "bdk_core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78dd27e151907dadd43f2d17d14ff613331ffd6c3eb8838d0b38976c8588b2fa" dependencies = [ - "bdk_chain", - "electrum-client", + "bitcoin", + "hashbrown 0.9.1", + "serde", ] [[package]] -name = "bdk_esplora" -version = "0.15.0" +name = "bdk_electrum" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "089babab213bbb32518bad79a7313ebb4c85a52c18c8b558402dfa810c27de3f" +checksum = "111d37830e50f5722a372b1f621a81d6b5d4d70c226ea4e342aea0cc7b35c635" dependencies = [ - "bdk_chain", - "esplora-client", - "miniscript", + "bdk_core", + "electrum-client", ] [[package]] -name = "bdk_sqlite" -version = "0.2.0" +name = "bdk_esplora" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0926dc5778fb3c5afaf7def9ed9b9c9d9fe9e3ba499e09cb816d3de43211be71" +checksum = "0ac3c89662ed981033ca27fa33c7446ecbadb65b39edbfe1d1f2e066003fc373" dependencies = [ - "bdk_chain", - "rusqlite", - "serde", - "serde_json", + "bdk_core", + "esplora-client", + "miniscript", ] [[package]] name = "bdk_wallet" -version = "1.0.0-alpha.13" +version = "1.0.0-beta.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2926afdbfc54ebf7df2caa51af5be4435b90c01c6fbe5578b51b7c2c0a264bd9" +checksum = "9b38aa12ef660265f50802437496cb18bda14774683ab736ba7a7e584885a91c" dependencies = [ "bdk_chain", "bip39", "bitcoin", - "getrandom", - "js-sys", "miniscript", - "rand", + "rand_core", "serde", "serde_json", ] @@ -374,12 +381,6 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" -[[package]] -name = "bumpalo" -version = "3.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" - [[package]] name = "byteorder" version = "1.5.0" @@ -484,8 +485,9 @@ checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" [[package]] name = "electrum-client" -version = "0.20.0" -source = "git+https://github.com/thunderbiscuit/rust-electrum-client/?branch=feature/rustls-ring#c7a96f7264348537f4b57dd149fd114f22844b16" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a0bd443023f9f5c4b7153053721939accc7113cbdf810a024434eed454b3db1" dependencies = [ "bitcoin", "byteorder", @@ -500,9 +502,9 @@ dependencies = [ [[package]] name = "esplora-client" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69c6d27ef4ff21019edd98aa92199757e10a88065bbfcef6bb750ca6ec5e4a45" +checksum = "9b546e91283ebfc56337de34e0cf814e3ad98083afde593b8e58495ee5355d0e" dependencies = [ "bitcoin", "hex-conservative", @@ -560,13 +562,23 @@ dependencies = [ "scroll", ] +[[package]] +name = "hashbrown" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" +dependencies = [ + "ahash 0.4.8", + "serde", +] + [[package]] name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ - "ahash", + "ahash 0.8.11", ] [[package]] @@ -575,7 +587,7 @@ version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" dependencies = [ - "hashbrown", + "hashbrown 0.14.5", ] [[package]] @@ -611,15 +623,6 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" -[[package]] -name = "js-sys" -version = "0.3.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" -dependencies = [ - "wasm-bindgen", -] - [[package]] name = "jsonrpc" version = "0.18.0" @@ -1269,60 +1272,6 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" -[[package]] -name = "wasm-bindgen" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.92" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" - [[package]] name = "webpki-roots" version = "0.25.4" diff --git a/bdk-ffi/Cargo.toml b/bdk-ffi/Cargo.toml index a15accba..d4f75263 100644 --- a/bdk-ffi/Cargo.toml +++ b/bdk-ffi/Cargo.toml @@ -18,14 +18,11 @@ path = "uniffi-bindgen.rs" default = ["uniffi/cli"] [dependencies] -bdk_wallet = { version = "1.0.0-alpha.13", features = ["all-keys", "keys-bip39"] } -bdk_esplora = { version = "0.15.0", default-features = false, features = ["std", "blocking", "blocking-https-rustls"] } -# NOTE: This is a temporary workaround to use the electrum-client with the use-rustls-ring feature. It points to a fork -# of bdk in which the bdk_electrum library uses the electrum-client with the use-rustls-ring feature. -bdk_electrum = { git = "https://github.com/thunderbiscuit/bdk/", package = "bdk_electrum", branch = "feature/electrum-client-ring-ffi-alpha13", default-features = false, features = ["use-rustls-ring"] } -# bdk_electrum = { version = "0.15.0" } -bdk_sqlite = { version = "0.2.0" } -bdk_bitcoind_rpc = { version = "0.12.0" } +bdk_wallet = { version = "1.0.0-beta.2", features = ["all-keys", "keys-bip39", "rusqlite"] } +bdk_core = { version = "0.1.0" } +bdk_esplora = { version = "0.17.0", default-features = false, features = ["std", "blocking", "blocking-https-rustls"] } +bdk_electrum = { version = "0.17.0", default-features = false, features = ["use-rustls-ring"] } +bdk_bitcoind_rpc = { version = "0.14.0" } bitcoin-internals = { version = "0.2.0", features = ["alloc"] } uniffi = { version = "=0.28.0" } diff --git a/bdk-ffi/src/bdk.udl b/bdk-ffi/src/bdk.udl index bacb8756..ed0b0b40 100644 --- a/bdk-ffi/src/bdk.udl +++ b/bdk-ffi/src/bdk.udl @@ -78,6 +78,13 @@ interface CreateTxError { MiniscriptPsbt(string error_message); }; +[Error] +interface CreateWithPersistError { + Persist(string error_message); + DataAlreadyExists(); + Descriptor(string error_message); +}; + [Error] interface DescriptorError { InvalidHdKeyPath(); @@ -161,6 +168,13 @@ interface FromScriptError { OtherFromScriptErr(); }; +[Error] +interface LoadWithPersistError { + Persist(string error_message); + InvalidChangeSet(string error_message); + CouldNotLoad(); +}; + [Error] interface ParseAmountError { OutOfRange(); @@ -220,7 +234,7 @@ interface PsbtParseError { }; [Error] -interface InspectError { +interface RequestBuilderError { RequestAlreadyConsumed(); }; @@ -242,11 +256,11 @@ interface SignerError { TxInputsIndexError(string error_message); MiniscriptPsbt(string error_message); External(string error_message); + Psbt(string error_message); }; [Error] interface SqliteError { - InvalidNetwork(Network expected, Network given); Sqlite(string rusqlite_error); }; @@ -266,14 +280,6 @@ interface TxidParseError { InvalidTxid(string txid); }; -[Error] -interface WalletCreationError { - Descriptor(string error_message); - LoadedGenesisDoesNotMatch(string expected, string got); - LoadedNetworkDoesNotMatch(Network expected, Network? got); - LoadedDescriptorDoesNotMatch(string got, KeychainKind keychain); -}; - // ------------------------------------------------------------------------ // bdk_wallet crate - types module // ------------------------------------------------------------------------ @@ -317,25 +323,45 @@ dictionary TxOut { [Enum] interface ChainPosition { - Confirmed(u32 height, u64 timestamp); + Confirmed(ConfirmationBlockTime confirmation_block_time); Unconfirmed(u64 timestamp); }; +dictionary ConfirmationBlockTime { + BlockId block_id; + u64 confirmation_time; +}; + +dictionary BlockId { + u32 height; + string hash; +}; + dictionary CanonicalTx { Transaction transaction; ChainPosition chain_position; }; -interface FullScanRequest { - [Throws=InspectError] - FullScanRequest inspect_spks_for_all_keychains(FullScanScriptInspector inspector); +interface FullScanRequestBuilder { + [Throws=RequestBuilderError] + FullScanRequestBuilder inspect_spks_for_all_keychains(FullScanScriptInspector inspector); + + [Throws=RequestBuilderError] + FullScanRequest build(); }; -interface SyncRequest { - [Throws=InspectError] - SyncRequest inspect_spks(SyncScriptInspector inspector); +interface SyncRequestBuilder { + [Throws=RequestBuilderError] + SyncRequestBuilder inspect_spks(SyncScriptInspector inspector); + + [Throws=RequestBuilderError] + SyncRequest build(); }; +interface FullScanRequest {}; + +interface SyncRequest {}; + [Trait, WithForeign] interface SyncScriptInspector { void inspect(Script script, u64 total); @@ -359,11 +385,11 @@ enum ChangeSpendPolicy { }; interface Wallet { - [Throws=WalletCreationError] - constructor(Descriptor descriptor, Descriptor change_descriptor, Network network); + [Throws=CreateWithPersistError] + constructor(Descriptor descriptor, Descriptor change_descriptor, Network network, Connection connection); - [Name=new_or_load, Throws=WalletCreationError] - constructor(Descriptor descriptor, Descriptor change_descriptor, ChangeSet? change_set, Network network); + [Name=load, Throws=LoadWithPersistError] + constructor(Descriptor descriptor, Descriptor change_descriptor, Connection connection); AddressInfo reveal_next_address(KeychainKind keychain); @@ -374,7 +400,7 @@ interface Wallet { [Throws=CannotConnectError] void apply_update(Update update); - boolean is_mine([ByRef] Script script); + boolean is_mine(Script script); [Throws=SignerError] boolean sign(Psbt psbt); @@ -396,11 +422,12 @@ interface Wallet { sequence list_output(); - FullScanRequest start_full_scan(); + FullScanRequestBuilder start_full_scan(); - SyncRequest start_sync_with_revealed_spks(); + SyncRequestBuilder start_sync_with_revealed_spks(); - ChangeSet? take_staged(); + [Throws=SqliteError] + boolean persist(Connection connection); }; interface Update {}; @@ -457,15 +484,12 @@ interface BumpFeeTxBuilder { // bdk_sqlite crate // ------------------------------------------------------------------------ -interface SqliteStore { +interface Connection { [Throws=SqliteError] constructor(string path); - [Throws=SqliteError] - void write([ByRef] ChangeSet change_set); - - [Throws=SqliteError] - ChangeSet? read(); + [Name=new_in_memory, Throws=SqliteError] + constructor(); }; // ------------------------------------------------------------------------ diff --git a/bdk-ffi/src/electrum.rs b/bdk-ffi/src/electrum.rs index 3bc1a9cf..0854dc2a 100644 --- a/bdk-ffi/src/electrum.rs +++ b/bdk-ffi/src/electrum.rs @@ -1,17 +1,16 @@ use crate::bitcoin::Transaction; use crate::error::ElectrumError; +use crate::types::Update; use crate::types::{FullScanRequest, SyncRequest}; -use crate::wallet::Update; +use bdk_core::spk_client::FullScanRequest as BdkFullScanRequest; +use bdk_core::spk_client::FullScanResult as BdkFullScanResult; +use bdk_core::spk_client::SyncRequest as BdkSyncRequest; +use bdk_core::spk_client::SyncResult as BdkSyncResult; use bdk_electrum::BdkElectrumClient as BdkBdkElectrumClient; -use bdk_electrum::{ElectrumFullScanResult, ElectrumSyncResult}; use bdk_wallet::bitcoin::Transaction as BdkTransaction; -use bdk_wallet::chain::spk_client::FullScanRequest as BdkFullScanRequest; -use bdk_wallet::chain::spk_client::FullScanResult as BdkFullScanResult; -use bdk_wallet::chain::spk_client::SyncRequest as BdkSyncRequest; -use bdk_wallet::chain::spk_client::SyncResult as BdkSyncResult; -use bdk_wallet::wallet::Update as BdkUpdate; use bdk_wallet::KeychainKind; +use bdk_wallet::Update as BdkUpdate; use std::collections::BTreeMap; use std::sync::Arc; @@ -44,19 +43,17 @@ impl ElectrumClient { .take() .ok_or(ElectrumError::RequestAlreadyConsumed)?; - let electrum_result: ElectrumFullScanResult = self.0.full_scan( + let full_scan_result: BdkFullScanResult = self.0.full_scan( request, stop_gap as usize, batch_size as usize, fetch_prev_txouts, )?; - let full_scan_result: BdkFullScanResult = - electrum_result.with_confirmation_time_height_anchor(&self.0)?; let update = BdkUpdate { last_active_indices: full_scan_result.last_active_indices, - graph: full_scan_result.graph_update, - chain: Some(full_scan_result.chain_update), + tx_update: full_scan_result.tx_update, + chain: full_scan_result.chain_update, }; Ok(Arc::new(Update(update))) @@ -69,23 +66,21 @@ impl ElectrumClient { fetch_prev_txouts: bool, ) -> Result, ElectrumError> { // using option and take is not ideal but the only way to take full ownership of the request - let request: BdkSyncRequest = request + let request: BdkSyncRequest<(KeychainKind, u32)> = request .0 .lock() .unwrap() .take() .ok_or(ElectrumError::RequestAlreadyConsumed)?; - let electrum_result: ElectrumSyncResult = + let sync_result: BdkSyncResult = self.0 .sync(request, batch_size as usize, fetch_prev_txouts)?; - let sync_result: BdkSyncResult = - electrum_result.with_confirmation_time_height_anchor(&self.0)?; let update = BdkUpdate { last_active_indices: BTreeMap::default(), - graph: sync_result.graph_update, - chain: Some(sync_result.chain_update), + tx_update: sync_result.tx_update, + chain: sync_result.chain_update, }; Ok(Arc::new(Update(update))) diff --git a/bdk-ffi/src/error.rs b/bdk-ffi/src/error.rs index 30353222..1e9313b9 100644 --- a/bdk-ffi/src/error.rs +++ b/bdk-ffi/src/error.rs @@ -1,10 +1,8 @@ use crate::bitcoin::OutPoint; -use crate::Network; use bdk_bitcoind_rpc::bitcoincore_rpc::bitcoin::address::ParseError; use bdk_electrum::electrum_client::Error as BdkElectrumError; use bdk_esplora::esplora_client::{Error as BdkEsploraError, Error}; -use bdk_sqlite::Error as BdkSqliteError; use bdk_wallet::bitcoin::address::FromScriptError as BdkFromScriptError; use bdk_wallet::bitcoin::address::ParseError as BdkParseError; use bdk_wallet::bitcoin::amount::ParseAmountError as BdkParseAmountError; @@ -14,18 +12,20 @@ use bdk_wallet::bitcoin::psbt::Error as BdkPsbtError; use bdk_wallet::bitcoin::psbt::ExtractTxError as BdkExtractTxError; use bdk_wallet::bitcoin::psbt::PsbtParseError as BdkPsbtParseError; use bdk_wallet::chain::local_chain::CannotConnectError as BdkCannotConnectError; +use bdk_wallet::chain::rusqlite::Error as BdkSqliteError; use bdk_wallet::chain::tx_graph::CalculateFeeError as BdkCalculateFeeError; use bdk_wallet::descriptor::DescriptorError as BdkDescriptorError; +use bdk_wallet::error::BuildFeeBumpError; +use bdk_wallet::error::CreateTxError as BdkCreateTxError; use bdk_wallet::keys::bip39::Error as BdkBip39Error; use bdk_wallet::miniscript::descriptor::DescriptorKeyParseError as BdkDescriptorKeyParseError; -use bdk_wallet::wallet::error::BuildFeeBumpError; -use bdk_wallet::wallet::error::CreateTxError as BdkCreateTxError; -use bdk_wallet::wallet::signer::SignerError as BdkSignerError; -use bdk_wallet::wallet::tx_builder::AddUtxoError; -use bdk_wallet::wallet::{NewError, NewOrLoadError}; -use bdk_wallet::KeychainKind; +use bdk_wallet::signer::SignerError as BdkSignerError; +use bdk_wallet::tx_builder::AddUtxoError; +use bdk_wallet::CreateWithPersistError as BdkCreateWithPersistError; +use bdk_wallet::LoadWithPersistError as BdkLoadWithPersistError; use bitcoin_internals::hex::display::DisplayHex; +use bdk_wallet::chain; use std::convert::TryInto; // ------------------------------------------------------------------------ @@ -201,6 +201,18 @@ pub enum CreateTxError { MiniscriptPsbt { error_message: String }, } +#[derive(Debug, thiserror::Error)] +pub enum CreateWithPersistError { + #[error("sqlite persistence error: {error_message}")] + Persist { error_message: String }, + + #[error("the wallet has already been created")] + DataAlreadyExists, + + #[error("the loaded changeset cannot construct wallet: {error_message}")] + Descriptor { error_message: String }, +} + #[derive(Debug, thiserror::Error)] pub enum DescriptorError { #[error("invalid hd key path")] @@ -391,11 +403,23 @@ pub enum FromScriptError { } #[derive(Debug, thiserror::Error)] -pub enum InspectError { +pub enum RequestBuilderError { #[error("the request has already been consumed")] RequestAlreadyConsumed, } +#[derive(Debug, thiserror::Error)] +pub enum LoadWithPersistError { + #[error("sqlite persistence error: {error_message}")] + Persist { error_message: String }, + + #[error("the loaded changeset cannot construct wallet: {error_message}")] + InvalidChangeSet { error_message: String }, + + #[error("could not load")] + CouldNotLoad, +} + #[derive(Debug, thiserror::Error)] pub enum ParseAmountError { #[error("amount out of range")] @@ -586,16 +610,14 @@ pub enum SignerError { #[error("external error: {error_message}")] External { error_message: String }, + + #[error("Psbt error: {error_message}")] + Psbt { error_message: String }, } #[derive(Debug, thiserror::Error)] pub enum SqliteError { - // NOTE: This error is renamed from Network to InvalidNetwork to avoid conflict with the Network - // enum in uniffi. - #[error("invalid network, cannot change the one already stored in the database")] - InvalidNetwork { expected: Network, given: Network }, - - #[error("SQLite error: {rusqlite_error}")] + #[error("sqlite error: {rusqlite_error}")] Sqlite { rusqlite_error: String }, } @@ -630,29 +652,6 @@ pub enum TxidParseError { InvalidTxid { txid: String }, } -// This error combines the Rust bdk_wallet::wallet::NewError and bdk_wallet::wallet::NewOrLoadError -#[derive(Debug, thiserror::Error)] -pub enum WalletCreationError { - // From NewError and NewOrLoadError - #[error("error with descriptor: {error_message}")] - Descriptor { error_message: String }, - - // From NewOrLoadError - #[error("loaded genesis hash '{got}' does not match the expected one '{expected}'")] - LoadedGenesisDoesNotMatch { expected: String, got: String }, - - // From NewOrLoadError - #[error("loaded network type is not {expected}, got {got:?}")] - LoadedNetworkDoesNotMatch { - expected: Network, - got: Option, - }, - - // From NewOrLoadError - #[error("loaded descriptor '{got}' does not match what was provided '{keychain:?}'")] - LoadedDescriptorDoesNotMatch { got: String, keychain: KeychainKind }, -} - // ------------------------------------------------------------------------ // error conversions // ------------------------------------------------------------------------ @@ -861,6 +860,23 @@ impl From for CreateTxError { } } +impl From> for CreateWithPersistError { + fn from(error: BdkCreateWithPersistError) -> Self { + match error { + BdkCreateWithPersistError::Persist(e) => CreateWithPersistError::Persist { + error_message: e.to_string(), + }, + BdkCreateWithPersistError::Descriptor(e) => CreateWithPersistError::Descriptor { + error_message: e.to_string(), + }, + // Objects cannot currently be used in enumerations + BdkCreateWithPersistError::DataAlreadyExists(_e) => { + CreateWithPersistError::DataAlreadyExists + } + } + } +} + impl From for CreateTxError { fn from(error: AddUtxoError) -> Self { match error { @@ -1058,6 +1074,21 @@ impl From for FromScriptError { } } +impl From> for LoadWithPersistError { + fn from(error: BdkLoadWithPersistError) -> Self { + match error { + BdkLoadWithPersistError::Persist(e) => LoadWithPersistError::Persist { + error_message: e.to_string(), + }, + BdkLoadWithPersistError::InvalidChangeSet(e) => { + LoadWithPersistError::InvalidChangeSet { + error_message: e.to_string(), + } + } + } + } +} + impl From for ParseAmountError { fn from(error: BdkParseAmountError) -> Self { match error { @@ -1179,19 +1210,16 @@ impl From for SignerError { BdkSignerError::MissingHdKeypath => SignerError::MissingHdKeypath, BdkSignerError::NonStandardSighash => SignerError::NonStandardSighash, BdkSignerError::InvalidSighash => SignerError::InvalidSighash, - BdkSignerError::SighashP2wpkh(e) => SignerError::SighashP2wpkh { - error_message: e.to_string(), - }, BdkSignerError::SighashTaproot(e) => SignerError::SighashTaproot { error_message: e.to_string(), }, - BdkSignerError::TxInputsIndexError(e) => SignerError::TxInputsIndexError { - error_message: e.to_string(), - }, BdkSignerError::MiniscriptPsbt(e) => SignerError::MiniscriptPsbt { error_message: e.to_string(), }, BdkSignerError::External(e) => SignerError::External { error_message: e }, + BdkSignerError::Psbt(e) => SignerError::Psbt { + error_message: e.to_string(), + }, } } } @@ -1221,58 +1249,12 @@ impl From for TransactionError { impl From for SqliteError { fn from(error: BdkSqliteError) -> Self { - match error { - BdkSqliteError::Network { expected, given } => { - SqliteError::InvalidNetwork { expected, given } - } - BdkSqliteError::Sqlite(e) => SqliteError::Sqlite { - rusqlite_error: e.to_string(), - }, - } - } -} - -impl From for SqliteError { - fn from(error: bdk_sqlite::rusqlite::Error) -> Self { SqliteError::Sqlite { rusqlite_error: error.to_string(), } } } -impl From for WalletCreationError { - fn from(error: NewError) -> Self { - WalletCreationError::Descriptor { - error_message: error.to_string(), - } - } -} - -impl From for WalletCreationError { - fn from(error: NewOrLoadError) -> Self { - match error { - NewOrLoadError::Descriptor(e) => WalletCreationError::Descriptor { - error_message: e.to_string(), - }, - NewOrLoadError::LoadedGenesisDoesNotMatch { expected, got } => { - WalletCreationError::LoadedGenesisDoesNotMatch { - expected: expected.to_string(), - got: format!("{:?}", got), - } - } - NewOrLoadError::LoadedNetworkDoesNotMatch { expected, got } => { - WalletCreationError::LoadedNetworkDoesNotMatch { expected, got } - } - NewOrLoadError::LoadedDescriptorDoesNotMatch { got, keychain } => { - WalletCreationError::LoadedDescriptorDoesNotMatch { - got: format!("{:?}", got), - keychain, - } - } - } - } -} - // ------------------------------------------------------------------------ // error tests // ------------------------------------------------------------------------ @@ -1281,8 +1263,8 @@ impl From for WalletCreationError { mod test { use crate::error::{ Bip32Error, Bip39Error, CannotConnectError, DescriptorError, DescriptorKeyError, - ElectrumError, EsploraError, ExtractTxError, FeeRateError, InspectError, PersistenceError, - PsbtError, PsbtParseError, TransactionError, TxidParseError, + ElectrumError, EsploraError, ExtractTxError, FeeRateError, PersistenceError, PsbtError, + PsbtParseError, RequestBuilderError, TransactionError, TxidParseError, }; use crate::SignerError; @@ -1670,7 +1652,7 @@ mod test { #[test] fn test_error_inspect() { let cases = vec![( - InspectError::RequestAlreadyConsumed, + RequestBuilderError::RequestAlreadyConsumed, "the request has already been consumed", )]; diff --git a/bdk-ffi/src/esplora.rs b/bdk-ffi/src/esplora.rs index f9045e4c..76d1d77e 100644 --- a/bdk-ffi/src/esplora.rs +++ b/bdk-ffi/src/esplora.rs @@ -1,7 +1,7 @@ use crate::bitcoin::Transaction; use crate::error::EsploraError; +use crate::types::Update; use crate::types::{FullScanRequest, SyncRequest}; -use crate::wallet::Update; use bdk_esplora::esplora_client::{BlockingClient, Builder}; use bdk_esplora::EsploraExt; @@ -10,8 +10,8 @@ use bdk_wallet::chain::spk_client::FullScanRequest as BdkFullScanRequest; use bdk_wallet::chain::spk_client::FullScanResult as BdkFullScanResult; use bdk_wallet::chain::spk_client::SyncRequest as BdkSyncRequest; use bdk_wallet::chain::spk_client::SyncResult as BdkSyncResult; -use bdk_wallet::wallet::Update as BdkUpdate; use bdk_wallet::KeychainKind; +use bdk_wallet::Update as BdkUpdate; use std::collections::BTreeMap; use std::sync::Arc; @@ -44,8 +44,8 @@ impl EsploraClient { let update = BdkUpdate { last_active_indices: result.last_active_indices, - graph: result.graph_update, - chain: Some(result.chain_update), + tx_update: result.tx_update, + chain: result.chain_update, }; Ok(Arc::new(Update(update))) @@ -57,7 +57,7 @@ impl EsploraClient { parallel_requests: u64, ) -> Result, EsploraError> { // using option and take is not ideal but the only way to take full ownership of the request - let request: BdkSyncRequest = request + let request: BdkSyncRequest<(KeychainKind, u32)> = request .0 .lock() .unwrap() @@ -68,8 +68,8 @@ impl EsploraClient { let update = BdkUpdate { last_active_indices: BTreeMap::default(), - graph: result.graph_update, - chain: Some(result.chain_update), + tx_update: result.tx_update, + chain: result.chain_update, }; Ok(Arc::new(Update(update))) diff --git a/bdk-ffi/src/lib.rs b/bdk-ffi/src/lib.rs index 9d66cd83..e539dd0d 100644 --- a/bdk-ffi/src/lib.rs +++ b/bdk-ffi/src/lib.rs @@ -25,6 +25,7 @@ use crate::error::Bip39Error; use crate::error::CalculateFeeError; use crate::error::CannotConnectError; use crate::error::CreateTxError; +use crate::error::CreateWithPersistError; use crate::error::DescriptorError; use crate::error::DescriptorKeyError; use crate::error::ElectrumError; @@ -32,42 +33,46 @@ use crate::error::EsploraError; use crate::error::ExtractTxError; use crate::error::FeeRateError; use crate::error::FromScriptError; -use crate::error::InspectError; +use crate::error::LoadWithPersistError; use crate::error::ParseAmountError; use crate::error::PersistenceError; use crate::error::PsbtError; use crate::error::PsbtParseError; +use crate::error::RequestBuilderError; use crate::error::SignerError; use crate::error::SqliteError; use crate::error::TransactionError; use crate::error::TxidParseError; -use crate::error::WalletCreationError; use crate::esplora::EsploraClient; use crate::keys::DerivationPath; use crate::keys::DescriptorPublicKey; use crate::keys::DescriptorSecretKey; use crate::keys::Mnemonic; -use crate::store::SqliteStore; +use crate::store::Connection; use crate::types::AddressInfo; use crate::types::Balance; +use crate::types::BlockId; use crate::types::CanonicalTx; use crate::types::ChainPosition; -use crate::types::ChangeSet; +use crate::types::ConfirmationBlockTime; use crate::types::FullScanRequest; +use crate::types::FullScanRequestBuilder; use crate::types::FullScanScriptInspector; use crate::types::LocalOutput; use crate::types::ScriptAmount; use crate::types::SyncRequest; +use crate::types::SyncRequestBuilder; use crate::types::SyncScriptInspector; +use crate::types::Update; use crate::wallet::BumpFeeTxBuilder; use crate::wallet::SentAndReceivedValues; use crate::wallet::TxBuilder; -use crate::wallet::Update; use crate::wallet::Wallet; use bdk_wallet::bitcoin::Network; use bdk_wallet::keys::bip39::WordCount; -use bdk_wallet::wallet::tx_builder::ChangeSpendPolicy; +use bdk_wallet::tx_builder::ChangeSpendPolicy; +use bdk_wallet::ChangeSet; use bdk_wallet::KeychainKind; uniffi::include_scaffolding!("bdk"); diff --git a/bdk-ffi/src/store.rs b/bdk-ffi/src/store.rs index d55352c6..7ae2bb2b 100644 --- a/bdk-ffi/src/store.rs +++ b/bdk-ffi/src/store.rs @@ -1,39 +1,24 @@ use crate::error::SqliteError; -use crate::types::ChangeSet; -use bdk_sqlite::rusqlite::Connection; -use bdk_sqlite::{Store as BdkSqliteStore, Store}; -use bdk_wallet::chain::ConfirmationTimeHeightAnchor; -use bdk_wallet::KeychainKind; +use bdk_wallet::rusqlite::Connection as BdkConnection; -use std::sync::{Arc, Mutex, MutexGuard}; +use std::sync::Mutex; +use std::sync::MutexGuard; -pub struct SqliteStore(Mutex>); +pub struct Connection(Mutex); -impl SqliteStore { +impl Connection { pub fn new(path: String) -> Result { - let connection = Connection::open(path)?; - let db = Store::new(connection)?; - Ok(Self(Mutex::new(db))) + let connection = BdkConnection::open(path)?; + Ok(Self(Mutex::new(connection))) } - pub(crate) fn get_store( - &self, - ) -> MutexGuard> { - self.0.lock().expect("sqlite store") + pub fn new_in_memory() -> Result { + let connection = BdkConnection::open_in_memory()?; + Ok(Self(Mutex::new(connection))) } - pub fn write(&self, changeset: &ChangeSet) -> Result<(), SqliteError> { - self.get_store() - .write(&changeset.0) - .map_err(SqliteError::from) - } - - pub fn read(&self) -> Result>, SqliteError> { - self.get_store() - .read() - .map_err(SqliteError::from) - .map(|optional_bdk_change_set| optional_bdk_change_set.map(ChangeSet::from)) - .map(|optional_change_set| optional_change_set.map(Arc::new)) + pub(crate) fn get_store(&self) -> MutexGuard { + self.0.lock().expect("must lock") } } diff --git a/bdk-ffi/src/types.rs b/bdk-ffi/src/types.rs index af8d6465..028d73b1 100644 --- a/bdk-ffi/src/types.rs +++ b/bdk-ffi/src/types.rs @@ -1,25 +1,45 @@ use crate::bitcoin::Amount; use crate::bitcoin::{Address, OutPoint, Script, Transaction, TxOut}; -use crate::InspectError; +use crate::error::RequestBuilderError; -use bdk_wallet::bitcoin::ScriptBuf as BdkScriptBuf; +use bdk_core::spk_client::SyncItem; use bdk_wallet::bitcoin::Transaction as BdkTransaction; use bdk_wallet::chain::spk_client::FullScanRequest as BdkFullScanRequest; +use bdk_wallet::chain::spk_client::FullScanRequestBuilder as BdkFullScanRequestBuilder; use bdk_wallet::chain::spk_client::SyncRequest as BdkSyncRequest; +use bdk_wallet::chain::spk_client::SyncRequestBuilder as BdkSyncRequestBuilder; use bdk_wallet::chain::tx_graph::CanonicalTx as BdkCanonicalTx; -use bdk_wallet::chain::{ChainPosition as BdkChainPosition, ConfirmationTimeHeightAnchor}; -use bdk_wallet::wallet::AddressInfo as BdkAddressInfo; -use bdk_wallet::wallet::Balance as BdkBalance; +use bdk_wallet::chain::{ + ChainPosition as BdkChainPosition, ConfirmationBlockTime as BdkConfirmationBlockTime, +}; +use bdk_wallet::AddressInfo as BdkAddressInfo; +use bdk_wallet::Balance as BdkBalance; use bdk_wallet::KeychainKind; use bdk_wallet::LocalOutput as BdkLocalOutput; +use bdk_wallet::Update as BdkUpdate; -use bdk_electrum::bdk_chain::CombinedChangeSet; use std::sync::{Arc, Mutex}; -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug)] pub enum ChainPosition { - Confirmed { height: u32, timestamp: u64 }, - Unconfirmed { timestamp: u64 }, + Confirmed { + confirmation_block_time: ConfirmationBlockTime, + }, + Unconfirmed { + timestamp: u64, + }, +} + +#[derive(Debug)] +pub struct ConfirmationBlockTime { + pub block_id: BlockId, + pub confirmation_time: u64, +} + +#[derive(Debug)] +pub struct BlockId { + pub height: u32, + pub hash: String, } pub struct CanonicalTx { @@ -27,13 +47,21 @@ pub struct CanonicalTx { pub chain_position: ChainPosition, } -impl From, ConfirmationTimeHeightAnchor>> for CanonicalTx { - fn from(tx: BdkCanonicalTx<'_, Arc, ConfirmationTimeHeightAnchor>) -> Self { +impl From, BdkConfirmationBlockTime>> for CanonicalTx { + fn from(tx: BdkCanonicalTx<'_, Arc, BdkConfirmationBlockTime>) -> Self { let chain_position = match tx.chain_position { - BdkChainPosition::Confirmed(anchor) => ChainPosition::Confirmed { - height: anchor.confirmation_height, - timestamp: anchor.confirmation_time, - }, + BdkChainPosition::Confirmed(anchor) => { + let block_id = BlockId { + height: anchor.block_id.height, + hash: anchor.block_id.hash.to_string(), + }; + ChainPosition::Confirmed { + confirmation_block_time: ConfirmationBlockTime { + block_id, + confirmation_time: anchor.confirmation_time, + }, + } + } BdkChainPosition::Unconfirmed(timestamp) => ChainPosition::Unconfirmed { timestamp }, }; @@ -87,14 +115,6 @@ impl From for Balance { } } -pub struct ChangeSet(pub(crate) CombinedChangeSet); - -impl From> for ChangeSet { - fn from(change_set: CombinedChangeSet) -> Self { - ChangeSet(change_set) - } -} - pub struct LocalOutput { pub outpoint: OutPoint, pub txout: TxOut, @@ -129,45 +149,76 @@ pub trait SyncScriptInspector: Sync + Send { fn inspect(&self, script: Arc