From e5540880670cc106fc8546964991eadf8463ddbb Mon Sep 17 00:00:00 2001 From: Matthew Date: Sat, 11 May 2024 14:30:15 -0500 Subject: [PATCH] chore: bump rust bdk to alpha 11 --- .../org/bitcoindevkit/LiveTxBuilderTest.kt | 8 +- .../org/bitcoindevkit/LiveWalletTest.kt | 8 +- .../org/bitcoindevkit/OfflineWalletTest.kt | 2 +- bdk-ffi/Cargo.lock | 20 +-- bdk-ffi/Cargo.toml | 6 +- bdk-ffi/src/bdk.udl | 46 +++++-- bdk-ffi/src/bitcoin.rs | 37 ++++++ bdk-ffi/src/error.rs | 114 +++++++++++++++++- bdk-ffi/src/lib.rs | 2 + bdk-ffi/src/types.rs | 28 +++-- bdk-ffi/src/wallet.rs | 25 ++-- .../org/bitcoindevkit/LiveTxBuilderTest.kt | 14 +-- .../org/bitcoindevkit/LiveWalletTest.kt | 10 +- .../org/bitcoindevkit/OfflineWalletTest.kt | 2 +- bdk-python/tests/test_live_tx_builder.py | 4 +- bdk-python/tests/test_live_wallet.py | 4 +- bdk-python/tests/test_offline_wallet.py | 2 +- .../LiveTxBuilderTests.swift | 10 +- .../BitcoinDevKitTests/LiveWalletTests.swift | 7 +- .../OfflineWalletTests.swift | 2 +- 20 files changed, 266 insertions(+), 85 deletions(-) diff --git a/bdk-android/lib/src/androidTest/kotlin/org/bitcoindevkit/LiveTxBuilderTest.kt b/bdk-android/lib/src/androidTest/kotlin/org/bitcoindevkit/LiveTxBuilderTest.kt index 6f71888e..1040eadb 100644 --- a/bdk-android/lib/src/androidTest/kotlin/org/bitcoindevkit/LiveTxBuilderTest.kt +++ b/bdk-android/lib/src/androidTest/kotlin/org/bitcoindevkit/LiveTxBuilderTest.kt @@ -33,9 +33,9 @@ class LiveTxBuilderTest { val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL) wallet.applyUpdate(update) wallet.commit() - println("Balance: ${wallet.getBalance().total}") + println("Balance: ${wallet.getBalance().total.toSat()}") - assert(wallet.getBalance().total > 0uL) { + assert(wallet.getBalance().total.toSat() > 0uL) { "Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address.asString()} and try again." } @@ -59,9 +59,9 @@ class LiveTxBuilderTest { val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL) wallet.applyUpdate(update) wallet.commit() - println("Balance: ${wallet.getBalance().total}") + println("Balance: ${wallet.getBalance().total.toSat()}") - assert(wallet.getBalance().total > 0uL) { + assert(wallet.getBalance().total.toSat() > 0uL) { "Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address.asString()} and try again." } diff --git a/bdk-android/lib/src/androidTest/kotlin/org/bitcoindevkit/LiveWalletTest.kt b/bdk-android/lib/src/androidTest/kotlin/org/bitcoindevkit/LiveWalletTest.kt index a30663d0..33ba584d 100644 --- a/bdk-android/lib/src/androidTest/kotlin/org/bitcoindevkit/LiveWalletTest.kt +++ b/bdk-android/lib/src/androidTest/kotlin/org/bitcoindevkit/LiveWalletTest.kt @@ -33,11 +33,11 @@ class LiveWalletTest { val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL) wallet.applyUpdate(update) wallet.commit() - println("Balance: ${wallet.getBalance().total}") + println("Balance: ${wallet.getBalance().total.toSat()}") val balance: Balance = wallet.getBalance() println("Balance: $balance") - assert(wallet.getBalance().total > 0uL) { + assert(wallet.getBalance().total.toSat() > 0uL) { "Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address.asString()} and try again." } @@ -60,9 +60,9 @@ class LiveWalletTest { val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL) wallet.applyUpdate(update) wallet.commit() - println("Balance: ${wallet.getBalance().total}") + println("Balance: ${wallet.getBalance().total.toSat()}") - assert(wallet.getBalance().total > 0uL) { + assert(wallet.getBalance().total.toSat() > 0uL) { "Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address} and try again." } diff --git a/bdk-android/lib/src/androidTest/kotlin/org/bitcoindevkit/OfflineWalletTest.kt b/bdk-android/lib/src/androidTest/kotlin/org/bitcoindevkit/OfflineWalletTest.kt index 17afe750..aa43b8a2 100644 --- a/bdk-android/lib/src/androidTest/kotlin/org/bitcoindevkit/OfflineWalletTest.kt +++ b/bdk-android/lib/src/androidTest/kotlin/org/bitcoindevkit/OfflineWalletTest.kt @@ -72,7 +72,7 @@ class OfflineWalletTest { assertEquals( expected = 0uL, - actual = wallet.getBalance().total + actual = wallet.getBalance().total.toSat() ) } } diff --git a/bdk-ffi/Cargo.lock b/bdk-ffi/Cargo.lock index de358256..9437eb2f 100644 --- a/bdk-ffi/Cargo.lock +++ b/bdk-ffi/Cargo.lock @@ -138,9 +138,9 @@ dependencies = [ [[package]] name = "bdk" -version = "1.0.0-alpha.10" +version = "1.0.0-alpha.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66fc0ebc2a63463f709cfdfbb7e7877b9975bcaea9d2d4f02f97ad012de37e3b" +checksum = "65c23f2903ac5dbb7b35934ae319aadc946201e4fa51b652440bd1c8fa3080ee" dependencies = [ "anyhow", "bdk_chain", @@ -170,9 +170,9 @@ dependencies = [ [[package]] name = "bdk_chain" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e879c03ebf3a64643295152a19a8b0e0a3af22e25539d2bc56ce07d07b059c33" +checksum = "440ec5b1c8911f126b540e05c98493b699b497a3cb90c5e9c5eee21cdd8d1e01" dependencies = [ "bitcoin", "miniscript", @@ -181,9 +181,9 @@ dependencies = [ [[package]] name = "bdk_esplora" -version = "0.12.0" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0aad9d99b103cd9c67ce1f4702720f2813db7aeba72abc9628ae9b00462a492" +checksum = "9fb5b46f8c256bc083640342bd0d35ec1963971f18800c3fee1a9189eda60ecd" dependencies = [ "bdk_chain", "esplora-client", @@ -191,9 +191,9 @@ dependencies = [ [[package]] name = "bdk_file_store" -version = "0.10.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "492a011ee853773bce14f2d899fa34fe3ac3b5f39eeb1504d0d2b28de448bd73" +checksum = "5dfd7e9a5edb8d384ea1836b0bcd4febdd3211815acc058d64c7e284776d69ab" dependencies = [ "anyhow", "bdk_chain", @@ -204,9 +204,9 @@ dependencies = [ [[package]] name = "bdk_persist" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f7d6b38071ee828329434f86799e0bb6aaa5a4256e225480c2c53b7b2df295" +checksum = "aba103c2108dd0f0b452650043d21c449ae07ce866dbaea29a9c59899a5964f0" dependencies = [ "anyhow", "bdk_chain", diff --git a/bdk-ffi/Cargo.toml b/bdk-ffi/Cargo.toml index 6e37499b..ba6bfbf5 100644 --- a/bdk-ffi/Cargo.toml +++ b/bdk-ffi/Cargo.toml @@ -18,9 +18,9 @@ path = "uniffi-bindgen.rs" default = ["uniffi/cli"] [dependencies] -bdk = { version = "1.0.0-alpha.10", features = ["all-keys", "keys-bip39"] } -bdk_esplora = { version = "0.12.0", default-features = false, features = ["std", "blocking", "blocking-https-rustls"] } -bdk_file_store = { version = "0.10.0" } +bdk = { version = "1.0.0-alpha.11", features = ["all-keys", "keys-bip39"] } +bdk_esplora = { version = "0.13.0", default-features = false, features = ["std", "blocking", "blocking-https-rustls"] } +bdk_file_store = { version = "0.11.0" } uniffi = { version = "=0.26.1" } bitcoin-internals = { version = "0.2.0", features = ["alloc"] } diff --git a/bdk-ffi/src/bdk.udl b/bdk-ffi/src/bdk.udl index 4083090b..529e7a7e 100644 --- a/bdk-ffi/src/bdk.udl +++ b/bdk-ffi/src/bdk.udl @@ -131,6 +131,19 @@ enum FeeRateError { "ArithmeticOverflow" }; +[Error] +interface ParseAmountError { + Negative(); + TooBig(); + TooPrecise(); + InvalidFormat(); + InputTooLarge(); + InvalidCharacter(string error_message); + UnknownDenomination(string error_message); + PossiblyConfusingDenomination(string error_message); + OtherParseAmountErr(); +}; + [Error] interface PersistenceError { Write(string error_message); @@ -185,6 +198,7 @@ interface WalletCreationError { NotInitialized(); LoadedGenesisDoesNotMatch(string expected, string got); LoadedNetworkDoesNotMatch(Network expected, Network? got); + LoadedDescriptorDoesNotMatch(string got, KeychainKind keychain); }; // ------------------------------------------------------------------------ @@ -203,17 +217,17 @@ dictionary AddressInfo { }; dictionary Balance { - u64 immature; + Amount immature; - u64 trusted_pending; + Amount trusted_pending; - u64 untrusted_pending; + Amount untrusted_pending; - u64 confirmed; + Amount confirmed; - u64 trusted_spendable; + Amount trusted_spendable; - u64 total; + Amount total; }; dictionary LocalOutput { @@ -302,7 +316,7 @@ interface Update {}; interface TxBuilder { constructor(); - TxBuilder add_recipient([ByRef] Script script, u64 amount); + TxBuilder add_recipient([ByRef] Script script, Amount amount); TxBuilder set_recipients(sequence recipients); @@ -456,12 +470,12 @@ interface EsploraClient { dictionary ScriptAmount { Script script; - u64 amount; + Amount amount; }; dictionary SentAndReceivedValues { - u64 sent; - u64 received; + Amount sent; + Amount received; }; // ------------------------------------------------------------------------ @@ -543,6 +557,18 @@ dictionary OutPoint { u32 vout; }; +interface Amount { + [Name=from_sat] + constructor(u64 from_sat); + + [Name=from_btc, Throws=ParseAmountError] + constructor(f64 from_btc); + + u64 to_sat(); + + f64 to_btc(); +}; + interface FeeRate { [Name=from_sat_per_vb, Throws=FeeRateError] constructor(u64 sat_per_vb); diff --git a/bdk-ffi/src/bitcoin.rs b/bdk-ffi/src/bitcoin.rs index 6a076a46..68bb0ed1 100644 --- a/bdk-ffi/src/bitcoin.rs +++ b/bdk-ffi/src/bitcoin.rs @@ -18,6 +18,43 @@ use std::io::Cursor; use std::str::FromStr; use std::sync::{Arc, Mutex}; +use bdk::bitcoin::amount::ParseAmountError; +use bdk::bitcoin::Amount as BdkAmount; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Amount(pub(crate) BdkAmount); + +impl Amount { + pub fn from_sat(sat: u64) -> Self { + Amount(BdkAmount::from_sat(sat)) + } + + pub fn from_btc(btc: f64) -> Result { + let bdk_amount = BdkAmount::from_btc(btc).map_err(ParseAmountError::from)?; + Ok(Amount(bdk_amount)) + } + + pub fn to_sat(&self) -> u64 { + self.0.to_sat() + } + + pub fn to_btc(&self) -> f64 { + self.0.to_btc() + } +} + +impl From for BdkAmount { + fn from(amount: Amount) -> Self { + amount.0 + } +} + +impl From for Amount { + fn from(amount: BdkAmount) -> Self { + Amount(amount) + } +} + #[derive(Clone, Debug, PartialEq, Eq)] pub struct Script(pub(crate) BdkScriptBuf); diff --git a/bdk-ffi/src/error.rs b/bdk-ffi/src/error.rs index c7f6df74..e73c96db 100644 --- a/bdk-ffi/src/error.rs +++ b/bdk-ffi/src/error.rs @@ -17,12 +17,15 @@ use bdk_esplora::esplora_client::{Error as BdkEsploraError, Error}; use bdk_file_store::FileError as BdkFileError; use bitcoin_internals::hex::display::DisplayHex; +use bdk::bitcoin::amount::ParseAmountError as BdkParseAmountError; + use std::convert::TryInto; use bdk::bitcoin::address::Error as BdkAddressError; use bdk::bitcoin::consensus::encode::Error as BdkEncodeError; use bdk::bitcoin::psbt::ExtractTxError as BdkExtractTxError; use bdk::chain::local_chain::CannotConnectError as BdkCannotConnectError; +use bdk::KeychainKind; // ------------------------------------------------------------------------ // error definitions @@ -317,6 +320,37 @@ pub enum FeeRateError { ArithmeticOverflow, } +#[derive(Debug, thiserror::Error)] +pub enum ParseAmountError { + #[error("amount is negative")] + Negative, + + #[error("amount is too large")] + TooBig, + + #[error("amount is too precise")] + TooPrecise, + + #[error("invalid amount format")] + InvalidFormat, + + #[error("input is too large")] + InputTooLarge, + + #[error("invalid character: {error_message}")] + InvalidCharacter { error_message: String }, + + #[error("unknown denomination: {error_message}")] + UnknownDenomination { error_message: String }, + + #[error("possibly confusing denomination: {error_message}")] + PossiblyConfusingDenomination { error_message: String }, + + // Has to handle non-exhaustive + #[error("unknown parse amount error")] + OtherParseAmountErr, +} + #[derive(Debug, thiserror::Error)] pub enum PersistenceError { #[error("writing to persistence error: {error_message}")] @@ -434,6 +468,9 @@ pub enum WalletCreationError { expected: Network, got: Option, }, + + #[error("loaded descriptor '{got}' does not match what was provided '{keychain:?}'")] + LoadedDescriptorDoesNotMatch { got: String, keychain: KeychainKind }, } // ------------------------------------------------------------------------ @@ -800,6 +837,28 @@ impl From for ExtractTxError { } } +impl From for ParseAmountError { + fn from(error: BdkParseAmountError) -> Self { + match error { + BdkParseAmountError::Negative => ParseAmountError::Negative, + BdkParseAmountError::TooBig => ParseAmountError::TooBig, + BdkParseAmountError::InvalidFormat => ParseAmountError::InvalidFormat, + BdkParseAmountError::TooPrecise => ParseAmountError::TooPrecise, + BdkParseAmountError::InputTooLarge => ParseAmountError::InputTooLarge, + BdkParseAmountError::InvalidCharacter(c) => ParseAmountError::InvalidCharacter { + error_message: c.to_string(), + }, + BdkParseAmountError::UnknownDenomination(s) => { + ParseAmountError::UnknownDenomination { error_message: s } + } + BdkParseAmountError::PossiblyConfusingDenomination(s) => { + ParseAmountError::PossiblyConfusingDenomination { error_message: s } + } + _ => ParseAmountError::OtherParseAmountErr, + } + } +} + impl From for PersistenceError { fn from(error: std::io::Error) -> Self { PersistenceError::Write { @@ -902,6 +961,12 @@ impl From for WalletCreationError { NewOrLoadError::LoadedNetworkDoesNotMatch { expected, got } => { WalletCreationError::LoadedNetworkDoesNotMatch { expected, got } } + NewOrLoadError::LoadedDescriptorDoesNotMatch { got, keychain } => { + WalletCreationError::LoadedDescriptorDoesNotMatch { + got: format!("{:?}", got), + keychain, + } + } } } } @@ -914,13 +979,14 @@ impl From for WalletCreationError { mod test { use crate::error::{ AddressError, Bip32Error, Bip39Error, CannotConnectError, CreateTxError, DescriptorError, - DescriptorKeyError, EsploraError, ExtractTxError, FeeRateError, PersistenceError, - PsbtParseError, TransactionError, TxidParseError, WalletCreationError, + DescriptorKeyError, EsploraError, ExtractTxError, FeeRateError, ParseAmountError, + PersistenceError, PsbtParseError, TransactionError, TxidParseError, WalletCreationError, }; use crate::CalculateFeeError; use crate::OutPoint; use crate::SignerError; use bdk::bitcoin::Network; + use bdk::KeychainKind; #[test] fn test_error_address() { @@ -1418,6 +1484,43 @@ mod test { } } + #[test] + fn test_error_parse_amount() { + let cases = vec![ + (ParseAmountError::Negative, "amount is negative"), + (ParseAmountError::TooBig, "amount is too large"), + (ParseAmountError::TooPrecise, "amount is too precise"), + (ParseAmountError::InvalidFormat, "invalid amount format"), + (ParseAmountError::InputTooLarge, "input is too large"), + ( + ParseAmountError::InvalidCharacter { + error_message: "invalid char".to_string(), + }, + "invalid character: invalid char", + ), + ( + ParseAmountError::UnknownDenomination { + error_message: "unknown denom".to_string(), + }, + "unknown denomination: unknown denom", + ), + ( + ParseAmountError::PossiblyConfusingDenomination { + error_message: "confusing denom".to_string(), + }, + "possibly confusing denomination: confusing denom", + ), + ( + ParseAmountError::OtherParseAmountErr, + "unknown parse amount error", + ), + ]; + + for (error, expected_message) in cases { + assert_eq!(error.to_string(), expected_message); + } + } + #[test] fn test_persistence_error() { let cases = vec![ @@ -1605,6 +1708,13 @@ mod test { }, "loaded network type is not bitcoin, got Some(Testnet)".to_string(), ), + ( + WalletCreationError::LoadedDescriptorDoesNotMatch { + got: "def".to_string(), + keychain: KeychainKind::External, + }, + "loaded descriptor 'def' does not match what was provided 'External'".to_string(), + ), ]; for (error, expected) in errors { diff --git a/bdk-ffi/src/lib.rs b/bdk-ffi/src/lib.rs index 8774ae42..878cae36 100644 --- a/bdk-ffi/src/lib.rs +++ b/bdk-ffi/src/lib.rs @@ -7,6 +7,7 @@ mod types; mod wallet; use crate::bitcoin::Address; +use crate::bitcoin::Amount; use crate::bitcoin::FeeRate; use crate::bitcoin::OutPoint; use crate::bitcoin::Psbt; @@ -25,6 +26,7 @@ use crate::error::DescriptorKeyError; use crate::error::EsploraError; use crate::error::ExtractTxError; use crate::error::FeeRateError; +use crate::error::ParseAmountError; use crate::error::PersistenceError; use crate::error::PsbtParseError; use crate::error::SignerError; diff --git a/bdk-ffi/src/types.rs b/bdk-ffi/src/types.rs index 2a60b31b..2127e4bb 100644 --- a/bdk-ffi/src/types.rs +++ b/bdk-ffi/src/types.rs @@ -11,6 +11,8 @@ use bdk::LocalOutput as BdkLocalOutput; use std::sync::{Arc, Mutex}; +use crate::bitcoin::Amount; + #[derive(Debug, Clone, PartialEq, Eq)] pub enum ChainPosition { Confirmed { height: u32, timestamp: u64 }, @@ -45,7 +47,7 @@ impl From, ConfirmationTimeHei pub struct ScriptAmount { pub script: Arc