Skip to content

Commit d676afa

Browse files
committed
feat: add wallet changeset primitives
1 parent ed7064f commit d676afa

File tree

5 files changed

+242
-17
lines changed

5 files changed

+242
-17
lines changed

bdk-ffi/src/bitcoin.rs

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,32 +37,54 @@ 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.
43-
pub txid: String,
43+
pub txid: Arc<Txid>,
4444
/// The index of the output in the transaction.
4545
pub vout: u32,
4646
}
4747

4848
impl From<&BdkOutPoint> for OutPoint {
4949
fn from(outpoint: &BdkOutPoint) -> Self {
5050
OutPoint {
51-
txid: outpoint.txid.to_string(),
51+
txid: Arc::new(Txid(outpoint.txid)),
5252
vout: outpoint.vout,
5353
}
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 {
60-
txid: BitcoinTxid::from_str(&outpoint.txid).unwrap(),
69+
txid: BitcoinTxid::from_raw_hash(outpoint.txid.0.into()),
6170
vout: outpoint.vout,
6271
}
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
@@ -549,7 +571,7 @@ impl From<&BdkTxIn> for TxIn {
549571
fn from(tx_in: &BdkTxIn) -> Self {
550572
TxIn {
551573
previous_output: OutPoint {
552-
txid: tx_in.previous_output.txid.to_string(),
574+
txid: Arc::new(Txid(tx_in.previous_output.txid)),
553575
vout: tx_in.previous_output.vout,
554576
},
555577
script_sig: Arc::new(Script(tx_in.script_sig.clone())),
@@ -583,6 +605,24 @@ impl From<&BdkTxOut> for TxOut {
583605
}
584606
}
585607

608+
impl From<BdkTxOut> for TxOut {
609+
fn from(tx_out: BdkTxOut) -> Self {
610+
Self {
611+
value: tx_out.value.to_sat(),
612+
script_pubkey: Arc::new(Script(tx_out.script_pubkey)),
613+
}
614+
}
615+
}
616+
617+
impl From<TxOut> for BdkTxOut {
618+
fn from(tx_out: TxOut) -> Self {
619+
Self {
620+
value: BdkAmount::from_sat(tx_out.value),
621+
script_pubkey: tx_out.script_pubkey.0.clone(),
622+
}
623+
}
624+
}
625+
586626
/// A bitcoin Block hash
587627
#[derive(Debug, Clone, Copy, PartialEq, Eq, std::hash::Hash, uniffi::Object)]
588628
#[uniffi::export(Display, Eq, Hash)]

bdk-ffi/src/types.rs

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

46
use bdk_core::bitcoin::absolute::LockTime as BdkLockTime;
@@ -23,7 +25,7 @@ use bdk_wallet::Balance as BdkBalance;
2325
use bdk_wallet::LocalOutput as BdkLocalOutput;
2426
use bdk_wallet::Update as BdkUpdate;
2527

26-
use std::collections::HashMap;
28+
use std::collections::{BTreeMap, BTreeSet, HashMap};
2729
use std::convert::TryFrom;
2830
use std::sync::{Arc, Mutex};
2931

@@ -66,7 +68,7 @@ impl From<BdkChainPosition<BdkConfirmationBlockTime>> for ChainPosition {
6668
} => {
6769
let block_id = BlockId {
6870
height: anchor.block_id.height,
69-
hash: anchor.block_id.hash.to_string(),
71+
hash: Arc::new(BlockHash(anchor.block_id.hash)),
7072
};
7173
ChainPosition::Confirmed {
7274
confirmation_block_time: ConfirmationBlockTime {
@@ -84,21 +86,57 @@ impl From<BdkChainPosition<BdkConfirmationBlockTime>> for ChainPosition {
8486
}
8587

8688
/// Represents the confirmation block and time of a transaction.
87-
#[derive(Debug, uniffi::Record)]
89+
#[derive(Debug, Clone, PartialEq, Eq, std::hash::Hash, uniffi::Record)]
8890
pub struct ConfirmationBlockTime {
8991
/// The anchor block.
9092
pub block_id: BlockId,
9193
/// The confirmation time of the transaction being anchored.
9294
pub confirmation_time: u64,
9395
}
9496

97+
impl From<BdkConfirmationBlockTime> for ConfirmationBlockTime {
98+
fn from(value: BdkConfirmationBlockTime) -> Self {
99+
Self {
100+
block_id: value.block_id.into(),
101+
confirmation_time: value.confirmation_time,
102+
}
103+
}
104+
}
105+
106+
impl From<ConfirmationBlockTime> for BdkConfirmationBlockTime {
107+
fn from(value: ConfirmationBlockTime) -> Self {
108+
Self {
109+
block_id: value.block_id.into(),
110+
confirmation_time: value.confirmation_time,
111+
}
112+
}
113+
}
114+
95115
/// A reference to a block in the canonical chain.
96-
#[derive(Debug, uniffi::Record)]
116+
#[derive(Debug, Clone, PartialEq, Eq, std::hash::Hash, uniffi::Record)]
97117
pub struct BlockId {
98118
/// The height of the block.
99119
pub height: u32,
100120
/// The hash of the block.
101-
pub hash: String,
121+
pub hash: Arc<BlockHash>,
122+
}
123+
124+
impl From<bdk_wallet::chain::BlockId> for BlockId {
125+
fn from(value: bdk_wallet::chain::BlockId) -> Self {
126+
Self {
127+
height: value.height,
128+
hash: Arc::new(BlockHash(value.hash)),
129+
}
130+
}
131+
}
132+
133+
impl From<BlockId> for bdk_wallet::chain::BlockId {
134+
fn from(value: BlockId) -> Self {
135+
Self {
136+
height: value.height,
137+
hash: value.hash.0,
138+
}
139+
}
102140
}
103141

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

bdk-ffi/tests/bindings/test.kts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@
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
1111

1212
// A type from bdk-ffi
13-
val blockId = BlockId(0uL, "abcd")
13+
val condition = Condition(null, null)

bdk-ffi/tests/bindings/test.py

Lines changed: 2 additions & 2 deletions
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
@@ -11,7 +11,7 @@ def test_some_enum(self):
1111

1212
# A type from the bdk-ffi library
1313
def test_some_dict(self):
14-
block_id = BlockId(height=0, hash="abcd")
14+
condition = Condition(csv = None, timelock = None)
1515

1616
if __name__=='__main__':
1717
unittest.main()

bdk-ffi/tests/bindings/test.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,4 @@ import BitcoinDevKit
1010
let network = Network.testnet
1111

1212
// A type from the bdk-ffi library
13-
let blockId = BlockId(height: 32, hash: "abcd")
13+
let condition = Condition(csv: nil, timelock: nil)

0 commit comments

Comments
 (0)