Skip to content

Commit

Permalink
Merge pull request #963 from evanlinjin/chain_redesign_tweaks
Browse files Browse the repository at this point in the history
Various tweaks to redesigned structures
  • Loading branch information
evanlinjin authored May 5, 2023
2 parents c61995c + 065c64a commit e3c1370
Show file tree
Hide file tree
Showing 10 changed files with 309 additions and 215 deletions.
58 changes: 52 additions & 6 deletions crates/chain/src/chain_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,12 +160,6 @@ impl Default for BlockId {
}
}

impl Anchor for BlockId {
fn anchor_block(&self) -> BlockId {
*self
}
}

impl From<(u32, BlockHash)> for BlockId {
fn from((height, hash): (u32, BlockHash)) -> Self {
Self { height, hash }
Expand All @@ -187,6 +181,58 @@ impl From<(&u32, &BlockHash)> for BlockId {
}
}

/// An [`Anchor`] implementation that also records the exact confirmation height of the transaction.
#[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.
#[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,

pub confirmation_height: u32,
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
}
}
/// A `TxOut` with as much data as we can retrieve about it
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct FullTxOut<P> {
Expand Down
7 changes: 4 additions & 3 deletions crates/chain/src/chain_oracle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,13 @@ pub trait ChainOracle {
/// Error type.
type Error: core::fmt::Debug;

/// Determines whether `block` of [`BlockId`] exists as an ancestor of `static_block`.
/// Determines whether `block` of [`BlockId`] exists as an ancestor of `chain_tip`.
///
/// If `None` is returned, it means the implementation cannot determine whether `block` exists.
/// If `None` is returned, it means the implementation cannot determine whether `block` exists
/// under `chain_tip`.
fn is_block_in_chain(
&self,
block: BlockId,
static_block: BlockId,
chain_tip: BlockId,
) -> Result<Option<bool>, Self::Error>;
}
7 changes: 5 additions & 2 deletions crates/chain/src/indexed_tx_graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use crate::{
/// A struct that combines [`TxGraph`] and an [`Indexer`] implementation.
///
/// This structure ensures that [`TxGraph`] and [`Indexer`] are updated atomically.
#[derive(Debug)]
pub struct IndexedTxGraph<A, I> {
/// Transaction index.
pub index: I,
Expand All @@ -27,12 +28,14 @@ impl<A, I: Default> Default for IndexedTxGraph<A, I> {
}
}

impl<A: Anchor, I: Indexer> IndexedTxGraph<A, I> {
impl<A, I> IndexedTxGraph<A, I> {
/// Get a reference of the internal transaction graph.
pub fn graph(&self) -> &TxGraph<A> {
&self.graph
}
}

impl<A: Anchor, I: Indexer> IndexedTxGraph<A, I> {
/// Applies the [`IndexedAdditions`] to the [`IndexedTxGraph`].
pub fn apply_additions(&mut self, additions: IndexedAdditions<A, I::Additions>) {
let IndexedAdditions {
Expand Down Expand Up @@ -217,7 +220,7 @@ impl<A: Anchor, I: OwnedIndexer> IndexedTxGraph<A, I> {
C: ChainOracle,
F: FnMut(&Script) -> bool,
{
let tip_height = chain_tip.anchor_block().height;
let tip_height = chain_tip.height;

let mut immature = 0;
let mut trusted_pending = 0;
Expand Down
6 changes: 3 additions & 3 deletions crates/chain/src/keychain/txout_index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ impl<K> Deref for KeychainTxOutIndex<K> {
}
}

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

fn index_txout(&mut self, outpoint: OutPoint, txout: &TxOut) -> Self::Additions {
Expand All @@ -109,9 +109,9 @@ impl<K: Clone + Ord + Debug + 'static> Indexer for KeychainTxOutIndex<K> {
}
}

impl<K: Clone + Ord + Debug + 'static> OwnedIndexer for KeychainTxOutIndex<K> {
impl<K: Clone + Ord + Debug> OwnedIndexer for KeychainTxOutIndex<K> {
fn is_spk_owned(&self, spk: &Script) -> bool {
self.inner().is_spk_owned(spk)
self.index_of_spk(spk).is_some()
}
}

Expand Down
65 changes: 51 additions & 14 deletions crates/chain/src/local_chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,6 @@ use bitcoin::BlockHash;
use crate::{BlockId, ChainOracle};

/// This is a local implementation of [`ChainOracle`].
///
/// TODO: We need a cache/snapshot thing for chain oracle.
/// * Minimize calls to remotes.
/// * Can we cache it forever? Should we drop stuff?
/// * Assume anything deeper than (i.e. 10) blocks won't be reorged.
/// * Is this a cache on txs or block? or both?
/// TODO: Parents of children are confirmed if children are confirmed.
#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct LocalChain {
blocks: BTreeMap<u32, BlockHash>,
Expand Down Expand Up @@ -71,20 +64,18 @@ impl LocalChain {
}
}

/// Get a reference to a map of block height to hash.
pub fn blocks(&self) -> &BTreeMap<u32, BlockHash> {
&self.blocks
}

pub fn tip(&self) -> Option<BlockId> {
self.blocks
.iter()
.last()
.map(|(&height, &hash)| BlockId { height, hash })
}

/// Get a block at the given height.
pub fn get_block(&self, height: u32) -> Option<BlockId> {
self.blocks
.get(&height)
.map(|&hash| BlockId { height, hash })
}

/// This is like the sparsechain's logic, expect we must guarantee that all invalidated heights
/// are to be re-filled.
pub fn determine_changeset(&self, update: &Self) -> Result<ChangeSet, UpdateNotConnectedError> {
Expand Down Expand Up @@ -173,6 +164,31 @@ impl LocalChain {
pub fn heights(&self) -> BTreeSet<u32> {
self.blocks.keys().cloned().collect()
}

/// Insert a block of [`BlockId`] into the [`LocalChain`].
///
/// # Error
///
/// If the insertion height already contains a block, and the block has a different blockhash,
/// this will result in an [`InsertBlockNotMatchingError`].
pub fn insert_block(
&mut self,
block_id: BlockId,
) -> Result<ChangeSet, InsertBlockNotMatchingError> {
let mut update = Self::from_blocks(self.tip());

if let Some(original_hash) = update.blocks.insert(block_id.height, block_id.hash) {
if original_hash != block_id.hash {
return Err(InsertBlockNotMatchingError {
height: block_id.height,
original_hash,
update_hash: block_id.hash,
});
}
}

Ok(self.apply_update(update).expect("should always connect"))
}
}

/// This is the return value of [`determine_changeset`] and represents changes to [`LocalChain`].
Expand Down Expand Up @@ -201,3 +217,24 @@ impl core::fmt::Display for UpdateNotConnectedError {

#[cfg(feature = "std")]
impl std::error::Error for UpdateNotConnectedError {}

/// Represents a failure when trying to insert a checkpoint into [`LocalChain`].
#[derive(Clone, Debug, PartialEq)]
pub struct InsertBlockNotMatchingError {
pub height: u32,
pub original_hash: BlockHash,
pub update_hash: BlockHash,
}

impl core::fmt::Display for InsertBlockNotMatchingError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
f,
"failed to insert block at height {} as blockhashes conflict: original={}, update={}",
self.height, self.original_hash, self.update_hash
)
}
}

#[cfg(feature = "std")]
impl std::error::Error for InsertBlockNotMatchingError {}
2 changes: 1 addition & 1 deletion crates/chain/src/spk_txout_index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ impl<I> Default for SpkTxOutIndex<I> {
}
}

impl<I: Clone + Ord + 'static> Indexer for SpkTxOutIndex<I> {
impl<I: Clone + Ord> Indexer for SpkTxOutIndex<I> {
type Additions = ();

fn index_txout(&mut self, outpoint: OutPoint, txout: &TxOut) -> Self::Additions {
Expand Down
32 changes: 10 additions & 22 deletions crates/chain/src/tx_graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,11 @@ impl<A> TxGraph<A> {
.filter(move |(_, conflicting_txid)| *conflicting_txid != txid)
}

/// Get all transaction anchors known by [`TxGraph`].
pub fn all_anchors(&self) -> &BTreeSet<(A, Txid)> {
&self.anchors
}

/// Whether the graph has any transactions or outputs in it.
pub fn is_empty(&self) -> bool {
self.txs.is_empty()
Expand Down Expand Up @@ -592,21 +597,6 @@ impl<A: Clone + Ord> TxGraph<A> {
}

impl<A: Anchor> TxGraph<A> {
/// Get all heights that are relevant to the graph.
pub fn relevant_heights(&self) -> impl Iterator<Item = u32> + '_ {
let mut last_height = Option::<u32>::None;
self.anchors
.iter()
.map(|(a, _)| a.anchor_block().height)
.filter(move |&height| {
let is_unique = Some(height) != last_height;
if is_unique {
last_height = Some(height);
}
is_unique
})
}

/// Get the position of the transaction in `chain` with tip `chain_tip`.
///
/// If the given transaction of `txid` does not exist in the chain of `chain_tip`, `None` is
Expand All @@ -624,11 +614,9 @@ impl<A: Anchor> TxGraph<A> {
chain_tip: BlockId,
txid: Txid,
) -> Result<Option<ObservedAs<&A>>, C::Error> {
let (tx_node, anchors, &last_seen) = match self.txs.get(&txid) {
Some((tx, anchors, last_seen)) if !(anchors.is_empty() && *last_seen == 0) => {
(tx, anchors, last_seen)
}
_ => return Ok(None),
let (tx_node, anchors, last_seen) = match self.txs.get(&txid) {
Some(v) => v,
None => return Ok(None),
};

for anchor in anchors {
Expand Down Expand Up @@ -657,12 +645,12 @@ impl<A: Anchor> TxGraph<A> {
return Ok(None);
}
}
if conflicting_tx.last_seen_unconfirmed > last_seen {
if conflicting_tx.last_seen_unconfirmed > *last_seen {
return Ok(None);
}
}

Ok(Some(ObservedAs::Unconfirmed(last_seen)))
Ok(Some(ObservedAs::Unconfirmed(*last_seen)))
}

/// Get the position of the transaction in `chain` with tip `chain_tip`.
Expand Down
Loading

0 comments on commit e3c1370

Please sign in to comment.