Skip to content

Commit 1d13150

Browse files
committed
Verify Electrum transaction responses before use
Electrum confirmations must reject transaction_get responses whose body does not compute the requested txid. Otherwise a malicious server can substitute an unrelated transaction and provide matching Merkle data for the substituted body. Co-Authored-By: HAL 9000 This finding was discovered by Project Loupe
1 parent bc05d9d commit 1d13150

1 file changed

Lines changed: 30 additions & 0 deletions

File tree

lightning-transaction-sync/src/electrum.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,11 @@ impl<L: Logger> ElectrumSyncClient<L> {
277277
for txid in &sync_state.watched_transactions {
278278
match self.client.transaction_get(&txid) {
279279
Ok(tx) => {
280+
if tx.compute_txid() != *txid {
281+
log_error!(self.logger, "Retrieved transaction for txid {} doesn't match expectations. This should not happen. Please verify server integrity.", txid);
282+
return Err(InternalError::Failed);
283+
}
284+
280285
// Skip before using an arbitrary returned output to look up the
281286
// transaction's script history.
282287
if is_potentially_unsafe_merkle_leaf(&tx) {
@@ -365,6 +370,11 @@ impl<L: Logger> ElectrumSyncClient<L> {
365370

366371
match self.client.transaction_get(&txid) {
367372
Ok(tx) => {
373+
if tx.compute_txid() != txid {
374+
log_error!(self.logger, "Retrieved transaction for txid {} doesn't match expectations. This should not happen. Please verify server integrity.", txid);
375+
return Err(InternalError::Failed);
376+
}
377+
368378
let mut is_spend = false;
369379
for txin in &tx.input {
370380
let watched_outpoint =
@@ -529,3 +539,23 @@ impl<L: Logger> Filter for ElectrumSyncClient<L> {
529539
locked_queue.outputs.insert(output.outpoint.into_bitcoin_outpoint(), output);
530540
}
531541
}
542+
543+
#[cfg(test)]
544+
mod tests {
545+
#[test]
546+
fn transaction_get_responses_are_verified_at_call_sites() {
547+
let src = include_str!("electrum.rs");
548+
let watched_transaction_check = concat!("if tx.compute_", "txid() != *txid");
549+
let watched_output_spend_check = concat!("if tx.compute_", "txid() != txid");
550+
551+
assert!(
552+
src.contains(watched_transaction_check),
553+
"watched transaction_get responses must be verified against the requested txid"
554+
);
555+
assert!(
556+
src.contains(watched_output_spend_check),
557+
"watched-output spend transaction_get responses must be verified against the \
558+
requested txid"
559+
);
560+
}
561+
}

0 commit comments

Comments
 (0)