Skip to content

Commit

Permalink
fix: define and document stop_gap
Browse files Browse the repository at this point in the history
- changes the code implementation to "the maximum number of consecutive unused addresses"
  in esplora async and blocking extensions.
- renames `past_gap_limit` to `gap_limit_reached` to indicate we want to break once the gap
  limit is reached and not go further in `full_scan`, as suggested in
  bitcoindevkit#1227 (comment)
- change the tests according to the new implementation.
- add notes on what `stop_gap` means and links to convergent definition in other
  Bitcoin-related software.

Closes bitcoindevkit#1227.
  • Loading branch information
storopoli committed Feb 16, 2024
1 parent 50c549b commit 26104e7
Show file tree
Hide file tree
Showing 4 changed files with 34 additions and 12 deletions.
17 changes: 14 additions & 3 deletions crates/esplora/src/async_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,17 @@ pub trait EsploraAsyncExt {
/// The full scan for each keychain stops after a gap of `stop_gap` script pubkeys with no associated
/// transactions. `parallel_requests` specifies the max number of HTTP requests to make in
/// parallel.
///
/// ## Note
///
/// `stop_gap` is defined as "the maximum number of consecutive unused addresses".
/// For example, with `stop_gap = 3`, `full_scan` will keep searching for script pubkeys
/// until it encounters 3 consecutive script pubkeys with no associated transactions.
///
/// This follows the same approach as other Bitcoin-related software,
/// such as [Electrum](https://electrum.readthedocs.io/en/latest/faq.html#what-is-the-gap-limit),
/// [BTCPay Server](https://docs.btcpayserver.org/FAQ/Wallet/#the-gap-limit-problem),
/// and [Sparrow](https://www.sparrowwallet.com/docs/faq.html#ive-restored-my-wallet-but-some-of-my-funds-are-missing).
async fn full_scan<K: Ord + Clone + Send>(
&self,
keychain_spks: BTreeMap<
Expand Down Expand Up @@ -226,12 +237,12 @@ impl EsploraAsyncExt for esplora_client::AsyncClient {
}

let last_index = last_index.expect("Must be set since handles wasn't empty.");
let past_gap_limit = if let Some(i) = last_active_index {
let gap_limit_reached = if let Some(i) = last_active_index {
last_index > i.saturating_add(stop_gap as u32)
} else {
last_index >= stop_gap as u32
last_index + 1 >= stop_gap as u32
};
if past_gap_limit {
if gap_limit_reached {
break;
}
}
Expand Down
17 changes: 14 additions & 3 deletions crates/esplora/src/blocking_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,17 @@ pub trait EsploraExt {
/// The full scan for each keychain stops after a gap of `stop_gap` script pubkeys with no associated
/// transactions. `parallel_requests` specifies the max number of HTTP requests to make in
/// parallel.
///
/// ## Note
///
/// `stop_gap` is defined as "the maximum number of consecutive unused addresses".
/// For example, with `stop_gap = 3`, `full_scan` will keep searching for script pubkeys
/// until it encounters 3 consecutive script pubkeys with no associated transactions.
///
/// This follows the same approach as other Bitcoin-related software,
/// such as [Electrum](https://electrum.readthedocs.io/en/latest/faq.html#what-is-the-gap-limit),
/// [BTCPay Server](https://docs.btcpayserver.org/FAQ/Wallet/#the-gap-limit-problem),
/// and [Sparrow](https://www.sparrowwallet.com/docs/faq.html#ive-restored-my-wallet-but-some-of-my-funds-are-missing).
fn full_scan<K: Ord + Clone>(
&self,
keychain_spks: BTreeMap<K, impl IntoIterator<Item = (u32, ScriptBuf)>>,
Expand Down Expand Up @@ -216,12 +227,12 @@ impl EsploraExt for esplora_client::BlockingClient {
}

let last_index = last_index.expect("Must be set since handles wasn't empty.");
let past_gap_limit = if let Some(i) = last_active_index {
let gap_limit_reached = if let Some(i) = last_active_index {
last_index > i.saturating_add(stop_gap as u32)
} else {
last_index >= stop_gap as u32
last_index + 1 >= stop_gap as u32
};
if past_gap_limit {
if gap_limit_reached {
break;
}
}
Expand Down
6 changes: 3 additions & 3 deletions crates/esplora/tests/async_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,12 +186,12 @@ pub async fn test_async_update_tx_graph_gap_limit() -> anyhow::Result<()> {
sleep(Duration::from_millis(10))
}

// A scan with a gap limit of 2 won't find the transaction, but a scan with a gap limit of 3
// A scan with a gap limit of 3 won't find the transaction, but a scan with a gap limit of 4
// will.
let (graph_update, active_indices) = env.client.full_scan(keychains.clone(), 2, 1).await?;
let (graph_update, active_indices) = env.client.full_scan(keychains.clone(), 3, 1).await?;
assert!(graph_update.full_txs().next().is_none());
assert!(active_indices.is_empty());
let (graph_update, active_indices) = env.client.full_scan(keychains.clone(), 3, 1).await?;
let (graph_update, active_indices) = env.client.full_scan(keychains.clone(), 4, 1).await?;
assert_eq!(graph_update.full_txs().next().unwrap().txid, txid_4th_addr);
assert_eq!(active_indices[&0], 3);

Expand Down
6 changes: 3 additions & 3 deletions crates/esplora/tests/blocking_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,12 +214,12 @@ pub fn test_update_tx_graph_gap_limit() -> anyhow::Result<()> {
sleep(Duration::from_millis(10))
}

// A scan with a gap limit of 2 won't find the transaction, but a scan with a gap limit of 3
// A scan with a gap limit of 3 won't find the transaction, but a scan with a gap limit of 4
// will.
let (graph_update, active_indices) = env.client.full_scan(keychains.clone(), 2, 1)?;
let (graph_update, active_indices) = env.client.full_scan(keychains.clone(), 3, 1)?;
assert!(graph_update.full_txs().next().is_none());
assert!(active_indices.is_empty());
let (graph_update, active_indices) = env.client.full_scan(keychains.clone(), 3, 1)?;
let (graph_update, active_indices) = env.client.full_scan(keychains.clone(), 4, 1)?;
assert_eq!(graph_update.full_txs().next().unwrap().txid, txid_4th_addr);
assert_eq!(active_indices[&0], 3);

Expand Down

0 comments on commit 26104e7

Please sign in to comment.