Skip to content

Commit 8a4e372

Browse files
committed
feat: add wallet changeset primitives
1 parent b82acb5 commit 8a4e372

File tree

4 files changed

+224
-7
lines changed

4 files changed

+224
-7
lines changed

bdk-ffi/src/bitcoin.rs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ use std::str::FromStr;
3737
use std::sync::{Arc, Mutex};
3838

3939
/// A reference to an unspent output by TXID and output index.
40-
#[derive(Debug, Clone, Eq, PartialEq, uniffi:: Record)]
40+
#[derive(Debug, Clone, Eq, PartialEq, std::hash::Hash, uniffi:: Record)]
4141
pub struct OutPoint {
4242
/// The transaction.
4343
pub txid: Arc<Txid>,
@@ -54,6 +54,15 @@ impl From<&BdkOutPoint> for OutPoint {
5454
}
5555
}
5656

57+
impl From<BdkOutPoint> for OutPoint {
58+
fn from(value: BdkOutPoint) -> Self {
59+
Self {
60+
txid: Arc::new(Txid(value.txid)),
61+
vout: value.vout,
62+
}
63+
}
64+
}
65+
5766
impl From<OutPoint> for BdkOutPoint {
5867
fn from(outpoint: OutPoint) -> Self {
5968
BdkOutPoint {
@@ -63,6 +72,19 @@ impl From<OutPoint> for BdkOutPoint {
6372
}
6473
}
6574

75+
/// An [`OutPoint`] suitable as a key in a hash map.
76+
#[derive(Debug, PartialEq, Eq, std::hash::Hash, uniffi::Object)]
77+
#[uniffi::export(Debug, Eq, Hash)]
78+
pub struct HashableOutPoint(pub(crate) OutPoint);
79+
80+
#[uniffi::export]
81+
impl HashableOutPoint {
82+
/// Get the internal [`OutPoint`]
83+
pub fn outpoint(&self) -> OutPoint {
84+
self.0.clone()
85+
}
86+
}
87+
6688
/// Represents fee rate.
6789
///
6890
/// This is an integer type representing fee rate in sat/kwu. It provides protection against mixing
@@ -588,6 +610,24 @@ impl From<&BdkTxOut> for TxOut {
588610
}
589611
}
590612

613+
impl From<BdkTxOut> for TxOut {
614+
fn from(tx_out: BdkTxOut) -> Self {
615+
Self {
616+
value: tx_out.value.to_sat(),
617+
script_pubkey: Arc::new(Script(tx_out.script_pubkey)),
618+
}
619+
}
620+
}
621+
622+
impl From<TxOut> for BdkTxOut {
623+
fn from(tx_out: TxOut) -> Self {
624+
Self {
625+
value: BdkAmount::from_sat(tx_out.value),
626+
script_pubkey: tx_out.script_pubkey.0.clone(),
627+
}
628+
}
629+
}
630+
591631
/// A bitcoin Block hash
592632
#[derive(Debug, Clone, Copy, PartialEq, Eq, std::hash::Hash, uniffi::Object)]
593633
#[uniffi::export(Display, Eq, Hash)]

bdk-ffi/src/types.rs

Lines changed: 181 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
use crate::bitcoin::{Address, Amount, BlockHash, OutPoint, Script, Transaction, TxOut, Txid};
1+
use crate::bitcoin::{
2+
Address, Amount, BlockHash, DescriptorId, HashableOutPoint, OutPoint, Script, Transaction,
3+
TxOut, Txid,
4+
};
25
use crate::error::{CreateTxError, RequestBuilderError};
36

47
use bdk_core::bitcoin::absolute::LockTime as BdkLockTime;
@@ -24,7 +27,7 @@ use bdk_wallet::Balance as BdkBalance;
2427
use bdk_wallet::LocalOutput as BdkLocalOutput;
2528
use bdk_wallet::Update as BdkUpdate;
2629

27-
use std::collections::HashMap;
30+
use std::collections::{BTreeMap, BTreeSet, HashMap};
2831
use std::convert::TryFrom;
2932
use std::sync::{Arc, Mutex};
3033

@@ -85,16 +88,34 @@ impl From<BdkChainPosition<BdkConfirmationBlockTime>> for ChainPosition {
8588
}
8689

8790
/// Represents the confirmation block and time of a transaction.
88-
#[derive(Debug, uniffi::Record)]
91+
#[derive(Debug, Clone, PartialEq, Eq, std::hash::Hash, uniffi::Record)]
8992
pub struct ConfirmationBlockTime {
9093
/// The anchor block.
9194
pub block_id: BlockId,
9295
/// The confirmation time of the transaction being anchored.
9396
pub confirmation_time: u64,
9497
}
9598

99+
impl From<BdkConfirmationBlockTime> for ConfirmationBlockTime {
100+
fn from(value: BdkConfirmationBlockTime) -> Self {
101+
Self {
102+
block_id: value.block_id.into(),
103+
confirmation_time: value.confirmation_time,
104+
}
105+
}
106+
}
107+
108+
impl From<ConfirmationBlockTime> for BdkConfirmationBlockTime {
109+
fn from(value: ConfirmationBlockTime) -> Self {
110+
Self {
111+
block_id: value.block_id.into(),
112+
confirmation_time: value.confirmation_time,
113+
}
114+
}
115+
}
116+
96117
/// A reference to a block in the canonical chain.
97-
#[derive(Debug, uniffi::Record)]
118+
#[derive(Debug, Clone, PartialEq, Eq, std::hash::Hash, uniffi::Record)]
98119
pub struct BlockId {
99120
/// The height of the block.
100121
pub height: u32,
@@ -111,6 +132,15 @@ impl From<BdkBlockId> for BlockId {
111132
}
112133
}
113134

135+
impl From<BlockId> for BdkBlockId {
136+
fn from(value: BlockId) -> Self {
137+
Self {
138+
height: value.height,
139+
hash: value.hash.0,
140+
}
141+
}
142+
}
143+
114144
/// A transaction that is deemed to be part of the canonical history.
115145
#[derive(uniffi::Record)]
116146
pub struct CanonicalTx {
@@ -725,3 +755,150 @@ pub struct UnconfirmedTx {
725755
pub tx: Arc<Transaction>,
726756
pub last_seen: u64,
727757
}
758+
759+
/// Mapping of descriptors to their last revealed index.
760+
#[derive(Debug, Clone, uniffi::Record)]
761+
pub struct IndexerChangeSet {
762+
pub last_revealed: HashMap<Arc<DescriptorId>, u32>,
763+
}
764+
765+
impl From<bdk_wallet::chain::indexer::keychain_txout::ChangeSet> for IndexerChangeSet {
766+
fn from(mut value: bdk_wallet::chain::indexer::keychain_txout::ChangeSet) -> Self {
767+
let mut changes = HashMap::new();
768+
for (id, index) in core::mem::take(&mut value.last_revealed) {
769+
changes.insert(Arc::new(DescriptorId(id.0)), index);
770+
}
771+
Self {
772+
last_revealed: changes,
773+
}
774+
}
775+
}
776+
777+
impl From<IndexerChangeSet> for bdk_wallet::chain::indexer::keychain_txout::ChangeSet {
778+
fn from(mut value: IndexerChangeSet) -> Self {
779+
let mut changes = BTreeMap::new();
780+
for (id, index) in core::mem::take(&mut value.last_revealed) {
781+
let descriptor_id = bdk_wallet::chain::DescriptorId(id.0);
782+
changes.insert(descriptor_id, index);
783+
}
784+
Self {
785+
last_revealed: changes,
786+
}
787+
}
788+
}
789+
790+
/// The hash added or removed at the given height.
791+
#[derive(Debug, Clone, uniffi::Record)]
792+
pub struct ChainChange {
793+
/// Effected height
794+
pub height: u32,
795+
/// A hash was added or must be removed.
796+
pub hash: Option<Arc<BlockHash>>,
797+
}
798+
799+
/// Changes to the local chain
800+
#[derive(Debug, Clone, uniffi::Record)]
801+
pub struct LocalChainChangeSet {
802+
pub changes: Vec<ChainChange>,
803+
}
804+
805+
impl From<bdk_wallet::chain::local_chain::ChangeSet> for LocalChainChangeSet {
806+
fn from(mut value: bdk_wallet::chain::local_chain::ChangeSet) -> Self {
807+
let mut changes = Vec::with_capacity(value.blocks.len());
808+
for (height, hash) in core::mem::take(&mut value.blocks) {
809+
let hash = hash.map(|h| Arc::new(BlockHash(h)));
810+
let change = ChainChange { height, hash };
811+
changes.push(change);
812+
}
813+
Self { changes }
814+
}
815+
}
816+
817+
impl From<LocalChainChangeSet> for bdk_wallet::chain::local_chain::ChangeSet {
818+
fn from(mut value: LocalChainChangeSet) -> Self {
819+
let mut changes = BTreeMap::new();
820+
for change in core::mem::take(&mut value.changes) {
821+
let height = change.height;
822+
let hash = change.hash.map(|h| h.0);
823+
changes.insert(height, hash);
824+
}
825+
Self { blocks: changes }
826+
}
827+
}
828+
829+
#[derive(Debug, Clone, uniffi::Record)]
830+
pub struct Anchor {
831+
pub confirmation_block_time: ConfirmationBlockTime,
832+
pub txid: Arc<Txid>,
833+
}
834+
835+
#[derive(Debug, Clone, uniffi::Record)]
836+
pub struct TxGraphChangeSet {
837+
pub txs: Vec<Arc<Transaction>>,
838+
pub txouts: HashMap<Arc<HashableOutPoint>, TxOut>,
839+
pub anchors: Vec<Anchor>,
840+
pub last_seen: HashMap<Arc<Txid>, u64>,
841+
}
842+
843+
impl From<bdk_wallet::chain::tx_graph::ChangeSet<BdkConfirmationBlockTime>> for TxGraphChangeSet {
844+
fn from(mut value: bdk_wallet::chain::tx_graph::ChangeSet<BdkConfirmationBlockTime>) -> Self {
845+
let btree_txs = core::mem::take(&mut value.txs);
846+
let txs = btree_txs
847+
.into_iter()
848+
.map(|tx| Arc::new(tx.as_ref().into()))
849+
.collect::<Vec<Arc<Transaction>>>();
850+
let mut txouts = HashMap::new();
851+
for (outpoint, txout) in core::mem::take(&mut value.txouts) {
852+
txouts.insert(Arc::new(HashableOutPoint(outpoint.into())), txout.into());
853+
}
854+
let mut anchors = Vec::new();
855+
for anchor in core::mem::take(&mut value.anchors) {
856+
let confirmation_block_time = anchor.0.into();
857+
let txid = Arc::new(Txid(anchor.1));
858+
let anchor = Anchor {
859+
confirmation_block_time,
860+
txid,
861+
};
862+
anchors.push(anchor);
863+
}
864+
let mut last_seens = HashMap::new();
865+
for (txid, time) in core::mem::take(&mut value.last_seen) {
866+
last_seens.insert(Arc::new(Txid(txid)), time);
867+
}
868+
TxGraphChangeSet {
869+
txs,
870+
txouts,
871+
anchors,
872+
last_seen: last_seens,
873+
}
874+
}
875+
}
876+
877+
impl From<TxGraphChangeSet> for bdk_wallet::chain::tx_graph::ChangeSet<BdkConfirmationBlockTime> {
878+
fn from(mut value: TxGraphChangeSet) -> Self {
879+
let mut txs = BTreeSet::new();
880+
for tx in core::mem::take(&mut value.txs) {
881+
let tx = Arc::new(tx.as_ref().into());
882+
txs.insert(tx);
883+
}
884+
let mut txouts = BTreeMap::new();
885+
for txout in core::mem::take(&mut value.txouts) {
886+
txouts.insert(txout.0.outpoint().into(), txout.1.into());
887+
}
888+
let mut anchors = BTreeSet::new();
889+
for anchor in core::mem::take(&mut value.anchors) {
890+
let txid = anchor.txid.0;
891+
anchors.insert((anchor.confirmation_block_time.into(), txid));
892+
}
893+
let mut last_seen = BTreeMap::new();
894+
for (txid, time) in core::mem::take(&mut value.last_seen) {
895+
last_seen.insert(txid.0, time);
896+
}
897+
Self {
898+
txs,
899+
txouts,
900+
anchors,
901+
last_seen,
902+
}
903+
}
904+
}

bdk-ffi/tests/bindings/test.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
*/
55

66
import org.bitcoindevkit.bitcoin.Network
7-
import org.bitcoindevkit.BlockId
7+
import org.bitcoindevkit.Condition
88

99
// A type from bitcoin-ffi
1010
val network = Network.TESTNET

bdk-ffi/tests/bindings/test.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from bdkpython import BlockId
1+
from bdkpython import Condition
22
from bdkpython.bitcoin import Network
33

44
import unittest

0 commit comments

Comments
 (0)