2023-03-01 11:09:08 +01:00
|
|
|
use bitcoin::{hashes::Hash, BlockHash, OutPoint, TxOut, Txid};
|
|
|
|
|
2023-05-24 11:37:26 +08:00
|
|
|
use crate::{Anchor, COINBASE_MATURITY};
|
2023-03-01 11:09:08 +01:00
|
|
|
|
2023-05-24 11:37:26 +08:00
|
|
|
/// Represents the observed position of some chain data.
|
2023-04-05 16:39:54 +08:00
|
|
|
///
|
2023-04-17 23:25:57 +08:00
|
|
|
/// The generic `A` should be a [`Anchor`] implementation.
|
2023-03-26 11:24:30 +08:00
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, core::hash::Hash)]
|
2023-05-24 11:37:26 +08:00
|
|
|
pub enum ChainPosition<A> {
|
2023-04-05 16:39:54 +08:00
|
|
|
/// The chain data is seen as confirmed, and in anchored by `A`.
|
|
|
|
Confirmed(A),
|
2023-03-26 11:24:30 +08:00
|
|
|
/// The chain data is seen in mempool at this given timestamp.
|
2023-04-05 16:39:54 +08:00
|
|
|
Unconfirmed(u64),
|
2023-03-26 11:24:30 +08:00
|
|
|
}
|
|
|
|
|
2023-05-24 11:37:26 +08:00
|
|
|
impl<A> ChainPosition<A> {
|
|
|
|
/// Returns whether [`ChainPosition`] is confirmed or not.
|
2023-05-12 16:17:17 +08:00
|
|
|
pub fn is_confirmed(&self) -> bool {
|
|
|
|
matches!(self, Self::Confirmed(_))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-24 11:37:26 +08:00
|
|
|
impl<A: Clone> ChainPosition<&A> {
|
2023-05-29 21:54:53 +08:00
|
|
|
/// Maps a [`ChainPosition<&A>`] into a [`ChainPosition<A>`] by cloning the contents.
|
2023-05-24 11:37:26 +08:00
|
|
|
pub fn cloned(self) -> ChainPosition<A> {
|
2023-03-27 19:55:57 +08:00
|
|
|
match self {
|
2023-05-24 11:37:26 +08:00
|
|
|
ChainPosition::Confirmed(a) => ChainPosition::Confirmed(a.clone()),
|
|
|
|
ChainPosition::Unconfirmed(last_seen) => ChainPosition::Unconfirmed(last_seen),
|
2023-03-26 11:24:30 +08:00
|
|
|
}
|
|
|
|
}
|
2023-03-24 15:47:39 +08:00
|
|
|
}
|
|
|
|
|
2023-05-24 11:37:26 +08:00
|
|
|
impl<A: Anchor> ChainPosition<A> {
|
2023-05-29 21:54:53 +08:00
|
|
|
/// Determines the upper bound of the confirmation height.
|
2023-05-24 11:37:26 +08:00
|
|
|
pub fn confirmation_height_upper_bound(&self) -> Option<u32> {
|
2023-03-01 11:09:08 +01:00
|
|
|
match self {
|
2023-05-24 11:37:26 +08:00
|
|
|
ChainPosition::Confirmed(a) => Some(a.confirmation_height_upper_bound()),
|
|
|
|
ChainPosition::Unconfirmed(_) => None,
|
2023-03-01 11:09:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-10 23:23:29 +05:30
|
|
|
/// Block height and timestamp at which a transaction is confirmed.
|
2023-03-01 11:09:08 +01:00
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Copy, PartialOrd, Ord, core::hash::Hash)]
|
|
|
|
#[cfg_attr(
|
|
|
|
feature = "serde",
|
|
|
|
derive(serde::Deserialize, serde::Serialize),
|
|
|
|
serde(crate = "serde_crate")
|
|
|
|
)]
|
|
|
|
pub enum ConfirmationTime {
|
2023-05-29 21:54:53 +08:00
|
|
|
/// The confirmed variant.
|
|
|
|
Confirmed {
|
|
|
|
/// Confirmation height.
|
|
|
|
height: u32,
|
|
|
|
/// Confirmation time in unix seconds.
|
|
|
|
time: u64,
|
|
|
|
},
|
|
|
|
/// The unconfirmed variant.
|
|
|
|
Unconfirmed {
|
|
|
|
/// The last-seen timestamp in unix seconds.
|
|
|
|
last_seen: u64,
|
|
|
|
},
|
2023-03-01 11:09:08 +01:00
|
|
|
}
|
|
|
|
|
2023-05-24 11:37:26 +08:00
|
|
|
impl ConfirmationTime {
|
2023-05-29 21:54:53 +08:00
|
|
|
/// Construct an unconfirmed variant using the given `last_seen` time in unix seconds.
|
2023-05-24 11:37:26 +08:00
|
|
|
pub fn unconfirmed(last_seen: u64) -> Self {
|
|
|
|
Self::Unconfirmed { last_seen }
|
2023-03-01 11:09:08 +01:00
|
|
|
}
|
|
|
|
|
2023-05-29 21:54:53 +08:00
|
|
|
/// Returns whether [`ConfirmationTime`] is the confirmed variant.
|
2023-03-01 11:09:08 +01:00
|
|
|
pub fn is_confirmed(&self) -> bool {
|
|
|
|
matches!(self, Self::Confirmed { .. })
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-24 11:37:26 +08:00
|
|
|
impl From<ChainPosition<ConfirmationTimeAnchor>> for ConfirmationTime {
|
|
|
|
fn from(observed_as: ChainPosition<ConfirmationTimeAnchor>) -> Self {
|
2023-05-09 21:49:33 +08:00
|
|
|
match observed_as {
|
2023-05-24 11:37:26 +08:00
|
|
|
ChainPosition::Confirmed(a) => Self::Confirmed {
|
2023-05-09 21:49:33 +08:00
|
|
|
height: a.confirmation_height,
|
|
|
|
time: a.confirmation_time,
|
|
|
|
},
|
2023-05-24 11:37:26 +08:00
|
|
|
ChainPosition::Unconfirmed(_) => Self::Unconfirmed { last_seen: 0 },
|
2023-05-09 21:49:33 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-10 23:23:29 +05:30
|
|
|
/// A reference to a block in the canonical chain.
|
2023-08-09 10:42:47 +03:00
|
|
|
///
|
|
|
|
/// `BlockId` implements [`Anchor`]. When a transaction is anchored to `BlockId`, the confirmation
|
|
|
|
/// block and anchor block are the same block.
|
2023-03-24 09:23:36 +08:00
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Copy, PartialOrd, Ord, core::hash::Hash)]
|
2023-03-01 11:09:08 +01:00
|
|
|
#[cfg_attr(
|
|
|
|
feature = "serde",
|
|
|
|
derive(serde::Deserialize, serde::Serialize),
|
|
|
|
serde(crate = "serde_crate")
|
|
|
|
)]
|
|
|
|
pub struct BlockId {
|
2023-03-10 23:23:29 +05:30
|
|
|
/// The height of the block.
|
2023-03-01 11:09:08 +01:00
|
|
|
pub height: u32,
|
2023-03-10 23:23:29 +05:30
|
|
|
/// The hash of the block.
|
2023-03-01 11:09:08 +01:00
|
|
|
pub hash: BlockHash,
|
|
|
|
}
|
|
|
|
|
2023-08-09 10:42:47 +03:00
|
|
|
impl Anchor for BlockId {
|
|
|
|
fn anchor_block(&self) -> Self {
|
|
|
|
*self
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-01 11:09:08 +01:00
|
|
|
impl Default for BlockId {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
height: Default::default(),
|
2023-06-21 17:59:34 +02:00
|
|
|
hash: BlockHash::all_zeros(),
|
2023-03-01 11:09:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<(u32, BlockHash)> for BlockId {
|
|
|
|
fn from((height, hash): (u32, BlockHash)) -> Self {
|
|
|
|
Self { height, hash }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<BlockId> for (u32, BlockHash) {
|
|
|
|
fn from(block_id: BlockId) -> Self {
|
|
|
|
(block_id.height, block_id.hash)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<(&u32, &BlockHash)> for BlockId {
|
|
|
|
fn from((height, hash): (&u32, &BlockHash)) -> Self {
|
|
|
|
Self {
|
|
|
|
height: *height,
|
|
|
|
hash: *hash,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-03 16:03:23 +08:00
|
|
|
/// An [`Anchor`] implementation that also records the exact confirmation height of the transaction.
|
2023-08-16 11:30:33 +03:00
|
|
|
///
|
|
|
|
/// Refer to [`Anchor`] for more details.
|
2023-05-03 16:03:23 +08:00
|
|
|
#[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 ConfirmationHeightAnchor {
|
|
|
|
/// The anchor block.
|
|
|
|
pub anchor_block: BlockId,
|
|
|
|
|
|
|
|
/// 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,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Anchor for ConfirmationHeightAnchor {
|
|
|
|
fn anchor_block(&self) -> BlockId {
|
|
|
|
self.anchor_block
|
|
|
|
}
|
|
|
|
|
|
|
|
fn confirmation_height_upper_bound(&self) -> u32 {
|
|
|
|
self.confirmation_height
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// An [`Anchor`] implementation that also records the exact confirmation time and height of the
|
|
|
|
/// transaction.
|
2023-08-16 11:30:33 +03:00
|
|
|
///
|
|
|
|
/// Refer to [`Anchor`] for more details.
|
2023-05-03 16:03:23 +08:00
|
|
|
#[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 ConfirmationTimeAnchor {
|
|
|
|
/// The anchor block.
|
|
|
|
pub anchor_block: BlockId,
|
2023-05-29 21:54:53 +08:00
|
|
|
/// The confirmation height of the chain data being anchored.
|
2023-05-03 16:03:23 +08:00
|
|
|
pub confirmation_height: u32,
|
2023-05-29 21:54:53 +08:00
|
|
|
/// The confirmation time of the chain data being anchored.
|
2023-05-03 16:03:23 +08:00
|
|
|
pub confirmation_time: u64,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Anchor for ConfirmationTimeAnchor {
|
|
|
|
fn anchor_block(&self) -> BlockId {
|
|
|
|
self.anchor_block
|
|
|
|
}
|
|
|
|
|
|
|
|
fn confirmation_height_upper_bound(&self) -> u32 {
|
|
|
|
self.confirmation_height
|
|
|
|
}
|
|
|
|
}
|
2023-03-10 23:23:29 +05:30
|
|
|
/// A `TxOut` with as much data as we can retrieve about it
|
2023-03-26 11:24:30 +08:00
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
2023-05-24 11:37:26 +08:00
|
|
|
pub struct FullTxOut<A> {
|
2023-03-10 23:23:29 +05:30
|
|
|
/// The location of the `TxOut`.
|
2023-03-01 11:09:08 +01:00
|
|
|
pub outpoint: OutPoint,
|
2023-03-10 23:23:29 +05:30
|
|
|
/// The `TxOut`.
|
2023-03-01 11:09:08 +01:00
|
|
|
pub txout: TxOut,
|
|
|
|
/// The position of the transaction in `outpoint` in the overall chain.
|
2023-05-24 11:37:26 +08:00
|
|
|
pub chain_position: ChainPosition<A>,
|
2023-03-01 11:09:08 +01:00
|
|
|
/// The txid and chain position of the transaction (if any) that has spent this output.
|
2023-05-24 11:37:26 +08:00
|
|
|
pub spent_by: Option<(ChainPosition<A>, Txid)>,
|
2023-03-10 23:23:29 +05:30
|
|
|
/// Whether this output is on a coinbase transaction.
|
2023-03-01 11:09:08 +01:00
|
|
|
pub is_on_coinbase: bool,
|
|
|
|
}
|
|
|
|
|
2023-05-24 11:37:26 +08:00
|
|
|
impl<A: Anchor> FullTxOut<A> {
|
2023-04-05 16:39:54 +08:00
|
|
|
/// Whether the `txout` is considered mature.
|
|
|
|
///
|
2023-04-22 23:39:49 +08:00
|
|
|
/// Depending on the implementation of [`confirmation_height_upper_bound`] in [`Anchor`], this
|
|
|
|
/// method may return false-negatives. In other words, interpretted confirmation count may be
|
|
|
|
/// less than the actual value.
|
|
|
|
///
|
|
|
|
/// [`confirmation_height_upper_bound`]: Anchor::confirmation_height_upper_bound
|
2023-04-21 13:29:44 +08:00
|
|
|
pub fn is_mature(&self, tip: u32) -> bool {
|
2023-04-22 23:39:49 +08:00
|
|
|
if self.is_on_coinbase {
|
|
|
|
let tx_height = match &self.chain_position {
|
2023-05-24 11:37:26 +08:00
|
|
|
ChainPosition::Confirmed(anchor) => anchor.confirmation_height_upper_bound(),
|
|
|
|
ChainPosition::Unconfirmed(_) => {
|
2023-04-22 23:39:49 +08:00
|
|
|
debug_assert!(false, "coinbase tx can never be unconfirmed");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
let age = tip.saturating_sub(tx_height);
|
|
|
|
if age + 1 < COINBASE_MATURITY {
|
2023-04-05 16:39:54 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
true
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Whether the utxo is/was/will be spendable with chain `tip`.
|
|
|
|
///
|
2023-04-21 13:29:44 +08:00
|
|
|
/// This method does not take into account the locktime.
|
2023-04-10 16:23:10 +08:00
|
|
|
///
|
2023-04-22 23:39:49 +08:00
|
|
|
/// Depending on the implementation of [`confirmation_height_upper_bound`] in [`Anchor`], this
|
|
|
|
/// method may return false-negatives. In other words, interpretted confirmation count may be
|
|
|
|
/// less than the actual value.
|
|
|
|
///
|
|
|
|
/// [`confirmation_height_upper_bound`]: Anchor::confirmation_height_upper_bound
|
2023-04-21 13:29:44 +08:00
|
|
|
pub fn is_confirmed_and_spendable(&self, tip: u32) -> bool {
|
|
|
|
if !self.is_mature(tip) {
|
2023-04-05 16:39:54 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-04-17 23:25:57 +08:00
|
|
|
let confirmation_height = match &self.chain_position {
|
2023-05-24 11:37:26 +08:00
|
|
|
ChainPosition::Confirmed(anchor) => anchor.confirmation_height_upper_bound(),
|
|
|
|
ChainPosition::Unconfirmed(_) => return false,
|
2023-04-05 16:39:54 +08:00
|
|
|
};
|
2023-04-17 23:25:57 +08:00
|
|
|
if confirmation_height > tip {
|
|
|
|
return false;
|
|
|
|
}
|
2023-04-05 16:39:54 +08:00
|
|
|
|
|
|
|
// if the spending tx is confirmed within tip height, the txout is no longer spendable
|
2023-05-24 11:37:26 +08:00
|
|
|
if let Some((ChainPosition::Confirmed(spending_anchor), _)) = &self.spent_by {
|
2023-04-05 16:39:54 +08:00
|
|
|
if spending_anchor.anchor_block().height <= tip {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
true
|
2023-03-27 19:55:57 +08:00
|
|
|
}
|
|
|
|
}
|