feat(chain)!: Implement ConfirmationBlockTime
Both `bdk_electrum` and `bdk_esplora` now report the exact block that the transaction is in, which removes the need for having the old `ConfirmationTimeHeightAnchor` and `ConfirmationHeightAnchor`. This PR introduces a new, simpler anchor type that can be modified to support additional data in the future.
This commit is contained in:
@@ -74,11 +74,11 @@ impl ConfirmationTime {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ChainPosition<ConfirmationTimeHeightAnchor>> for ConfirmationTime {
|
||||
fn from(observed_as: ChainPosition<ConfirmationTimeHeightAnchor>) -> Self {
|
||||
impl From<ChainPosition<ConfirmationBlockTime>> for ConfirmationTime {
|
||||
fn from(observed_as: ChainPosition<ConfirmationBlockTime>) -> Self {
|
||||
match observed_as {
|
||||
ChainPosition::Confirmed(a) => Self::Confirmed {
|
||||
height: a.confirmation_height,
|
||||
height: a.block_id.height,
|
||||
time: a.confirmation_time,
|
||||
},
|
||||
ChainPosition::Unconfirmed(last_seen) => Self::Unconfirmed { last_seen },
|
||||
@@ -145,9 +145,7 @@ impl From<(&u32, &BlockHash)> for BlockId {
|
||||
}
|
||||
}
|
||||
|
||||
/// An [`Anchor`] implementation that also records the exact confirmation height of the transaction.
|
||||
///
|
||||
/// Note that the confirmation block and the anchor block can be different here.
|
||||
/// An [`Anchor`] implementation that also records the exact confirmation time of the transaction.
|
||||
///
|
||||
/// Refer to [`Anchor`] for more details.
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq, Copy, PartialOrd, Ord, core::hash::Hash)]
|
||||
@@ -156,70 +154,27 @@ impl From<(&u32, &BlockHash)> for BlockId {
|
||||
derive(serde::Deserialize, serde::Serialize),
|
||||
serde(crate = "serde_crate")
|
||||
)]
|
||||
pub struct ConfirmationHeightAnchor {
|
||||
/// The exact confirmation height of the transaction.
|
||||
///
|
||||
/// It is assumed that this value is never larger than the height of the anchor block.
|
||||
pub confirmation_height: u32,
|
||||
pub struct ConfirmationBlockTime {
|
||||
/// The anchor block.
|
||||
pub anchor_block: BlockId,
|
||||
}
|
||||
|
||||
impl Anchor for ConfirmationHeightAnchor {
|
||||
fn anchor_block(&self) -> BlockId {
|
||||
self.anchor_block
|
||||
}
|
||||
|
||||
fn confirmation_height_upper_bound(&self) -> u32 {
|
||||
self.confirmation_height
|
||||
}
|
||||
}
|
||||
|
||||
impl AnchorFromBlockPosition for ConfirmationHeightAnchor {
|
||||
fn from_block_position(_block: &bitcoin::Block, block_id: BlockId, _tx_pos: usize) -> Self {
|
||||
Self {
|
||||
anchor_block: block_id,
|
||||
confirmation_height: block_id.height,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An [`Anchor`] implementation that also records the exact confirmation time and height of the
|
||||
/// transaction.
|
||||
///
|
||||
/// Note that the confirmation block and the anchor block can be different here.
|
||||
///
|
||||
/// Refer to [`Anchor`] for more details.
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq, Copy, PartialOrd, Ord, core::hash::Hash)]
|
||||
#[cfg_attr(
|
||||
feature = "serde",
|
||||
derive(serde::Deserialize, serde::Serialize),
|
||||
serde(crate = "serde_crate")
|
||||
)]
|
||||
pub struct ConfirmationTimeHeightAnchor {
|
||||
/// The confirmation height of the transaction being anchored.
|
||||
pub confirmation_height: u32,
|
||||
pub block_id: BlockId,
|
||||
/// The confirmation time of the transaction being anchored.
|
||||
pub confirmation_time: u64,
|
||||
/// The anchor block.
|
||||
pub anchor_block: BlockId,
|
||||
}
|
||||
|
||||
impl Anchor for ConfirmationTimeHeightAnchor {
|
||||
impl Anchor for ConfirmationBlockTime {
|
||||
fn anchor_block(&self) -> BlockId {
|
||||
self.anchor_block
|
||||
self.block_id
|
||||
}
|
||||
|
||||
fn confirmation_height_upper_bound(&self) -> u32 {
|
||||
self.confirmation_height
|
||||
self.block_id.height
|
||||
}
|
||||
}
|
||||
|
||||
impl AnchorFromBlockPosition for ConfirmationTimeHeightAnchor {
|
||||
impl AnchorFromBlockPosition for ConfirmationBlockTime {
|
||||
fn from_block_position(block: &bitcoin::Block, block_id: BlockId, _tx_pos: usize) -> Self {
|
||||
Self {
|
||||
anchor_block: block_id,
|
||||
confirmation_height: block_id.height,
|
||||
block_id,
|
||||
confirmation_time: block.header.time as _,
|
||||
}
|
||||
}
|
||||
@@ -305,19 +260,19 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn chain_position_ord() {
|
||||
let unconf1 = ChainPosition::<ConfirmationHeightAnchor>::Unconfirmed(10);
|
||||
let unconf2 = ChainPosition::<ConfirmationHeightAnchor>::Unconfirmed(20);
|
||||
let conf1 = ChainPosition::Confirmed(ConfirmationHeightAnchor {
|
||||
confirmation_height: 9,
|
||||
anchor_block: BlockId {
|
||||
height: 20,
|
||||
let unconf1 = ChainPosition::<ConfirmationBlockTime>::Unconfirmed(10);
|
||||
let unconf2 = ChainPosition::<ConfirmationBlockTime>::Unconfirmed(20);
|
||||
let conf1 = ChainPosition::Confirmed(ConfirmationBlockTime {
|
||||
confirmation_time: 20,
|
||||
block_id: BlockId {
|
||||
height: 9,
|
||||
..Default::default()
|
||||
},
|
||||
});
|
||||
let conf2 = ChainPosition::Confirmed(ConfirmationHeightAnchor {
|
||||
confirmation_height: 12,
|
||||
anchor_block: BlockId {
|
||||
height: 15,
|
||||
let conf2 = ChainPosition::Confirmed(ConfirmationBlockTime {
|
||||
confirmation_time: 15,
|
||||
block_id: BlockId {
|
||||
height: 12,
|
||||
..Default::default()
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
//! Helper types for spk-based blockchain clients.
|
||||
|
||||
use crate::{
|
||||
collections::BTreeMap, local_chain::CheckPoint, ConfirmationTimeHeightAnchor, Indexed, TxGraph,
|
||||
collections::BTreeMap, local_chain::CheckPoint, ConfirmationBlockTime, Indexed, TxGraph,
|
||||
};
|
||||
use alloc::boxed::Box;
|
||||
use bitcoin::{OutPoint, Script, ScriptBuf, Txid};
|
||||
@@ -176,7 +176,7 @@ impl SyncRequest {
|
||||
/// Data returned from a spk-based blockchain client sync.
|
||||
///
|
||||
/// See also [`SyncRequest`].
|
||||
pub struct SyncResult<A = ConfirmationTimeHeightAnchor> {
|
||||
pub struct SyncResult<A = ConfirmationBlockTime> {
|
||||
/// The update to apply to the receiving [`TxGraph`].
|
||||
pub graph_update: TxGraph<A>,
|
||||
/// The update to apply to the receiving [`LocalChain`](crate::local_chain::LocalChain).
|
||||
@@ -317,7 +317,7 @@ impl<K: Ord + Clone> FullScanRequest<K> {
|
||||
/// Data returned from a spk-based blockchain client full scan.
|
||||
///
|
||||
/// See also [`FullScanRequest`].
|
||||
pub struct FullScanResult<K, A = ConfirmationTimeHeightAnchor> {
|
||||
pub struct FullScanResult<K, A = ConfirmationBlockTime> {
|
||||
/// The update to apply to the receiving [`LocalChain`](crate::local_chain::LocalChain).
|
||||
pub graph_update: TxGraph<A>,
|
||||
/// The update to apply to the receiving [`TxGraph`].
|
||||
|
||||
@@ -20,8 +20,7 @@ use alloc::vec::Vec;
|
||||
/// # use bdk_chain::local_chain::LocalChain;
|
||||
/// # use bdk_chain::tx_graph::TxGraph;
|
||||
/// # use bdk_chain::BlockId;
|
||||
/// # use bdk_chain::ConfirmationHeightAnchor;
|
||||
/// # use bdk_chain::ConfirmationTimeHeightAnchor;
|
||||
/// # use bdk_chain::ConfirmationBlockTime;
|
||||
/// # use bdk_chain::example_utils::*;
|
||||
/// # use bitcoin::hashes::Hash;
|
||||
/// // Initialize the local chain with two blocks.
|
||||
@@ -50,39 +49,19 @@ use alloc::vec::Vec;
|
||||
/// },
|
||||
/// );
|
||||
///
|
||||
/// // Insert `tx` into a `TxGraph` that uses `ConfirmationHeightAnchor` as the anchor type.
|
||||
/// // This anchor records the anchor block and the confirmation height of the transaction.
|
||||
/// // When a transaction is anchored with `ConfirmationHeightAnchor`, the anchor block and
|
||||
/// // confirmation block can be different. However, the confirmation block cannot be higher than
|
||||
/// // the anchor block and both blocks must be in the same chain for the anchor to be valid.
|
||||
/// let mut graph_b = TxGraph::<ConfirmationHeightAnchor>::default();
|
||||
/// let _ = graph_b.insert_tx(tx.clone());
|
||||
/// graph_b.insert_anchor(
|
||||
/// tx.compute_txid(),
|
||||
/// ConfirmationHeightAnchor {
|
||||
/// anchor_block: BlockId {
|
||||
/// height: 2,
|
||||
/// hash: Hash::hash("second".as_bytes()),
|
||||
/// },
|
||||
/// confirmation_height: 1,
|
||||
/// },
|
||||
/// );
|
||||
///
|
||||
/// // Insert `tx` into a `TxGraph` that uses `ConfirmationTimeHeightAnchor` as the anchor type.
|
||||
/// // This anchor records the anchor block, the confirmation height and time of the transaction.
|
||||
/// // When a transaction is anchored with `ConfirmationTimeHeightAnchor`, the anchor block and
|
||||
/// // confirmation block can be different. However, the confirmation block cannot be higher than
|
||||
/// // the anchor block and both blocks must be in the same chain for the anchor to be valid.
|
||||
/// let mut graph_c = TxGraph::<ConfirmationTimeHeightAnchor>::default();
|
||||
/// // Insert `tx` into a `TxGraph` that uses `ConfirmationBlockTime` as the anchor type.
|
||||
/// // This anchor records the anchor block and the confirmation time of the transaction. When a
|
||||
/// // transaction is anchored with `ConfirmationBlockTime`, the anchor block and confirmation block
|
||||
/// // of the transaction is the same block.
|
||||
/// let mut graph_c = TxGraph::<ConfirmationBlockTime>::default();
|
||||
/// let _ = graph_c.insert_tx(tx.clone());
|
||||
/// graph_c.insert_anchor(
|
||||
/// tx.compute_txid(),
|
||||
/// ConfirmationTimeHeightAnchor {
|
||||
/// anchor_block: BlockId {
|
||||
/// ConfirmationBlockTime {
|
||||
/// block_id: BlockId {
|
||||
/// height: 2,
|
||||
/// hash: Hash::hash("third".as_bytes()),
|
||||
/// },
|
||||
/// confirmation_height: 1,
|
||||
/// confirmation_time: 123,
|
||||
/// },
|
||||
/// );
|
||||
|
||||
@@ -10,7 +10,7 @@ use bdk_chain::{
|
||||
indexed_tx_graph::{self, IndexedTxGraph},
|
||||
indexer::keychain_txout::KeychainTxOutIndex,
|
||||
local_chain::LocalChain,
|
||||
tx_graph, Balance, ChainPosition, ConfirmationHeightAnchor, DescriptorExt, Merge,
|
||||
tx_graph, Balance, ChainPosition, ConfirmationBlockTime, DescriptorExt, Merge,
|
||||
};
|
||||
use bitcoin::{
|
||||
secp256k1::Secp256k1, Amount, OutPoint, Script, ScriptBuf, Transaction, TxIn, TxOut,
|
||||
@@ -32,7 +32,7 @@ fn insert_relevant_txs() {
|
||||
let spk_0 = descriptor.at_derivation_index(0).unwrap().script_pubkey();
|
||||
let spk_1 = descriptor.at_derivation_index(9).unwrap().script_pubkey();
|
||||
|
||||
let mut graph = IndexedTxGraph::<ConfirmationHeightAnchor, KeychainTxOutIndex<()>>::new(
|
||||
let mut graph = IndexedTxGraph::<ConfirmationBlockTime, KeychainTxOutIndex<()>>::new(
|
||||
KeychainTxOutIndex::new(10),
|
||||
);
|
||||
let _ = graph
|
||||
@@ -140,7 +140,7 @@ fn test_list_owned_txouts() {
|
||||
let (desc_2, _) =
|
||||
Descriptor::parse_descriptor(&Secp256k1::signing_only(), common::DESCRIPTORS[3]).unwrap();
|
||||
|
||||
let mut graph = IndexedTxGraph::<ConfirmationHeightAnchor, KeychainTxOutIndex<String>>::new(
|
||||
let mut graph = IndexedTxGraph::<ConfirmationBlockTime, KeychainTxOutIndex<String>>::new(
|
||||
KeychainTxOutIndex::new(10),
|
||||
);
|
||||
|
||||
@@ -250,9 +250,9 @@ fn test_list_owned_txouts() {
|
||||
local_chain
|
||||
.get(height)
|
||||
.map(|cp| cp.block_id())
|
||||
.map(|anchor_block| ConfirmationHeightAnchor {
|
||||
anchor_block,
|
||||
confirmation_height: anchor_block.height,
|
||||
.map(|block_id| ConfirmationBlockTime {
|
||||
block_id,
|
||||
confirmation_time: 100,
|
||||
}),
|
||||
)
|
||||
}));
|
||||
@@ -261,8 +261,7 @@ fn test_list_owned_txouts() {
|
||||
|
||||
// A helper lambda to extract and filter data from the graph.
|
||||
let fetch =
|
||||
|height: u32,
|
||||
graph: &IndexedTxGraph<ConfirmationHeightAnchor, KeychainTxOutIndex<String>>| {
|
||||
|height: u32, graph: &IndexedTxGraph<ConfirmationBlockTime, KeychainTxOutIndex<String>>| {
|
||||
let chain_tip = local_chain
|
||||
.get(height)
|
||||
.map(|cp| cp.block_id())
|
||||
|
||||
@@ -7,7 +7,7 @@ use bdk_chain::{
|
||||
collections::*,
|
||||
local_chain::LocalChain,
|
||||
tx_graph::{ChangeSet, TxGraph},
|
||||
Anchor, BlockId, ChainOracle, ChainPosition, ConfirmationHeightAnchor, Merge,
|
||||
Anchor, BlockId, ChainOracle, ChainPosition, ConfirmationBlockTime, Merge,
|
||||
};
|
||||
use bitcoin::{
|
||||
absolute, hashes::Hash, transaction, Amount, BlockHash, OutPoint, ScriptBuf, SignedAmount,
|
||||
@@ -935,7 +935,7 @@ fn test_chain_spends() {
|
||||
..common::new_tx(0)
|
||||
};
|
||||
|
||||
let mut graph = TxGraph::<ConfirmationHeightAnchor>::default();
|
||||
let mut graph = TxGraph::<ConfirmationBlockTime>::default();
|
||||
|
||||
let _ = graph.insert_tx(tx_0.clone());
|
||||
let _ = graph.insert_tx(tx_1.clone());
|
||||
@@ -944,9 +944,9 @@ fn test_chain_spends() {
|
||||
for (ht, tx) in [(95, &tx_0), (98, &tx_1)] {
|
||||
let _ = graph.insert_anchor(
|
||||
tx.compute_txid(),
|
||||
ConfirmationHeightAnchor {
|
||||
anchor_block: tip.block_id(),
|
||||
confirmation_height: ht,
|
||||
ConfirmationBlockTime {
|
||||
block_id: tip.get(ht).unwrap().block_id(),
|
||||
confirmation_time: 100,
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -959,9 +959,12 @@ fn test_chain_spends() {
|
||||
OutPoint::new(tx_0.compute_txid(), 0)
|
||||
),
|
||||
Some((
|
||||
ChainPosition::Confirmed(&ConfirmationHeightAnchor {
|
||||
anchor_block: tip.block_id(),
|
||||
confirmation_height: 98
|
||||
ChainPosition::Confirmed(&ConfirmationBlockTime {
|
||||
block_id: BlockId {
|
||||
hash: tip.get(98).unwrap().hash(),
|
||||
height: 98,
|
||||
},
|
||||
confirmation_time: 100
|
||||
}),
|
||||
tx_1.compute_txid(),
|
||||
)),
|
||||
@@ -971,9 +974,12 @@ fn test_chain_spends() {
|
||||
assert_eq!(
|
||||
graph.get_chain_position(&local_chain, tip.block_id(), tx_0.compute_txid()),
|
||||
// Some(ObservedAs::Confirmed(&local_chain.get_block(95).expect("block expected"))),
|
||||
Some(ChainPosition::Confirmed(&ConfirmationHeightAnchor {
|
||||
anchor_block: tip.block_id(),
|
||||
confirmation_height: 95
|
||||
Some(ChainPosition::Confirmed(&ConfirmationBlockTime {
|
||||
block_id: BlockId {
|
||||
hash: tip.get(95).unwrap().hash(),
|
||||
height: 95,
|
||||
},
|
||||
confirmation_time: 100
|
||||
}))
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user