bdk/crates/chain/src/tx_data_traits.rs

195 lines
7.2 KiB
Rust
Raw Normal View History

use crate::collections::BTreeMap;
use crate::collections::BTreeSet;
use crate::BlockId;
use alloc::vec::Vec;
/// Trait that "anchors" blockchain data to a specific block of height and hash.
///
2023-10-17 11:00:05 +02:00
/// If transaction A is anchored in block B, and block B is in the best chain, we can
/// assume that transaction A is also confirmed in the best chain. This does not necessarily mean
/// that transaction A is confirmed in block B. It could also mean transaction A is confirmed in a
/// parent block of B.
///
2023-10-17 11:00:05 +02:00
/// Every [`Anchor`] implementation must contain a [`BlockId`] parameter, and must implement
/// [`Ord`]. When implementing [`Ord`], the anchors' [`BlockId`]s should take precedence
/// over other elements inside the [`Anchor`]s for comparison purposes, i.e., you should first
/// compare the anchors' [`BlockId`]s and then care about the rest.
///
/// The example shows different types of anchors:
/// ```
/// # use bdk_chain::local_chain::LocalChain;
/// # use bdk_chain::tx_graph::TxGraph;
/// # use bdk_chain::BlockId;
/// # use bdk_chain::ConfirmationHeightAnchor;
2023-10-17 11:00:05 +02:00
/// # use bdk_chain::ConfirmationTimeHeightAnchor;
/// # use bdk_chain::example_utils::*;
/// # use bitcoin::hashes::Hash;
/// // Initialize the local chain with two blocks.
/// let chain = LocalChain::from_blocks(
/// [
/// (1, Hash::hash("first".as_bytes())),
/// (2, Hash::hash("second".as_bytes())),
/// ]
/// .into_iter()
/// .collect(),
/// );
///
/// // Transaction to be inserted into `TxGraph`s with different anchor types.
/// let tx = tx_from_hex(RAW_TX_1);
///
/// // Insert `tx` into a `TxGraph` that uses `BlockId` as the anchor type.
/// // When a transaction is anchored with `BlockId`, the anchor block and the confirmation block of
/// // the transaction is the same block.
/// let mut graph_a = TxGraph::<BlockId>::default();
/// let _ = graph_a.insert_tx(tx.clone());
/// graph_a.insert_anchor(
deps(bdk): bump `bitcoin` to `0.32.0`, miniscript to `12.0.0` deps(chain): bump `bitcoin` to `0.32.0`, miniscript to `12.0.0` fix(chain): use `minimal_non_dust()` instead of `dust_value()` fix(chain): use `compute_txid()` instead of `txid` deps(testenv): bump `electrsd` to `0.28.0` deps(electrum): bump `electrum-client` to `0.20.0` fix(electrum): use `compute_txid()` instead of `txid` deps(esplora): bump `esplora-client` to `0.8.0` deps(bitcoind_rpc): bump `bitcoin` to `0.32.0`, `bitcoincore-rpc` to `0.19.0` fix(bitcoind_rpc): use `compute_txid()` instead of `txid` fix(nursery/tmp_plan): use proper `sighash` errors, and fix the expected `Signature` fields fix(sqlite): use `compute_txid()` instead of `txid` deps(hwi): bump `hwi` to `0.9.0` deps(wallet): bump `bitcoin` to `0.32.0`, miniscript to `12.0.0` fix(wallet): use `compute_txid()` and `minimal_non_dust()` - update to use `compute_txid()` instead of deprecated `txid()` - update to use `minimal_non_dust()` instead of `dust_value()` - remove unused `bitcoin::hex::FromHex`. fix(wallet): uses `.into` conversion on `Network` for `NetworkKind` - uses `.into()` when appropriate, otherwise use the explicit `NetworkKind`, and it's `.is_mainnet()` method. fix(wallet): add P2wpkh, Taproot, InputsIndex errors to `SignerError` fix(wallet): fields on taproot, and ecdsa `Signature` structure fix(wallet/wallet): convert `Weight` to `usize` for now - converts the `bitcoin-units::Weight` type to `usize` with help of `to_wu()` method. - it should be updated/refactored in the future to handle the `Weight` type throughout the code instead of current `usize`, only converting it for now. - allows the usage of deprecated `is_provably_unspendable()`, needs further discussion if suggested `is_op_return` is suitable. - update the expect field to `signature`, as it was renamed from `sig`. fix(wallet/wallet): use `is_op_return` instead of `is_provably_unspendable` fix(wallet/wallet): use `relative::Locktime` instead of `Sequence` fix(wallet/descriptor): use `ParsePublicKeyError` fix(wallet/descriptor): use `.into()` to convert from `AbsLockTime` and `RelLockTime` to `absolute::LockTime` and `relative::LockTime` fix(wallet/wallet): use `Message::from_digest()` instead of relying on deprecated `ThirtyTwoByteHash` trait. fix(wallet/descriptor+wallet): expect `Threshold` type, and handle it internally fix(wallet/wallet): remove `0x` prefix from expected `TxId` display fix(examples): use `compute_txid()` instead of `txid` fix(ci): remove usage of `bitcoin/no-std` feature - remove comment: `# The `no-std` feature it's implied when the `std` feature is disabled.`
2024-05-22 18:34:30 -03:00
/// tx.compute_txid(),
/// BlockId {
/// height: 1,
/// hash: Hash::hash("first".as_bytes()),
/// },
/// );
///
/// // Insert `tx` into a `TxGraph` that uses `ConfirmationHeightAnchor` as the anchor type.
2023-10-17 11:00:05 +02:00
/// // 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(
deps(bdk): bump `bitcoin` to `0.32.0`, miniscript to `12.0.0` deps(chain): bump `bitcoin` to `0.32.0`, miniscript to `12.0.0` fix(chain): use `minimal_non_dust()` instead of `dust_value()` fix(chain): use `compute_txid()` instead of `txid` deps(testenv): bump `electrsd` to `0.28.0` deps(electrum): bump `electrum-client` to `0.20.0` fix(electrum): use `compute_txid()` instead of `txid` deps(esplora): bump `esplora-client` to `0.8.0` deps(bitcoind_rpc): bump `bitcoin` to `0.32.0`, `bitcoincore-rpc` to `0.19.0` fix(bitcoind_rpc): use `compute_txid()` instead of `txid` fix(nursery/tmp_plan): use proper `sighash` errors, and fix the expected `Signature` fields fix(sqlite): use `compute_txid()` instead of `txid` deps(hwi): bump `hwi` to `0.9.0` deps(wallet): bump `bitcoin` to `0.32.0`, miniscript to `12.0.0` fix(wallet): use `compute_txid()` and `minimal_non_dust()` - update to use `compute_txid()` instead of deprecated `txid()` - update to use `minimal_non_dust()` instead of `dust_value()` - remove unused `bitcoin::hex::FromHex`. fix(wallet): uses `.into` conversion on `Network` for `NetworkKind` - uses `.into()` when appropriate, otherwise use the explicit `NetworkKind`, and it's `.is_mainnet()` method. fix(wallet): add P2wpkh, Taproot, InputsIndex errors to `SignerError` fix(wallet): fields on taproot, and ecdsa `Signature` structure fix(wallet/wallet): convert `Weight` to `usize` for now - converts the `bitcoin-units::Weight` type to `usize` with help of `to_wu()` method. - it should be updated/refactored in the future to handle the `Weight` type throughout the code instead of current `usize`, only converting it for now. - allows the usage of deprecated `is_provably_unspendable()`, needs further discussion if suggested `is_op_return` is suitable. - update the expect field to `signature`, as it was renamed from `sig`. fix(wallet/wallet): use `is_op_return` instead of `is_provably_unspendable` fix(wallet/wallet): use `relative::Locktime` instead of `Sequence` fix(wallet/descriptor): use `ParsePublicKeyError` fix(wallet/descriptor): use `.into()` to convert from `AbsLockTime` and `RelLockTime` to `absolute::LockTime` and `relative::LockTime` fix(wallet/wallet): use `Message::from_digest()` instead of relying on deprecated `ThirtyTwoByteHash` trait. fix(wallet/descriptor+wallet): expect `Threshold` type, and handle it internally fix(wallet/wallet): remove `0x` prefix from expected `TxId` display fix(examples): use `compute_txid()` instead of `txid` fix(ci): remove usage of `bitcoin/no-std` feature - remove comment: `# The `no-std` feature it's implied when the `std` feature is disabled.`
2024-05-22 18:34:30 -03:00
/// tx.compute_txid(),
/// ConfirmationHeightAnchor {
/// anchor_block: BlockId {
/// height: 2,
/// hash: Hash::hash("second".as_bytes()),
/// },
/// confirmation_height: 1,
/// },
/// );
2023-10-17 11:00:05 +02:00
///
/// // 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();
/// let _ = graph_c.insert_tx(tx.clone());
/// graph_c.insert_anchor(
deps(bdk): bump `bitcoin` to `0.32.0`, miniscript to `12.0.0` deps(chain): bump `bitcoin` to `0.32.0`, miniscript to `12.0.0` fix(chain): use `minimal_non_dust()` instead of `dust_value()` fix(chain): use `compute_txid()` instead of `txid` deps(testenv): bump `electrsd` to `0.28.0` deps(electrum): bump `electrum-client` to `0.20.0` fix(electrum): use `compute_txid()` instead of `txid` deps(esplora): bump `esplora-client` to `0.8.0` deps(bitcoind_rpc): bump `bitcoin` to `0.32.0`, `bitcoincore-rpc` to `0.19.0` fix(bitcoind_rpc): use `compute_txid()` instead of `txid` fix(nursery/tmp_plan): use proper `sighash` errors, and fix the expected `Signature` fields fix(sqlite): use `compute_txid()` instead of `txid` deps(hwi): bump `hwi` to `0.9.0` deps(wallet): bump `bitcoin` to `0.32.0`, miniscript to `12.0.0` fix(wallet): use `compute_txid()` and `minimal_non_dust()` - update to use `compute_txid()` instead of deprecated `txid()` - update to use `minimal_non_dust()` instead of `dust_value()` - remove unused `bitcoin::hex::FromHex`. fix(wallet): uses `.into` conversion on `Network` for `NetworkKind` - uses `.into()` when appropriate, otherwise use the explicit `NetworkKind`, and it's `.is_mainnet()` method. fix(wallet): add P2wpkh, Taproot, InputsIndex errors to `SignerError` fix(wallet): fields on taproot, and ecdsa `Signature` structure fix(wallet/wallet): convert `Weight` to `usize` for now - converts the `bitcoin-units::Weight` type to `usize` with help of `to_wu()` method. - it should be updated/refactored in the future to handle the `Weight` type throughout the code instead of current `usize`, only converting it for now. - allows the usage of deprecated `is_provably_unspendable()`, needs further discussion if suggested `is_op_return` is suitable. - update the expect field to `signature`, as it was renamed from `sig`. fix(wallet/wallet): use `is_op_return` instead of `is_provably_unspendable` fix(wallet/wallet): use `relative::Locktime` instead of `Sequence` fix(wallet/descriptor): use `ParsePublicKeyError` fix(wallet/descriptor): use `.into()` to convert from `AbsLockTime` and `RelLockTime` to `absolute::LockTime` and `relative::LockTime` fix(wallet/wallet): use `Message::from_digest()` instead of relying on deprecated `ThirtyTwoByteHash` trait. fix(wallet/descriptor+wallet): expect `Threshold` type, and handle it internally fix(wallet/wallet): remove `0x` prefix from expected `TxId` display fix(examples): use `compute_txid()` instead of `txid` fix(ci): remove usage of `bitcoin/no-std` feature - remove comment: `# The `no-std` feature it's implied when the `std` feature is disabled.`
2024-05-22 18:34:30 -03:00
/// tx.compute_txid(),
2023-10-17 11:00:05 +02:00
/// ConfirmationTimeHeightAnchor {
/// anchor_block: BlockId {
/// height: 2,
/// hash: Hash::hash("third".as_bytes()),
/// },
/// confirmation_height: 1,
/// confirmation_time: 123,
/// },
/// );
/// ```
pub trait Anchor: core::fmt::Debug + Clone + Eq + PartialOrd + Ord + core::hash::Hash {
/// Returns the [`BlockId`] that the associated blockchain data is "anchored" in.
fn anchor_block(&self) -> BlockId;
/// Get the upper bound of the chain data's confirmation height.
///
/// The default definition gives a pessimistic answer. This can be overridden by the `Anchor`
/// implementation for a more accurate value.
fn confirmation_height_upper_bound(&self) -> u32 {
self.anchor_block().height
}
}
impl<'a, A: Anchor> Anchor for &'a A {
fn anchor_block(&self) -> BlockId {
<A as Anchor>::anchor_block(self)
}
}
/// An [`Anchor`] that can be constructed from a given block, block height and transaction position
/// within the block.
pub trait AnchorFromBlockPosition: Anchor {
/// Construct the anchor from a given `block`, block height and `tx_pos` within the block.
fn from_block_position(block: &bitcoin::Block, block_id: BlockId, tx_pos: usize) -> Self;
}
/// Trait that makes an object appendable.
pub trait Append: Default {
/// Append another object of the same type onto `self`.
fn append(&mut self, other: Self);
/// Returns whether the structure is considered empty.
fn is_empty(&self) -> bool;
/// Take the value, replacing it with the default value.
fn take(&mut self) -> Option<Self> {
if self.is_empty() {
None
} else {
Some(core::mem::take(self))
}
}
}
impl<K: Ord, V> Append for BTreeMap<K, V> {
fn append(&mut self, other: Self) {
// We use `extend` instead of `BTreeMap::append` due to performance issues with `append`.
// Refer to https://github.com/rust-lang/rust/issues/34666#issuecomment-675658420
BTreeMap::extend(self, other)
}
fn is_empty(&self) -> bool {
BTreeMap::is_empty(self)
}
}
impl<T: Ord> Append for BTreeSet<T> {
fn append(&mut self, other: Self) {
// We use `extend` instead of `BTreeMap::append` due to performance issues with `append`.
// Refer to https://github.com/rust-lang/rust/issues/34666#issuecomment-675658420
BTreeSet::extend(self, other)
}
fn is_empty(&self) -> bool {
BTreeSet::is_empty(self)
}
}
impl<T> Append for Vec<T> {
fn append(&mut self, mut other: Self) {
Vec::append(self, &mut other)
}
fn is_empty(&self) -> bool {
Vec::is_empty(self)
}
}
2023-09-05 07:53:50 +08:00
macro_rules! impl_append_for_tuple {
($($a:ident $b:tt)*) => {
impl<$($a),*> Append for ($($a,)*) where $($a: Append),* {
2023-09-05 07:53:50 +08:00
fn append(&mut self, _other: Self) {
$(Append::append(&mut self.$b, _other.$b) );*
}
fn is_empty(&self) -> bool {
$(Append::is_empty(&self.$b) && )* true
}
}
}
}
2023-09-05 07:53:50 +08:00
impl_append_for_tuple!();
impl_append_for_tuple!(T0 0);
impl_append_for_tuple!(T0 0 T1 1);
impl_append_for_tuple!(T0 0 T1 1 T2 2);
impl_append_for_tuple!(T0 0 T1 1 T2 2 T3 3);
impl_append_for_tuple!(T0 0 T1 1 T2 2 T3 3 T4 4);
impl_append_for_tuple!(T0 0 T1 1 T2 2 T3 3 T4 4 T5 5);
impl_append_for_tuple!(T0 0 T1 1 T2 2 T3 3 T4 4 T5 5 T6 6);
impl_append_for_tuple!(T0 0 T1 1 T2 2 T3 3 T4 4 T5 5 T6 6 T7 7);
impl_append_for_tuple!(T0 0 T1 1 T2 2 T3 3 T4 4 T5 5 T6 6 T7 7 T8 8);
impl_append_for_tuple!(T0 0 T1 1 T2 2 T3 3 T4 4 T5 5 T6 6 T7 7 T8 8 T9 9);
impl_append_for_tuple!(T0 0 T1 1 T2 2 T3 3 T4 4 T5 5 T6 6 T7 7 T8 8 T9 9 T10 10);