2023-03-26 11:24:30 +08:00
|
|
|
use alloc::collections::{BTreeMap, BTreeSet};
|
2023-03-24 09:23:36 +08:00
|
|
|
use bitcoin::{Block, BlockHash, OutPoint, Transaction, TxOut};
|
|
|
|
|
|
|
|
use crate::BlockId;
|
2023-03-01 11:09:08 +01:00
|
|
|
|
|
|
|
/// Trait to do something with every txout contained in a structure.
|
|
|
|
///
|
2023-03-10 23:23:29 +05:30
|
|
|
/// We would prefer to just work with things that can give us an `Iterator<Item=(OutPoint, &TxOut)>`
|
|
|
|
/// here, but rust's type system makes it extremely hard to do this (without trait objects).
|
2023-03-01 11:09:08 +01:00
|
|
|
pub trait ForEachTxOut {
|
2023-03-10 23:23:29 +05:30
|
|
|
/// The provided closure `f` will be called with each `outpoint/txout` pair.
|
2023-03-01 11:09:08 +01:00
|
|
|
fn for_each_txout(&self, f: impl FnMut((OutPoint, &TxOut)));
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ForEachTxOut for Block {
|
|
|
|
fn for_each_txout(&self, mut f: impl FnMut((OutPoint, &TxOut))) {
|
|
|
|
for tx in self.txdata.iter() {
|
|
|
|
tx.for_each_txout(&mut f)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-08 11:39:25 +13:00
|
|
|
impl ForEachTxOut for Transaction {
|
2023-03-01 11:09:08 +01:00
|
|
|
fn for_each_txout(&self, mut f: impl FnMut((OutPoint, &TxOut))) {
|
2023-03-08 11:39:25 +13:00
|
|
|
let txid = self.txid();
|
|
|
|
for (i, txout) in self.output.iter().enumerate() {
|
2023-03-01 11:09:08 +01:00
|
|
|
f((
|
|
|
|
OutPoint {
|
|
|
|
txid,
|
|
|
|
vout: i as u32,
|
|
|
|
},
|
|
|
|
txout,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-03-24 09:23:36 +08:00
|
|
|
|
|
|
|
/// Trait that "anchors" blockchain data in a specific block of height and hash.
|
|
|
|
///
|
|
|
|
/// This trait is typically associated with blockchain data such as transactions.
|
|
|
|
pub trait BlockAnchor:
|
|
|
|
core::fmt::Debug + Clone + Eq + PartialOrd + Ord + core::hash::Hash + Send + Sync + 'static
|
|
|
|
{
|
|
|
|
/// Returns the [`BlockId`] that the associated blockchain data is "anchored" in.
|
|
|
|
fn anchor_block(&self) -> BlockId;
|
|
|
|
}
|
|
|
|
|
2023-03-24 15:47:39 +08:00
|
|
|
impl<A: BlockAnchor> BlockAnchor for &'static A {
|
|
|
|
fn anchor_block(&self) -> BlockId {
|
|
|
|
<A as BlockAnchor>::anchor_block(self)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-24 09:23:36 +08:00
|
|
|
impl BlockAnchor for (u32, BlockHash) {
|
|
|
|
fn anchor_block(&self) -> BlockId {
|
|
|
|
(*self).into()
|
|
|
|
}
|
|
|
|
}
|
2023-03-24 15:47:39 +08:00
|
|
|
|
|
|
|
/// Represents a service that tracks the best chain history.
|
2023-03-29 22:45:01 +08:00
|
|
|
/// TODO: How do we ensure the chain oracle is consistent across a single call?
|
|
|
|
/// * We need to somehow lock the data! What if the ChainOracle is remote?
|
|
|
|
/// * Get tip method! And check the tip still exists at the end! And every internal call
|
|
|
|
/// does not go beyond the initial tip.
|
2023-03-24 15:47:39 +08:00
|
|
|
pub trait ChainOracle {
|
|
|
|
/// Error type.
|
|
|
|
type Error: core::fmt::Debug;
|
|
|
|
|
|
|
|
/// Returns the block hash (if any) of the given `height`.
|
|
|
|
fn get_block_in_best_chain(&self, height: u32) -> Result<Option<BlockHash>, Self::Error>;
|
|
|
|
|
|
|
|
/// Determines whether the block of [`BlockId`] exists in the best chain.
|
|
|
|
fn is_block_in_best_chain(&self, block_id: BlockId) -> Result<bool, Self::Error> {
|
|
|
|
Ok(matches!(self.get_block_in_best_chain(block_id.height)?, Some(h) if h == block_id.hash))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-03-29 22:45:01 +08:00
|
|
|
// [TODO] We need stuff for smart pointers. Maybe? How does rust lib do this?
|
|
|
|
// Box<dyn ChainOracle>, Arc<dyn ChainOracle> ????? I will figure it out
|
2023-03-24 15:47:39 +08:00
|
|
|
impl<C: ChainOracle> ChainOracle for &C {
|
|
|
|
type Error = C::Error;
|
|
|
|
|
|
|
|
fn get_block_in_best_chain(&self, height: u32) -> Result<Option<BlockHash>, Self::Error> {
|
|
|
|
<C as ChainOracle>::get_block_in_best_chain(self, height)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn is_block_in_best_chain(&self, block_id: BlockId) -> Result<bool, Self::Error> {
|
|
|
|
<C as ChainOracle>::is_block_in_best_chain(self, block_id)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Represents changes to a [`TxIndex`] implementation.
|
|
|
|
pub trait TxIndexAdditions: Default {
|
|
|
|
/// Append `other` on top of `self`.
|
|
|
|
fn append_additions(&mut self, other: Self);
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<I: Ord> TxIndexAdditions for BTreeSet<I> {
|
|
|
|
fn append_additions(&mut self, mut other: Self) {
|
|
|
|
self.append(&mut other);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Represents an index of transaction data.
|
|
|
|
pub trait TxIndex {
|
|
|
|
/// The resultant "additions" when new transaction data is indexed.
|
|
|
|
type Additions: TxIndexAdditions;
|
|
|
|
|
2023-03-26 11:24:30 +08:00
|
|
|
type SpkIndex: Ord;
|
|
|
|
|
2023-03-24 15:47:39 +08:00
|
|
|
/// Scan and index the given `outpoint` and `txout`.
|
|
|
|
fn index_txout(&mut self, outpoint: OutPoint, txout: &TxOut) -> Self::Additions;
|
|
|
|
|
|
|
|
/// Scan and index the given transaction.
|
|
|
|
fn index_tx(&mut self, tx: &Transaction) -> Self::Additions {
|
|
|
|
let txid = tx.txid();
|
|
|
|
tx.output
|
|
|
|
.iter()
|
|
|
|
.enumerate()
|
|
|
|
.map(|(vout, txout)| self.index_txout(OutPoint::new(txid, vout as _), txout))
|
|
|
|
.reduce(|mut acc, other| {
|
|
|
|
acc.append_additions(other);
|
|
|
|
acc
|
|
|
|
})
|
|
|
|
.unwrap_or_default()
|
|
|
|
}
|
|
|
|
|
2023-03-27 15:36:37 +08:00
|
|
|
/// Apply additions to itself.
|
|
|
|
fn apply_additions(&mut self, additions: Self::Additions);
|
|
|
|
|
2023-03-24 15:47:39 +08:00
|
|
|
/// A transaction is relevant if it contains a txout with a script_pubkey that we own, or if it
|
|
|
|
/// spends an already-indexed outpoint that we have previously indexed.
|
|
|
|
fn is_tx_relevant(&self, tx: &Transaction) -> bool;
|
2023-03-26 11:24:30 +08:00
|
|
|
|
|
|
|
/// Lists all relevant txouts known by the index.
|
|
|
|
fn relevant_txouts(&self) -> &BTreeMap<OutPoint, (Self::SpkIndex, TxOut)>;
|
2023-03-24 15:47:39 +08:00
|
|
|
}
|