Remove transaction-based type parameters and traits
This commit is contained in:
parent
de9457fce6
commit
0505cd7242
@ -23,7 +23,7 @@ pub use bdk_chain::keychain::Balance;
|
|||||||
use bdk_chain::{
|
use bdk_chain::{
|
||||||
chain_graph,
|
chain_graph,
|
||||||
keychain::{persist, KeychainChangeSet, KeychainScan, KeychainTracker},
|
keychain::{persist, KeychainChangeSet, KeychainScan, KeychainTracker},
|
||||||
sparse_chain, BlockId, ConfirmationTime, IntoOwned,
|
sparse_chain, BlockId, ConfirmationTime,
|
||||||
};
|
};
|
||||||
use bitcoin::consensus::encode::serialize;
|
use bitcoin::consensus::encode::serialize;
|
||||||
use bitcoin::secp256k1::Secp256k1;
|
use bitcoin::secp256k1::Secp256k1;
|
||||||
@ -91,11 +91,11 @@ pub struct Wallet<D = ()> {
|
|||||||
|
|
||||||
/// The update to a [`Wallet`] used in [`Wallet::apply_update`]. This is usually returned from blockchain data sources.
|
/// The update to a [`Wallet`] used in [`Wallet::apply_update`]. This is usually returned from blockchain data sources.
|
||||||
/// The type parameter `T` indicates the kind of transaction contained in the update. It's usually a [`bitcoin::Transaction`].
|
/// The type parameter `T` indicates the kind of transaction contained in the update. It's usually a [`bitcoin::Transaction`].
|
||||||
pub type Update<T> = KeychainScan<KeychainKind, ConfirmationTime, T>;
|
pub type Update = KeychainScan<KeychainKind, ConfirmationTime>;
|
||||||
/// Error indicating that something was wrong with an [`Update<T>`].
|
/// Error indicating that something was wrong with an [`Update<T>`].
|
||||||
pub type UpdateError = chain_graph::UpdateError<ConfirmationTime>;
|
pub type UpdateError = chain_graph::UpdateError<ConfirmationTime>;
|
||||||
/// The changeset produced internally by applying an update
|
/// The changeset produced internally by applying an update
|
||||||
pub(crate) type ChangeSet = KeychainChangeSet<KeychainKind, ConfirmationTime, Transaction>;
|
pub(crate) type ChangeSet = KeychainChangeSet<KeychainKind, ConfirmationTime>;
|
||||||
|
|
||||||
/// The address index selection strategy to use to derived an address from the wallet's external
|
/// The address index selection strategy to use to derived an address from the wallet's external
|
||||||
/// descriptor. See [`Wallet::get_address`]. If you're unsure which one to use use `WalletIndex::New`.
|
/// descriptor. See [`Wallet::get_address`]. If you're unsure which one to use use `WalletIndex::New`.
|
||||||
@ -1686,10 +1686,9 @@ impl<D> Wallet<D> {
|
|||||||
/// transactions related to your wallet into it.
|
/// transactions related to your wallet into it.
|
||||||
///
|
///
|
||||||
/// [`commit`]: Self::commit
|
/// [`commit`]: Self::commit
|
||||||
pub fn apply_update<Tx>(&mut self, update: Update<Tx>) -> Result<(), UpdateError>
|
pub fn apply_update(&mut self, update: Update) -> Result<(), UpdateError>
|
||||||
where
|
where
|
||||||
D: persist::PersistBackend<KeychainKind, ConfirmationTime>,
|
D: persist::PersistBackend<KeychainKind, ConfirmationTime>,
|
||||||
Tx: IntoOwned<Transaction> + Clone,
|
|
||||||
{
|
{
|
||||||
let changeset = self.keychain_tracker.apply_update(update)?;
|
let changeset = self.keychain_tracker.apply_update(update)?;
|
||||||
self.persist.stage(changeset);
|
self.persist.stage(changeset);
|
||||||
|
@ -3,7 +3,7 @@ use crate::{
|
|||||||
collections::HashSet,
|
collections::HashSet,
|
||||||
sparse_chain::{self, ChainPosition, SparseChain},
|
sparse_chain::{self, ChainPosition, SparseChain},
|
||||||
tx_graph::{self, TxGraph},
|
tx_graph::{self, TxGraph},
|
||||||
AsTransaction, BlockId, ForEachTxOut, FullTxOut, IntoOwned, TxHeight,
|
BlockId, ForEachTxOut, FullTxOut, TxHeight,
|
||||||
};
|
};
|
||||||
use alloc::{string::ToString, vec::Vec};
|
use alloc::{string::ToString, vec::Vec};
|
||||||
use bitcoin::{OutPoint, Transaction, TxOut, Txid};
|
use bitcoin::{OutPoint, Transaction, TxOut, Txid};
|
||||||
@ -25,12 +25,12 @@ use core::fmt::Debug;
|
|||||||
/// `graph` but not the other way around. Transactions may fall out of the *chain* (via re-org or
|
/// `graph` but not the other way around. Transactions may fall out of the *chain* (via re-org or
|
||||||
/// mempool eviction) but will remain in the *graph*.
|
/// mempool eviction) but will remain in the *graph*.
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct ChainGraph<P = TxHeight, T = Transaction> {
|
pub struct ChainGraph<P = TxHeight> {
|
||||||
chain: SparseChain<P>,
|
chain: SparseChain<P>,
|
||||||
graph: TxGraph<T>,
|
graph: TxGraph,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P, T> Default for ChainGraph<P, T> {
|
impl<P> Default for ChainGraph<P> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
chain: Default::default(),
|
chain: Default::default(),
|
||||||
@ -39,40 +39,39 @@ impl<P, T> Default for ChainGraph<P, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P, T> AsRef<SparseChain<P>> for ChainGraph<P, T> {
|
impl<P> AsRef<SparseChain<P>> for ChainGraph<P> {
|
||||||
fn as_ref(&self) -> &SparseChain<P> {
|
fn as_ref(&self) -> &SparseChain<P> {
|
||||||
&self.chain
|
&self.chain
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P, T> AsRef<TxGraph<T>> for ChainGraph<P, T> {
|
impl<P> AsRef<TxGraph> for ChainGraph<P> {
|
||||||
fn as_ref(&self) -> &TxGraph<T> {
|
fn as_ref(&self) -> &TxGraph {
|
||||||
&self.graph
|
&self.graph
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P, T> AsRef<ChainGraph<P, T>> for ChainGraph<P, T> {
|
impl<P> AsRef<ChainGraph<P>> for ChainGraph<P> {
|
||||||
fn as_ref(&self) -> &ChainGraph<P, T> {
|
fn as_ref(&self) -> &ChainGraph<P> {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P, T> ChainGraph<P, T> {
|
impl<P> ChainGraph<P> {
|
||||||
/// Returns a reference to the internal [`SparseChain`].
|
/// Returns a reference to the internal [`SparseChain`].
|
||||||
pub fn chain(&self) -> &SparseChain<P> {
|
pub fn chain(&self) -> &SparseChain<P> {
|
||||||
&self.chain
|
&self.chain
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a reference to the internal [`TxGraph`].
|
/// Returns a reference to the internal [`TxGraph`].
|
||||||
pub fn graph(&self) -> &TxGraph<T> {
|
pub fn graph(&self) -> &TxGraph {
|
||||||
&self.graph
|
&self.graph
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P, T> ChainGraph<P, T>
|
impl<P> ChainGraph<P>
|
||||||
where
|
where
|
||||||
P: ChainPosition,
|
P: ChainPosition,
|
||||||
T: AsTransaction + Clone + Ord,
|
|
||||||
{
|
{
|
||||||
/// Create a new chain graph from a `chain` and a `graph`.
|
/// Create a new chain graph from a `chain` and a `graph`.
|
||||||
///
|
///
|
||||||
@ -82,14 +81,12 @@ where
|
|||||||
/// transaction in `graph`.
|
/// transaction in `graph`.
|
||||||
/// 2. The `chain` has two transactions that allegedly in it but they conflict in the `graph`
|
/// 2. The `chain` has two transactions that allegedly in it but they conflict in the `graph`
|
||||||
/// (so could not possibly be in the same chain).
|
/// (so could not possibly be in the same chain).
|
||||||
pub fn new(chain: SparseChain<P>, graph: TxGraph<T>) -> Result<Self, NewError<P>> {
|
pub fn new(chain: SparseChain<P>, graph: TxGraph) -> Result<Self, NewError<P>> {
|
||||||
let mut missing = HashSet::default();
|
let mut missing = HashSet::default();
|
||||||
for (pos, txid) in chain.txids() {
|
for (pos, txid) in chain.txids() {
|
||||||
if let Some(tx) = graph.get_tx(*txid) {
|
if let Some(tx) = graph.get_tx(*txid) {
|
||||||
let conflict = graph
|
let conflict = graph
|
||||||
.walk_conflicts(tx.as_tx(), |_, txid| {
|
.walk_conflicts(tx, |_, txid| Some((chain.tx_position(txid)?.clone(), txid)))
|
||||||
Some((chain.tx_position(txid)?.clone(), txid))
|
|
||||||
})
|
|
||||||
.next();
|
.next();
|
||||||
if let Some((conflict_pos, conflict)) = conflict {
|
if let Some((conflict_pos, conflict)) = conflict {
|
||||||
return Err(NewError::Conflict {
|
return Err(NewError::Conflict {
|
||||||
@ -128,8 +125,8 @@ where
|
|||||||
pub fn inflate_update(
|
pub fn inflate_update(
|
||||||
&self,
|
&self,
|
||||||
update: SparseChain<P>,
|
update: SparseChain<P>,
|
||||||
new_txs: impl IntoIterator<Item = T>,
|
new_txs: impl IntoIterator<Item = Transaction>,
|
||||||
) -> Result<ChainGraph<P, T>, NewError<P>> {
|
) -> Result<ChainGraph<P>, NewError<P>> {
|
||||||
let mut inflated_chain = SparseChain::default();
|
let mut inflated_chain = SparseChain::default();
|
||||||
let mut inflated_graph = TxGraph::default();
|
let mut inflated_graph = TxGraph::default();
|
||||||
|
|
||||||
@ -188,7 +185,7 @@ where
|
|||||||
|
|
||||||
/// Determines the changes required to invalidate checkpoints `from_height` (inclusive) and
|
/// Determines the changes required to invalidate checkpoints `from_height` (inclusive) and
|
||||||
/// above. Displaced transactions will have their positions moved to [`TxHeight::Unconfirmed`].
|
/// above. Displaced transactions will have their positions moved to [`TxHeight::Unconfirmed`].
|
||||||
pub fn invalidate_checkpoints_preview(&self, from_height: u32) -> ChangeSet<P, T> {
|
pub fn invalidate_checkpoints_preview(&self, from_height: u32) -> ChangeSet<P> {
|
||||||
ChangeSet {
|
ChangeSet {
|
||||||
chain: self.chain.invalidate_checkpoints_preview(from_height),
|
chain: self.chain.invalidate_checkpoints_preview(from_height),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@ -200,9 +197,9 @@ where
|
|||||||
///
|
///
|
||||||
/// This is equivalent to calling [`Self::invalidate_checkpoints_preview`] and
|
/// This is equivalent to calling [`Self::invalidate_checkpoints_preview`] and
|
||||||
/// [`Self::apply_changeset`] in sequence.
|
/// [`Self::apply_changeset`] in sequence.
|
||||||
pub fn invalidate_checkpoints(&mut self, from_height: u32) -> ChangeSet<P, T>
|
pub fn invalidate_checkpoints(&mut self, from_height: u32) -> ChangeSet<P>
|
||||||
where
|
where
|
||||||
ChangeSet<P, T>: Clone,
|
ChangeSet<P>: Clone,
|
||||||
{
|
{
|
||||||
let changeset = self.invalidate_checkpoints_preview(from_height);
|
let changeset = self.invalidate_checkpoints_preview(from_height);
|
||||||
self.apply_changeset(changeset.clone());
|
self.apply_changeset(changeset.clone());
|
||||||
@ -213,7 +210,7 @@ where
|
|||||||
///
|
///
|
||||||
/// This does not necessarily mean that it is *confirmed* in the blockchain, it might just be in
|
/// This does not necessarily mean that it is *confirmed* in the blockchain, it might just be in
|
||||||
/// the unconfirmed transaction list within the [`SparseChain`].
|
/// the unconfirmed transaction list within the [`SparseChain`].
|
||||||
pub fn get_tx_in_chain(&self, txid: Txid) -> Option<(&P, &T)> {
|
pub fn get_tx_in_chain(&self, txid: Txid) -> Option<(&P, &Transaction)> {
|
||||||
let position = self.chain.tx_position(txid)?;
|
let position = self.chain.tx_position(txid)?;
|
||||||
let full_tx = self.graph.get_tx(txid).expect("must exist");
|
let full_tx = self.graph.get_tx(txid).expect("must exist");
|
||||||
Some((position, full_tx))
|
Some((position, full_tx))
|
||||||
@ -224,9 +221,13 @@ where
|
|||||||
///
|
///
|
||||||
/// If inserting it into the chain `position` will result in conflicts, the returned
|
/// If inserting it into the chain `position` will result in conflicts, the returned
|
||||||
/// [`ChangeSet`] should evict conflicting transactions.
|
/// [`ChangeSet`] should evict conflicting transactions.
|
||||||
pub fn insert_tx_preview(&self, tx: T, pos: P) -> Result<ChangeSet<P, T>, InsertTxError<P>> {
|
pub fn insert_tx_preview(
|
||||||
|
&self,
|
||||||
|
tx: Transaction,
|
||||||
|
pos: P,
|
||||||
|
) -> Result<ChangeSet<P>, InsertTxError<P>> {
|
||||||
let mut changeset = ChangeSet {
|
let mut changeset = ChangeSet {
|
||||||
chain: self.chain.insert_tx_preview(tx.as_tx().txid(), pos)?,
|
chain: self.chain.insert_tx_preview(tx.txid(), pos)?,
|
||||||
graph: self.graph.insert_tx_preview(tx),
|
graph: self.graph.insert_tx_preview(tx),
|
||||||
};
|
};
|
||||||
self.fix_conflicts(&mut changeset)?;
|
self.fix_conflicts(&mut changeset)?;
|
||||||
@ -237,14 +238,14 @@ where
|
|||||||
///
|
///
|
||||||
/// This is equivalent to calling [`Self::insert_tx_preview`] and [`Self::apply_changeset`] in
|
/// This is equivalent to calling [`Self::insert_tx_preview`] and [`Self::apply_changeset`] in
|
||||||
/// sequence.
|
/// sequence.
|
||||||
pub fn insert_tx(&mut self, tx: T, pos: P) -> Result<ChangeSet<P, T>, InsertTxError<P>> {
|
pub fn insert_tx(&mut self, tx: Transaction, pos: P) -> Result<ChangeSet<P>, InsertTxError<P>> {
|
||||||
let changeset = self.insert_tx_preview(tx, pos)?;
|
let changeset = self.insert_tx_preview(tx, pos)?;
|
||||||
self.apply_changeset(changeset.clone());
|
self.apply_changeset(changeset.clone());
|
||||||
Ok(changeset)
|
Ok(changeset)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Determines the changes required to insert a [`TxOut`] into the internal [`TxGraph`].
|
/// Determines the changes required to insert a [`TxOut`] into the internal [`TxGraph`].
|
||||||
pub fn insert_txout_preview(&self, outpoint: OutPoint, txout: TxOut) -> ChangeSet<P, T> {
|
pub fn insert_txout_preview(&self, outpoint: OutPoint, txout: TxOut) -> ChangeSet<P> {
|
||||||
ChangeSet {
|
ChangeSet {
|
||||||
chain: Default::default(),
|
chain: Default::default(),
|
||||||
graph: self.graph.insert_txout_preview(outpoint, txout),
|
graph: self.graph.insert_txout_preview(outpoint, txout),
|
||||||
@ -255,7 +256,7 @@ where
|
|||||||
///
|
///
|
||||||
/// This is equivalent to calling [`Self::insert_txout_preview`] and [`Self::apply_changeset`]
|
/// This is equivalent to calling [`Self::insert_txout_preview`] and [`Self::apply_changeset`]
|
||||||
/// in sequence.
|
/// in sequence.
|
||||||
pub fn insert_txout(&mut self, outpoint: OutPoint, txout: TxOut) -> ChangeSet<P, T> {
|
pub fn insert_txout(&mut self, outpoint: OutPoint, txout: TxOut) -> ChangeSet<P> {
|
||||||
let changeset = self.insert_txout_preview(outpoint, txout);
|
let changeset = self.insert_txout_preview(outpoint, txout);
|
||||||
self.apply_changeset(changeset.clone());
|
self.apply_changeset(changeset.clone());
|
||||||
changeset
|
changeset
|
||||||
@ -269,7 +270,7 @@ where
|
|||||||
pub fn insert_checkpoint_preview(
|
pub fn insert_checkpoint_preview(
|
||||||
&self,
|
&self,
|
||||||
block_id: BlockId,
|
block_id: BlockId,
|
||||||
) -> Result<ChangeSet<P, T>, InsertCheckpointError> {
|
) -> Result<ChangeSet<P>, InsertCheckpointError> {
|
||||||
self.chain
|
self.chain
|
||||||
.insert_checkpoint_preview(block_id)
|
.insert_checkpoint_preview(block_id)
|
||||||
.map(|chain_changeset| ChangeSet {
|
.map(|chain_changeset| ChangeSet {
|
||||||
@ -285,20 +286,17 @@ where
|
|||||||
pub fn insert_checkpoint(
|
pub fn insert_checkpoint(
|
||||||
&mut self,
|
&mut self,
|
||||||
block_id: BlockId,
|
block_id: BlockId,
|
||||||
) -> Result<ChangeSet<P, T>, InsertCheckpointError> {
|
) -> Result<ChangeSet<P>, InsertCheckpointError> {
|
||||||
let changeset = self.insert_checkpoint_preview(block_id)?;
|
let changeset = self.insert_checkpoint_preview(block_id)?;
|
||||||
self.apply_changeset(changeset.clone());
|
self.apply_changeset(changeset.clone());
|
||||||
Ok(changeset)
|
Ok(changeset)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Calculates the difference between self and `update` in the form of a [`ChangeSet`].
|
/// Calculates the difference between self and `update` in the form of a [`ChangeSet`].
|
||||||
pub fn determine_changeset<T2>(
|
pub fn determine_changeset(
|
||||||
&self,
|
&self,
|
||||||
update: &ChainGraph<P, T2>,
|
update: &ChainGraph<P>,
|
||||||
) -> Result<ChangeSet<P, T>, UpdateError<P>>
|
) -> Result<ChangeSet<P>, UpdateError<P>> {
|
||||||
where
|
|
||||||
T2: IntoOwned<T> + Clone,
|
|
||||||
{
|
|
||||||
let chain_changeset = self
|
let chain_changeset = self
|
||||||
.chain
|
.chain
|
||||||
.determine_changeset(&update.chain)
|
.determine_changeset(&update.chain)
|
||||||
@ -333,10 +331,7 @@ where
|
|||||||
///
|
///
|
||||||
/// **WARNING:** If there are any missing full txs, conflict resolution will not be complete. In
|
/// **WARNING:** If there are any missing full txs, conflict resolution will not be complete. In
|
||||||
/// debug mode, this will result in panic.
|
/// debug mode, this will result in panic.
|
||||||
fn fix_conflicts(
|
fn fix_conflicts(&self, changeset: &mut ChangeSet<P>) -> Result<(), UnresolvableConflict<P>> {
|
||||||
&self,
|
|
||||||
changeset: &mut ChangeSet<P, T>,
|
|
||||||
) -> Result<(), UnresolvableConflict<P>> {
|
|
||||||
let mut chain_conflicts = vec![];
|
let mut chain_conflicts = vec![];
|
||||||
|
|
||||||
for (&txid, pos_change) in &changeset.chain.txids {
|
for (&txid, pos_change) in &changeset.chain.txids {
|
||||||
@ -355,11 +350,7 @@ where
|
|||||||
let mut full_tx = self.graph.get_tx(txid);
|
let mut full_tx = self.graph.get_tx(txid);
|
||||||
|
|
||||||
if full_tx.is_none() {
|
if full_tx.is_none() {
|
||||||
full_tx = changeset
|
full_tx = changeset.graph.tx.iter().find(|tx| tx.txid() == txid)
|
||||||
.graph
|
|
||||||
.tx
|
|
||||||
.iter()
|
|
||||||
.find(|tx| tx.as_tx().txid() == txid)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
debug_assert!(full_tx.is_some(), "should have full tx at this point");
|
debug_assert!(full_tx.is_some(), "should have full tx at this point");
|
||||||
@ -369,7 +360,7 @@ where
|
|||||||
None => continue,
|
None => continue,
|
||||||
};
|
};
|
||||||
|
|
||||||
for (conflict_pos, conflict_txid) in self.tx_conflicts_in_chain(full_tx.as_tx()) {
|
for (conflict_pos, conflict_txid) in self.tx_conflicts_in_chain(full_tx) {
|
||||||
chain_conflicts.push((pos.clone(), txid, conflict_pos, conflict_txid))
|
chain_conflicts.push((pos.clone(), txid, conflict_pos, conflict_txid))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -416,17 +407,14 @@ where
|
|||||||
///
|
///
|
||||||
/// **Warning** this method assumes the changeset is assumed to be correctly formed. If it isn't
|
/// **Warning** this method assumes the changeset is assumed to be correctly formed. If it isn't
|
||||||
/// then the chain graph may not behave correctly in the future and may panic unexpectedly.
|
/// then the chain graph may not behave correctly in the future and may panic unexpectedly.
|
||||||
pub fn apply_changeset(&mut self, changeset: ChangeSet<P, T>) {
|
pub fn apply_changeset(&mut self, changeset: ChangeSet<P>) {
|
||||||
self.chain.apply_changeset(changeset.chain);
|
self.chain.apply_changeset(changeset.chain);
|
||||||
self.graph.apply_additions(changeset.graph);
|
self.graph.apply_additions(changeset.graph);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Applies the `update` chain graph. Note this is shorthand for calling
|
/// Applies the `update` chain graph. Note this is shorthand for calling
|
||||||
/// [`Self::determine_changeset()`] and [`Self::apply_changeset()`] in sequence.
|
/// [`Self::determine_changeset()`] and [`Self::apply_changeset()`] in sequence.
|
||||||
pub fn apply_update<T2: IntoOwned<T> + Clone>(
|
pub fn apply_update(&mut self, update: ChainGraph<P>) -> Result<ChangeSet<P>, UpdateError<P>> {
|
||||||
&mut self,
|
|
||||||
update: ChainGraph<P, T2>,
|
|
||||||
) -> Result<ChangeSet<P, T>, UpdateError<P>> {
|
|
||||||
let changeset = self.determine_changeset(&update)?;
|
let changeset = self.determine_changeset(&update)?;
|
||||||
self.apply_changeset(changeset.clone());
|
self.apply_changeset(changeset.clone());
|
||||||
Ok(changeset)
|
Ok(changeset)
|
||||||
@ -439,7 +427,7 @@ where
|
|||||||
|
|
||||||
/// Iterate over the full transactions and their position in the chain ordered by their position
|
/// Iterate over the full transactions and their position in the chain ordered by their position
|
||||||
/// in ascending order.
|
/// in ascending order.
|
||||||
pub fn transactions_in_chain(&self) -> impl DoubleEndedIterator<Item = (&P, &T)> {
|
pub fn transactions_in_chain(&self) -> impl DoubleEndedIterator<Item = (&P, &Transaction)> {
|
||||||
self.chain
|
self.chain
|
||||||
.txids()
|
.txids()
|
||||||
.map(move |(pos, txid)| (pos, self.graph.get_tx(*txid).expect("must exist")))
|
.map(move |(pos, txid)| (pos, self.graph.get_tx(*txid).expect("must exist")))
|
||||||
@ -468,18 +456,18 @@ where
|
|||||||
serde(
|
serde(
|
||||||
crate = "serde_crate",
|
crate = "serde_crate",
|
||||||
bound(
|
bound(
|
||||||
deserialize = "P: serde::Deserialize<'de>, T: Ord + serde::Deserialize<'de>",
|
deserialize = "P: serde::Deserialize<'de>",
|
||||||
serialize = "P: serde::Serialize, T: Ord + serde::Serialize"
|
serialize = "P: serde::Serialize"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)]
|
)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub struct ChangeSet<P, T> {
|
pub struct ChangeSet<P> {
|
||||||
pub chain: sparse_chain::ChangeSet<P>,
|
pub chain: sparse_chain::ChangeSet<P>,
|
||||||
pub graph: tx_graph::Additions<T>,
|
pub graph: tx_graph::Additions,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P, T> ChangeSet<P, T> {
|
impl<P> ChangeSet<P> {
|
||||||
/// Returns `true` if this [`ChangeSet`] records no changes.
|
/// Returns `true` if this [`ChangeSet`] records no changes.
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.chain.is_empty() && self.graph.is_empty()
|
self.chain.is_empty() && self.graph.is_empty()
|
||||||
@ -495,17 +483,16 @@ impl<P, T> ChangeSet<P, T> {
|
|||||||
|
|
||||||
/// Appends the changes in `other` into self such that applying `self` afterwards has the same
|
/// Appends the changes in `other` into self such that applying `self` afterwards has the same
|
||||||
/// effect as sequentially applying the original `self` and `other`.
|
/// effect as sequentially applying the original `self` and `other`.
|
||||||
pub fn append(&mut self, other: ChangeSet<P, T>)
|
pub fn append(&mut self, other: ChangeSet<P>)
|
||||||
where
|
where
|
||||||
P: ChainPosition,
|
P: ChainPosition,
|
||||||
T: Ord,
|
|
||||||
{
|
{
|
||||||
self.chain.append(other.chain);
|
self.chain.append(other.chain);
|
||||||
self.graph.append(other.graph);
|
self.graph.append(other.graph);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P, T> Default for ChangeSet<P, T> {
|
impl<P> Default for ChangeSet<P> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
chain: Default::default(),
|
chain: Default::default(),
|
||||||
@ -514,13 +501,13 @@ impl<P, T> Default for ChangeSet<P, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P, T: AsTransaction> ForEachTxOut for ChainGraph<P, T> {
|
impl<P> ForEachTxOut for ChainGraph<P> {
|
||||||
fn for_each_txout(&self, f: impl FnMut((OutPoint, &TxOut))) {
|
fn for_each_txout(&self, f: impl FnMut((OutPoint, &TxOut))) {
|
||||||
self.graph.for_each_txout(f)
|
self.graph.for_each_txout(f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P, T: AsTransaction> ForEachTxOut for ChangeSet<P, T> {
|
impl<P> ForEachTxOut for ChangeSet<P> {
|
||||||
fn for_each_txout(&self, f: impl FnMut((OutPoint, &TxOut))) {
|
fn for_each_txout(&self, f: impl FnMut((OutPoint, &TxOut))) {
|
||||||
self.graph.for_each_txout(f)
|
self.graph.for_each_txout(f)
|
||||||
}
|
}
|
||||||
|
@ -19,9 +19,8 @@ use crate::{
|
|||||||
collections::BTreeMap,
|
collections::BTreeMap,
|
||||||
sparse_chain::ChainPosition,
|
sparse_chain::ChainPosition,
|
||||||
tx_graph::TxGraph,
|
tx_graph::TxGraph,
|
||||||
AsTransaction, ForEachTxOut,
|
ForEachTxOut,
|
||||||
};
|
};
|
||||||
use bitcoin::Transaction;
|
|
||||||
|
|
||||||
#[cfg(feature = "miniscript")]
|
#[cfg(feature = "miniscript")]
|
||||||
pub mod persist;
|
pub mod persist;
|
||||||
@ -100,14 +99,14 @@ impl<K> AsRef<BTreeMap<K, u32>> for DerivationAdditions<K> {
|
|||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
/// An update that includes the last active indexes of each keychain.
|
/// An update that includes the last active indexes of each keychain.
|
||||||
pub struct KeychainScan<K, P, T = Transaction> {
|
pub struct KeychainScan<K, P> {
|
||||||
/// The update data in the form of a chain that could be applied
|
/// The update data in the form of a chain that could be applied
|
||||||
pub update: ChainGraph<P, T>,
|
pub update: ChainGraph<P>,
|
||||||
/// The last active indexes of each keychain
|
/// The last active indexes of each keychain
|
||||||
pub last_active_indices: BTreeMap<K, u32>,
|
pub last_active_indices: BTreeMap<K, u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K, P, T> Default for KeychainScan<K, P, T> {
|
impl<K, P> Default for KeychainScan<K, P> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
update: Default::default(),
|
update: Default::default(),
|
||||||
@ -116,8 +115,8 @@ impl<K, P, T> Default for KeychainScan<K, P, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K, P, T> From<ChainGraph<P, T>> for KeychainScan<K, P, T> {
|
impl<K, P> From<ChainGraph<P>> for KeychainScan<K, P> {
|
||||||
fn from(update: ChainGraph<P, T>) -> Self {
|
fn from(update: ChainGraph<P>) -> Self {
|
||||||
KeychainScan {
|
KeychainScan {
|
||||||
update,
|
update,
|
||||||
last_active_indices: Default::default(),
|
last_active_indices: Default::default(),
|
||||||
@ -135,20 +134,20 @@ impl<K, P, T> From<ChainGraph<P, T>> for KeychainScan<K, P, T> {
|
|||||||
serde(
|
serde(
|
||||||
crate = "serde_crate",
|
crate = "serde_crate",
|
||||||
bound(
|
bound(
|
||||||
deserialize = "K: Ord + serde::Deserialize<'de>, P: serde::Deserialize<'de>, T: Ord + serde::Deserialize<'de>",
|
deserialize = "K: Ord + serde::Deserialize<'de>, P: serde::Deserialize<'de>",
|
||||||
serialize = "K: Ord + serde::Serialize, P: serde::Serialize, T: Ord+ serde::Serialize"
|
serialize = "K: Ord + serde::Serialize, P: serde::Serialize"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)]
|
)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub struct KeychainChangeSet<K, P, T = Transaction> {
|
pub struct KeychainChangeSet<K, P> {
|
||||||
/// The changes in local keychain derivation indices
|
/// The changes in local keychain derivation indices
|
||||||
pub derivation_indices: DerivationAdditions<K>,
|
pub derivation_indices: DerivationAdditions<K>,
|
||||||
/// The changes that have occurred in the blockchain
|
/// The changes that have occurred in the blockchain
|
||||||
pub chain_graph: chain_graph::ChangeSet<P, T>,
|
pub chain_graph: chain_graph::ChangeSet<P>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K, P, T> Default for KeychainChangeSet<K, P, T> {
|
impl<K, P> Default for KeychainChangeSet<K, P> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
chain_graph: Default::default(),
|
chain_graph: Default::default(),
|
||||||
@ -157,7 +156,7 @@ impl<K, P, T> Default for KeychainChangeSet<K, P, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K, P, T> KeychainChangeSet<K, P, T> {
|
impl<K, P> KeychainChangeSet<K, P> {
|
||||||
/// Returns whether the [`KeychainChangeSet`] is empty (no changes recorded).
|
/// Returns whether the [`KeychainChangeSet`] is empty (no changes recorded).
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.chain_graph.is_empty() && self.derivation_indices.is_empty()
|
self.chain_graph.is_empty() && self.derivation_indices.is_empty()
|
||||||
@ -168,19 +167,18 @@ impl<K, P, T> KeychainChangeSet<K, P, T> {
|
|||||||
///
|
///
|
||||||
/// Note the derivation indices cannot be decreased so `other` will only change the derivation
|
/// Note the derivation indices cannot be decreased so `other` will only change the derivation
|
||||||
/// index for a keychain if it's entry is higher than the one in `self`.
|
/// index for a keychain if it's entry is higher than the one in `self`.
|
||||||
pub fn append(&mut self, other: KeychainChangeSet<K, P, T>)
|
pub fn append(&mut self, other: KeychainChangeSet<K, P>)
|
||||||
where
|
where
|
||||||
K: Ord,
|
K: Ord,
|
||||||
P: ChainPosition,
|
P: ChainPosition,
|
||||||
T: Ord,
|
|
||||||
{
|
{
|
||||||
self.derivation_indices.append(other.derivation_indices);
|
self.derivation_indices.append(other.derivation_indices);
|
||||||
self.chain_graph.append(other.chain_graph);
|
self.chain_graph.append(other.chain_graph);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K, P, T> From<chain_graph::ChangeSet<P, T>> for KeychainChangeSet<K, P, T> {
|
impl<K, P> From<chain_graph::ChangeSet<P>> for KeychainChangeSet<K, P> {
|
||||||
fn from(changeset: chain_graph::ChangeSet<P, T>) -> Self {
|
fn from(changeset: chain_graph::ChangeSet<P>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
chain_graph: changeset,
|
chain_graph: changeset,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@ -188,7 +186,7 @@ impl<K, P, T> From<chain_graph::ChangeSet<P, T>> for KeychainChangeSet<K, P, T>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K, P, T> From<DerivationAdditions<K>> for KeychainChangeSet<K, P, T> {
|
impl<K, P> From<DerivationAdditions<K>> for KeychainChangeSet<K, P> {
|
||||||
fn from(additions: DerivationAdditions<K>) -> Self {
|
fn from(additions: DerivationAdditions<K>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
derivation_indices: additions,
|
derivation_indices: additions,
|
||||||
@ -197,13 +195,13 @@ impl<K, P, T> From<DerivationAdditions<K>> for KeychainChangeSet<K, P, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K, P, T> AsRef<TxGraph<T>> for KeychainScan<K, P, T> {
|
impl<K, P> AsRef<TxGraph> for KeychainScan<K, P> {
|
||||||
fn as_ref(&self) -> &TxGraph<T> {
|
fn as_ref(&self) -> &TxGraph {
|
||||||
self.update.graph()
|
self.update.graph()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K, P, T: AsTransaction> ForEachTxOut for KeychainChangeSet<K, P, T> {
|
impl<K, P> ForEachTxOut for KeychainChangeSet<K, P> {
|
||||||
fn for_each_txout(&self, f: impl FnMut((bitcoin::OutPoint, &bitcoin::TxOut))) {
|
fn for_each_txout(&self, f: impl FnMut((bitcoin::OutPoint, &bitcoin::TxOut))) {
|
||||||
self.chain_graph.for_each_txout(f)
|
self.chain_graph.for_each_txout(f)
|
||||||
}
|
}
|
||||||
@ -267,8 +265,6 @@ impl core::ops::Add for Balance {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use bitcoin::Transaction;
|
|
||||||
|
|
||||||
use crate::TxHeight;
|
use crate::TxHeight;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
@ -291,12 +287,12 @@ mod test {
|
|||||||
rhs_di.insert(Keychain::Four, 4);
|
rhs_di.insert(Keychain::Four, 4);
|
||||||
let mut lhs = KeychainChangeSet {
|
let mut lhs = KeychainChangeSet {
|
||||||
derivation_indices: DerivationAdditions(lhs_di),
|
derivation_indices: DerivationAdditions(lhs_di),
|
||||||
chain_graph: chain_graph::ChangeSet::<TxHeight, Transaction>::default(),
|
chain_graph: chain_graph::ChangeSet::<TxHeight>::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let rhs = KeychainChangeSet {
|
let rhs = KeychainChangeSet {
|
||||||
derivation_indices: DerivationAdditions(rhs_di),
|
derivation_indices: DerivationAdditions(rhs_di),
|
||||||
chain_graph: chain_graph::ChangeSet::<TxHeight, Transaction>::default(),
|
chain_graph: chain_graph::ChangeSet::<TxHeight>::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
lhs.append(rhs);
|
lhs.append(rhs);
|
||||||
|
@ -7,7 +7,7 @@ use crate::{
|
|||||||
keychain::{KeychainChangeSet, KeychainScan, KeychainTxOutIndex},
|
keychain::{KeychainChangeSet, KeychainScan, KeychainTxOutIndex},
|
||||||
sparse_chain::{self, SparseChain},
|
sparse_chain::{self, SparseChain},
|
||||||
tx_graph::TxGraph,
|
tx_graph::TxGraph,
|
||||||
AsTransaction, BlockId, FullTxOut, IntoOwned, TxHeight,
|
BlockId, FullTxOut, TxHeight,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{Balance, DerivationAdditions};
|
use super::{Balance, DerivationAdditions};
|
||||||
@ -17,17 +17,16 @@ use super::{Balance, DerivationAdditions};
|
|||||||
/// The [`KeychainTracker`] atomically updates its [`KeychainTxOutIndex`] whenever new chain data is
|
/// The [`KeychainTracker`] atomically updates its [`KeychainTxOutIndex`] whenever new chain data is
|
||||||
/// incorporated into its internal [`ChainGraph`].
|
/// incorporated into its internal [`ChainGraph`].
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct KeychainTracker<K, P, T = Transaction> {
|
pub struct KeychainTracker<K, P> {
|
||||||
/// Index between script pubkeys to transaction outputs
|
/// Index between script pubkeys to transaction outputs
|
||||||
pub txout_index: KeychainTxOutIndex<K>,
|
pub txout_index: KeychainTxOutIndex<K>,
|
||||||
chain_graph: ChainGraph<P, T>,
|
chain_graph: ChainGraph<P>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K, P, T> KeychainTracker<K, P, T>
|
impl<K, P> KeychainTracker<K, P>
|
||||||
where
|
where
|
||||||
P: sparse_chain::ChainPosition,
|
P: sparse_chain::ChainPosition,
|
||||||
K: Ord + Clone + core::fmt::Debug,
|
K: Ord + Clone + core::fmt::Debug,
|
||||||
T: AsTransaction + Clone + Ord,
|
|
||||||
{
|
{
|
||||||
/// Add a keychain to the tracker's `txout_index` with a descriptor to derive addresses for it.
|
/// Add a keychain to the tracker's `txout_index` with a descriptor to derive addresses for it.
|
||||||
/// This is just shorthand for calling [`KeychainTxOutIndex::add_keychain`] on the internal
|
/// This is just shorthand for calling [`KeychainTxOutIndex::add_keychain`] on the internal
|
||||||
@ -63,13 +62,10 @@ where
|
|||||||
///
|
///
|
||||||
/// Internally, we call [`ChainGraph::determine_changeset`] and also determine the additions of
|
/// Internally, we call [`ChainGraph::determine_changeset`] and also determine the additions of
|
||||||
/// [`KeychainTxOutIndex`].
|
/// [`KeychainTxOutIndex`].
|
||||||
pub fn determine_changeset<T2>(
|
pub fn determine_changeset(
|
||||||
&self,
|
&self,
|
||||||
scan: &KeychainScan<K, P, T2>,
|
scan: &KeychainScan<K, P>,
|
||||||
) -> Result<KeychainChangeSet<K, P, T>, chain_graph::UpdateError<P>>
|
) -> Result<KeychainChangeSet<K, P>, chain_graph::UpdateError<P>> {
|
||||||
where
|
|
||||||
T2: IntoOwned<T> + Clone,
|
|
||||||
{
|
|
||||||
// TODO: `KeychainTxOutIndex::determine_additions`
|
// TODO: `KeychainTxOutIndex::determine_additions`
|
||||||
let mut derivation_indices = scan.last_active_indices.clone();
|
let mut derivation_indices = scan.last_active_indices.clone();
|
||||||
derivation_indices.retain(|keychain, index| {
|
derivation_indices.retain(|keychain, index| {
|
||||||
@ -91,13 +87,10 @@ where
|
|||||||
///
|
///
|
||||||
/// [`determine_changeset`]: Self::determine_changeset
|
/// [`determine_changeset`]: Self::determine_changeset
|
||||||
/// [`apply_changeset`]: Self::apply_changeset
|
/// [`apply_changeset`]: Self::apply_changeset
|
||||||
pub fn apply_update<T2>(
|
pub fn apply_update(
|
||||||
&mut self,
|
&mut self,
|
||||||
scan: KeychainScan<K, P, T2>,
|
scan: KeychainScan<K, P>,
|
||||||
) -> Result<KeychainChangeSet<K, P, T>, chain_graph::UpdateError<P>>
|
) -> Result<KeychainChangeSet<K, P>, chain_graph::UpdateError<P>> {
|
||||||
where
|
|
||||||
T2: IntoOwned<T> + Clone,
|
|
||||||
{
|
|
||||||
let changeset = self.determine_changeset(&scan)?;
|
let changeset = self.determine_changeset(&scan)?;
|
||||||
self.apply_changeset(changeset.clone());
|
self.apply_changeset(changeset.clone());
|
||||||
Ok(changeset)
|
Ok(changeset)
|
||||||
@ -107,7 +100,7 @@ where
|
|||||||
///
|
///
|
||||||
/// Internally, this calls [`KeychainTxOutIndex::apply_additions`] and
|
/// Internally, this calls [`KeychainTxOutIndex::apply_additions`] and
|
||||||
/// [`ChainGraph::apply_changeset`] in sequence.
|
/// [`ChainGraph::apply_changeset`] in sequence.
|
||||||
pub fn apply_changeset(&mut self, changeset: KeychainChangeSet<K, P, T>) {
|
pub fn apply_changeset(&mut self, changeset: KeychainChangeSet<K, P>) {
|
||||||
let KeychainChangeSet {
|
let KeychainChangeSet {
|
||||||
derivation_indices,
|
derivation_indices,
|
||||||
chain_graph,
|
chain_graph,
|
||||||
@ -139,12 +132,12 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a reference to the internal [`ChainGraph`].
|
/// Returns a reference to the internal [`ChainGraph`].
|
||||||
pub fn chain_graph(&self) -> &ChainGraph<P, T> {
|
pub fn chain_graph(&self) -> &ChainGraph<P> {
|
||||||
&self.chain_graph
|
&self.chain_graph
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a reference to the internal [`TxGraph`] (which is part of the [`ChainGraph`]).
|
/// Returns a reference to the internal [`TxGraph`] (which is part of the [`ChainGraph`]).
|
||||||
pub fn graph(&self) -> &TxGraph<T> {
|
pub fn graph(&self) -> &TxGraph {
|
||||||
self.chain_graph().graph()
|
self.chain_graph().graph()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,7 +159,7 @@ where
|
|||||||
pub fn insert_checkpoint_preview(
|
pub fn insert_checkpoint_preview(
|
||||||
&self,
|
&self,
|
||||||
block_id: BlockId,
|
block_id: BlockId,
|
||||||
) -> Result<KeychainChangeSet<K, P, T>, chain_graph::InsertCheckpointError> {
|
) -> Result<KeychainChangeSet<K, P>, chain_graph::InsertCheckpointError> {
|
||||||
Ok(KeychainChangeSet {
|
Ok(KeychainChangeSet {
|
||||||
chain_graph: self.chain_graph.insert_checkpoint_preview(block_id)?,
|
chain_graph: self.chain_graph.insert_checkpoint_preview(block_id)?,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@ -183,7 +176,7 @@ where
|
|||||||
pub fn insert_checkpoint(
|
pub fn insert_checkpoint(
|
||||||
&mut self,
|
&mut self,
|
||||||
block_id: BlockId,
|
block_id: BlockId,
|
||||||
) -> Result<KeychainChangeSet<K, P, T>, chain_graph::InsertCheckpointError> {
|
) -> Result<KeychainChangeSet<K, P>, chain_graph::InsertCheckpointError> {
|
||||||
let changeset = self.insert_checkpoint_preview(block_id)?;
|
let changeset = self.insert_checkpoint_preview(block_id)?;
|
||||||
self.apply_changeset(changeset.clone());
|
self.apply_changeset(changeset.clone());
|
||||||
Ok(changeset)
|
Ok(changeset)
|
||||||
@ -196,9 +189,9 @@ where
|
|||||||
/// responsible for persisting these changes to disk if you need to restore them.
|
/// responsible for persisting these changes to disk if you need to restore them.
|
||||||
pub fn insert_tx_preview(
|
pub fn insert_tx_preview(
|
||||||
&self,
|
&self,
|
||||||
tx: T,
|
tx: Transaction,
|
||||||
pos: P,
|
pos: P,
|
||||||
) -> Result<KeychainChangeSet<K, P, T>, chain_graph::InsertTxError<P>> {
|
) -> Result<KeychainChangeSet<K, P>, chain_graph::InsertTxError<P>> {
|
||||||
Ok(KeychainChangeSet {
|
Ok(KeychainChangeSet {
|
||||||
chain_graph: self.chain_graph.insert_tx_preview(tx, pos)?,
|
chain_graph: self.chain_graph.insert_tx_preview(tx, pos)?,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@ -214,9 +207,9 @@ where
|
|||||||
/// [`apply_changeset`]: Self::apply_changeset
|
/// [`apply_changeset`]: Self::apply_changeset
|
||||||
pub fn insert_tx(
|
pub fn insert_tx(
|
||||||
&mut self,
|
&mut self,
|
||||||
tx: T,
|
tx: Transaction,
|
||||||
pos: P,
|
pos: P,
|
||||||
) -> Result<KeychainChangeSet<K, P, T>, chain_graph::InsertTxError<P>> {
|
) -> Result<KeychainChangeSet<K, P>, chain_graph::InsertTxError<P>> {
|
||||||
let changeset = self.insert_tx_preview(tx, pos)?;
|
let changeset = self.insert_tx_preview(tx, pos)?;
|
||||||
self.apply_changeset(changeset.clone());
|
self.apply_changeset(changeset.clone());
|
||||||
Ok(changeset)
|
Ok(changeset)
|
||||||
|
@ -311,7 +311,7 @@ use core::{
|
|||||||
ops::{Bound, RangeBounds},
|
ops::{Bound, RangeBounds},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{collections::*, tx_graph::TxGraph, AsTransaction, BlockId, FullTxOut, TxHeight};
|
use crate::{collections::*, tx_graph::TxGraph, BlockId, FullTxOut, TxHeight};
|
||||||
use bitcoin::{hashes::Hash, BlockHash, OutPoint, Txid};
|
use bitcoin::{hashes::Hash, BlockHash, OutPoint, Txid};
|
||||||
|
|
||||||
/// This is a non-monotone structure that tracks relevant [`Txid`]s that are ordered by chain
|
/// This is a non-monotone structure that tracks relevant [`Txid`]s that are ordered by chain
|
||||||
@ -899,16 +899,12 @@ impl<P: ChainPosition> SparseChain<P> {
|
|||||||
/// Attempt to retrieve a [`FullTxOut`] of the given `outpoint`.
|
/// Attempt to retrieve a [`FullTxOut`] of the given `outpoint`.
|
||||||
///
|
///
|
||||||
/// This will return `Some` only if the output's transaction is in both `self` and `graph`.
|
/// This will return `Some` only if the output's transaction is in both `self` and `graph`.
|
||||||
pub fn full_txout(
|
pub fn full_txout(&self, graph: &TxGraph, outpoint: OutPoint) -> Option<FullTxOut<P>> {
|
||||||
&self,
|
|
||||||
graph: &TxGraph<impl AsTransaction>,
|
|
||||||
outpoint: OutPoint,
|
|
||||||
) -> Option<FullTxOut<P>> {
|
|
||||||
let chain_pos = self.tx_position(outpoint.txid)?;
|
let chain_pos = self.tx_position(outpoint.txid)?;
|
||||||
|
|
||||||
let tx = graph.get_tx(outpoint.txid)?;
|
let tx = graph.get_tx(outpoint.txid)?;
|
||||||
let is_on_coinbase = tx.as_tx().is_coin_base();
|
let is_on_coinbase = tx.is_coin_base();
|
||||||
let txout = tx.as_tx().output.get(outpoint.vout as usize)?.clone();
|
let txout = tx.output.get(outpoint.vout as usize)?.clone();
|
||||||
|
|
||||||
let spent_by = self
|
let spent_by = self
|
||||||
.spent_by(graph, outpoint)
|
.spent_by(graph, outpoint)
|
||||||
@ -976,7 +972,7 @@ impl<P: ChainPosition> SparseChain<P> {
|
|||||||
///
|
///
|
||||||
/// Note that the transaction including `outpoint` does not need to be in the `graph` or the
|
/// Note that the transaction including `outpoint` does not need to be in the `graph` or the
|
||||||
/// `chain` for this to return `Some`.
|
/// `chain` for this to return `Some`.
|
||||||
pub fn spent_by<T>(&self, graph: &TxGraph<T>, outpoint: OutPoint) -> Option<(&P, Txid)> {
|
pub fn spent_by(&self, graph: &TxGraph, outpoint: OutPoint) -> Option<(&P, Txid)> {
|
||||||
graph
|
graph
|
||||||
.outspends(outpoint)
|
.outspends(outpoint)
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -1,6 +1,3 @@
|
|||||||
use core::borrow::Borrow;
|
|
||||||
|
|
||||||
use alloc::{borrow::Cow, boxed::Box, rc::Rc, sync::Arc};
|
|
||||||
use bitcoin::{Block, OutPoint, Transaction, TxOut};
|
use bitcoin::{Block, OutPoint, Transaction, TxOut};
|
||||||
|
|
||||||
/// Trait to do something with every txout contained in a structure.
|
/// Trait to do something with every txout contained in a structure.
|
||||||
@ -20,58 +17,10 @@ impl ForEachTxOut for Block {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trait for things that have a single [`Transaction`] in them.
|
impl ForEachTxOut for Transaction {
|
||||||
///
|
|
||||||
/// This alows polymorphism in structures such as [`TxGraph<T>`] where `T` can be anything that
|
|
||||||
/// implements `AsTransaction`. You might think that we could just use [`core::convert::AsRef`] for
|
|
||||||
/// this but the problem is that we need to implement it on `Cow<T>` where `T: AsTransaction` which
|
|
||||||
/// we can't do with a foreign trait like `AsTransaction`.
|
|
||||||
///
|
|
||||||
/// [`Transaction`]: bitcoin::Transaction
|
|
||||||
/// [`TxGraph<T>`]: crate::tx_graph::TxGraph
|
|
||||||
pub trait AsTransaction {
|
|
||||||
/// Get a reference to the transaction.
|
|
||||||
fn as_tx(&self) -> &Transaction;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AsTransaction for Transaction {
|
|
||||||
fn as_tx(&self) -> &Transaction {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: AsTransaction> AsTransaction for Rc<T> {
|
|
||||||
fn as_tx(&self) -> &Transaction {
|
|
||||||
self.as_ref().as_tx()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: AsTransaction> AsTransaction for Arc<T> {
|
|
||||||
fn as_tx(&self) -> &Transaction {
|
|
||||||
self.as_ref().as_tx()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: AsTransaction> AsTransaction for Box<T> {
|
|
||||||
fn as_tx(&self) -> &Transaction {
|
|
||||||
self.as_ref().as_tx()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: AsTransaction + Clone> AsTransaction for Cow<'a, T> {
|
|
||||||
fn as_tx(&self) -> &Transaction {
|
|
||||||
<Cow<'_, T> as Borrow<T>>::borrow(self).as_tx()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> ForEachTxOut for T
|
|
||||||
where
|
|
||||||
T: AsTransaction,
|
|
||||||
{
|
|
||||||
fn for_each_txout(&self, mut f: impl FnMut((OutPoint, &TxOut))) {
|
fn for_each_txout(&self, mut f: impl FnMut((OutPoint, &TxOut))) {
|
||||||
let tx = self.as_tx();
|
let txid = self.txid();
|
||||||
let txid = tx.txid();
|
for (i, txout) in self.output.iter().enumerate() {
|
||||||
for (i, txout) in tx.output.iter().enumerate() {
|
|
||||||
f((
|
f((
|
||||||
OutPoint {
|
OutPoint {
|
||||||
txid,
|
txid,
|
||||||
@ -82,34 +31,3 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A trait like [`core::convert::Into`] for converting one thing into another.
|
|
||||||
///
|
|
||||||
/// We use it to convert one transaction type into another so that an update for `T2` can be used on
|
|
||||||
/// a `TxGraph<T1>` as long as `T2: IntoOwned<T1>`.
|
|
||||||
///
|
|
||||||
/// We couldn't use `Into` because we needed to implement it for [`Cow<'a, T>`].
|
|
||||||
///
|
|
||||||
/// [`Cow<'a, T>`]: std::borrow::Cow
|
|
||||||
pub trait IntoOwned<T> {
|
|
||||||
/// Converts the provided type into another (owned) type.
|
|
||||||
fn into_owned(self) -> T;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> IntoOwned<T> for T {
|
|
||||||
fn into_owned(self) -> T {
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: Clone> IntoOwned<T> for Cow<'a, T> {
|
|
||||||
fn into_owned(self) -> T {
|
|
||||||
Cow::into_owned(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, T: Clone> IntoOwned<T> for &'a T {
|
|
||||||
fn into_owned(self) -> T {
|
|
||||||
self.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
//! # use bitcoin::Transaction;
|
//! # use bitcoin::Transaction;
|
||||||
//! # let tx_a = tx_from_hex(RAW_TX_1);
|
//! # let tx_a = tx_from_hex(RAW_TX_1);
|
||||||
//! # let tx_b = tx_from_hex(RAW_TX_2);
|
//! # let tx_b = tx_from_hex(RAW_TX_2);
|
||||||
//! let mut graph = TxGraph::<Transaction>::default();
|
//! let mut graph = TxGraph::default();
|
||||||
//!
|
//!
|
||||||
//! // preview a transaction insertion (not actually inserted)
|
//! // preview a transaction insertion (not actually inserted)
|
||||||
//! let additions = graph.insert_tx_preview(tx_a);
|
//! let additions = graph.insert_tx_preview(tx_a);
|
||||||
@ -39,8 +39,8 @@
|
|||||||
//! # use bitcoin::Transaction;
|
//! # use bitcoin::Transaction;
|
||||||
//! # let tx_a = tx_from_hex(RAW_TX_1);
|
//! # let tx_a = tx_from_hex(RAW_TX_1);
|
||||||
//! # let tx_b = tx_from_hex(RAW_TX_2);
|
//! # let tx_b = tx_from_hex(RAW_TX_2);
|
||||||
//! let mut graph = TxGraph::<Transaction>::default();
|
//! let mut graph = TxGraph::default();
|
||||||
//! let update = TxGraph::<Transaction>::new(vec![tx_a, tx_b]);
|
//! let update = TxGraph::new(vec![tx_a, tx_b]);
|
||||||
//!
|
//!
|
||||||
//! // preview additions as result of the update
|
//! // preview additions as result of the update
|
||||||
//! let additions = graph.determine_additions(&update);
|
//! let additions = graph.determine_additions(&update);
|
||||||
@ -52,7 +52,7 @@
|
|||||||
//! let additions = graph.apply_update(update);
|
//! let additions = graph.apply_update(update);
|
||||||
//! assert!(additions.is_empty());
|
//! assert!(additions.is_empty());
|
||||||
//! ```
|
//! ```
|
||||||
use crate::{collections::*, AsTransaction, ForEachTxOut, IntoOwned};
|
use crate::{collections::*, ForEachTxOut};
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use bitcoin::{OutPoint, Transaction, TxOut, Txid};
|
use bitcoin::{OutPoint, Transaction, TxOut, Txid};
|
||||||
use core::ops::RangeInclusive;
|
use core::ops::RangeInclusive;
|
||||||
@ -63,8 +63,8 @@ use core::ops::RangeInclusive;
|
|||||||
///
|
///
|
||||||
/// [module-level documentation]: crate::tx_graph
|
/// [module-level documentation]: crate::tx_graph
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub struct TxGraph<T = Transaction> {
|
pub struct TxGraph {
|
||||||
txs: HashMap<Txid, TxNode<T>>,
|
txs: HashMap<Txid, TxNode>,
|
||||||
spends: BTreeMap<OutPoint, HashSet<Txid>>,
|
spends: BTreeMap<OutPoint, HashSet<Txid>>,
|
||||||
|
|
||||||
// This atrocity exists so that `TxGraph::outspends()` can return a reference.
|
// This atrocity exists so that `TxGraph::outspends()` can return a reference.
|
||||||
@ -72,7 +72,7 @@ pub struct TxGraph<T = Transaction> {
|
|||||||
empty_outspends: HashSet<Txid>,
|
empty_outspends: HashSet<Txid>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Default for TxGraph<T> {
|
impl Default for TxGraph {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
txs: Default::default(),
|
txs: Default::default(),
|
||||||
@ -85,23 +85,22 @@ impl<T> Default for TxGraph<T> {
|
|||||||
/// Node of a [`TxGraph`]. This can either be a whole transaction, or a partial transaction (where
|
/// Node of a [`TxGraph`]. This can either be a whole transaction, or a partial transaction (where
|
||||||
/// we only have select outputs).
|
/// we only have select outputs).
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
enum TxNode<T = Transaction> {
|
enum TxNode {
|
||||||
Whole(T),
|
Whole(Transaction),
|
||||||
Partial(BTreeMap<u32, TxOut>),
|
Partial(BTreeMap<u32, TxOut>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Default for TxNode<T> {
|
impl Default for TxNode {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::Partial(BTreeMap::new())
|
Self::Partial(BTreeMap::new())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: AsTransaction> TxGraph<T> {
|
impl TxGraph {
|
||||||
/// Iterate over all tx outputs known by [`TxGraph`].
|
/// Iterate over all tx outputs known by [`TxGraph`].
|
||||||
pub fn all_txouts(&self) -> impl Iterator<Item = (OutPoint, &TxOut)> {
|
pub fn all_txouts(&self) -> impl Iterator<Item = (OutPoint, &TxOut)> {
|
||||||
self.txs.iter().flat_map(|(txid, tx)| match tx {
|
self.txs.iter().flat_map(|(txid, tx)| match tx {
|
||||||
TxNode::Whole(tx) => tx
|
TxNode::Whole(tx) => tx
|
||||||
.as_tx()
|
|
||||||
.output
|
.output
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
@ -115,7 +114,7 @@ impl<T: AsTransaction> TxGraph<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Iterate over all full transactions in the graph.
|
/// Iterate over all full transactions in the graph.
|
||||||
pub fn full_transactions(&self) -> impl Iterator<Item = &T> {
|
pub fn full_transactions(&self) -> impl Iterator<Item = &Transaction> {
|
||||||
self.txs.iter().filter_map(|(_, tx)| match tx {
|
self.txs.iter().filter_map(|(_, tx)| match tx {
|
||||||
TxNode::Whole(tx) => Some(tx),
|
TxNode::Whole(tx) => Some(tx),
|
||||||
TxNode::Partial(_) => None,
|
TxNode::Partial(_) => None,
|
||||||
@ -127,7 +126,7 @@ impl<T: AsTransaction> TxGraph<T> {
|
|||||||
/// Refer to [`get_txout`] for getting a specific [`TxOut`].
|
/// Refer to [`get_txout`] for getting a specific [`TxOut`].
|
||||||
///
|
///
|
||||||
/// [`get_txout`]: Self::get_txout
|
/// [`get_txout`]: Self::get_txout
|
||||||
pub fn get_tx(&self, txid: Txid) -> Option<&T> {
|
pub fn get_tx(&self, txid: Txid) -> Option<&Transaction> {
|
||||||
match self.txs.get(&txid)? {
|
match self.txs.get(&txid)? {
|
||||||
TxNode::Whole(tx) => Some(tx),
|
TxNode::Whole(tx) => Some(tx),
|
||||||
TxNode::Partial(_) => None,
|
TxNode::Partial(_) => None,
|
||||||
@ -137,7 +136,7 @@ impl<T: AsTransaction> TxGraph<T> {
|
|||||||
/// Obtains a single tx output (if any) at specified outpoint.
|
/// Obtains a single tx output (if any) at specified outpoint.
|
||||||
pub fn get_txout(&self, outpoint: OutPoint) -> Option<&TxOut> {
|
pub fn get_txout(&self, outpoint: OutPoint) -> Option<&TxOut> {
|
||||||
match self.txs.get(&outpoint.txid)? {
|
match self.txs.get(&outpoint.txid)? {
|
||||||
TxNode::Whole(tx) => tx.as_tx().output.get(outpoint.vout as usize),
|
TxNode::Whole(tx) => tx.output.get(outpoint.vout as usize),
|
||||||
TxNode::Partial(txouts) => txouts.get(&outpoint.vout),
|
TxNode::Partial(txouts) => txouts.get(&outpoint.vout),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -146,7 +145,6 @@ impl<T: AsTransaction> TxGraph<T> {
|
|||||||
pub fn txouts(&self, txid: Txid) -> Option<BTreeMap<u32, &TxOut>> {
|
pub fn txouts(&self, txid: Txid) -> Option<BTreeMap<u32, &TxOut>> {
|
||||||
Some(match self.txs.get(&txid)? {
|
Some(match self.txs.get(&txid)? {
|
||||||
TxNode::Whole(tx) => tx
|
TxNode::Whole(tx) => tx
|
||||||
.as_tx()
|
|
||||||
.output
|
.output
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
@ -190,9 +188,9 @@ impl<T: AsTransaction> TxGraph<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: AsTransaction + Ord + Clone> TxGraph<T> {
|
impl TxGraph {
|
||||||
/// Contruct a new [`TxGraph`] from a list of transaction.
|
/// Contruct a new [`TxGraph`] from a list of transaction.
|
||||||
pub fn new(txs: impl IntoIterator<Item = T>) -> Self {
|
pub fn new(txs: impl IntoIterator<Item = Transaction>) -> Self {
|
||||||
let mut new = Self::default();
|
let mut new = Self::default();
|
||||||
for tx in txs.into_iter() {
|
for tx in txs.into_iter() {
|
||||||
let _ = new.insert_tx(tx);
|
let _ = new.insert_tx(tx);
|
||||||
@ -203,7 +201,7 @@ impl<T: AsTransaction + Ord + Clone> TxGraph<T> {
|
|||||||
///
|
///
|
||||||
/// Note this will ignore the action if we already have the full transaction that the txout is
|
/// Note this will ignore the action if we already have the full transaction that the txout is
|
||||||
/// alledged to be on (even if it doesn't match it!).
|
/// alledged to be on (even if it doesn't match it!).
|
||||||
pub fn insert_txout(&mut self, outpoint: OutPoint, txout: TxOut) -> Additions<T> {
|
pub fn insert_txout(&mut self, outpoint: OutPoint, txout: TxOut) -> Additions {
|
||||||
let additions = self.insert_txout_preview(outpoint, txout);
|
let additions = self.insert_txout_preview(outpoint, txout);
|
||||||
self.apply_additions(additions.clone());
|
self.apply_additions(additions.clone());
|
||||||
additions
|
additions
|
||||||
@ -212,7 +210,7 @@ impl<T: AsTransaction + Ord + Clone> TxGraph<T> {
|
|||||||
/// Inserts the given transaction into [`TxGraph`].
|
/// Inserts the given transaction into [`TxGraph`].
|
||||||
///
|
///
|
||||||
/// The [`Additions`] returned will be empty if `tx` already exists.
|
/// The [`Additions`] returned will be empty if `tx` already exists.
|
||||||
pub fn insert_tx(&mut self, tx: T) -> Additions<T> {
|
pub fn insert_tx(&mut self, tx: Transaction) -> Additions {
|
||||||
let additions = self.insert_tx_preview(tx);
|
let additions = self.insert_tx_preview(tx);
|
||||||
self.apply_additions(additions.clone());
|
self.apply_additions(additions.clone());
|
||||||
additions
|
additions
|
||||||
@ -223,22 +221,18 @@ impl<T: AsTransaction + Ord + Clone> TxGraph<T> {
|
|||||||
///
|
///
|
||||||
/// The returned [`Additions`] is the set difference of `update` and `self` (transactions that
|
/// The returned [`Additions`] is the set difference of `update` and `self` (transactions that
|
||||||
/// exist in `update` but not in `self`).
|
/// exist in `update` but not in `self`).
|
||||||
pub fn apply_update<T2>(&mut self, update: TxGraph<T2>) -> Additions<T>
|
pub fn apply_update(&mut self, update: TxGraph) -> Additions {
|
||||||
where
|
|
||||||
T2: IntoOwned<T> + Clone,
|
|
||||||
{
|
|
||||||
let additions = self.determine_additions(&update);
|
let additions = self.determine_additions(&update);
|
||||||
self.apply_additions(additions.clone());
|
self.apply_additions(additions.clone());
|
||||||
additions
|
additions
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Applies [`Additions`] to [`TxGraph`].
|
/// Applies [`Additions`] to [`TxGraph`].
|
||||||
pub fn apply_additions(&mut self, additions: Additions<T>) {
|
pub fn apply_additions(&mut self, additions: Additions) {
|
||||||
for tx in additions.tx {
|
for tx in additions.tx {
|
||||||
let txid = tx.as_tx().txid();
|
let txid = tx.txid();
|
||||||
|
|
||||||
tx.as_tx()
|
tx.input
|
||||||
.input
|
|
||||||
.iter()
|
.iter()
|
||||||
.map(|txin| txin.previous_output)
|
.map(|txin| txin.previous_output)
|
||||||
// coinbase spends are not to be counted
|
// coinbase spends are not to be counted
|
||||||
@ -250,7 +244,7 @@ impl<T: AsTransaction + Ord + Clone> TxGraph<T> {
|
|||||||
|
|
||||||
if let Some(TxNode::Whole(old_tx)) = self.txs.insert(txid, TxNode::Whole(tx)) {
|
if let Some(TxNode::Whole(old_tx)) = self.txs.insert(txid, TxNode::Whole(tx)) {
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
old_tx.as_tx().txid(),
|
old_tx.txid(),
|
||||||
txid,
|
txid,
|
||||||
"old tx of same txid should not be different"
|
"old tx of same txid should not be different"
|
||||||
);
|
);
|
||||||
@ -276,11 +270,8 @@ impl<T: AsTransaction + Ord + Clone> TxGraph<T> {
|
|||||||
///
|
///
|
||||||
/// The [`Additions`] would be the set difference of `update` and `self` (transactions that
|
/// The [`Additions`] would be the set difference of `update` and `self` (transactions that
|
||||||
/// exist in `update` but not in `self`).
|
/// exist in `update` but not in `self`).
|
||||||
pub fn determine_additions<T2>(&self, update: &TxGraph<T2>) -> Additions<T>
|
pub fn determine_additions(&self, update: &TxGraph) -> Additions {
|
||||||
where
|
let mut additions = Additions::default();
|
||||||
T2: IntoOwned<T> + Clone,
|
|
||||||
{
|
|
||||||
let mut additions = Additions::<T>::default();
|
|
||||||
|
|
||||||
for (&txid, update_tx) in &update.txs {
|
for (&txid, update_tx) in &update.txs {
|
||||||
if self.get_tx(txid).is_some() {
|
if self.get_tx(txid).is_some() {
|
||||||
@ -290,9 +281,7 @@ impl<T: AsTransaction + Ord + Clone> TxGraph<T> {
|
|||||||
match update_tx {
|
match update_tx {
|
||||||
TxNode::Whole(tx) => {
|
TxNode::Whole(tx) => {
|
||||||
if matches!(self.txs.get(&txid), None | Some(TxNode::Partial(_))) {
|
if matches!(self.txs.get(&txid), None | Some(TxNode::Partial(_))) {
|
||||||
additions
|
additions.tx.insert(tx.clone());
|
||||||
.tx
|
|
||||||
.insert(<T2 as IntoOwned<T>>::into_owned(tx.clone()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TxNode::Partial(partial) => {
|
TxNode::Partial(partial) => {
|
||||||
@ -314,9 +303,9 @@ impl<T: AsTransaction + Ord + Clone> TxGraph<T> {
|
|||||||
/// mutate [`Self`].
|
/// mutate [`Self`].
|
||||||
///
|
///
|
||||||
/// The [`Additions`] result will be empty if `tx` already existed in `self`.
|
/// The [`Additions`] result will be empty if `tx` already existed in `self`.
|
||||||
pub fn insert_tx_preview(&self, tx: T) -> Additions<T> {
|
pub fn insert_tx_preview(&self, tx: Transaction) -> Additions {
|
||||||
let mut update = Self::default();
|
let mut update = Self::default();
|
||||||
update.txs.insert(tx.as_tx().txid(), TxNode::Whole(tx));
|
update.txs.insert(tx.txid(), TxNode::Whole(tx));
|
||||||
self.determine_additions(&update)
|
self.determine_additions(&update)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -325,7 +314,7 @@ impl<T: AsTransaction + Ord + Clone> TxGraph<T> {
|
|||||||
///
|
///
|
||||||
/// The [`Additions`] result will be empty if the `outpoint` (or a full transaction containing
|
/// The [`Additions`] result will be empty if the `outpoint` (or a full transaction containing
|
||||||
/// the `outpoint`) already existed in `self`.
|
/// the `outpoint`) already existed in `self`.
|
||||||
pub fn insert_txout_preview(&self, outpoint: OutPoint, txout: TxOut) -> Additions<T> {
|
pub fn insert_txout_preview(&self, outpoint: OutPoint, txout: TxOut) -> Additions {
|
||||||
let mut update = Self::default();
|
let mut update = Self::default();
|
||||||
update.txs.insert(
|
update.txs.insert(
|
||||||
outpoint.txid,
|
outpoint.txid,
|
||||||
@ -335,7 +324,7 @@ impl<T: AsTransaction + Ord + Clone> TxGraph<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> TxGraph<T> {
|
impl TxGraph {
|
||||||
/// The transactions spending from this output.
|
/// The transactions spending from this output.
|
||||||
///
|
///
|
||||||
/// `TxGraph` allows conflicting transactions within the graph. Obviously the transactions in
|
/// `TxGraph` allows conflicting transactions within the graph. Obviously the transactions in
|
||||||
@ -382,7 +371,7 @@ impl<T> TxGraph<T> {
|
|||||||
///
|
///
|
||||||
/// The supplied closure returns an `Option<T>`, allowing the caller to map each node it vists
|
/// The supplied closure returns an `Option<T>`, allowing the caller to map each node it vists
|
||||||
/// and decide whether to visit descendants.
|
/// and decide whether to visit descendants.
|
||||||
pub fn walk_descendants<'g, F, O>(&'g self, txid: Txid, walk_map: F) -> TxDescendants<F, T>
|
pub fn walk_descendants<'g, F, O>(&'g self, txid: Txid, walk_map: F) -> TxDescendants<F>
|
||||||
where
|
where
|
||||||
F: FnMut(usize, Txid) -> Option<O> + 'g,
|
F: FnMut(usize, Txid) -> Option<O> + 'g,
|
||||||
{
|
{
|
||||||
@ -393,11 +382,7 @@ impl<T> TxGraph<T> {
|
|||||||
/// descendants of directly-conflicting transactions, which are also considered conflicts).
|
/// descendants of directly-conflicting transactions, which are also considered conflicts).
|
||||||
///
|
///
|
||||||
/// Refer to [`Self::walk_descendants`] for `walk_map` usage.
|
/// Refer to [`Self::walk_descendants`] for `walk_map` usage.
|
||||||
pub fn walk_conflicts<'g, F, O>(
|
pub fn walk_conflicts<'g, F, O>(&'g self, tx: &'g Transaction, walk_map: F) -> TxDescendants<F>
|
||||||
&'g self,
|
|
||||||
tx: &'g Transaction,
|
|
||||||
walk_map: F,
|
|
||||||
) -> TxDescendants<F, T>
|
|
||||||
where
|
where
|
||||||
F: FnMut(usize, Txid) -> Option<O> + 'g,
|
F: FnMut(usize, Txid) -> Option<O> + 'g,
|
||||||
{
|
{
|
||||||
@ -442,55 +427,42 @@ impl<T> TxGraph<T> {
|
|||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "serde",
|
feature = "serde",
|
||||||
derive(serde::Deserialize, serde::Serialize),
|
derive(serde::Deserialize, serde::Serialize),
|
||||||
serde(
|
serde(crate = "serde_crate")
|
||||||
crate = "serde_crate",
|
|
||||||
bound(
|
|
||||||
deserialize = "T: Ord + serde::Deserialize<'de>",
|
|
||||||
serialize = "T: Ord + serde::Serialize"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)]
|
)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub struct Additions<T> {
|
pub struct Additions {
|
||||||
pub tx: BTreeSet<T>,
|
pub tx: BTreeSet<Transaction>,
|
||||||
pub txout: BTreeMap<OutPoint, TxOut>,
|
pub txout: BTreeMap<OutPoint, TxOut>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Additions<T> {
|
impl Additions {
|
||||||
/// Returns true if the [`Additions`] is empty (no transactions or txouts).
|
/// Returns true if the [`Additions`] is empty (no transactions or txouts).
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.tx.is_empty() && self.txout.is_empty()
|
self.tx.is_empty() && self.txout.is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iterates over all outpoints contained within [`Additions`].
|
/// Iterates over all outpoints contained within [`Additions`].
|
||||||
pub fn txouts(&self) -> impl Iterator<Item = (OutPoint, &TxOut)>
|
pub fn txouts(&self) -> impl Iterator<Item = (OutPoint, &TxOut)> {
|
||||||
where
|
|
||||||
T: AsTransaction,
|
|
||||||
{
|
|
||||||
self.tx
|
self.tx
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|tx| {
|
.flat_map(|tx| {
|
||||||
tx.as_tx()
|
tx.output
|
||||||
.output
|
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(move |(vout, txout)| (OutPoint::new(tx.as_tx().txid(), vout as _), txout))
|
.map(move |(vout, txout)| (OutPoint::new(tx.txid(), vout as _), txout))
|
||||||
})
|
})
|
||||||
.chain(self.txout.iter().map(|(op, txout)| (*op, txout)))
|
.chain(self.txout.iter().map(|(op, txout)| (*op, txout)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Appends the changes in `other` into self such that applying `self` afterwards has the same
|
/// Appends the changes in `other` into self such that applying `self` afterwards has the same
|
||||||
/// effect as sequentially applying the original `self` and `other`.
|
/// effect as sequentially applying the original `self` and `other`.
|
||||||
pub fn append(&mut self, mut other: Additions<T>)
|
pub fn append(&mut self, mut other: Additions) {
|
||||||
where
|
|
||||||
T: Ord,
|
|
||||||
{
|
|
||||||
self.tx.append(&mut other.tx);
|
self.tx.append(&mut other.tx);
|
||||||
self.txout.append(&mut other.txout);
|
self.txout.append(&mut other.txout);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> Default for Additions<T> {
|
impl Default for Additions {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
tx: Default::default(),
|
tx: Default::default(),
|
||||||
@ -505,13 +477,13 @@ impl AsRef<TxGraph> for TxGraph {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: AsTransaction> ForEachTxOut for Additions<T> {
|
impl ForEachTxOut for Additions {
|
||||||
fn for_each_txout(&self, f: impl FnMut((OutPoint, &TxOut))) {
|
fn for_each_txout(&self, f: impl FnMut((OutPoint, &TxOut))) {
|
||||||
self.txouts().for_each(f)
|
self.txouts().for_each(f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: AsTransaction> ForEachTxOut for TxGraph<T> {
|
impl ForEachTxOut for TxGraph {
|
||||||
fn for_each_txout(&self, f: impl FnMut((OutPoint, &TxOut))) {
|
fn for_each_txout(&self, f: impl FnMut((OutPoint, &TxOut))) {
|
||||||
self.all_txouts().for_each(f)
|
self.all_txouts().for_each(f)
|
||||||
}
|
}
|
||||||
@ -522,17 +494,17 @@ impl<T: AsTransaction> ForEachTxOut for TxGraph<T> {
|
|||||||
/// This `struct` is created by the [`walk_descendants`] method of [`TxGraph`].
|
/// This `struct` is created by the [`walk_descendants`] method of [`TxGraph`].
|
||||||
///
|
///
|
||||||
/// [`walk_descendants`]: TxGraph::walk_descendants
|
/// [`walk_descendants`]: TxGraph::walk_descendants
|
||||||
pub struct TxDescendants<'g, F, T> {
|
pub struct TxDescendants<'g, F> {
|
||||||
graph: &'g TxGraph<T>,
|
graph: &'g TxGraph,
|
||||||
visited: HashSet<Txid>,
|
visited: HashSet<Txid>,
|
||||||
stack: Vec<(usize, Txid)>,
|
stack: Vec<(usize, Txid)>,
|
||||||
filter_map: F,
|
filter_map: F,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'g, F, T> TxDescendants<'g, F, T> {
|
impl<'g, F> TxDescendants<'g, F> {
|
||||||
/// Creates a `TxDescendants` that includes the starting `txid` when iterating.
|
/// Creates a `TxDescendants` that includes the starting `txid` when iterating.
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub(crate) fn new_include_root(graph: &'g TxGraph<T>, txid: Txid, filter_map: F) -> Self {
|
pub(crate) fn new_include_root(graph: &'g TxGraph, txid: Txid, filter_map: F) -> Self {
|
||||||
Self {
|
Self {
|
||||||
graph,
|
graph,
|
||||||
visited: Default::default(),
|
visited: Default::default(),
|
||||||
@ -542,7 +514,7 @@ impl<'g, F, T> TxDescendants<'g, F, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a `TxDescendants` that excludes the starting `txid` when iterating.
|
/// Creates a `TxDescendants` that excludes the starting `txid` when iterating.
|
||||||
pub(crate) fn new_exclude_root(graph: &'g TxGraph<T>, txid: Txid, filter_map: F) -> Self {
|
pub(crate) fn new_exclude_root(graph: &'g TxGraph, txid: Txid, filter_map: F) -> Self {
|
||||||
let mut descendants = Self {
|
let mut descendants = Self {
|
||||||
graph,
|
graph,
|
||||||
visited: Default::default(),
|
visited: Default::default(),
|
||||||
@ -555,11 +527,7 @@ impl<'g, F, T> TxDescendants<'g, F, T> {
|
|||||||
|
|
||||||
/// Creates a `TxDescendants` from multiple starting transactions that includes the starting
|
/// Creates a `TxDescendants` from multiple starting transactions that includes the starting
|
||||||
/// `txid`s when iterating.
|
/// `txid`s when iterating.
|
||||||
pub(crate) fn from_multiple_include_root<I>(
|
pub(crate) fn from_multiple_include_root<I>(graph: &'g TxGraph, txids: I, filter_map: F) -> Self
|
||||||
graph: &'g TxGraph<T>,
|
|
||||||
txids: I,
|
|
||||||
filter_map: F,
|
|
||||||
) -> Self
|
|
||||||
where
|
where
|
||||||
I: IntoIterator<Item = Txid>,
|
I: IntoIterator<Item = Txid>,
|
||||||
{
|
{
|
||||||
@ -574,11 +542,7 @@ impl<'g, F, T> TxDescendants<'g, F, T> {
|
|||||||
/// Creates a `TxDescendants` from multiple starting transactions that excludes the starting
|
/// Creates a `TxDescendants` from multiple starting transactions that excludes the starting
|
||||||
/// `txid`s when iterating.
|
/// `txid`s when iterating.
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
pub(crate) fn from_multiple_exclude_root<I>(
|
pub(crate) fn from_multiple_exclude_root<I>(graph: &'g TxGraph, txids: I, filter_map: F) -> Self
|
||||||
graph: &'g TxGraph<T>,
|
|
||||||
txids: I,
|
|
||||||
filter_map: F,
|
|
||||||
) -> Self
|
|
||||||
where
|
where
|
||||||
I: IntoIterator<Item = Txid>,
|
I: IntoIterator<Item = Txid>,
|
||||||
{
|
{
|
||||||
@ -595,7 +559,7 @@ impl<'g, F, T> TxDescendants<'g, F, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'g, F, T> TxDescendants<'g, F, T> {
|
impl<'g, F> TxDescendants<'g, F> {
|
||||||
fn populate_stack(&mut self, depth: usize, txid: Txid) {
|
fn populate_stack(&mut self, depth: usize, txid: Txid) {
|
||||||
let spend_paths = self
|
let spend_paths = self
|
||||||
.graph
|
.graph
|
||||||
@ -607,7 +571,7 @@ impl<'g, F, T> TxDescendants<'g, F, T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'g, F, O, T> Iterator for TxDescendants<'g, F, T>
|
impl<'g, F, O> Iterator for TxDescendants<'g, F>
|
||||||
where
|
where
|
||||||
F: FnMut(usize, Txid) -> Option<O>,
|
F: FnMut(usize, Txid) -> Option<O>,
|
||||||
{
|
{
|
||||||
|
@ -124,7 +124,7 @@ fn update_evicts_conflicting_tx() {
|
|||||||
cg
|
cg
|
||||||
};
|
};
|
||||||
|
|
||||||
let changeset = ChangeSet::<TxHeight, Transaction> {
|
let changeset = ChangeSet::<TxHeight> {
|
||||||
chain: sparse_chain::ChangeSet {
|
chain: sparse_chain::ChangeSet {
|
||||||
checkpoints: Default::default(),
|
checkpoints: Default::default(),
|
||||||
txids: [
|
txids: [
|
||||||
@ -203,7 +203,7 @@ fn update_evicts_conflicting_tx() {
|
|||||||
cg
|
cg
|
||||||
};
|
};
|
||||||
|
|
||||||
let changeset = ChangeSet::<TxHeight, Transaction> {
|
let changeset = ChangeSet::<TxHeight> {
|
||||||
chain: sparse_chain::ChangeSet {
|
chain: sparse_chain::ChangeSet {
|
||||||
checkpoints: [(1, Some(h!("B'")))].into(),
|
checkpoints: [(1, Some(h!("B'")))].into(),
|
||||||
txids: [
|
txids: [
|
||||||
@ -287,7 +287,7 @@ fn chain_graph_new_missing() {
|
|||||||
|
|
||||||
let new_graph = ChainGraph::new(update.clone(), graph.clone()).unwrap();
|
let new_graph = ChainGraph::new(update.clone(), graph.clone()).unwrap();
|
||||||
let expected_graph = {
|
let expected_graph = {
|
||||||
let mut cg = ChainGraph::<TxHeight, Transaction>::default();
|
let mut cg = ChainGraph::<TxHeight>::default();
|
||||||
let _ = cg
|
let _ = cg
|
||||||
.insert_checkpoint(update.latest_checkpoint().unwrap())
|
.insert_checkpoint(update.latest_checkpoint().unwrap())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -35,7 +35,7 @@ fn insert_txouts() {
|
|||||||
)];
|
)];
|
||||||
|
|
||||||
let mut graph = {
|
let mut graph = {
|
||||||
let mut graph = TxGraph::<Transaction>::default();
|
let mut graph = TxGraph::default();
|
||||||
for (outpoint, txout) in &original_ops {
|
for (outpoint, txout) in &original_ops {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graph.insert_txout(*outpoint, txout.clone()),
|
graph.insert_txout(*outpoint, txout.clone()),
|
||||||
@ -49,7 +49,7 @@ fn insert_txouts() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let update = {
|
let update = {
|
||||||
let mut graph = TxGraph::<Transaction>::default();
|
let mut graph = TxGraph::default();
|
||||||
for (outpoint, txout) in &update_ops {
|
for (outpoint, txout) in &update_ops {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
graph.insert_txout(*outpoint, txout.clone()),
|
graph.insert_txout(*outpoint, txout.clone()),
|
||||||
@ -362,7 +362,7 @@ fn test_calculate_fee_on_coinbase() {
|
|||||||
output: vec![TxOut::default()],
|
output: vec![TxOut::default()],
|
||||||
};
|
};
|
||||||
|
|
||||||
let graph = TxGraph::<Transaction>::default();
|
let graph = TxGraph::default();
|
||||||
|
|
||||||
assert_eq!(graph.calculate_fee(&tx), Some(0));
|
assert_eq!(graph.calculate_fee(&tx), Some(0));
|
||||||
}
|
}
|
||||||
|
@ -32,7 +32,7 @@ use bdk_chain::{
|
|||||||
keychain::KeychainScan,
|
keychain::KeychainScan,
|
||||||
sparse_chain::{self, ChainPosition, SparseChain},
|
sparse_chain::{self, ChainPosition, SparseChain},
|
||||||
tx_graph::TxGraph,
|
tx_graph::TxGraph,
|
||||||
AsTransaction, BlockId, ConfirmationTime, TxHeight,
|
BlockId, ConfirmationTime, TxHeight,
|
||||||
};
|
};
|
||||||
pub use electrum_client;
|
pub use electrum_client;
|
||||||
use electrum_client::{Client, ElectrumApi, Error};
|
use electrum_client::{Client, ElectrumApi, Error};
|
||||||
@ -228,10 +228,9 @@ impl<K: Ord + Clone + Debug, P: ChainPosition> ElectrumUpdate<K, P> {
|
|||||||
/// Return a list of missing full transactions that are required to [`inflate_update`].
|
/// Return a list of missing full transactions that are required to [`inflate_update`].
|
||||||
///
|
///
|
||||||
/// [`inflate_update`]: bdk_chain::chain_graph::ChainGraph::inflate_update
|
/// [`inflate_update`]: bdk_chain::chain_graph::ChainGraph::inflate_update
|
||||||
pub fn missing_full_txs<T, G>(&self, graph: G) -> Vec<&Txid>
|
pub fn missing_full_txs<G>(&self, graph: G) -> Vec<&Txid>
|
||||||
where
|
where
|
||||||
T: AsTransaction,
|
G: AsRef<TxGraph>,
|
||||||
G: AsRef<TxGraph<T>>,
|
|
||||||
{
|
{
|
||||||
self.chain_update
|
self.chain_update
|
||||||
.txids()
|
.txids()
|
||||||
@ -244,14 +243,13 @@ impl<K: Ord + Clone + Debug, P: ChainPosition> ElectrumUpdate<K, P> {
|
|||||||
/// `tracker`.
|
/// `tracker`.
|
||||||
///
|
///
|
||||||
/// This will fail if there are missing full transactions not provided via `new_txs`.
|
/// This will fail if there are missing full transactions not provided via `new_txs`.
|
||||||
pub fn into_keychain_scan<T, CG>(
|
pub fn into_keychain_scan<CG>(
|
||||||
self,
|
self,
|
||||||
new_txs: Vec<T>,
|
new_txs: Vec<Transaction>,
|
||||||
chain_graph: &CG,
|
chain_graph: &CG,
|
||||||
) -> Result<KeychainScan<K, P, T>, chain_graph::NewError<P>>
|
) -> Result<KeychainScan<K, P>, chain_graph::NewError<P>>
|
||||||
where
|
where
|
||||||
T: AsTransaction + Clone + Ord,
|
CG: AsRef<ChainGraph<P>>,
|
||||||
CG: AsRef<ChainGraph<P, T>>,
|
|
||||||
{
|
{
|
||||||
Ok(KeychainScan {
|
Ok(KeychainScan {
|
||||||
update: chain_graph
|
update: chain_graph
|
||||||
|
@ -3,9 +3,8 @@
|
|||||||
//! The star of the show is [`KeychainStore`] which maintains an append-only file of
|
//! The star of the show is [`KeychainStore`] which maintains an append-only file of
|
||||||
//! [`KeychainChangeSet`]s which can be used to restore a [`KeychainTracker`].
|
//! [`KeychainChangeSet`]s which can be used to restore a [`KeychainTracker`].
|
||||||
use bdk_chain::{
|
use bdk_chain::{
|
||||||
bitcoin::Transaction,
|
|
||||||
keychain::{KeychainChangeSet, KeychainTracker},
|
keychain::{KeychainChangeSet, KeychainTracker},
|
||||||
sparse_chain, AsTransaction,
|
sparse_chain,
|
||||||
};
|
};
|
||||||
use bincode::{DefaultOptions, Options};
|
use bincode::{DefaultOptions, Options};
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
@ -24,21 +23,20 @@ const MAGIC_BYTES: [u8; MAGIC_BYTES_LEN] = [98, 100, 107, 102, 115, 48, 48, 48,
|
|||||||
/// Persists an append only list of `KeychainChangeSet<K,P>` to a single file.
|
/// Persists an append only list of `KeychainChangeSet<K,P>` to a single file.
|
||||||
/// [`KeychainChangeSet<K,P>`] record the changes made to a [`KeychainTracker<K,P>`].
|
/// [`KeychainChangeSet<K,P>`] record the changes made to a [`KeychainTracker<K,P>`].
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct KeychainStore<K, P, T = Transaction> {
|
pub struct KeychainStore<K, P> {
|
||||||
db_file: File,
|
db_file: File,
|
||||||
changeset_type_params: core::marker::PhantomData<(K, P, T)>,
|
changeset_type_params: core::marker::PhantomData<(K, P)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bincode() -> impl bincode::Options {
|
fn bincode() -> impl bincode::Options {
|
||||||
DefaultOptions::new().with_varint_encoding()
|
DefaultOptions::new().with_varint_encoding()
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K, P, T> KeychainStore<K, P, T>
|
impl<K, P> KeychainStore<K, P>
|
||||||
where
|
where
|
||||||
K: Ord + Clone + core::fmt::Debug,
|
K: Ord + Clone + core::fmt::Debug,
|
||||||
P: sparse_chain::ChainPosition,
|
P: sparse_chain::ChainPosition,
|
||||||
T: Ord + AsTransaction + Clone,
|
KeychainChangeSet<K, P>: serde::Serialize + serde::de::DeserializeOwned,
|
||||||
KeychainChangeSet<K, P, T>: serde::Serialize + serde::de::DeserializeOwned,
|
|
||||||
{
|
{
|
||||||
/// Creates a new store from a [`File`].
|
/// Creates a new store from a [`File`].
|
||||||
///
|
///
|
||||||
@ -87,9 +85,7 @@ where
|
|||||||
/// **WARNING**: This method changes the write position in the underlying file. You should
|
/// **WARNING**: This method changes the write position in the underlying file. You should
|
||||||
/// always iterate over all entries until `None` is returned if you want your next write to go
|
/// always iterate over all entries until `None` is returned if you want your next write to go
|
||||||
/// at the end, otherwise you will write over existing enties.
|
/// at the end, otherwise you will write over existing enties.
|
||||||
pub fn iter_changesets(
|
pub fn iter_changesets(&mut self) -> Result<EntryIter<'_, KeychainChangeSet<K, P>>, io::Error> {
|
||||||
&mut self,
|
|
||||||
) -> Result<EntryIter<'_, KeychainChangeSet<K, P, T>>, io::Error> {
|
|
||||||
self.db_file
|
self.db_file
|
||||||
.seek(io::SeekFrom::Start(MAGIC_BYTES_LEN as _))?;
|
.seek(io::SeekFrom::Start(MAGIC_BYTES_LEN as _))?;
|
||||||
|
|
||||||
@ -108,7 +104,7 @@ where
|
|||||||
///
|
///
|
||||||
/// **WARNING**: This method changes the write position of the underlying file. The next
|
/// **WARNING**: This method changes the write position of the underlying file. The next
|
||||||
/// changeset will be written over the erroring entry (or the end of the file if none existed).
|
/// changeset will be written over the erroring entry (or the end of the file if none existed).
|
||||||
pub fn aggregate_changeset(&mut self) -> (KeychainChangeSet<K, P, T>, Result<(), IterError>) {
|
pub fn aggregate_changeset(&mut self) -> (KeychainChangeSet<K, P>, Result<(), IterError>) {
|
||||||
let mut changeset = KeychainChangeSet::default();
|
let mut changeset = KeychainChangeSet::default();
|
||||||
let result = (|| {
|
let result = (|| {
|
||||||
let iter_changeset = self.iter_changesets()?;
|
let iter_changeset = self.iter_changesets()?;
|
||||||
@ -128,7 +124,7 @@ where
|
|||||||
/// changeset will be written over the erroring entry (or the end of the file if none existed).
|
/// changeset will be written over the erroring entry (or the end of the file if none existed).
|
||||||
pub fn load_into_keychain_tracker(
|
pub fn load_into_keychain_tracker(
|
||||||
&mut self,
|
&mut self,
|
||||||
tracker: &mut KeychainTracker<K, P, T>,
|
tracker: &mut KeychainTracker<K, P>,
|
||||||
) -> Result<(), IterError> {
|
) -> Result<(), IterError> {
|
||||||
for changeset in self.iter_changesets()? {
|
for changeset in self.iter_changesets()? {
|
||||||
tracker.apply_changeset(changeset?)
|
tracker.apply_changeset(changeset?)
|
||||||
@ -142,7 +138,7 @@ where
|
|||||||
/// directly after the appended changeset.
|
/// directly after the appended changeset.
|
||||||
pub fn append_changeset(
|
pub fn append_changeset(
|
||||||
&mut self,
|
&mut self,
|
||||||
changeset: &KeychainChangeSet<K, P, T>,
|
changeset: &KeychainChangeSet<K, P>,
|
||||||
) -> Result<(), io::Error> {
|
) -> Result<(), io::Error> {
|
||||||
if changeset.is_empty() {
|
if changeset.is_empty() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
@ -291,7 +287,6 @@ impl From<io::Error> for IterError {
|
|||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use bdk_chain::{
|
use bdk_chain::{
|
||||||
bitcoin::Transaction,
|
|
||||||
keychain::{DerivationAdditions, KeychainChangeSet},
|
keychain::{DerivationAdditions, KeychainChangeSet},
|
||||||
TxHeight,
|
TxHeight,
|
||||||
};
|
};
|
||||||
@ -337,7 +332,7 @@ mod test {
|
|||||||
file.write_all(&MAGIC_BYTES[..MAGIC_BYTES_LEN - 1])
|
file.write_all(&MAGIC_BYTES[..MAGIC_BYTES_LEN - 1])
|
||||||
.expect("should write");
|
.expect("should write");
|
||||||
|
|
||||||
match KeychainStore::<TestKeychain, TxHeight, Transaction>::new(file.reopen().unwrap()) {
|
match KeychainStore::<TestKeychain, TxHeight>::new(file.reopen().unwrap()) {
|
||||||
Err(FileError::Io(e)) => assert_eq!(e.kind(), std::io::ErrorKind::UnexpectedEof),
|
Err(FileError::Io(e)) => assert_eq!(e.kind(), std::io::ErrorKind::UnexpectedEof),
|
||||||
unexpected => panic!("unexpected result: {:?}", unexpected),
|
unexpected => panic!("unexpected result: {:?}", unexpected),
|
||||||
};
|
};
|
||||||
@ -351,7 +346,7 @@ mod test {
|
|||||||
file.write_all(invalid_magic_bytes.as_bytes())
|
file.write_all(invalid_magic_bytes.as_bytes())
|
||||||
.expect("should write");
|
.expect("should write");
|
||||||
|
|
||||||
match KeychainStore::<TestKeychain, TxHeight, Transaction>::new(file.reopen().unwrap()) {
|
match KeychainStore::<TestKeychain, TxHeight>::new(file.reopen().unwrap()) {
|
||||||
Err(FileError::InvalidMagicBytes(b)) => {
|
Err(FileError::InvalidMagicBytes(b)) => {
|
||||||
assert_eq!(b, invalid_magic_bytes.as_bytes())
|
assert_eq!(b, invalid_magic_bytes.as_bytes())
|
||||||
}
|
}
|
||||||
@ -375,8 +370,7 @@ mod test {
|
|||||||
let mut file = NamedTempFile::new().unwrap();
|
let mut file = NamedTempFile::new().unwrap();
|
||||||
file.write_all(&data).expect("should write");
|
file.write_all(&data).expect("should write");
|
||||||
|
|
||||||
let mut store =
|
let mut store = KeychainStore::<TestKeychain, TxHeight>::new(file.reopen().unwrap())
|
||||||
KeychainStore::<TestKeychain, TxHeight, Transaction>::new(file.reopen().unwrap())
|
|
||||||
.expect("should open");
|
.expect("should open");
|
||||||
match store.iter_changesets().expect("seek should succeed").next() {
|
match store.iter_changesets().expect("seek should succeed").next() {
|
||||||
Some(Err(IterError::Bincode(_))) => {}
|
Some(Err(IterError::Bincode(_))) => {}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
mod file_store;
|
mod file_store;
|
||||||
use bdk_chain::{
|
use bdk_chain::{
|
||||||
bitcoin::Transaction,
|
|
||||||
keychain::{KeychainChangeSet, KeychainTracker, PersistBackend},
|
keychain::{KeychainChangeSet, KeychainTracker, PersistBackend},
|
||||||
sparse_chain::ChainPosition,
|
sparse_chain::ChainPosition,
|
||||||
};
|
};
|
||||||
@ -10,7 +9,7 @@ impl<K, P> PersistBackend<K, P> for KeychainStore<K, P>
|
|||||||
where
|
where
|
||||||
K: Ord + Clone + core::fmt::Debug,
|
K: Ord + Clone + core::fmt::Debug,
|
||||||
P: ChainPosition,
|
P: ChainPosition,
|
||||||
KeychainChangeSet<K, P, Transaction>: serde::Serialize + serde::de::DeserializeOwned,
|
KeychainChangeSet<K, P>: serde::Serialize + serde::de::DeserializeOwned,
|
||||||
{
|
{
|
||||||
type WriteError = std::io::Error;
|
type WriteError = std::io::Error;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user