Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Various tweaks to redesigned structures #963

Merged
merged 6 commits into from
May 5, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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: 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> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure how idiomatic this is, since we always end up using IndexedTxGraph with A: Anchor, I: Indexer, but I'm not opposed to this change if it's useful for us somehow

Copy link
Member Author

@evanlinjin evanlinjin May 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even if it's not useful directly, it may allow building logic around/above it not require as many generic constraints. I would leave it as is unless if there are downsides to this?

/// 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
66 changes: 54 additions & 12 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,18 +64,21 @@ impl LocalChain {
}
}

/// Get a reference to the inner map of block height to hash.
pub fn inner(&self) -> &BTreeMap<u32, BlockHash> {
evanlinjin marked this conversation as resolved.
Show resolved Hide resolved
&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 })
/// Get a [`BlockHash`] at the given height.
pub fn get_blockhash(&self, height: u32) -> Option<BlockHash> {
self.blocks.get(&height).cloned()
evanlinjin marked this conversation as resolved.
Show resolved Hide resolved
}

/// This is like the sparsechain's logic, expect we must guarantee that all invalidated heights
Expand Down Expand Up @@ -173,6 +169,31 @@ impl LocalChain {
pub fn heights(&self) -> BTreeSet<u32> {
self.blocks.keys().cloned().collect()
}

evanlinjin marked this conversation as resolved.
Show resolved Hide resolved
/// 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 +222,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