Skip to content

Commit d612397

Browse files
committed
feat: add wallet changeset primitives
1 parent 161dc92 commit d612397

File tree

5 files changed

+242
-17
lines changed

5 files changed

+242
-17
lines changed

bdk-ffi/src/bitcoin.rs

+45-5
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
@@ -547,7 +569,7 @@ impl From<&BdkTxIn> for TxIn {
547569
fn from(tx_in: &BdkTxIn) -> Self {
548570
TxIn {
549571
previous_output: OutPoint {
550-
txid: tx_in.previous_output.txid.to_string(),
572+
txid: Arc::new(Txid(tx_in.previous_output.txid)),
551573
vout: tx_in.previous_output.vout,
552574
},
553575
script_sig: Arc::new(Script(tx_in.script_sig.clone())),
@@ -581,6 +603,24 @@ impl From<&BdkTxOut> for TxOut {
581603
}
582604
}
583605

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

bdk-ffi/src/types.rs

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

bdk-ffi/tests/bindings/test.kts

+2-2
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

+2-2
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

+1-1
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)