Skip to content

Commit 6206001

Browse files
committed
Add Esplora point in time txout set and basic tests
1 parent f8b1958 commit 6206001

File tree

4 files changed

+413
-135
lines changed

4 files changed

+413
-135
lines changed

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,12 @@ repository = "https://github.com/bitcoindevkit/bdk-reserves"
1212
[dependencies]
1313
bdk = { version = "0.28", default-features = false }
1414
bitcoinconsensus = "0.19.0-3"
15+
esplora-client = { version = "0.4", default-features = false, optional = true }
1516
log = "^0.4"
1617

18+
[features]
19+
use-esplora-blocking = ["esplora-client/blocking"]
20+
1721
[dev-dependencies]
1822
rstest = "^0.11"
1923
bdk-testutils = "^0.4"

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@
1212
pub extern crate bdk;
1313

1414
pub mod reserves;
15+
pub mod txout_set;

src/reserves.rs

Lines changed: 2 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ use bdk::wallet::Wallet;
3232
use bdk::Error;
3333

3434
use std::collections::BTreeMap;
35-
use std::collections::BTreeSet;
36-
use std::iter::FromIterator;
35+
36+
pub use crate::txout_set::{TxOutSet, WalletAtHeight};
3737

3838
pub const PSBT_IN_POR_COMMITMENT: u8 = 0x09;
3939

@@ -162,139 +162,6 @@ where
162162
}
163163
}
164164

165-
/// Trait to look up `TxOut`s by `OutPoint`
166-
pub trait TxOutSet {
167-
/// Lookup error return type
168-
type Error;
169-
170-
/// Atomically look up txouts
171-
fn get_prevouts<'a, I: IntoIterator<Item=&'a OutPoint>, T: FromIterator<Option<TxOut>>>(&self, outpoints: I) -> Result<T, Error>;
172-
}
173-
174-
impl<D> TxOutSet for &Wallet<D>
175-
where
176-
D: BatchDatabase,
177-
{
178-
type Error = bdk::Error;
179-
180-
fn get_prevouts<'a, I: IntoIterator<Item=&'a OutPoint>, T: FromIterator<Option<TxOut>>>(&self, outpoints: I) -> Result<T, Error> {
181-
let wallet_at_height = WalletAtHeight::new(self, u32::MAX);
182-
183-
wallet_at_height.get_prevouts(outpoints)
184-
}
185-
}
186-
187-
impl TxOutSet for &BTreeMap<OutPoint, TxOut> {
188-
type Error = ();
189-
190-
fn get_prevouts<'a, I: IntoIterator<Item=&'a OutPoint>, T: FromIterator<Option<TxOut>>>(&self, outpoints: I) -> Result<T, Error> {
191-
let iter = outpoints
192-
.into_iter()
193-
.map(|outpoint|
194-
self
195-
.get(outpoint)
196-
.map(|txout| txout.to_owned())
197-
);
198-
199-
Ok(T::from_iter(iter))
200-
}
201-
}
202-
203-
/// Adapter for a wallet to a TxOutSet at a particular block height
204-
pub struct WalletAtHeight<'a, D>
205-
where
206-
D: BatchDatabase
207-
{
208-
wallet: &'a Wallet<D>,
209-
max_block_height: u32,
210-
}
211-
212-
impl <'a, D> WalletAtHeight<'a, D>
213-
where
214-
D: BatchDatabase
215-
{
216-
pub fn new(wallet: &'a Wallet<D>, max_block_height: u32) -> Self {
217-
WalletAtHeight {
218-
wallet,
219-
max_block_height,
220-
}
221-
}
222-
}
223-
224-
impl<'a, D> TxOutSet for WalletAtHeight<'a, D>
225-
where
226-
D: BatchDatabase
227-
{
228-
type Error = bdk::Error;
229-
230-
fn get_prevouts<'b, I: IntoIterator<Item=&'b OutPoint>, T: FromIterator<Option<TxOut>>>(&self, outpoints: I) -> Result<T, Error> {
231-
let outpoints: Vec<_> = outpoints
232-
.into_iter()
233-
.collect();
234-
235-
let outpoint_set: BTreeSet<&OutPoint> = outpoints
236-
.iter()
237-
.map(|outpoint| *outpoint)
238-
.collect();
239-
240-
let tx_heights: BTreeMap<_, _> = if self.max_block_height < u32::MAX {
241-
outpoint_set
242-
.iter()
243-
.map(|outpoint| {
244-
let tx_details = match self.wallet.get_tx(&outpoint.txid, false)? {
245-
Some(tx_details) => { tx_details },
246-
None => { return Ok((outpoint.txid, None)); },
247-
};
248-
249-
Ok((
250-
outpoint.txid,
251-
tx_details.confirmation_time
252-
.map(|tx_details| tx_details.height)
253-
))
254-
})
255-
.filter_map(|result| match result {
256-
Ok((txid, Some(height))) => { Some(Ok((txid, height))) },
257-
Ok((_, None)) => { None },
258-
Err(e) => {Some(Err(e)) },
259-
})
260-
.collect::<Result<_, Self::Error>>()?
261-
} else {
262-
// If max_block_height is u32::MAX, skip the potentially expensive tx detail lookup
263-
BTreeMap::new()
264-
};
265-
266-
let unspent: BTreeMap<_, _> = self.wallet
267-
.list_unspent()?
268-
.into_iter()
269-
.filter_map(|output| {
270-
if outpoint_set.contains(&output.outpoint) {
271-
let confirmation_height = tx_heights
272-
.get(&output.outpoint.txid)
273-
.unwrap_or(&u32::MAX);
274-
275-
if *confirmation_height <= self.max_block_height {
276-
Some((output.outpoint, output.txout))
277-
} else {
278-
None
279-
}
280-
} else {
281-
None
282-
}
283-
})
284-
.collect();
285-
286-
let iter = outpoints
287-
.into_iter()
288-
.map(|outpoint|
289-
unspent
290-
.get(outpoint)
291-
.map(|outpoint| outpoint.to_owned())
292-
);
293-
294-
Ok(T::from_iter(iter))
295-
}
296-
}
297-
298165
/// Trait for Transaction-centric proofs
299166
pub trait ReserveProof {
300167
/// Verify a proof transaction.

0 commit comments

Comments
 (0)