Skip to content

Commit

Permalink
refactor(electrum): implement merkle proofs WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
LagginTimes committed Jun 17, 2024
1 parent 5ad369d commit a2a955f
Showing 1 changed file with 76 additions and 65 deletions.
141 changes: 76 additions & 65 deletions crates/electrum/src/bdk_electrum_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,76 +99,66 @@ impl<E: ElectrumApi> BdkElectrumClient<E> {
// * val: (script_pubkey, has_tx_history).
let mut scanned_spks = BTreeMap::<(K, u32), (ScriptBuf, bool)>::new();

let update = loop {
let (tip, _) = construct_update_tip(&self.inner, request.chain_tip.clone())?;
let mut graph_update = TxGraph::<ConfirmationHeightAnchor>::default();
let cps = tip
.iter()
.take(10)
.map(|cp| (cp.height(), cp))
.collect::<BTreeMap<u32, CheckPoint<BlockId>>>();

if !request_spks.is_empty() {
if !scanned_spks.is_empty() {
scanned_spks.append(
&mut self.populate_with_spks(
&cps,
&mut graph_update,
&mut scanned_spks
.iter()
.map(|(i, (spk, _))| (i.clone(), spk.clone())),
stop_gap,
batch_size,
)?,
);
}
for (keychain, keychain_spks) in &mut request_spks {
scanned_spks.extend(
self.populate_with_spks(
&cps,
&mut graph_update,
keychain_spks,
stop_gap,
batch_size,
)?
.into_iter()
.map(|(spk_i, spk)| ((keychain.clone(), spk_i), spk)),
);
}
}
let (tip, _) = construct_update_tip(&self.inner, request.chain_tip.clone())?;
let mut graph_update = TxGraph::<ConfirmationHeightAnchor>::default();
let cps = tip
.iter()
.take(10)
.map(|cp| (cp.height(), cp))
.collect::<BTreeMap<u32, CheckPoint<BlockId>>>();

// check for reorgs during scan process
let server_blockhash = self.inner.block_header(tip.height() as usize)?.block_hash();
if tip.hash() != server_blockhash {
continue; // reorg
if !request_spks.is_empty() {
if !scanned_spks.is_empty() {
scanned_spks.append(
&mut self.populate_with_spks(
&cps,
&mut graph_update,
&mut scanned_spks
.iter()
.map(|(i, (spk, _))| (i.clone(), spk.clone())),
stop_gap,
batch_size,
)?,
);
}

// Fetch previous `TxOut`s for fee calculation if flag is enabled.
if fetch_prev_txouts {
self.fetch_prev_txout(&mut graph_update)?;
for (keychain, keychain_spks) in &mut request_spks {
scanned_spks.extend(
self.populate_with_spks(
&cps,
&mut graph_update,
keychain_spks,
stop_gap,
batch_size,
)?
.into_iter()
.map(|(spk_i, spk)| ((keychain.clone(), spk_i), spk)),
);
}
}

let chain_update = tip;
// Fetch previous `TxOut`s for fee calculation if flag is enabled.
if fetch_prev_txouts {
self.fetch_prev_txout(&mut graph_update)?;
}

let keychain_update = request_spks
.into_keys()
.filter_map(|k| {
scanned_spks
.range((k.clone(), u32::MIN)..=(k.clone(), u32::MAX))
.rev()
.find(|(_, (_, active))| *active)
.map(|((_, i), _)| (k, *i))
})
.collect::<BTreeMap<_, _>>();
let chain_update = tip;

break FullScanResult {
graph_update,
chain_update,
last_active_indices: keychain_update,
};
};
let keychain_update = request_spks
.into_keys()
.filter_map(|k| {
scanned_spks
.range((k.clone(), u32::MIN)..=(k.clone(), u32::MAX))
.rev()
.find(|(_, (_, active))| *active)
.map(|((_, i), _)| (k, *i))
})
.collect::<BTreeMap<_, _>>();

Ok(ElectrumFullScanResult(update))
Ok(ElectrumFullScanResult(FullScanResult {
graph_update,
chain_update,
last_active_indices: keychain_update,
}))
}

/// Sync a set of scripts with the blockchain (via an Electrum client) for the data specified
Expand Down Expand Up @@ -263,8 +253,29 @@ impl<E: ElectrumApi> BdkElectrumClient<E> {

for tx_res in spk_history {
let _ = graph_update.insert_tx(self.fetch_tx(tx_res.tx_hash)?);
if let Some(anchor) = determine_tx_anchor(cps, tx_res.height, tx_res.tx_hash) {
let _ = graph_update.insert_anchor(tx_res.tx_hash, anchor);

// We want to validate that the tx is in a confirmed block before inserting
// an anchor by checking the merkle proof.
if let Ok(merkle_res) = self
.inner
.transaction_get_merkle(&tx_res.tx_hash, tx_res.height as usize)
{
let header = self.inner.block_header(merkle_res.block_height)?;
let is_confirmed_tx = electrum_client::utils::validate_merkle_proof(
&tx_res.tx_hash,
&header.merkle_root,
&merkle_res,
);

if is_confirmed_tx {
if let Some(anchor) = determine_tx_anchor(
cps,
merkle_res.block_height as i32,
tx_res.tx_hash,
) {
let _ = graph_update.insert_anchor(tx_res.tx_hash, anchor);
}
}
}
}
}
Expand Down

0 comments on commit a2a955f

Please sign in to comment.