Skip to content

Commit e3c1370

Browse files
authored
Merge pull request #963 from evanlinjin/chain_redesign_tweaks
Various tweaks to redesigned structures
2 parents c61995c + 065c64a commit e3c1370

File tree

10 files changed

+309
-215
lines changed

10 files changed

+309
-215
lines changed

crates/chain/src/chain_data.rs

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -160,12 +160,6 @@ impl Default for BlockId {
160160
}
161161
}
162162

163-
impl Anchor for BlockId {
164-
fn anchor_block(&self) -> BlockId {
165-
*self
166-
}
167-
}
168-
169163
impl From<(u32, BlockHash)> for BlockId {
170164
fn from((height, hash): (u32, BlockHash)) -> Self {
171165
Self { height, hash }
@@ -187,6 +181,58 @@ impl From<(&u32, &BlockHash)> for BlockId {
187181
}
188182
}
189183

184+
/// An [`Anchor`] implementation that also records the exact confirmation height of the transaction.
185+
#[derive(Debug, Default, Clone, PartialEq, Eq, Copy, PartialOrd, Ord, core::hash::Hash)]
186+
#[cfg_attr(
187+
feature = "serde",
188+
derive(serde::Deserialize, serde::Serialize),
189+
serde(crate = "serde_crate")
190+
)]
191+
pub struct ConfirmationHeightAnchor {
192+
/// The anchor block.
193+
pub anchor_block: BlockId,
194+
195+
/// The exact confirmation height of the transaction.
196+
///
197+
/// It is assumed that this value is never larger than the height of the anchor block.
198+
pub confirmation_height: u32,
199+
}
200+
201+
impl Anchor for ConfirmationHeightAnchor {
202+
fn anchor_block(&self) -> BlockId {
203+
self.anchor_block
204+
}
205+
206+
fn confirmation_height_upper_bound(&self) -> u32 {
207+
self.confirmation_height
208+
}
209+
}
210+
211+
/// An [`Anchor`] implementation that also records the exact confirmation time and height of the
212+
/// transaction.
213+
#[derive(Debug, Default, Clone, PartialEq, Eq, Copy, PartialOrd, Ord, core::hash::Hash)]
214+
#[cfg_attr(
215+
feature = "serde",
216+
derive(serde::Deserialize, serde::Serialize),
217+
serde(crate = "serde_crate")
218+
)]
219+
pub struct ConfirmationTimeAnchor {
220+
/// The anchor block.
221+
pub anchor_block: BlockId,
222+
223+
pub confirmation_height: u32,
224+
pub confirmation_time: u64,
225+
}
226+
227+
impl Anchor for ConfirmationTimeAnchor {
228+
fn anchor_block(&self) -> BlockId {
229+
self.anchor_block
230+
}
231+
232+
fn confirmation_height_upper_bound(&self) -> u32 {
233+
self.confirmation_height
234+
}
235+
}
190236
/// A `TxOut` with as much data as we can retrieve about it
191237
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
192238
pub struct FullTxOut<P> {

crates/chain/src/chain_oracle.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,13 @@ pub trait ChainOracle {
1010
/// Error type.
1111
type Error: core::fmt::Debug;
1212

13-
/// Determines whether `block` of [`BlockId`] exists as an ancestor of `static_block`.
13+
/// Determines whether `block` of [`BlockId`] exists as an ancestor of `chain_tip`.
1414
///
15-
/// If `None` is returned, it means the implementation cannot determine whether `block` exists.
15+
/// If `None` is returned, it means the implementation cannot determine whether `block` exists
16+
/// under `chain_tip`.
1617
fn is_block_in_chain(
1718
&self,
1819
block: BlockId,
19-
static_block: BlockId,
20+
chain_tip: BlockId,
2021
) -> Result<Option<bool>, Self::Error>;
2122
}

crates/chain/src/indexed_tx_graph.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use crate::{
1212
/// A struct that combines [`TxGraph`] and an [`Indexer`] implementation.
1313
///
1414
/// This structure ensures that [`TxGraph`] and [`Indexer`] are updated atomically.
15+
#[derive(Debug)]
1516
pub struct IndexedTxGraph<A, I> {
1617
/// Transaction index.
1718
pub index: I,
@@ -27,12 +28,14 @@ impl<A, I: Default> Default for IndexedTxGraph<A, I> {
2728
}
2829
}
2930

30-
impl<A: Anchor, I: Indexer> IndexedTxGraph<A, I> {
31+
impl<A, I> IndexedTxGraph<A, I> {
3132
/// Get a reference of the internal transaction graph.
3233
pub fn graph(&self) -> &TxGraph<A> {
3334
&self.graph
3435
}
36+
}
3537

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

222225
let mut immature = 0;
223226
let mut trusted_pending = 0;

crates/chain/src/keychain/txout_index.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ impl<K> Deref for KeychainTxOutIndex<K> {
8989
}
9090
}
9191

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

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

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

crates/chain/src/local_chain.rs

Lines changed: 51 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,6 @@ use bitcoin::BlockHash;
66
use crate::{BlockId, ChainOracle};
77

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

67+
/// Get a reference to a map of block height to hash.
68+
pub fn blocks(&self) -> &BTreeMap<u32, BlockHash> {
69+
&self.blocks
70+
}
71+
7472
pub fn tip(&self) -> Option<BlockId> {
7573
self.blocks
7674
.iter()
7775
.last()
7876
.map(|(&height, &hash)| BlockId { height, hash })
7977
}
8078

81-
/// Get a block at the given height.
82-
pub fn get_block(&self, height: u32) -> Option<BlockId> {
83-
self.blocks
84-
.get(&height)
85-
.map(|&hash| BlockId { height, hash })
86-
}
87-
8879
/// This is like the sparsechain's logic, expect we must guarantee that all invalidated heights
8980
/// are to be re-filled.
9081
pub fn determine_changeset(&self, update: &Self) -> Result<ChangeSet, UpdateNotConnectedError> {
@@ -173,6 +164,31 @@ impl LocalChain {
173164
pub fn heights(&self) -> BTreeSet<u32> {
174165
self.blocks.keys().cloned().collect()
175166
}
167+
168+
/// Insert a block of [`BlockId`] into the [`LocalChain`].
169+
///
170+
/// # Error
171+
///
172+
/// If the insertion height already contains a block, and the block has a different blockhash,
173+
/// this will result in an [`InsertBlockNotMatchingError`].
174+
pub fn insert_block(
175+
&mut self,
176+
block_id: BlockId,
177+
) -> Result<ChangeSet, InsertBlockNotMatchingError> {
178+
let mut update = Self::from_blocks(self.tip());
179+
180+
if let Some(original_hash) = update.blocks.insert(block_id.height, block_id.hash) {
181+
if original_hash != block_id.hash {
182+
return Err(InsertBlockNotMatchingError {
183+
height: block_id.height,
184+
original_hash,
185+
update_hash: block_id.hash,
186+
});
187+
}
188+
}
189+
190+
Ok(self.apply_update(update).expect("should always connect"))
191+
}
176192
}
177193

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

202218
#[cfg(feature = "std")]
203219
impl std::error::Error for UpdateNotConnectedError {}
220+
221+
/// Represents a failure when trying to insert a checkpoint into [`LocalChain`].
222+
#[derive(Clone, Debug, PartialEq)]
223+
pub struct InsertBlockNotMatchingError {
224+
pub height: u32,
225+
pub original_hash: BlockHash,
226+
pub update_hash: BlockHash,
227+
}
228+
229+
impl core::fmt::Display for InsertBlockNotMatchingError {
230+
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
231+
write!(
232+
f,
233+
"failed to insert block at height {} as blockhashes conflict: original={}, update={}",
234+
self.height, self.original_hash, self.update_hash
235+
)
236+
}
237+
}
238+
239+
#[cfg(feature = "std")]
240+
impl std::error::Error for InsertBlockNotMatchingError {}

crates/chain/src/spk_txout_index.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ impl<I> Default for SpkTxOutIndex<I> {
5353
}
5454
}
5555

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

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

crates/chain/src/tx_graph.rs

Lines changed: 10 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,11 @@ impl<A> TxGraph<A> {
349349
.filter(move |(_, conflicting_txid)| *conflicting_txid != txid)
350350
}
351351

352+
/// Get all transaction anchors known by [`TxGraph`].
353+
pub fn all_anchors(&self) -> &BTreeSet<(A, Txid)> {
354+
&self.anchors
355+
}
356+
352357
/// Whether the graph has any transactions or outputs in it.
353358
pub fn is_empty(&self) -> bool {
354359
self.txs.is_empty()
@@ -592,21 +597,6 @@ impl<A: Clone + Ord> TxGraph<A> {
592597
}
593598

594599
impl<A: Anchor> TxGraph<A> {
595-
/// Get all heights that are relevant to the graph.
596-
pub fn relevant_heights(&self) -> impl Iterator<Item = u32> + '_ {
597-
let mut last_height = Option::<u32>::None;
598-
self.anchors
599-
.iter()
600-
.map(|(a, _)| a.anchor_block().height)
601-
.filter(move |&height| {
602-
let is_unique = Some(height) != last_height;
603-
if is_unique {
604-
last_height = Some(height);
605-
}
606-
is_unique
607-
})
608-
}
609-
610600
/// Get the position of the transaction in `chain` with tip `chain_tip`.
611601
///
612602
/// If the given transaction of `txid` does not exist in the chain of `chain_tip`, `None` is
@@ -624,11 +614,9 @@ impl<A: Anchor> TxGraph<A> {
624614
chain_tip: BlockId,
625615
txid: Txid,
626616
) -> Result<Option<ObservedAs<&A>>, C::Error> {
627-
let (tx_node, anchors, &last_seen) = match self.txs.get(&txid) {
628-
Some((tx, anchors, last_seen)) if !(anchors.is_empty() && *last_seen == 0) => {
629-
(tx, anchors, last_seen)
630-
}
631-
_ => return Ok(None),
617+
let (tx_node, anchors, last_seen) = match self.txs.get(&txid) {
618+
Some(v) => v,
619+
None => return Ok(None),
632620
};
633621

634622
for anchor in anchors {
@@ -657,12 +645,12 @@ impl<A: Anchor> TxGraph<A> {
657645
return Ok(None);
658646
}
659647
}
660-
if conflicting_tx.last_seen_unconfirmed > last_seen {
648+
if conflicting_tx.last_seen_unconfirmed > *last_seen {
661649
return Ok(None);
662650
}
663651
}
664652

665-
Ok(Some(ObservedAs::Unconfirmed(last_seen)))
653+
Ok(Some(ObservedAs::Unconfirmed(*last_seen)))
666654
}
667655

668656
/// Get the position of the transaction in `chain` with tip `chain_tip`.

0 commit comments

Comments
 (0)