From 61104c6519542ebc26db7b07476308e5393ca7be Mon Sep 17 00:00:00 2001 From: BitcoinZavior Date: Sat, 19 Oct 2024 16:51:33 -0400 Subject: [PATCH 01/11] feat: expose Wallet::policies method --- bdk-ffi/src/bdk.udl | 5 +++++ bdk-ffi/src/lib.rs | 1 + bdk-ffi/src/types.rs | 16 +++++++++++++++- bdk-ffi/src/wallet.rs | 16 +++++++++++----- 4 files changed, 32 insertions(+), 6 deletions(-) diff --git a/bdk-ffi/src/bdk.udl b/bdk-ffi/src/bdk.udl index 1c716777..2eb41a3f 100644 --- a/bdk-ffi/src/bdk.udl +++ b/bdk-ffi/src/bdk.udl @@ -401,6 +401,9 @@ interface Wallet { string descriptor_checksum(KeychainKind keychain); + [Throws=DescriptorError] + Policy? policies(KeychainKind keychain); + Balance balance(); [Throws=CannotConnectError] @@ -441,6 +444,8 @@ interface Wallet { interface Update {}; +interface Policy {}; + interface TxBuilder { constructor(); diff --git a/bdk-ffi/src/lib.rs b/bdk-ffi/src/lib.rs index b8db0d78..8ed81e3c 100644 --- a/bdk-ffi/src/lib.rs +++ b/bdk-ffi/src/lib.rs @@ -57,6 +57,7 @@ use crate::types::FullScanRequestBuilder; use crate::types::FullScanScriptInspector; use crate::types::KeychainAndIndex; use crate::types::LocalOutput; +use crate::types::Policy; use crate::types::ScriptAmount; use crate::types::SentAndReceivedValues; use crate::types::SyncRequest; diff --git a/bdk-ffi/src/types.rs b/bdk-ffi/src/types.rs index eaf3a2ac..392d4539 100644 --- a/bdk-ffi/src/types.rs +++ b/bdk-ffi/src/types.rs @@ -15,12 +15,12 @@ use bdk_wallet::chain::tx_graph::CanonicalTx as BdkCanonicalTx; use bdk_wallet::chain::{ ChainPosition as BdkChainPosition, ConfirmationBlockTime as BdkConfirmationBlockTime, }; +use bdk_wallet::descriptor::Policy as BdkPolicy; 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 std::sync::{Arc, Mutex}; #[derive(Debug)] @@ -237,3 +237,17 @@ pub struct KeychainAndIndex { pub keychain: KeychainKind, pub index: u32, } + +/// Descriptor spending policy +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct Policy(BdkPolicy); +impl From for Policy { + fn from(value: BdkPolicy) -> Self { + Policy(value) + } +} +impl From for BdkPolicy { + fn from(value: Policy) -> Self { + value.0 + } +} diff --git a/bdk-ffi/src/wallet.rs b/bdk-ffi/src/wallet.rs index fd8d4b75..f89ac4fe 100644 --- a/bdk-ffi/src/wallet.rs +++ b/bdk-ffi/src/wallet.rs @@ -1,13 +1,13 @@ use crate::bitcoin::{Psbt, Transaction}; use crate::descriptor::Descriptor; use crate::error::{ - CalculateFeeError, CannotConnectError, CreateWithPersistError, LoadWithPersistError, - SignerError, SqliteError, TxidParseError, + CalculateFeeError, CannotConnectError, CreateWithPersistError, DescriptorError, + LoadWithPersistError, SignerError, SqliteError, TxidParseError, }; use crate::store::Connection; use crate::types::{ AddressInfo, Balance, CanonicalTx, FullScanRequestBuilder, KeychainAndIndex, LocalOutput, - SentAndReceivedValues, SyncRequestBuilder, Update, + Policy, SentAndReceivedValues, SyncRequestBuilder, Update, }; use bitcoin_ffi::{Amount, FeeRate, OutPoint, Script}; @@ -139,6 +139,13 @@ impl Wallet { self.get_wallet().descriptor_checksum(keychain) } + pub fn policies(&self, keychain: KeychainKind) -> Result>, DescriptorError> { + self.get_wallet() + .policies(keychain) + .map_err(DescriptorError::from) + .map(|e| e.map(|p| Arc::new(p.into()))) + } + pub fn network(&self) -> Network { self.get_wallet().network() } @@ -154,8 +161,7 @@ impl Wallet { pub(crate) fn sign( &self, - psbt: Arc, - // sign_options: Option, + psbt: Arc, // sign_options: Option, ) -> Result { let mut psbt = psbt.0.lock().unwrap(); self.get_wallet() From c6c5eb94205bc97ee36d15ed29f93173e85ae682 Mon Sep 17 00:00:00 2001 From: BitcoinZavior Date: Mon, 21 Oct 2024 20:01:12 -0400 Subject: [PATCH 02/11] feat: expose TxBuilder::policy_path --- bdk-ffi/src/bdk.udl | 2 ++ bdk-ffi/src/tx_builder.rs | 25 +++++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/bdk-ffi/src/bdk.udl b/bdk-ffi/src/bdk.udl index 2eb41a3f..4a7373f2 100644 --- a/bdk-ffi/src/bdk.udl +++ b/bdk-ffi/src/bdk.udl @@ -461,6 +461,8 @@ interface TxBuilder { TxBuilder add_utxo(OutPoint outpoint); + TxBuilder policy_path(record> policy_path, KeychainKind keychain); + TxBuilder change_policy(ChangeSpendPolicy change_policy); TxBuilder do_not_spend_change(); diff --git a/bdk-ffi/src/tx_builder.rs b/bdk-ffi/src/tx_builder.rs index 7ade7c30..78882646 100644 --- a/bdk-ffi/src/tx_builder.rs +++ b/bdk-ffi/src/tx_builder.rs @@ -3,6 +3,7 @@ use crate::error::CreateTxError; use crate::types::ScriptAmount; use crate::wallet::Wallet; +use bdk_wallet::KeychainKind; use bitcoin_ffi::{Amount, FeeRate, Script}; use bdk_wallet::bitcoin::amount::Amount as BdkAmount; @@ -11,6 +12,8 @@ use bdk_wallet::bitcoin::ScriptBuf as BdkScriptBuf; use bdk_wallet::bitcoin::{OutPoint, Sequence, Txid}; use bdk_wallet::ChangeSpendPolicy; +use std::collections::BTreeMap; +use std::collections::HashMap; use std::collections::HashSet; use std::str::FromStr; use std::sync::Arc; @@ -21,6 +24,7 @@ pub struct TxBuilder { pub(crate) recipients: Vec<(BdkScriptBuf, BdkAmount)>, pub(crate) utxos: Vec, pub(crate) unspendable: HashSet, + pub(crate) policy_path: (HashMap>, KeychainKind), pub(crate) change_policy: ChangeSpendPolicy, pub(crate) manually_selected_only: bool, pub(crate) fee_rate: Option, @@ -37,6 +41,7 @@ impl TxBuilder { recipients: Vec::new(), utxos: Vec::new(), unspendable: HashSet::new(), + policy_path: (HashMap::new(), KeychainKind::External), change_policy: ChangeSpendPolicy::ChangeAllowed, manually_selected_only: false, fee_rate: None, @@ -104,6 +109,17 @@ impl TxBuilder { }) } + pub(crate) fn policy_path( + &self, + policy_path: HashMap>, + keychain: KeychainKind, + ) -> Arc { + Arc::new(TxBuilder { + policy_path: (policy_path, keychain), + ..self.clone() + }) + } + pub(crate) fn change_policy(&self, change_policy: ChangeSpendPolicy) -> Arc { Arc::new(TxBuilder { change_policy, @@ -177,6 +193,15 @@ impl TxBuilder { for (script, amount) in &self.recipients { tx_builder.add_recipient(script.clone(), *amount); } + tx_builder.policy_path( + self.policy_path + .0 + .clone() + .into_iter() + .map(|(key, value)| (key, value.into_iter().map(|x| x as usize).collect())) + .collect::>>(), + self.policy_path.1, + ); tx_builder.change_policy(self.change_policy); if !self.utxos.is_empty() { tx_builder From 4925a8d7484cac05ef0b0d44cf551360e2771bac Mon Sep 17 00:00:00 2001 From: BitcoinZavior Date: Tue, 22 Oct 2024 17:20:37 -0400 Subject: [PATCH 03/11] feat: expose Policy::id method --- bdk-ffi/src/bdk.udl | 4 +++- bdk-ffi/src/types.rs | 5 +++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/bdk-ffi/src/bdk.udl b/bdk-ffi/src/bdk.udl index 4a7373f2..61985e13 100644 --- a/bdk-ffi/src/bdk.udl +++ b/bdk-ffi/src/bdk.udl @@ -444,7 +444,9 @@ interface Wallet { interface Update {}; -interface Policy {}; +interface Policy { + string id(); +}; interface TxBuilder { constructor(); diff --git a/bdk-ffi/src/types.rs b/bdk-ffi/src/types.rs index 392d4539..d8ff5ea5 100644 --- a/bdk-ffi/src/types.rs +++ b/bdk-ffi/src/types.rs @@ -251,3 +251,8 @@ impl From for BdkPolicy { value.0 } } +impl Policy { + pub fn id(&self) -> String { + self.0.id.clone() + } +} From 1479893025c60dbbf7e384de40d959cd565cec25 Mon Sep 17 00:00:00 2001 From: BitcoinZavior Date: Thu, 31 Oct 2024 17:23:22 -0400 Subject: [PATCH 04/11] feat: expose Policy::as_string & Policy::requires_path --- bdk-ffi/src/bdk.udl | 4 ++++ bdk-ffi/src/types.rs | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/bdk-ffi/src/bdk.udl b/bdk-ffi/src/bdk.udl index 61985e13..c046518b 100644 --- a/bdk-ffi/src/bdk.udl +++ b/bdk-ffi/src/bdk.udl @@ -446,6 +446,10 @@ interface Update {}; interface Policy { string id(); + + string as_string(); + + boolean requires_path(); }; interface TxBuilder { diff --git a/bdk-ffi/src/types.rs b/bdk-ffi/src/types.rs index d8ff5ea5..57a8406f 100644 --- a/bdk-ffi/src/types.rs +++ b/bdk-ffi/src/types.rs @@ -255,4 +255,12 @@ impl Policy { pub fn id(&self) -> String { self.0.id.clone() } + + pub fn as_string(&self) -> String { + bdk_wallet::serde_json::to_string(&self.0).unwrap() + } + + pub fn requires_path(&self) -> bool { + self.0.requires_path() + } } From c79b3788a1224c13081e6f4e857107232498a4e6 Mon Sep 17 00:00:00 2001 From: BitcoinZavior Date: Fri, 1 Nov 2024 20:23:22 -0400 Subject: [PATCH 05/11] feat: expose policy::item --- bdk-ffi/src/bdk.udl | 29 ++++++++++ bdk-ffi/src/lib.rs | 3 + bdk-ffi/src/types.rs | 131 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 162 insertions(+), 1 deletion(-) diff --git a/bdk-ffi/src/bdk.udl b/bdk-ffi/src/bdk.udl index c046518b..a66de378 100644 --- a/bdk-ffi/src/bdk.udl +++ b/bdk-ffi/src/bdk.udl @@ -450,6 +450,35 @@ interface Policy { string as_string(); boolean requires_path(); + + SatisfiableItem item(); +}; + +[Enum] +interface SatisfiableItem { + EcdsaSignature(PkOrF key); + SchnorrSignature(PkOrF key); + Sha256Preimage(string hash); + Hash256Preimage(string hash); + Ripemd160Preimage(string hash); + Hash160Preimage(string hash); + AbsoluteTimelock(LockTime value); + RelativeTimelock(u32 value); + Multisig(sequence keys, u64 threshold); + Thresh(sequence items, u64 threshold); +}; + +[Enum] +interface PkOrF { + Pubkey(string value); + XOnlyPubkey(string value); + Fingerprint(string value); +}; + +[Enum] +interface LockTime { + Blocks(u32 height); + Seconds(u32 consensus_time); }; interface TxBuilder { diff --git a/bdk-ffi/src/lib.rs b/bdk-ffi/src/lib.rs index 8ed81e3c..2d462611 100644 --- a/bdk-ffi/src/lib.rs +++ b/bdk-ffi/src/lib.rs @@ -57,7 +57,10 @@ use crate::types::FullScanRequestBuilder; use crate::types::FullScanScriptInspector; use crate::types::KeychainAndIndex; use crate::types::LocalOutput; +use crate::types::LockTime; +use crate::types::PkOrF; use crate::types::Policy; +use crate::types::SatisfiableItem; use crate::types::ScriptAmount; use crate::types::SentAndReceivedValues; use crate::types::SyncRequest; diff --git a/bdk-ffi/src/types.rs b/bdk-ffi/src/types.rs index 57a8406f..9a3800dd 100644 --- a/bdk-ffi/src/types.rs +++ b/bdk-ffi/src/types.rs @@ -5,7 +5,9 @@ use bitcoin_ffi::Amount; use bitcoin_ffi::OutPoint; use bitcoin_ffi::Script; +use bdk_core::bitcoin::absolute::LockTime as BdkLockTime; 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; @@ -15,7 +17,10 @@ use bdk_wallet::chain::tx_graph::CanonicalTx as BdkCanonicalTx; use bdk_wallet::chain::{ ChainPosition as BdkChainPosition, ConfirmationBlockTime as BdkConfirmationBlockTime, }; -use bdk_wallet::descriptor::Policy as BdkPolicy; + +use bdk_wallet::descriptor::policy::{ + PkOrF as BdkPkOrF, Policy as BdkPolicy, SatisfiableItem as BdkSatisfiableItem, +}; use bdk_wallet::AddressInfo as BdkAddressInfo; use bdk_wallet::Balance as BdkBalance; use bdk_wallet::KeychainKind; @@ -263,4 +268,128 @@ impl Policy { pub fn requires_path(&self) -> bool { self.0.requires_path() } + pub fn item(&self) -> SatisfiableItem { + self.0.item.clone().into() + } +} + +#[derive(Debug, Clone)] +pub enum SatisfiableItem { + EcdsaSignature { + key: PkOrF, + }, + SchnorrSignature { + key: PkOrF, + }, + Sha256Preimage { + hash: String, + }, + Hash256Preimage { + hash: String, + }, + Ripemd160Preimage { + hash: String, + }, + Hash160Preimage { + hash: String, + }, + AbsoluteTimelock { + value: LockTime, + }, + RelativeTimelock { + value: u32, + }, + + Multisig { + keys: Vec, + + threshold: u64, + }, + + Thresh { + items: Vec>, + + threshold: u64, + }, +} +impl From for SatisfiableItem { + fn from(value: BdkSatisfiableItem) -> Self { + match value { + BdkSatisfiableItem::EcdsaSignature(pk_or_f) => SatisfiableItem::EcdsaSignature { + key: pk_or_f.into(), + }, + BdkSatisfiableItem::SchnorrSignature(pk_or_f) => SatisfiableItem::SchnorrSignature { + key: pk_or_f.into(), + }, + BdkSatisfiableItem::Sha256Preimage { hash } => SatisfiableItem::Sha256Preimage { + hash: hash.to_string(), + }, + BdkSatisfiableItem::Hash256Preimage { hash } => SatisfiableItem::Hash256Preimage { + hash: hash.to_string(), + }, + BdkSatisfiableItem::Ripemd160Preimage { hash } => SatisfiableItem::Ripemd160Preimage { + hash: hash.to_string(), + }, + BdkSatisfiableItem::Hash160Preimage { hash } => SatisfiableItem::Hash160Preimage { + hash: hash.to_string(), + }, + BdkSatisfiableItem::AbsoluteTimelock { value } => SatisfiableItem::AbsoluteTimelock { + value: value.into(), + }, + BdkSatisfiableItem::RelativeTimelock { value } => SatisfiableItem::RelativeTimelock { + value: value.to_consensus_u32(), + }, + BdkSatisfiableItem::Multisig { keys, threshold } => SatisfiableItem::Multisig { + keys: keys.iter().map(|e| e.to_owned().into()).collect(), + threshold: threshold as u64, + }, + BdkSatisfiableItem::Thresh { items, threshold } => SatisfiableItem::Thresh { + items: items + .iter() + .map(|e| Arc::new(e.to_owned().into())) + .collect(), + threshold: threshold as u64, + }, + } + } +} + +#[derive(Debug, Clone)] +pub enum PkOrF { + Pubkey { value: String }, + XOnlyPubkey { value: String }, + Fingerprint { value: String }, +} +impl From for PkOrF { + fn from(value: BdkPkOrF) -> Self { + match value { + BdkPkOrF::Pubkey(public_key) => PkOrF::Pubkey { + value: public_key.to_string(), + }, + BdkPkOrF::XOnlyPubkey(xonly_public_key) => PkOrF::XOnlyPubkey { + value: xonly_public_key.to_string(), + }, + BdkPkOrF::Fingerprint(fingerprint) => PkOrF::Fingerprint { + value: fingerprint.to_string(), + }, + } + } +} + +#[derive(Debug, Clone)] +pub enum LockTime { + Blocks { height: u32 }, + Seconds { consensus_time: u32 }, +} +impl From for LockTime { + fn from(value: BdkLockTime) -> Self { + match value { + BdkLockTime::Blocks(height) => LockTime::Blocks { + height: height.to_consensus_u32(), + }, + BdkLockTime::Seconds(time) => LockTime::Seconds { + consensus_time: time.to_consensus_u32(), + }, + } + } } From d22510f80f91333ed888a1d6a43647ed525ecdb2 Mon Sep 17 00:00:00 2001 From: BitcoinZavior Date: Sat, 2 Nov 2024 13:40:32 -0400 Subject: [PATCH 06/11] feat: expose policy::satisfaction --- bdk-ffi/src/bdk.udl | 15 +++++++ bdk-ffi/src/lib.rs | 2 + bdk-ffi/src/types.rs | 101 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 117 insertions(+), 1 deletion(-) diff --git a/bdk-ffi/src/bdk.udl b/bdk-ffi/src/bdk.udl index a66de378..898c2cfd 100644 --- a/bdk-ffi/src/bdk.udl +++ b/bdk-ffi/src/bdk.udl @@ -452,6 +452,8 @@ interface Policy { boolean requires_path(); SatisfiableItem item(); + + Satisfaction satisfaction(); }; [Enum] @@ -481,6 +483,19 @@ interface LockTime { Seconds(u32 consensus_time); }; +[Enum] +interface Satisfaction { + Partial(u64 n, u64 m, sequence items, boolean? sorted, record> conditions); + PartialComplete(u64 n, u64 m, sequence items, boolean? sorted, record, sequence> conditions); + Complete(Condition condition); + None(string msg); +}; + +dictionary Condition { + u32? csv; + LockTime? timelock; +}; + interface TxBuilder { constructor(); diff --git a/bdk-ffi/src/lib.rs b/bdk-ffi/src/lib.rs index 2d462611..2a082439 100644 --- a/bdk-ffi/src/lib.rs +++ b/bdk-ffi/src/lib.rs @@ -51,6 +51,7 @@ use crate::types::Balance; use crate::types::BlockId; use crate::types::CanonicalTx; use crate::types::ChainPosition; +use crate::types::Condition; use crate::types::ConfirmationBlockTime; use crate::types::FullScanRequest; use crate::types::FullScanRequestBuilder; @@ -60,6 +61,7 @@ use crate::types::LocalOutput; use crate::types::LockTime; use crate::types::PkOrF; use crate::types::Policy; +use crate::types::Satisfaction; use crate::types::SatisfiableItem; use crate::types::ScriptAmount; use crate::types::SentAndReceivedValues; diff --git a/bdk-ffi/src/types.rs b/bdk-ffi/src/types.rs index 9a3800dd..654606b4 100644 --- a/bdk-ffi/src/types.rs +++ b/bdk-ffi/src/types.rs @@ -19,13 +19,15 @@ use bdk_wallet::chain::{ }; use bdk_wallet::descriptor::policy::{ - PkOrF as BdkPkOrF, Policy as BdkPolicy, SatisfiableItem as BdkSatisfiableItem, + Condition as BdkCondition, PkOrF as BdkPkOrF, Policy as BdkPolicy, + Satisfaction as BdkSatisfaction, SatisfiableItem as BdkSatisfiableItem, }; 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 std::collections::HashMap; use std::sync::{Arc, Mutex}; #[derive(Debug)] @@ -268,9 +270,14 @@ impl Policy { pub fn requires_path(&self) -> bool { self.0.requires_path() } + pub fn item(&self) -> SatisfiableItem { self.0.item.clone().into() } + + pub fn satisfaction(&self) -> Satisfaction { + self.0.satisfaction.clone().into() + } } #[derive(Debug, Clone)] @@ -393,3 +400,95 @@ impl From for LockTime { } } } +#[derive(Debug, Clone)] +pub enum Satisfaction { + Partial { + n: u64, + m: u64, + items: Vec, + sorted: Option, + conditions: HashMap>, + }, + PartialComplete { + n: u64, + m: u64, + items: Vec, + sorted: Option, + conditions: HashMap, Vec>, + }, + Complete { + condition: Condition, + }, + + None { + msg: String, + }, +} +impl From for Satisfaction { + fn from(value: BdkSatisfaction) -> Self { + match value { + BdkSatisfaction::Partial { + n, + m, + items, + sorted, + conditions, + } => Satisfaction::Partial { + n: n as u64, + m: m as u64, + items: items.iter().map(|e| e.to_owned() as u64).collect(), + sorted, + conditions: conditions + .into_iter() + .map(|(index, conditions)| { + ( + index as u32, + conditions.into_iter().map(|e| e.into()).collect(), + ) + }) + .collect(), + }, + BdkSatisfaction::PartialComplete { + n, + m, + items, + sorted, + conditions, + } => Satisfaction::PartialComplete { + n: n as u64, + m: m as u64, + items: items.iter().map(|e| e.to_owned() as u64).collect(), + sorted, + conditions: conditions + .into_iter() + .map(|(index, conditions)| { + ( + index.iter().map(|e| e.to_owned() as u32).collect(), + conditions.into_iter().map(|e| e.into()).collect(), // Convert each `Condition` to `YourType` + ) + }) + .collect(), + }, + BdkSatisfaction::Complete { condition } => Satisfaction::Complete { + condition: condition.into(), + }, + BdkSatisfaction::None => Satisfaction::None { + msg: "Cannot satisfy or contribute to the policy item".to_string(), + }, + } + } +} + +#[derive(Debug, Clone)] +pub struct Condition { + pub csv: Option, + pub timelock: Option, +} +impl From for Condition { + fn from(value: BdkCondition) -> Self { + Condition { + csv: value.csv.map(|e| e.to_consensus_u32()), + timelock: value.timelock.map(|e| e.into()), + } + } +} From 437fcd047ab6d534a0d750928218a12db9d7ca8b Mon Sep 17 00:00:00 2001 From: BitcoinZavior Date: Mon, 4 Nov 2024 14:21:43 -0500 Subject: [PATCH 07/11] feat: expose policy::contribution --- bdk-ffi/src/bdk.udl | 2 ++ bdk-ffi/src/types.rs | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/bdk-ffi/src/bdk.udl b/bdk-ffi/src/bdk.udl index 898c2cfd..104c5a74 100644 --- a/bdk-ffi/src/bdk.udl +++ b/bdk-ffi/src/bdk.udl @@ -454,6 +454,8 @@ interface Policy { SatisfiableItem item(); Satisfaction satisfaction(); + + Satisfaction contribution(); }; [Enum] diff --git a/bdk-ffi/src/types.rs b/bdk-ffi/src/types.rs index 654606b4..904ab6de 100644 --- a/bdk-ffi/src/types.rs +++ b/bdk-ffi/src/types.rs @@ -278,6 +278,10 @@ impl Policy { pub fn satisfaction(&self) -> Satisfaction { self.0.satisfaction.clone().into() } + + pub fn contribution(&self) -> Satisfaction { + self.0.contribution.clone().into() + } } #[derive(Debug, Clone)] From 2eff41220fe3e851bcf5971a53c82130008c2160 Mon Sep 17 00:00:00 2001 From: BitcoinZavior Date: Mon, 4 Nov 2024 19:43:33 -0500 Subject: [PATCH 08/11] Refactor policy_path in TxBuilder to prevent values from being overwritten. --- bdk-ffi/src/tx_builder.rs | 37 ++++++++++++++++++++++--------------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/bdk-ffi/src/tx_builder.rs b/bdk-ffi/src/tx_builder.rs index 78882646..e71cf325 100644 --- a/bdk-ffi/src/tx_builder.rs +++ b/bdk-ffi/src/tx_builder.rs @@ -24,7 +24,8 @@ pub struct TxBuilder { pub(crate) recipients: Vec<(BdkScriptBuf, BdkAmount)>, pub(crate) utxos: Vec, pub(crate) unspendable: HashSet, - pub(crate) policy_path: (HashMap>, KeychainKind), + pub(crate) internal_policy_path: Option>>, + pub(crate) external_policy_path: Option>>, pub(crate) change_policy: ChangeSpendPolicy, pub(crate) manually_selected_only: bool, pub(crate) fee_rate: Option, @@ -41,7 +42,8 @@ impl TxBuilder { recipients: Vec::new(), utxos: Vec::new(), unspendable: HashSet::new(), - policy_path: (HashMap::new(), KeychainKind::External), + internal_policy_path: None, + external_policy_path: None, change_policy: ChangeSpendPolicy::ChangeAllowed, manually_selected_only: false, fee_rate: None, @@ -114,10 +116,18 @@ impl TxBuilder { policy_path: HashMap>, keychain: KeychainKind, ) -> Arc { - Arc::new(TxBuilder { - policy_path: (policy_path, keychain), - ..self.clone() - }) + let mut updated_self = self.clone(); + let to_update = match keychain { + KeychainKind::Internal => &mut updated_self.internal_policy_path, + KeychainKind::External => &mut updated_self.external_policy_path, + }; + *to_update = Some( + policy_path + .into_iter() + .map(|(key, value)| (key, value.into_iter().map(|x| x as usize).collect())) + .collect::>>(), + ); + Arc::new(updated_self) } pub(crate) fn change_policy(&self, change_policy: ChangeSpendPolicy) -> Arc { @@ -193,15 +203,12 @@ impl TxBuilder { for (script, amount) in &self.recipients { tx_builder.add_recipient(script.clone(), *amount); } - tx_builder.policy_path( - self.policy_path - .0 - .clone() - .into_iter() - .map(|(key, value)| (key, value.into_iter().map(|x| x as usize).collect())) - .collect::>>(), - self.policy_path.1, - ); + if let Some(policy_path) = &self.external_policy_path { + tx_builder.policy_path(policy_path.clone(), KeychainKind::External); + } + if let Some(policy_path) = &self.internal_policy_path { + tx_builder.policy_path(policy_path.clone(), KeychainKind::Internal); + } tx_builder.change_policy(self.change_policy); if !self.utxos.is_empty() { tx_builder From a4f9a0094ec754443201e6daf2b0a43e3607181c Mon Sep 17 00:00:00 2001 From: BitcoinZavior Date: Tue, 5 Nov 2024 14:32:44 -0500 Subject: [PATCH 09/11] test: add test for tx_builder::policy_path method --- bdk-ffi/src/tx_builder.rs | 89 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/bdk-ffi/src/tx_builder.rs b/bdk-ffi/src/tx_builder.rs index e71cf325..da5f9628 100644 --- a/bdk-ffi/src/tx_builder.rs +++ b/bdk-ffi/src/tx_builder.rs @@ -283,3 +283,92 @@ impl BumpFeeTxBuilder { Ok(Arc::new(psbt.into())) } } + +#[cfg(test)] +mod tests { + use std::sync::Arc; + + use bitcoin_ffi::Network; + + use crate::{ + descriptor::Descriptor, esplora::EsploraClient, store::Connection, + types::FullScanScriptInspector, wallet::Wallet, + }; + + struct FullScanInspector; + impl FullScanScriptInspector for FullScanInspector { + fn inspect(&self, _: bdk_wallet::KeychainKind, _: u32, _: Arc) {} + } + + #[test] + fn test_policy_path() { + let wallet = create_and_sync_wallet(); + let address = wallet + .next_unused_address(bdk_wallet::KeychainKind::External) + .address; + println!("Wallet address: {:?}", address); + + let ext_policy = wallet.policies(bdk_wallet::KeychainKind::External); + let int_policy = wallet.policies(bdk_wallet::KeychainKind::Internal); + + if let (Ok(Some(ext_policy)), Ok(Some(int_policy))) = (ext_policy, int_policy) { + let ext_path = vec![(ext_policy.id().clone(), vec![0, 1])] + .into_iter() + .collect(); + println!("External Policy path : {:?}\n", ext_path); + let int_path = vec![(int_policy.id().clone(), vec![0, 1])] + .into_iter() + .collect(); + println!("Internal Policy Path: {:?}\n", int_path); + + match crate::tx_builder::TxBuilder::new() + .add_recipient( + &(*address.script_pubkey()).to_owned(), + Arc::new(bitcoin_ffi::Amount::from_sat(1000)), + ) + .do_not_spend_change() + .policy_path(int_path, bdk_wallet::KeychainKind::Internal) + .policy_path(ext_path, bdk_wallet::KeychainKind::External) + .finish(&Arc::new(wallet)) + { + Ok(tx) => println!("Transaction serialized: {}\n", tx.serialize()), + Err(e) => println!("Error: {}", e.to_string()), + } + } else { + println!("Failed to retrieve valid policies for keychains."); + } + } + + fn create_and_sync_wallet() -> Wallet { + let external_descriptor = format!( + "wsh(thresh(2,pk({}/0/*),sj:and_v(v:pk({}/0/*),n:older(6)),snj:and_v(v:pk({}/0/*),after(630000))))", + "tpubD6NzVbkrYhZ4XJBfEJ6gt9DiVdfWJijsQTCE3jtXByW3Tk6AVGQ3vL1NNxg3SjB7QkJAuutACCQjrXD8zdZSM1ZmBENszCqy49ECEHmD6rf", + "tpubD6NzVbkrYhZ4YfAr3jCBRk4SpqB9L1Hh442y83njwfMaker7EqZd7fHMqyTWrfRYJ1e5t2ue6BYjW5i5yQnmwqbzY1a3kfqNxog1AFcD1aE", + "tprv8ZgxMBicQKsPeitVUz3s6cfyCECovNP7t82FaKPa4UKqV1kssWcXgLkMDjzDbgG9GWoza4pL7z727QitfzkiwX99E1Has3T3a1MKHvYWmQZ" + ); + let internal_descriptor = format!( + "wsh(thresh(2,pk({}/1/*),sj:and_v(v:pk({}/1/*),n:older(6)),snj:and_v(v:pk({}/1/*),after(630000))))", + "tpubD6NzVbkrYhZ4XJBfEJ6gt9DiVdfWJijsQTCE3jtXByW3Tk6AVGQ3vL1NNxg3SjB7QkJAuutACCQjrXD8zdZSM1ZmBENszCqy49ECEHmD6rf", + "tpubD6NzVbkrYhZ4YfAr3jCBRk4SpqB9L1Hh442y83njwfMaker7EqZd7fHMqyTWrfRYJ1e5t2ue6BYjW5i5yQnmwqbzY1a3kfqNxog1AFcD1aE", + "tprv8ZgxMBicQKsPeitVUz3s6cfyCECovNP7t82FaKPa4UKqV1kssWcXgLkMDjzDbgG9GWoza4pL7z727QitfzkiwX99E1Has3T3a1MKHvYWmQZ" + ); + let wallet = Wallet::new( + Arc::new(Descriptor::new(external_descriptor, Network::Signet).unwrap()), + Arc::new(Descriptor::new(internal_descriptor, Network::Signet).unwrap()), + Network::Signet, + Arc::new(Connection::new_in_memory().unwrap()), + ) + .unwrap(); + let client = EsploraClient::new("https://mutinynet.com/api/".to_string()); + let full_scan_builder = wallet.start_full_scan(); + let full_scan_request = full_scan_builder + .inspect_spks_for_all_keychains(Arc::new(FullScanInspector)) + .unwrap() + .build() + .unwrap(); + let update = client.full_scan(full_scan_request, 10, 10).unwrap(); + wallet.apply_update(update).unwrap(); + println!("Wallet balance: {:?}", wallet.balance().total.to_sat()); + wallet + } +} From e0251b4c3d90995b5150795c128ca20067e2c2d6 Mon Sep 17 00:00:00 2001 From: BitcoinZavior Date: Wed, 6 Nov 2024 19:30:24 -0500 Subject: [PATCH 10/11] new lines corrected --- bdk-ffi/src/types.rs | 1 + bdk-ffi/src/wallet.rs | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/bdk-ffi/src/types.rs b/bdk-ffi/src/types.rs index 904ab6de..8adf99a0 100644 --- a/bdk-ffi/src/types.rs +++ b/bdk-ffi/src/types.rs @@ -27,6 +27,7 @@ use bdk_wallet::Balance as BdkBalance; use bdk_wallet::KeychainKind; use bdk_wallet::LocalOutput as BdkLocalOutput; use bdk_wallet::Update as BdkUpdate; + use std::collections::HashMap; use std::sync::{Arc, Mutex}; diff --git a/bdk-ffi/src/wallet.rs b/bdk-ffi/src/wallet.rs index f89ac4fe..582a7724 100644 --- a/bdk-ffi/src/wallet.rs +++ b/bdk-ffi/src/wallet.rs @@ -161,7 +161,8 @@ impl Wallet { pub(crate) fn sign( &self, - psbt: Arc, // sign_options: Option, + psbt: Arc, + // sign_options: Option, ) -> Result { let mut psbt = psbt.0.lock().unwrap(); self.get_wallet() From 2b8592e4dc2c9405da98effa5db893e082ac173b Mon Sep 17 00:00:00 2001 From: BitcoinZavior Date: Thu, 7 Nov 2024 21:26:22 -0500 Subject: [PATCH 11/11] Fix Clippy warning: simplify error handling --- bdk-ffi/src/tx_builder.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bdk-ffi/src/tx_builder.rs b/bdk-ffi/src/tx_builder.rs index da5f9628..c11e6b0b 100644 --- a/bdk-ffi/src/tx_builder.rs +++ b/bdk-ffi/src/tx_builder.rs @@ -332,7 +332,7 @@ mod tests { .finish(&Arc::new(wallet)) { Ok(tx) => println!("Transaction serialized: {}\n", tx.serialize()), - Err(e) => println!("Error: {}", e.to_string()), + Err(e) => eprintln!("Error: {:?}", e), } } else { println!("Failed to retrieve valid policies for keychains.");