/// Apply an `update` directly.
///
- /// `update` is a [`tx_graph::Update<A>`] and the resultant changes is returned as [`ChangeSet`].
+ /// `update` is a [`tx_graph::TxUpdate<A>`] and the resultant changes is returned as [`ChangeSet`].
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
- pub fn apply_update(&mut self, update: tx_graph::Update<A>) -> ChangeSet<A, I::ChangeSet> {
+ pub fn apply_update(&mut self, update: tx_graph::TxUpdate<A>) -> ChangeSet<A, I::ChangeSet> {
let tx_graph = self.graph.apply_update(update);
let indexer = self.index_tx_graph_changeset(&tx_graph);
ChangeSet { tx_graph, indexer }
/// set to the current time.
pub fn apply_update_at(
&mut self,
- update: tx_graph::Update<A>,
+ update: tx_graph::TxUpdate<A>,
seen_at: Option<u64>,
) -> ChangeSet<A, I::ChangeSet> {
let tx_graph = self.graph.apply_update_at(update, seen_at);
//! # let tx_b = tx_from_hex(RAW_TX_2);
//! let mut graph: TxGraph = TxGraph::default();
//!
-//! let mut update = tx_graph::Update::default();
+//! let mut update = tx_graph::TxUpdate::default();
//! update.txs.push(Arc::new(tx_a));
//! update.txs.push(Arc::new(tx_b));
//!
use alloc::collections::vec_deque::VecDeque;
use alloc::sync::Arc;
use alloc::vec::Vec;
-pub use bdk_core::tx_graph::Update;
+pub use bdk_core::TxUpdate;
use bitcoin::{Amount, OutPoint, ScriptBuf, SignedAmount, Transaction, TxOut, Txid};
use core::fmt::{self, Formatter};
use core::{
ops::{Deref, RangeInclusive},
};
-impl<A> From<TxGraph<A>> for Update<A> {
+impl<A> From<TxGraph<A>> for TxUpdate<A> {
fn from(graph: TxGraph<A>) -> Self {
Self {
txs: graph.full_txs().map(|tx_node| tx_node.tx).collect(),
}
}
-impl<A: Ord + Clone> From<Update<A>> for TxGraph<A> {
- fn from(update: Update<A>) -> Self {
+impl<A: Ord + Clone> From<TxUpdate<A>> for TxGraph<A> {
+ fn from(update: TxUpdate<A>) -> Self {
let mut graph = TxGraph::<A>::default();
let _ = graph.apply_update_at(update, None);
graph
/// exist in `update` but not in `self`).
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
- pub fn apply_update(&mut self, update: Update<A>) -> ChangeSet<A> {
+ pub fn apply_update(&mut self, update: TxUpdate<A>) -> ChangeSet<A> {
use std::time::*;
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
///
/// Use [`apply_update`](TxGraph::apply_update) to have the `seen_at` value automatically set
/// to the current time.
- pub fn apply_update_at(&mut self, update: Update<A>, seen_at: Option<u64>) -> ChangeSet<A> {
+ pub fn apply_update_at(&mut self, update: TxUpdate<A>, seen_at: Option<u64>) -> ChangeSet<A> {
let mut changeset = ChangeSet::<A>::default();
let mut unanchored_txs = HashSet::<Txid>::new();
for tx in update.txs {
// Make the update graph
let update = {
- let mut update = tx_graph::Update::default();
+ let mut update = tx_graph::TxUpdate::default();
for (outpoint, txout) in &update_ops {
// Insert partials transactions.
update.txouts.insert(*outpoint, txout.clone());
);
}
-/// Tests `From` impls for conversion between [`TxGraph`] and [`tx_graph::Update`].
+/// Tests `From` impls for conversion between [`TxGraph`] and [`tx_graph::TxUpdate`].
#[test]
fn tx_graph_update_conversion() {
- use tx_graph::Update;
+ use tx_graph::TxUpdate;
- type TestCase = (&'static str, Update<ConfirmationBlockTime>);
+ type TestCase = (&'static str, TxUpdate<ConfirmationBlockTime>);
fn make_tx(v: i32) -> Transaction {
Transaction {
}
let test_cases: &[TestCase] = &[
- ("empty_update", Update::default()),
+ ("empty_update", TxUpdate::default()),
(
"single_tx",
- Update {
+ TxUpdate {
txs: vec![make_tx(0).into()],
..Default::default()
},
),
(
"two_txs",
- Update {
+ TxUpdate {
txs: vec![make_tx(0).into(), make_tx(1).into()],
..Default::default()
},
),
(
"with_floating_txouts",
- Update {
+ TxUpdate {
txs: vec![make_tx(0).into(), make_tx(1).into()],
txouts: [
(OutPoint::new(h!("a"), 0), make_txout(0)),
),
(
"with_anchors",
- Update {
+ TxUpdate {
txs: vec![make_tx(0).into(), make_tx(1).into()],
txouts: [
(OutPoint::new(h!("a"), 0), make_txout(0)),
),
(
"with_seen_ats",
- Update {
+ TxUpdate {
txs: vec![make_tx(0).into(), make_tx(1).into()],
txouts: [
(OutPoint::new(h!("a"), 0), make_txout(0)),
for (test_name, update) in test_cases {
let mut tx_graph = TxGraph::<ConfirmationBlockTime>::default();
let _ = tx_graph.apply_update_at(update.clone(), None);
- let update_from_tx_graph: Update<ConfirmationBlockTime> = tx_graph.into();
+ let update_from_tx_graph: TxUpdate<ConfirmationBlockTime> = tx_graph.into();
assert_eq!(
update
--- /dev/null
+use bitcoin::{hashes::Hash, BlockHash};
+
+/// A reference to a block in the canonical chain.
+#[derive(Debug, Clone, PartialEq, Eq, Copy, PartialOrd, Ord, core::hash::Hash)]
+#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
+pub struct BlockId {
+ /// The height of the block.
+ pub height: u32,
+ /// The hash of the block.
+ pub hash: BlockHash,
+}
+
+impl Default for BlockId {
+ fn default() -> Self {
+ Self {
+ height: Default::default(),
+ hash: BlockHash::all_zeros(),
+ }
+ }
+}
+
+impl From<(u32, BlockHash)> for BlockId {
+ fn from((height, hash): (u32, BlockHash)) -> Self {
+ Self { height, hash }
+ }
+}
+
+impl From<BlockId> for (u32, BlockHash) {
+ fn from(block_id: BlockId) -> Self {
+ (block_id.height, block_id.hash)
+ }
+}
+
+impl From<(&u32, &BlockHash)> for BlockId {
+ fn from((height, hash): (&u32, &BlockHash)) -> Self {
+ Self {
+ height: *height,
+ hash: *hash,
+ }
+ }
+}
+
+/// Represents the confirmation block and time of a transaction.
+#[derive(Debug, Default, Clone, PartialEq, Eq, Copy, PartialOrd, Ord, core::hash::Hash)]
+#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
+pub struct ConfirmationBlockTime {
+ /// The anchor block.
+ pub block_id: BlockId,
+ /// The confirmation time of the transaction being anchored.
+ pub confirmation_time: u64,
+}
+++ /dev/null
-use bitcoin::{hashes::Hash, BlockHash};
-
-/// A reference to a block in the canonical chain.
-#[derive(Debug, Clone, PartialEq, Eq, Copy, PartialOrd, Ord, core::hash::Hash)]
-#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
-pub struct BlockId {
- /// The height of the block.
- pub height: u32,
- /// The hash of the block.
- pub hash: BlockHash,
-}
-
-impl Default for BlockId {
- fn default() -> Self {
- Self {
- height: Default::default(),
- hash: BlockHash::all_zeros(),
- }
- }
-}
-
-impl From<(u32, BlockHash)> for BlockId {
- fn from((height, hash): (u32, BlockHash)) -> Self {
- Self { height, hash }
- }
-}
-
-impl From<BlockId> for (u32, BlockHash) {
- fn from(block_id: BlockId) -> Self {
- (block_id.height, block_id.hash)
- }
-}
-
-impl From<(&u32, &BlockHash)> for BlockId {
- fn from((height, hash): (&u32, &BlockHash)) -> Self {
- Self {
- height: *height,
- hash: *hash,
- }
- }
-}
-
-/// Represents the confirmation block and time of a transaction.
-#[derive(Debug, Default, Clone, PartialEq, Eq, Copy, PartialOrd, Ord, core::hash::Hash)]
-#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
-pub struct ConfirmationBlockTime {
- /// The anchor block.
- pub block_id: BlockId,
- /// The confirmation time of the transaction being anchored.
- pub confirmation_time: u64,
-}
/// A tuple of keychain `K`, derivation index (`u32`) and a `T` associated with them.
pub type KeychainIndexed<K, T> = ((K, u32), T);
-mod chain_data;
-pub use chain_data::*;
+mod block_id;
+pub use block_id::*;
mod checkpoint;
pub use checkpoint::*;
-pub mod spk_client;
-
-/// Core structures for [`TxGraph`].
-///
-/// [`TxGraph`]: https://docs.rs/bdk_chain/latest/bdk_chain/tx_graph/struct.TxGraph.html
-pub mod tx_graph {
- use crate::collections::{BTreeMap, BTreeSet, HashMap};
- use alloc::{sync::Arc, vec::Vec};
- use bitcoin::{OutPoint, Transaction, TxOut, Txid};
-
- /// Data object used to update a [`TxGraph`].
- ///
- /// [`TxGraph`]: https://docs.rs/bdk_chain/latest/bdk_chain/tx_graph/struct.TxGraph.html
- #[derive(Debug, Clone)]
- pub struct Update<A = ()> {
- /// Full transactions.
- pub txs: Vec<Arc<Transaction>>,
- /// Floating txouts.
- pub txouts: BTreeMap<OutPoint, TxOut>,
- /// Transaction anchors.
- pub anchors: BTreeSet<(A, Txid)>,
- /// Seen at times for transactions.
- pub seen_ats: HashMap<Txid, u64>,
- }
+mod tx_update;
+pub use tx_update::*;
- impl<A> Default for Update<A> {
- fn default() -> Self {
- Self {
- txs: Default::default(),
- txouts: Default::default(),
- anchors: Default::default(),
- seen_ats: Default::default(),
- }
- }
- }
-
- impl<A: Ord> Update<A> {
- /// Extend this update with `other`.
- pub fn extend(&mut self, other: Update<A>) {
- self.txs.extend(other.txs);
- self.txouts.extend(other.txouts);
- self.anchors.extend(other.anchors);
- self.seen_ats.extend(other.seen_ats);
- }
- }
-}
+pub mod spk_client;
#[must_use]
#[derive(Debug)]
pub struct SyncResult<A = ConfirmationBlockTime> {
- /// The update to apply to the receiving
- /// [`TxGraph`](../../bdk_chain/tx_graph/struct.TxGraph.html).
- pub graph_update: crate::tx_graph::Update<A>,
- /// The update to apply to the receiving
- /// [`LocalChain`](../../bdk_chain/local_chain/struct.LocalChain.html).
+ /// Relevant transaction data discovered during the scan.
+ pub tx_update: crate::TxUpdate<A>,
+ /// Changes to the chain discovered during the scan.
pub chain_update: Option<CheckPoint>,
}
impl<A> Default for SyncResult<A> {
fn default() -> Self {
Self {
- graph_update: Default::default(),
+ tx_update: Default::default(),
chain_update: Default::default(),
}
}
#[must_use]
#[derive(Debug)]
pub struct FullScanResult<K, A = ConfirmationBlockTime> {
- /// The update to apply to the receiving
- /// [`LocalChain`](../../bdk_chain/local_chain/struct.LocalChain.html).
- pub graph_update: crate::tx_graph::Update<A>,
- /// The update to apply to the receiving [`TxGraph`](../../bdk_chain/tx_graph/struct.TxGraph.html).
- pub chain_update: Option<CheckPoint>,
- /// Last active indices for the corresponding keychains (`K`).
+ /// Relevant transaction data discovered during the scan.
+ pub tx_update: crate::TxUpdate<A>,
+ /// Last active indices for the corresponding keychains (`K`). An index is active if it had a
+ /// transaction associated with the script pubkey at that index.
pub last_active_indices: BTreeMap<K, u32>,
+ /// Changes to the chain discovered during the scan.
+ pub chain_update: Option<CheckPoint>,
}
impl<K, A> Default for FullScanResult<K, A> {
fn default() -> Self {
Self {
- graph_update: Default::default(),
+ tx_update: Default::default(),
chain_update: Default::default(),
last_active_indices: Default::default(),
}
--- /dev/null
+use crate::collections::{BTreeMap, BTreeSet, HashMap};
+use alloc::{sync::Arc, vec::Vec};
+use bitcoin::{OutPoint, Transaction, TxOut, Txid};
+
+/// Data object used to communicate updates about relevant transactions from some chain data soruce
+/// to the core model (usually a `bdk_chain::TxGraph`).
+#[derive(Debug, Clone)]
+pub struct TxUpdate<A = ()> {
+ /// Full transactions. These are transactions that were determined to be relevant to the wallet
+ /// given the request.
+ pub txs: Vec<Arc<Transaction>>,
+ /// Floating txouts. These are `TxOut`s that exist but the whole transaction wasn't included in
+ /// `txs` since only knowing about the output is important. These are often used to help determine
+ /// the fee of a wallet transaction.
+ pub txouts: BTreeMap<OutPoint, TxOut>,
+ /// Transaction anchors. Anchors tells us a position in the chain where a transaction was
+ /// confirmed.
+ pub anchors: BTreeSet<(A, Txid)>,
+ /// Seen at times for transactions. This records when a transaction was most recently seen in
+ /// the user's mempool for the sake of tie-breaking other conflicting transactions.
+ pub seen_ats: HashMap<Txid, u64>,
+}
+
+impl<A> Default for TxUpdate<A> {
+ fn default() -> Self {
+ Self {
+ txs: Default::default(),
+ txouts: Default::default(),
+ anchors: Default::default(),
+ seen_ats: Default::default(),
+ }
+ }
+}
+
+impl<A: Ord> TxUpdate<A> {
+ /// Extend this update with `other`.
+ pub fn extend(&mut self, other: TxUpdate<A>) {
+ self.txs.extend(other.txs);
+ self.txouts.extend(other.txouts);
+ self.anchors.extend(other.anchors);
+ self.seen_ats.extend(other.seen_ats);
+ }
+}
bitcoin::{block::Header, BlockHash, OutPoint, ScriptBuf, Transaction, Txid},
collections::{BTreeMap, HashMap},
spk_client::{FullScanRequest, FullScanResult, SyncRequest, SyncResult},
- tx_graph, BlockId, CheckPoint, ConfirmationBlockTime,
+ BlockId, CheckPoint, ConfirmationBlockTime, TxUpdate,
};
use electrum_client::{ElectrumApi, Error, HeaderNotification};
use std::{
None => None,
};
- let mut graph_update = tx_graph::Update::<ConfirmationBlockTime>::default();
+ let mut tx_update = TxUpdate::<ConfirmationBlockTime>::default();
let mut last_active_indices = BTreeMap::<K, u32>::default();
for keychain in request.keychains() {
let spks = request.iter_spks(keychain.clone());
if let Some(last_active_index) =
- self.populate_with_spks(&mut graph_update, spks, stop_gap, batch_size)?
+ self.populate_with_spks(&mut tx_update, spks, stop_gap, batch_size)?
{
last_active_indices.insert(keychain, last_active_index);
}
// Fetch previous `TxOut`s for fee calculation if flag is enabled.
if fetch_prev_txouts {
- self.fetch_prev_txout(&mut graph_update)?;
+ self.fetch_prev_txout(&mut tx_update)?;
}
let chain_update = match tip_and_latest_blocks {
Some((chain_tip, latest_blocks)) => Some(chain_update(
chain_tip,
&latest_blocks,
- graph_update.anchors.iter().cloned(),
+ tx_update.anchors.iter().cloned(),
)?),
_ => None,
};
Ok(FullScanResult {
- graph_update,
+ tx_update,
chain_update,
last_active_indices,
})
None => None,
};
- let mut graph_update = tx_graph::Update::<ConfirmationBlockTime>::default();
+ let mut tx_update = TxUpdate::<ConfirmationBlockTime>::default();
self.populate_with_spks(
- &mut graph_update,
+ &mut tx_update,
request
.iter_spks()
.enumerate()
usize::MAX,
batch_size,
)?;
- self.populate_with_txids(&mut graph_update, request.iter_txids())?;
- self.populate_with_outpoints(&mut graph_update, request.iter_outpoints())?;
+ self.populate_with_txids(&mut tx_update, request.iter_txids())?;
+ self.populate_with_outpoints(&mut tx_update, request.iter_outpoints())?;
// Fetch previous `TxOut`s for fee calculation if flag is enabled.
if fetch_prev_txouts {
- self.fetch_prev_txout(&mut graph_update)?;
+ self.fetch_prev_txout(&mut tx_update)?;
}
let chain_update = match tip_and_latest_blocks {
Some((chain_tip, latest_blocks)) => Some(chain_update(
chain_tip,
&latest_blocks,
- graph_update.anchors.iter().cloned(),
+ tx_update.anchors.iter().cloned(),
)?),
None => None,
};
Ok(SyncResult {
- graph_update,
+ tx_update,
chain_update,
})
}
- /// Populate the `graph_update` with transactions/anchors associated with the given `spks`.
+ /// Populate the `tx_update` with transactions/anchors associated with the given `spks`.
///
/// Transactions that contains an output with requested spk, or spends form an output with
- /// requested spk will be added to `graph_update`. Anchors of the aforementioned transactions are
+ /// requested spk will be added to `tx_update`. Anchors of the aforementioned transactions are
/// also included.
fn populate_with_spks(
&self,
- graph_update: &mut tx_graph::Update<ConfirmationBlockTime>,
+ tx_update: &mut TxUpdate<ConfirmationBlockTime>,
mut spks: impl Iterator<Item = (u32, ScriptBuf)>,
stop_gap: usize,
batch_size: usize,
}
for tx_res in spk_history {
- graph_update.txs.push(self.fetch_tx(tx_res.tx_hash)?);
- self.validate_merkle_for_anchor(graph_update, tx_res.tx_hash, tx_res.height)?;
+ tx_update.txs.push(self.fetch_tx(tx_res.tx_hash)?);
+ self.validate_merkle_for_anchor(tx_update, tx_res.tx_hash, tx_res.height)?;
}
}
}
}
- /// Populate the `graph_update` with associated transactions/anchors of `outpoints`.
+ /// Populate the `tx_update` with associated transactions/anchors of `outpoints`.
///
/// Transactions in which the outpoint resides, and transactions that spend from the outpoint are
/// included. Anchors of the aforementioned transactions are included.
fn populate_with_outpoints(
&self,
- graph_update: &mut tx_graph::Update<ConfirmationBlockTime>,
+ tx_update: &mut TxUpdate<ConfirmationBlockTime>,
outpoints: impl IntoIterator<Item = OutPoint>,
) -> Result<(), Error> {
for outpoint in outpoints {
if !has_residing && res.tx_hash == op_txid {
has_residing = true;
- graph_update.txs.push(Arc::clone(&op_tx));
- self.validate_merkle_for_anchor(graph_update, res.tx_hash, res.height)?;
+ tx_update.txs.push(Arc::clone(&op_tx));
+ self.validate_merkle_for_anchor(tx_update, res.tx_hash, res.height)?;
}
if !has_spending && res.tx_hash != op_txid {
if !has_spending {
continue;
}
- graph_update.txs.push(Arc::clone(&res_tx));
- self.validate_merkle_for_anchor(graph_update, res.tx_hash, res.height)?;
+ tx_update.txs.push(Arc::clone(&res_tx));
+ self.validate_merkle_for_anchor(tx_update, res.tx_hash, res.height)?;
}
}
}
Ok(())
}
- /// Populate the `graph_update` with transactions/anchors of the provided `txids`.
+ /// Populate the `tx_update` with transactions/anchors of the provided `txids`.
fn populate_with_txids(
&self,
- graph_update: &mut tx_graph::Update<ConfirmationBlockTime>,
+ tx_update: &mut TxUpdate<ConfirmationBlockTime>,
txids: impl IntoIterator<Item = Txid>,
) -> Result<(), Error> {
for txid in txids {
.into_iter()
.find(|r| r.tx_hash == txid)
{
- self.validate_merkle_for_anchor(graph_update, txid, r.height)?;
+ self.validate_merkle_for_anchor(tx_update, txid, r.height)?;
}
- graph_update.txs.push(tx);
+ tx_update.txs.push(tx);
}
Ok(())
}
// An anchor is inserted if the transaction is validated to be in a confirmed block.
fn validate_merkle_for_anchor(
&self,
- graph_update: &mut tx_graph::Update<ConfirmationBlockTime>,
+ tx_update: &mut TxUpdate<ConfirmationBlockTime>,
txid: Txid,
confirmation_height: i32,
) -> Result<(), Error> {
}
if is_confirmed_tx {
- graph_update.anchors.insert((
+ tx_update.anchors.insert((
ConfirmationBlockTime {
confirmation_time: header.time as u64,
block_id: BlockId {
// which we do not have by default. This data is needed to calculate the transaction fee.
fn fetch_prev_txout(
&self,
- graph_update: &mut tx_graph::Update<ConfirmationBlockTime>,
+ tx_update: &mut TxUpdate<ConfirmationBlockTime>,
) -> Result<(), Error> {
let mut no_dup = HashSet::<Txid>::new();
- for tx in &graph_update.txs {
+ for tx in &tx_update.txs {
if no_dup.insert(tx.compute_txid()) {
for vin in &tx.input {
let outpoint = vin.previous_output;
let vout = outpoint.vout;
let prev_tx = self.fetch_tx(outpoint.txid)?;
let txout = prev_tx.output[vout as usize].clone();
- let _ = graph_update.txouts.insert(outpoint, txout);
+ let _ = tx_update.txouts.insert(outpoint, txout);
}
}
}
.apply_update(chain_update)
.map_err(|err| anyhow::anyhow!("LocalChain update error: {:?}", err))?;
}
- let _ = graph.apply_update(update.graph_update.clone());
+ let _ = graph.apply_update(update.tx_update.clone());
Ok(update)
}
"update should not alter original checkpoint tip since we already started with all checkpoints",
);
- let graph_update = sync_update.graph_update;
+ let tx_update = sync_update.tx_update;
let updated_graph = {
let mut graph = TxGraph::<ConfirmationBlockTime>::default();
- let _ = graph.apply_update(graph_update.clone());
+ let _ = graph.apply_update(tx_update.clone());
graph
};
// Check to see if we have the floating txouts available from our two created transactions'
// previous outputs in order to calculate transaction fees.
- for tx in &graph_update.txs {
+ for tx in &tx_update.txs {
// Retrieve the calculated fee from `TxGraph`, which will panic if we do not have the
// floating txouts available from the transactions' previous outputs.
let fee = updated_graph.calculate_fee(tx).expect("Fee must exist");
}
assert_eq!(
- graph_update
+ tx_update
.txs
.iter()
.map(|tx| tx.compute_txid())
.spks_for_keychain(0, spks.clone());
client.full_scan(request, 3, 1, false)?
};
- assert!(full_scan_update.graph_update.txs.is_empty());
+ assert!(full_scan_update.tx_update.txs.is_empty());
assert!(full_scan_update.last_active_indices.is_empty());
let full_scan_update = {
let request = FullScanRequest::builder()
};
assert_eq!(
full_scan_update
- .graph_update
+ .tx_update
.txs
.first()
.unwrap()
client.full_scan(request, 5, 1, false)?
};
let txs: HashSet<_> = full_scan_update
- .graph_update
+ .tx_update
.txs
.iter()
.map(|tx| tx.compute_txid())
client.full_scan(request, 6, 1, false)?
};
let txs: HashSet<_> = full_scan_update
- .graph_update
+ .tx_update
.txs
.iter()
.map(|tx| tx.compute_txid())
)?;
// Retain a snapshot of all anchors before reorg process.
- let initial_anchors = update.graph_update.anchors.clone();
+ let initial_anchors = update.tx_update.anchors.clone();
assert_eq!(initial_anchors.len(), REORG_COUNT);
for i in 0..REORG_COUNT {
let (anchor, txid) = initial_anchors.iter().nth(i).unwrap();
)?;
// Check that no new anchors are added during current reorg.
- assert!(initial_anchors.is_superset(&update.graph_update.anchors));
+ assert!(initial_anchors.is_superset(&update.tx_update.anchors));
assert_eq!(
get_balance(&recv_chain, &recv_graph)?,
use bdk_core::spk_client::{FullScanRequest, FullScanResult, SyncRequest, SyncResult};
use bdk_core::{
bitcoin::{BlockHash, OutPoint, ScriptBuf, Txid},
- tx_graph, BlockId, CheckPoint, ConfirmationBlockTime, Indexed,
+ BlockId, CheckPoint, ConfirmationBlockTime, Indexed, TxUpdate,
};
use futures::{stream::FuturesOrdered, TryStreamExt};
None
};
- let mut graph_update = tx_graph::Update::<ConfirmationBlockTime>::default();
+ let mut tx_update = TxUpdate::<ConfirmationBlockTime>::default();
let mut inserted_txs = HashSet::<Txid>::new();
let mut last_active_indices = BTreeMap::<K, u32>::new();
for keychain in keychains {
parallel_requests,
)
.await?;
- graph_update.extend(update);
+ tx_update.extend(update);
if let Some(last_active_index) = last_active_index {
last_active_indices.insert(keychain, last_active_index);
}
let chain_update = match (chain_tip, latest_blocks) {
(Some(chain_tip), Some(latest_blocks)) => {
- Some(chain_update(self, &latest_blocks, &chain_tip, &graph_update.anchors).await?)
+ Some(chain_update(self, &latest_blocks, &chain_tip, &tx_update.anchors).await?)
}
_ => None,
};
Ok(FullScanResult {
chain_update,
- graph_update,
+ tx_update,
last_active_indices,
})
}
None
};
- let mut graph_update = tx_graph::Update::<ConfirmationBlockTime>::default();
+ let mut tx_update = TxUpdate::<ConfirmationBlockTime>::default();
let mut inserted_txs = HashSet::<Txid>::new();
- graph_update.extend(
+ tx_update.extend(
fetch_txs_with_spks(
self,
&mut inserted_txs,
)
.await?,
);
- graph_update.extend(
+ tx_update.extend(
fetch_txs_with_txids(
self,
&mut inserted_txs,
)
.await?,
);
- graph_update.extend(
+ tx_update.extend(
fetch_txs_with_outpoints(
self,
&mut inserted_txs,
let chain_update = match (chain_tip, latest_blocks) {
(Some(chain_tip), Some(latest_blocks)) => {
- Some(chain_update(self, &latest_blocks, &chain_tip, &graph_update.anchors).await?)
+ Some(chain_update(self, &latest_blocks, &chain_tip, &tx_update.anchors).await?)
}
_ => None,
};
Ok(SyncResult {
chain_update,
- graph_update,
+ tx_update,
})
}
}
mut keychain_spks: I,
stop_gap: usize,
parallel_requests: usize,
-) -> Result<(tx_graph::Update<ConfirmationBlockTime>, Option<u32>), Error> {
+) -> Result<(TxUpdate<ConfirmationBlockTime>, Option<u32>), Error> {
type TxsOfSpkIndex = (u32, Vec<esplora_client::Tx>);
- let mut update = tx_graph::Update::<ConfirmationBlockTime>::default();
+ let mut update = TxUpdate::<ConfirmationBlockTime>::default();
let mut last_index = Option::<u32>::None;
let mut last_active_index = Option::<u32>::None;
inserted_txs: &mut HashSet<Txid>,
spks: I,
parallel_requests: usize,
-) -> Result<tx_graph::Update<ConfirmationBlockTime>, Error>
+) -> Result<TxUpdate<ConfirmationBlockTime>, Error>
where
I::IntoIter: Send,
{
inserted_txs: &mut HashSet<Txid>,
txids: I,
parallel_requests: usize,
-) -> Result<tx_graph::Update<ConfirmationBlockTime>, Error>
+) -> Result<TxUpdate<ConfirmationBlockTime>, Error>
where
I::IntoIter: Send,
{
- let mut update = tx_graph::Update::<ConfirmationBlockTime>::default();
+ let mut update = TxUpdate::<ConfirmationBlockTime>::default();
// Only fetch for non-inserted txs.
let mut txids = txids
.into_iter()
inserted_txs: &mut HashSet<Txid>,
outpoints: I,
parallel_requests: usize,
-) -> Result<tx_graph::Update<ConfirmationBlockTime>, Error>
+) -> Result<TxUpdate<ConfirmationBlockTime>, Error>
where
I::IntoIter: Send,
{
let outpoints = outpoints.into_iter().collect::<Vec<_>>();
- let mut update = tx_graph::Update::<ConfirmationBlockTime>::default();
+ let mut update = TxUpdate::<ConfirmationBlockTime>::default();
// make sure txs exists in graph and tx statuses are updated
// TODO: We should maintain a tx cache (like we do with Electrum).
use bdk_core::spk_client::{FullScanRequest, FullScanResult, SyncRequest, SyncResult};
use bdk_core::{
bitcoin::{BlockHash, OutPoint, ScriptBuf, Txid},
- tx_graph, BlockId, CheckPoint, ConfirmationBlockTime, Indexed,
+ BlockId, CheckPoint, ConfirmationBlockTime, Indexed, TxUpdate,
};
use esplora_client::{OutputStatus, Tx};
use std::thread::JoinHandle;
None
};
- let mut graph_update = tx_graph::Update::default();
+ let mut tx_update = TxUpdate::default();
let mut inserted_txs = HashSet::<Txid>::new();
let mut last_active_indices = BTreeMap::<K, u32>::new();
for keychain in request.keychains() {
stop_gap,
parallel_requests,
)?;
- graph_update.extend(update);
+ tx_update.extend(update);
if let Some(last_active_index) = last_active_index {
last_active_indices.insert(keychain, last_active_index);
}
self,
&latest_blocks,
&chain_tip,
- &graph_update.anchors,
+ &tx_update.anchors,
)?),
_ => None,
};
Ok(FullScanResult {
chain_update,
- graph_update,
+ tx_update,
last_active_indices,
})
}
None
};
- let mut graph_update = tx_graph::Update::<ConfirmationBlockTime>::default();
+ let mut tx_update = TxUpdate::<ConfirmationBlockTime>::default();
let mut inserted_txs = HashSet::<Txid>::new();
- graph_update.extend(fetch_txs_with_spks(
+ tx_update.extend(fetch_txs_with_spks(
self,
&mut inserted_txs,
request.iter_spks(),
parallel_requests,
)?);
- graph_update.extend(fetch_txs_with_txids(
+ tx_update.extend(fetch_txs_with_txids(
self,
&mut inserted_txs,
request.iter_txids(),
parallel_requests,
)?);
- graph_update.extend(fetch_txs_with_outpoints(
+ tx_update.extend(fetch_txs_with_outpoints(
self,
&mut inserted_txs,
request.iter_outpoints(),
self,
&latest_blocks,
&chain_tip,
- &graph_update.anchors,
+ &tx_update.anchors,
)?),
_ => None,
};
Ok(SyncResult {
chain_update,
- graph_update,
+ tx_update,
})
}
}
mut keychain_spks: I,
stop_gap: usize,
parallel_requests: usize,
-) -> Result<(tx_graph::Update<ConfirmationBlockTime>, Option<u32>), Error> {
+) -> Result<(TxUpdate<ConfirmationBlockTime>, Option<u32>), Error> {
type TxsOfSpkIndex = (u32, Vec<esplora_client::Tx>);
- let mut update = tx_graph::Update::<ConfirmationBlockTime>::default();
+ let mut update = TxUpdate::<ConfirmationBlockTime>::default();
let mut last_index = Option::<u32>::None;
let mut last_active_index = Option::<u32>::None;
inserted_txs: &mut HashSet<Txid>,
spks: I,
parallel_requests: usize,
-) -> Result<tx_graph::Update<ConfirmationBlockTime>, Error> {
+) -> Result<TxUpdate<ConfirmationBlockTime>, Error> {
fetch_txs_with_keychain_spks(
client,
inserted_txs,
inserted_txs: &mut HashSet<Txid>,
txids: I,
parallel_requests: usize,
-) -> Result<tx_graph::Update<ConfirmationBlockTime>, Error> {
- let mut update = tx_graph::Update::<ConfirmationBlockTime>::default();
+) -> Result<TxUpdate<ConfirmationBlockTime>, Error> {
+ let mut update = TxUpdate::<ConfirmationBlockTime>::default();
// Only fetch for non-inserted txs.
let mut txids = txids
.into_iter()
inserted_txs: &mut HashSet<Txid>,
outpoints: I,
parallel_requests: usize,
-) -> Result<tx_graph::Update<ConfirmationBlockTime>, Error> {
+) -> Result<TxUpdate<ConfirmationBlockTime>, Error> {
let outpoints = outpoints.into_iter().collect::<Vec<_>>();
- let mut update = tx_graph::Update::<ConfirmationBlockTime>::default();
+ let mut update = TxUpdate::<ConfirmationBlockTime>::default();
// make sure txs exists in graph and tx statuses are updated
// TODO: We should maintain a tx cache (like we do with Electrum).
//! [`esplora_client::AsyncClient`].
use bdk_core::bitcoin::{Amount, OutPoint, TxOut, Txid};
-use bdk_core::{tx_graph, BlockId, ConfirmationBlockTime};
+use bdk_core::{BlockId, ConfirmationBlockTime, TxUpdate};
use esplora_client::TxStatus;
pub use esplora_client;
pub use async_ext::*;
fn insert_anchor_from_status(
- update: &mut tx_graph::Update<ConfirmationBlockTime>,
+ update: &mut TxUpdate<ConfirmationBlockTime>,
txid: Txid,
status: TxStatus,
) {
/// Inserts floating txouts into `tx_graph` using [`Vin`](esplora_client::api::Vin)s returned by
/// Esplora.
fn insert_prevouts(
- update: &mut tx_graph::Update<ConfirmationBlockTime>,
+ update: &mut TxUpdate<ConfirmationBlockTime>,
esplora_inputs: impl IntoIterator<Item = esplora_client::api::Vin>,
) {
let prevouts = esplora_inputs
"update should not alter original checkpoint tip since we already started with all checkpoints",
);
- let graph_update = sync_update.graph_update;
+ let tx_update = sync_update.tx_update;
let updated_graph = {
let mut graph = TxGraph::<ConfirmationBlockTime>::default();
- let _ = graph.apply_update(graph_update.clone());
+ let _ = graph.apply_update(tx_update.clone());
graph
};
// Check to see if we have the floating txouts available from our two created transactions'
// previous outputs in order to calculate transaction fees.
- for tx in &graph_update.txs {
+ for tx in &tx_update.txs {
// Retrieve the calculated fee from `TxGraph`, which will panic if we do not have the
// floating txouts available from the transactions' previous outputs.
let fee = updated_graph.calculate_fee(tx).expect("Fee must exist");
}
assert_eq!(
- graph_update
+ tx_update
.txs
.iter()
.map(|tx| tx.compute_txid())
.spks_for_keychain(0, spks.clone());
client.full_scan(request, 3, 1).await?
};
- assert!(full_scan_update.graph_update.txs.is_empty());
+ assert!(full_scan_update.tx_update.txs.is_empty());
assert!(full_scan_update.last_active_indices.is_empty());
let full_scan_update = {
let request = FullScanRequest::builder()
};
assert_eq!(
full_scan_update
- .graph_update
+ .tx_update
.txs
.first()
.unwrap()
client.full_scan(request, 5, 1).await?
};
let txs: HashSet<_> = full_scan_update
- .graph_update
+ .tx_update
.txs
.iter()
.map(|tx| tx.compute_txid())
client.full_scan(request, 6, 1).await?
};
let txs: HashSet<_> = full_scan_update
- .graph_update
+ .tx_update
.txs
.iter()
.map(|tx| tx.compute_txid())
"update should not alter original checkpoint tip since we already started with all checkpoints",
);
- let graph_update = sync_update.graph_update;
+ let tx_update = sync_update.tx_update;
let updated_graph = {
let mut graph = TxGraph::<ConfirmationBlockTime>::default();
- let _ = graph.apply_update(graph_update.clone());
+ let _ = graph.apply_update(tx_update.clone());
graph
};
// Check to see if we have the floating txouts available from our two created transactions'
// previous outputs in order to calculate transaction fees.
- for tx in &graph_update.txs {
+ for tx in &tx_update.txs {
// Retrieve the calculated fee from `TxGraph`, which will panic if we do not have the
// floating txouts available from the transactions' previous outputs.
let fee = updated_graph.calculate_fee(tx).expect("Fee must exist");
}
assert_eq!(
- graph_update
+ tx_update
.txs
.iter()
.map(|tx| tx.compute_txid())
.spks_for_keychain(0, spks.clone());
client.full_scan(request, 3, 1)?
};
- assert!(full_scan_update.graph_update.txs.is_empty());
+ assert!(full_scan_update.tx_update.txs.is_empty());
assert!(full_scan_update.last_active_indices.is_empty());
let full_scan_update = {
let request = FullScanRequest::builder()
};
assert_eq!(
full_scan_update
- .graph_update
+ .tx_update
.txs
.first()
.unwrap()
client.full_scan(request, 5, 1)?
};
let txs: HashSet<_> = full_scan_update
- .graph_update
+ .tx_update
.txs
.iter()
.map(|tx| tx.compute_txid())
client.full_scan(request, 6, 1)?
};
let txs: HashSet<_> = full_scan_update
- .graph_update
+ .tx_update
.txs
.iter()
.map(|tx| tx.compute_txid())
};
wallet
.apply_update(Update {
- graph: tx_graph::Update {
+ tx_update: tx_graph::TxUpdate {
anchors: [(anchor, txid)].into_iter().collect(),
..Default::default()
},
FullScanRequest, FullScanRequestBuilder, FullScanResult, SyncRequest, SyncRequestBuilder,
SyncResult,
},
- tx_graph::{CanonicalTx, TxGraph, TxNode},
+ tx_graph::{CanonicalTx, TxGraph, TxNode, TxUpdate},
BlockId, ChainPosition, ConfirmationBlockTime, ConfirmationTime, DescriptorExt, FullTxOut,
Indexed, IndexedTxGraph, Merge,
};
pub last_active_indices: BTreeMap<KeychainKind, u32>,
/// Update for the wallet's internal [`TxGraph`].
- pub graph: chain::tx_graph::Update<ConfirmationBlockTime>,
+ pub tx_update: TxUpdate<ConfirmationBlockTime>,
/// Update for the wallet's internal [`LocalChain`].
///
fn from(value: FullScanResult<KeychainKind>) -> Self {
Self {
last_active_indices: value.last_active_indices,
- graph: value.graph_update,
+ tx_update: value.tx_update,
chain: value.chain_update,
}
}
fn from(value: SyncResult) -> Self {
Self {
last_active_indices: BTreeMap::new(),
- graph: value.graph_update,
+ tx_update: value.tx_update,
chain: value.chain_update,
}
}
changeset.merge(index_changeset.into());
changeset.merge(
self.indexed_graph
- .apply_update_at(update.graph, seen_at)
+ .apply_update_at(update.tx_update, seen_at)
.into(),
);
self.stage.merge(changeset);
block_id,
};
let update = Update {
- graph: tx_graph::Update {
+ tx_update: tx_graph::TxUpdate {
anchors: [(anchor, txid)].into_iter().collect(),
..Default::default()
},
wallet
.apply_update(Update {
- graph: tx_graph::Update {
+ tx_update: tx_graph::TxUpdate {
anchors: [(anchor, txid)].into(),
..Default::default()
},
use bdk_wallet::Update;
wallet
.apply_update(Update {
- graph: tx_graph::Update {
+ tx_update: tx_graph::TxUpdate {
seen_ats: [(txid, seen_at)].into_iter().collect(),
..Default::default()
},
.map(|tx_node| tx_node.tx),
);
- let (chain_update, graph_update, keychain_update) = match electrum_cmd.clone() {
+ let (chain_update, tx_update, keychain_update) = match electrum_cmd.clone() {
ElectrumCommands::Scan {
stop_gap,
scan_options,
.context("scanning the blockchain")?;
(
res.chain_update,
- res.graph_update,
+ res.tx_update,
Some(res.last_active_indices),
)
}
// drop lock on graph and chain
drop((graph, chain));
- (res.chain_update, res.graph_update, None)
+ (res.chain_update, res.tx_update, None)
}
};
let keychain_changeset = graph.index.reveal_to_target_multi(&keychain_update);
indexed_tx_graph_changeset.merge(keychain_changeset.into());
}
- indexed_tx_graph_changeset.merge(graph.apply_update(graph_update));
+ indexed_tx_graph_changeset.merge(graph.apply_update(tx_update));
ChangeSet {
local_chain: chain_changeset,
};
// The client scans keychain spks for transaction histories, stopping after `stop_gap`
- // is reached. It returns a `TxGraph` update (`graph_update`) and a structure that
+ // is reached. It returns a `TxGraph` update (`tx_update`) and a structure that
// represents the last active spk derivation indices of keychains
// (`keychain_indices_update`).
let update = client
let index_changeset = graph
.index
.reveal_to_target_multi(&update.last_active_indices);
- let mut indexed_tx_graph_changeset = graph.apply_update(update.graph_update);
+ let mut indexed_tx_graph_changeset = graph.apply_update(update.tx_update);
indexed_tx_graph_changeset.merge(index_changeset.into());
indexed_tx_graph_changeset
},
.lock()
.unwrap()
.apply_update(update.chain_update.expect("request has chain tip"))?,
- graph.lock().unwrap().apply_update(update.graph_update),
+ graph.lock().unwrap().apply_update(update.tx_update),
)
}
};