chore(esplora): Clarify consistency guarantees

This commit is contained in:
LLFourn 2024-01-30 13:57:06 +11:00
parent 63fa710319
commit 216648bcfd
No known key found for this signature in database
GPG Key ID: A27093B54DA11F65
2 changed files with 24 additions and 6 deletions

View File

@ -26,6 +26,12 @@ pub trait EsploraAsyncExt {
/// ///
/// The result of this method can be applied to [`LocalChain::apply_update`]. /// The result of this method can be applied to [`LocalChain::apply_update`].
/// ///
/// ## Consistency
///
/// The chain update returned is guaranteed to be consistent as long as there is not a *large* re-org
/// during the call. The size of re-org we can tollerate is server dependent but will be at
/// least 10.
///
/// [`LocalChain`]: bdk_chain::local_chain::LocalChain /// [`LocalChain`]: bdk_chain::local_chain::LocalChain
/// [`LocalChain::tip`]: bdk_chain::local_chain::LocalChain::tip /// [`LocalChain::tip`]: bdk_chain::local_chain::LocalChain::tip
/// [`LocalChain::apply_update`]: bdk_chain::local_chain::LocalChain::apply_update /// [`LocalChain::apply_update`]: bdk_chain::local_chain::LocalChain::apply_update
@ -85,9 +91,8 @@ impl EsploraAsyncExt for esplora_client::AsyncClient {
local_tip: CheckPoint, local_tip: CheckPoint,
request_heights: impl IntoIterator<IntoIter = impl Iterator<Item = u32> + Send> + Send, request_heights: impl IntoIterator<IntoIter = impl Iterator<Item = u32> + Send> + Send,
) -> Result<local_chain::Update, Error> { ) -> Result<local_chain::Update, Error> {
// Atomically fetch latest blocks from Esplora. This way, we avoid creating an update with // Fetch latest N (server dependent) blocks from Esplora. The server guarantees these are
// an inconsistent set of blocks (assuming that a reorg depth cannot be greater than the // consistent.
// latest blocks fetched).
let mut fetched_blocks = self let mut fetched_blocks = self
.get_blocks(None) .get_blocks(None)
.await? .await?
@ -109,6 +114,10 @@ impl EsploraAsyncExt for esplora_client::AsyncClient {
} }
// only fetch what is missing // only fetch what is missing
if let btree_map::Entry::Vacant(entry) = fetched_blocks.entry(height) { if let btree_map::Entry::Vacant(entry) = fetched_blocks.entry(height) {
// ❗The return value of `get_block_hash` is not strictly guaranteed to be consistent
// with the chain at the time of `get_blocks` above (there could have been a deep
// re-org). Since `get_blocks` returns 10 (or so) blocks we are assuming that it's
// not possible to have a re-org deeper than that.
entry.insert(self.get_block_hash(height).await?); entry.insert(self.get_block_hash(height).await?);
} }
} }

View File

@ -24,6 +24,12 @@ pub trait EsploraExt {
/// ///
/// The result of this method can be applied to [`LocalChain::apply_update`]. /// The result of this method can be applied to [`LocalChain::apply_update`].
/// ///
/// ## Consistency
///
/// The chain update returned is guaranteed to be consistent as long as there is not a *large* re-org
/// during the call. The size of re-org we can tollerate is server dependent but will be at
/// least 10.
///
/// [`LocalChain`]: bdk_chain::local_chain::LocalChain /// [`LocalChain`]: bdk_chain::local_chain::LocalChain
/// [`LocalChain::tip`]: bdk_chain::local_chain::LocalChain::tip /// [`LocalChain::tip`]: bdk_chain::local_chain::LocalChain::tip
/// [`LocalChain::apply_update`]: bdk_chain::local_chain::LocalChain::apply_update /// [`LocalChain::apply_update`]: bdk_chain::local_chain::LocalChain::apply_update
@ -78,9 +84,8 @@ impl EsploraExt for esplora_client::BlockingClient {
local_tip: CheckPoint, local_tip: CheckPoint,
request_heights: impl IntoIterator<Item = u32>, request_heights: impl IntoIterator<Item = u32>,
) -> Result<local_chain::Update, Error> { ) -> Result<local_chain::Update, Error> {
// Atomically fetch latest blocks from Esplora. This way, we avoid creating an update with // Fetch latest N (server dependent) blocks from Esplora. The server guarantees these are
// an inconsistent set of blocks (assuming that a reorg depth cannot be greater than the // consistent.
// latest blocks fetched).
let mut fetched_blocks = self let mut fetched_blocks = self
.get_blocks(None)? .get_blocks(None)?
.into_iter() .into_iter()
@ -101,6 +106,10 @@ impl EsploraExt for esplora_client::BlockingClient {
} }
// only fetch what is missing // only fetch what is missing
if let btree_map::Entry::Vacant(entry) = fetched_blocks.entry(height) { if let btree_map::Entry::Vacant(entry) = fetched_blocks.entry(height) {
// ❗The return value of `get_block_hash` is not strictly guaranteed to be consistent
// with the chain at the time of `get_blocks` above (there could have been a deep
// re-org). Since `get_blocks` returns 10 (or so) blocks we are assuming that it's
// not possible to have a re-org deeper than that.
entry.insert(self.get_block_hash(height)?); entry.insert(self.get_block_hash(height)?);
} }
} }