Skip to content

Commit

Permalink
[bdk_chain_redesign] WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
evanlinjin committed Mar 25, 2023
1 parent 5ae5fe3 commit aecc8f2
Show file tree
Hide file tree
Showing 7 changed files with 315 additions and 5 deletions.
9 changes: 9 additions & 0 deletions crates/chain/src/chain_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,15 @@ use crate::{
BlockAnchor, COINBASE_MATURITY,
};

/// Represents an observation of some chain data.
#[derive(Debug, Clone, Copy)]
pub enum Observation<A> {
/// The chain data is seen in a block identified by `A`.
InBlock(A),
/// The chain data is seen at this given unix timestamp.
SeenAt(u64),
}

/// Represents the height at which a transaction is confirmed.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(
Expand Down
8 changes: 7 additions & 1 deletion crates/chain/src/keychain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use crate::{
collections::BTreeMap,
sparse_chain::ChainPosition,
tx_graph::TxGraph,
ForEachTxOut,
ForEachTxOut, TxIndexAdditions,
};

#[cfg(feature = "miniscript")]
Expand Down Expand Up @@ -85,6 +85,12 @@ impl<K: Ord> DerivationAdditions<K> {
}
}

impl<K: Ord> TxIndexAdditions for DerivationAdditions<K> {
fn append_additions(&mut self, other: Self) {
self.append(other)
}
}

impl<K> Default for DerivationAdditions<K> {
fn default() -> Self {
Self(Default::default())
Expand Down
18 changes: 17 additions & 1 deletion crates/chain/src/keychain/txout_index.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{
collections::*,
miniscript::{Descriptor, DescriptorPublicKey},
ForEachTxOut, SpkTxOutIndex,
ForEachTxOut, SpkTxOutIndex, TxIndex,
};
use alloc::{borrow::Cow, vec::Vec};
use bitcoin::{secp256k1::Secp256k1, OutPoint, Script, TxOut};
Expand Down Expand Up @@ -88,6 +88,22 @@ impl<K> Deref for KeychainTxOutIndex<K> {
}
}

impl<K: Clone + Ord + Debug> TxIndex for KeychainTxOutIndex<K> {
type Additions = DerivationAdditions<K>;

fn index_txout(&mut self, outpoint: OutPoint, txout: &TxOut) -> Self::Additions {
self.scan_txout(outpoint, txout)
}

fn index_tx(&mut self, tx: &bitcoin::Transaction) -> Self::Additions {
self.scan(tx)
}

fn is_tx_relevant(&self, tx: &bitcoin::Transaction) -> bool {
self.is_relevant(tx)
}
}

impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
/// Scans an object for relevant outpoints, which are stored and indexed internally.
///
Expand Down
10 changes: 9 additions & 1 deletion crates/chain/src/sparse_chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ use core::{
ops::{Bound, RangeBounds},
};

use crate::{collections::*, tx_graph::TxGraph, BlockId, FullTxOut, TxHeight};
use crate::{collections::*, tx_graph::TxGraph, BlockId, ChainOracle, FullTxOut, TxHeight};
use bitcoin::{hashes::Hash, BlockHash, OutPoint, Txid};

/// This is a non-monotone structure that tracks relevant [`Txid`]s that are ordered by chain
Expand Down Expand Up @@ -456,6 +456,14 @@ impl<P: core::fmt::Debug> core::fmt::Display for UpdateError<P> {
#[cfg(feature = "std")]
impl<P: core::fmt::Debug> std::error::Error for UpdateError<P> {}

impl<P: ChainPosition> ChainOracle for SparseChain<P> {
type Error = ();

fn get_block_in_best_chain(&self, height: u32) -> Result<Option<BlockHash>, Self::Error> {
Ok(self.checkpoint_at(height).map(|b| b.hash))
}
}

impl<P: ChainPosition> SparseChain<P> {
/// Creates a new chain from a list of block hashes and heights. The caller must guarantee they
/// are in the same chain.
Expand Down
21 changes: 20 additions & 1 deletion crates/chain/src/spk_txout_index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use core::ops::RangeBounds;

use crate::{
collections::{hash_map::Entry, BTreeMap, BTreeSet, HashMap},
ForEachTxOut,
ForEachTxOut, TxIndex,
};
use bitcoin::{self, OutPoint, Script, Transaction, TxOut, Txid};

Expand Down Expand Up @@ -52,6 +52,25 @@ impl<I> Default for SpkTxOutIndex<I> {
}
}

impl<I: Clone + Ord> TxIndex for SpkTxOutIndex<I> {
type Additions = BTreeSet<I>;

fn index_txout(&mut self, outpoint: OutPoint, txout: &TxOut) -> Self::Additions {
self.scan_txout(outpoint, txout)
.cloned()
.into_iter()
.collect()
}

fn index_tx(&mut self, tx: &Transaction) -> Self::Additions {
self.scan(tx)
}

fn is_tx_relevant(&self, tx: &Transaction) -> bool {
self.is_relevant(tx)
}
}

/// This macro is used instead of a member function of `SpkTxOutIndex`, which would result in a
/// compiler error[E0521]: "borrowed data escapes out of closure" when we attempt to take a
/// reference out of the `ForEachTxOut` closure during scanning.
Expand Down
72 changes: 72 additions & 0 deletions crates/chain/src/tx_data_traits.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use alloc::collections::BTreeSet;
use bitcoin::{Block, BlockHash, OutPoint, Transaction, TxOut};

use crate::BlockId;
Expand Down Expand Up @@ -44,8 +45,79 @@ pub trait BlockAnchor:
fn anchor_block(&self) -> BlockId;
}

impl<A: BlockAnchor> BlockAnchor for &'static A {
fn anchor_block(&self) -> BlockId {
<A as BlockAnchor>::anchor_block(self)
}
}

impl BlockAnchor for (u32, BlockHash) {
fn anchor_block(&self) -> BlockId {
(*self).into()
}
}

/// Represents a service that tracks the best chain history.
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))
}
}

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;

/// 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()
}

/// 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;
}
Loading

0 comments on commit aecc8f2

Please sign in to comment.