/// Update for the wallet's internal [`LocalChain`].
///
/// [`LocalChain`]: local_chain::LocalChain
- pub chain: Option<local_chain::Update>,
+ pub chain: Option<CheckPoint>,
}
/// The changes made to a wallet by applying an [`Update`].
use bdk_chain::{
bitcoin::{Address, Amount, Txid},
keychain::Balance,
- local_chain::{self, CheckPoint, LocalChain},
+ local_chain::{CheckPoint, LocalChain},
Append, BlockId, IndexedTxGraph, SpkTxOutIndex,
};
use bdk_testenv::TestEnv;
);
assert_eq!(
- local_chain.apply_update(local_chain::Update {
- tip: emission.checkpoint,
- introduce_older_blocks: false,
- })?,
+ local_chain.apply_update(emission.checkpoint,)?,
BTreeMap::from([(height, Some(hash))]),
"chain update changeset is unexpected",
);
);
assert_eq!(
- local_chain.apply_update(local_chain::Update {
- tip: emission.checkpoint,
- introduce_older_blocks: false,
- })?,
+ local_chain.apply_update(emission.checkpoint,)?,
if exp_height == exp_hashes.len() - reorged_blocks.len() {
core::iter::once((height, Some(hash)))
.chain((height + 1..exp_hashes.len() as u32).map(|h| (h, None)))
while let Some(emission) = emitter.next_block()? {
let height = emission.block_height();
- let _ = chain.apply_update(local_chain::Update {
- tip: emission.checkpoint,
- introduce_older_blocks: false,
- })?;
+ let _ = chain.apply_update(emission.checkpoint)?;
let indexed_additions = indexed_tx_graph.apply_block_relevant(&emission.block, height);
assert!(indexed_additions.is_empty());
}
{
let emission = emitter.next_block()?.expect("must get mined block");
let height = emission.block_height();
- let _ = chain.apply_update(local_chain::Update {
- tip: emission.checkpoint,
- introduce_older_blocks: false,
- })?;
+ let _ = chain.apply_update(emission.checkpoint)?;
let indexed_additions = indexed_tx_graph.apply_block_relevant(&emission.block, height);
assert!(indexed_additions.graph.txs.is_empty());
assert!(indexed_additions.graph.txouts.is_empty());
block: Block,
block_height: u32,
) -> anyhow::Result<()> {
- recv_chain
- .apply_update(CheckPoint::from_header(&block.header, block_height).into_update(false))?;
+ recv_chain.apply_update(CheckPoint::from_header(&block.header, block_height))?;
let _ = recv_graph.apply_block(block, block_height);
Ok(())
}
.expect("must construct checkpoint")
}
- /// Convenience method to convert the [`CheckPoint`] into an [`Update`].
- ///
- /// For more information, refer to [`Update`].
- pub fn into_update(self, introduce_older_blocks: bool) -> Update {
- Update {
- tip: self,
- introduce_older_blocks,
- }
- }
-
/// Puts another checkpoint onto the linked list representing the blockchain.
///
/// Returns an `Err(self)` if the block you are pushing on is not at a greater height that the one you
}
}
-/// Used to update [`LocalChain`].
-///
-/// This is used as input for [`LocalChain::apply_update`]. It contains the update's chain `tip` and
-/// a flag `introduce_older_blocks` which signals whether this update intends to introduce missing
-/// blocks to the original chain.
-///
-/// Block-by-block syncing mechanisms would typically create updates that builds upon the previous
-/// tip. In this case, `introduce_older_blocks` would be `false`.
-///
-/// Script-pubkey based syncing mechanisms may not introduce transactions in a chronological order
-/// so some updates require introducing older blocks (to anchor older transactions). For
-/// script-pubkey based syncing, `introduce_older_blocks` would typically be `true`.
-#[derive(Debug, Clone, PartialEq)]
-pub struct Update {
- /// The update chain's new tip.
- pub tip: CheckPoint,
-
- /// Whether the update allows for introducing older blocks.
- ///
- /// Refer to [struct-level documentation] for more.
- ///
- /// [struct-level documentation]: Update
- pub introduce_older_blocks: bool,
-}
-
/// This is a local implementation of [`ChainOracle`].
#[derive(Debug, Clone, PartialEq)]
pub struct LocalChain {
/// the existing chain and invalidate the block after it (if it exists) by including a block at
/// the same height but with a different hash to explicitly exclude it as a connection point.
///
- /// Additionally, an empty chain can be updated with any chain, and a chain with a single block
- /// can have it's block invalidated by an update chain with a block at the same height but
- /// different hash.
+ /// Additionally, a chain with a single block can have it's block invalidated by an update
+ /// chain with a block at the same height but different hash.
///
/// # Errors
///
/// An error will occur if the update does not correctly connect with `self`.
///
- /// Refer to [`Update`] for more about the update struct.
- ///
/// [module-level documentation]: crate::local_chain
- pub fn apply_update(&mut self, update: Update) -> Result<ChangeSet, CannotConnectError> {
- let changeset = merge_chains(
- self.tip.clone(),
- update.tip.clone(),
- update.introduce_older_blocks,
- )?;
+ pub fn apply_update(&mut self, update: CheckPoint) -> Result<ChangeSet, CannotConnectError> {
+ let changeset = merge_chains(self.tip.clone(), update.clone())?;
// `._check_index_is_consistent_with_tip` and `._check_changeset_is_applied` is called in
// `.apply_changeset`
self.apply_changeset(&changeset)
conn => Some(conn),
};
- let update = Update {
- tip: CheckPoint::from_block_ids([conn, prev, Some(this)].into_iter().flatten())
- .expect("block ids must be in order"),
- introduce_older_blocks: false,
- };
+ let update = CheckPoint::from_block_ids([conn, prev, Some(this)].into_iter().flatten())
+ .expect("block ids must be in order");
self.apply_update(update)
.map_err(ApplyHeaderError::CannotConnect)
fn merge_chains(
original_tip: CheckPoint,
update_tip: CheckPoint,
- introduce_older_blocks: bool,
) -> Result<ChangeSet, CannotConnectError> {
let mut changeset = ChangeSet::default();
let mut orig = original_tip.into_iter();
}
point_of_agreement_found = true;
prev_orig_was_invalidated = false;
- // OPTIMIZATION 1 -- If we know that older blocks cannot be introduced without
- // invalidation, we can break after finding the point of agreement.
// OPTIMIZATION 2 -- if we have the same underlying pointer at this point, we
// can guarantee that no older blocks are introduced.
- if !introduce_older_blocks || Arc::as_ptr(&o.0) == Arc::as_ptr(&u.0) {
+ if Arc::as_ptr(&o.0) == Arc::as_ptr(&u.0) {
return Ok(changeset);
}
} else {
macro_rules! chain_update {
[ $(($height:expr, $hash:expr)), * ] => {{
#[allow(unused_mut)]
- bdk_chain::local_chain::Update {
- tip: bdk_chain::local_chain::LocalChain::from_blocks([$(($height, $hash).into()),*].into_iter().collect())
- .expect("chain must have genesis block")
- .tip(),
- introduce_older_blocks: true,
- }
+ bdk_chain::local_chain::LocalChain::from_blocks([$(($height, $hash).into()),*].into_iter().collect())
+ .expect("chain must have genesis block")
+ .tip()
}};
}
use bdk_chain::{
local_chain::{
AlterCheckPointError, ApplyHeaderError, CannotConnectError, ChangeSet, CheckPoint,
- LocalChain, MissingGenesisError, Update,
+ LocalChain, MissingGenesisError,
},
BlockId,
};
struct TestLocalChain<'a> {
name: &'static str,
chain: LocalChain,
- update: Update,
+ update: CheckPoint,
exp: ExpectedResult<'a>,
}
use bdk_chain::{
bitcoin::{OutPoint, ScriptBuf, Transaction, Txid},
- local_chain::{self, CheckPoint},
+ local_chain::CheckPoint,
tx_graph::{self, TxGraph},
Anchor, BlockId, ConfirmationHeightAnchor, ConfirmationTimeHeightAnchor,
};
#[derive(Debug)]
pub struct ElectrumUpdate {
/// Chain update
- pub chain_update: local_chain::Update,
+ pub chain_update: CheckPoint,
/// Transaction updates from electrum
pub relevant_txids: RelevantTxids,
}
continue; // reorg
}
- let chain_update = local_chain::Update {
- tip,
- introduce_older_blocks: true,
- };
+ let chain_update = tip;
let keychain_update = request_spks
.into_keys()
use bdk_chain::{
bitcoin::{BlockHash, OutPoint, ScriptBuf, TxOut, Txid},
collections::BTreeMap,
- local_chain::{self, CheckPoint},
+ local_chain::CheckPoint,
BlockId, ConfirmationTimeHeightAnchor, TxGraph,
};
use esplora_client::{Amount, TxStatus};
///
/// A `stop_gap` of 0 will be treated as a `stop_gap` of 1.
///
- /// [`LocalChain::tip`]: local_chain::LocalChain::tip
+ /// [`LocalChain::tip`]: bdk_chain::local_chain::LocalChain::tip
async fn full_scan<K: Ord + Clone + Send>(
&self,
local_tip: CheckPoint,
/// If the scripts to sync are unknown, such as when restoring or importing a keychain that
/// may include scripts that have been used, use [`full_scan`] with the keychain.
///
- /// [`LocalChain::tip`]: local_chain::LocalChain::tip
+ /// [`LocalChain::tip`]: bdk_chain::local_chain::LocalChain::tip
/// [`full_scan`]: EsploraAsyncExt::full_scan
async fn sync(
&self,
latest_blocks: &BTreeMap<u32, BlockHash>,
local_tip: &CheckPoint,
anchors: &BTreeSet<(A, Txid)>,
-) -> Result<local_chain::Update, Error> {
+) -> Result<CheckPoint, Error> {
let mut point_of_agreement = None;
let mut conflicts = vec![];
for local_cp in local_tip.iter() {
tip = tip.insert(BlockId { height, hash });
}
- Ok(local_chain::Update {
- tip,
- introduce_older_blocks: true,
- })
+ Ok(tip)
}
/// This performs a full scan to get an update for the [`TxGraph`] and
use bdk_chain::Anchor;
use bdk_chain::{
bitcoin::{Amount, BlockHash, OutPoint, ScriptBuf, TxOut, Txid},
- local_chain::{self, CheckPoint},
+ local_chain::CheckPoint,
BlockId, ConfirmationTimeHeightAnchor, TxGraph,
};
use esplora_client::TxStatus;
///
/// A `stop_gap` of 0 will be treated as a `stop_gap` of 1.
///
- /// [`LocalChain::tip`]: local_chain::LocalChain::tip
+ /// [`LocalChain::tip`]: bdk_chain::local_chain::LocalChain::tip
fn full_scan<K: Ord + Clone>(
&self,
local_tip: CheckPoint,
/// If the scripts to sync are unknown, such as when restoring or importing a keychain that
/// may include scripts that have been used, use [`full_scan`] with the keychain.
///
- /// [`LocalChain::tip`]: local_chain::LocalChain::tip
+ /// [`LocalChain::tip`]: bdk_chain::local_chain::LocalChain::tip
/// [`full_scan`]: EsploraExt::full_scan
fn sync(
&self,
latest_blocks: &BTreeMap<u32, BlockHash>,
local_tip: &CheckPoint,
anchors: &BTreeSet<(A, Txid)>,
-) -> Result<local_chain::Update, Error> {
+) -> Result<CheckPoint, Error> {
let mut point_of_agreement = None;
let mut conflicts = vec![];
for local_cp in local_tip.iter() {
tip = tip.insert(BlockId { height, hash });
}
- Ok(local_chain::Update {
- tip,
- introduce_older_blocks: true,
- })
+ Ok(tip)
}
/// This performs a full scan to get an update for the [`TxGraph`] and
)?;
let update_blocks = chain_update
- .tip
.iter()
.map(|cp| cp.block_id())
.collect::<BTreeSet<_>>();
use std::collections::BTreeMap;
-use bdk_chain::{local_chain, BlockId, ConfirmationTimeHeightAnchor, TxGraph};
+use bdk_chain::{local_chain::CheckPoint, BlockId, ConfirmationTimeHeightAnchor, TxGraph};
use esplora_client::TxStatus;
pub use esplora_client;
/// Update returns from a full scan.
pub struct FullScanUpdate<K> {
- /// The update to apply to the receiving [`LocalChain`](local_chain::LocalChain).
- pub local_chain: local_chain::Update,
+ /// The update to apply to the receiving [`LocalChain`](bdk_chain::local_chain::LocalChain).
+ pub local_chain: CheckPoint,
/// The update to apply to the receiving [`TxGraph`].
pub tx_graph: TxGraph<ConfirmationTimeHeightAnchor>,
/// Last active indices for the corresponding keychains (`K`).
/// Update returned from a sync.
pub struct SyncUpdate {
- /// The update to apply to the receiving [`LocalChain`](local_chain::LocalChain).
- pub local_chain: local_chain::Update,
+ /// The update to apply to the receiving [`LocalChain`](bdk_chain::local_chain::LocalChain).
+ pub local_chain: CheckPoint,
/// The update to apply to the receiving [`TxGraph`].
pub tx_graph: TxGraph<ConfirmationTimeHeightAnchor>,
}
{
let update_cps = sync_update
.local_chain
- .tip
.iter()
.map(|cp| cp.block_id())
.collect::<BTreeSet<_>>();
{
let update_cps = sync_update
.local_chain
- .tip
.iter()
.map(|cp| cp.block_id())
.collect::<BTreeSet<_>>();
let mut db = db.lock().unwrap();
let chain_changeset = chain
- .apply_update(local_chain::Update {
- tip: emission.checkpoint,
- introduce_older_blocks: false,
- })
+ .apply_update(emission.checkpoint)
.expect("must always apply as we receive blocks in order from emitter");
let graph_changeset = graph.apply_block_relevant(&emission.block, height);
db.stage((chain_changeset, graph_changeset));
let changeset = match emission {
Emission::Block(block_emission) => {
let height = block_emission.block_height();
- let chain_update = local_chain::Update {
- tip: block_emission.checkpoint,
- introduce_older_blocks: false,
- };
let chain_changeset = chain
- .apply_update(chain_update)
+ .apply_update(block_emission.checkpoint)
.expect("must always apply as we receive blocks in order from emitter");
let graph_changeset =
graph.apply_block_relevant(&block_emission.block, height);