Skip to content

Commit 34818ca

Browse files
committed
Fail all pending HTLCs if the remote broadcasts a revoked tx
1 parent 264d065 commit 34818ca

File tree

1 file changed

+72
-8
lines changed

1 file changed

+72
-8
lines changed

src/ln/channelmonitor.rs

Lines changed: 72 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,8 @@ enum Storage {
306306
prev_latest_per_commitment_point: Option<PublicKey>,
307307
latest_per_commitment_point: Option<PublicKey>,
308308
funding_info: Option<(OutPoint, Script)>,
309+
current_remote_commitment_txid: Option<Sha256dHash>,
310+
prev_remote_commitment_txid: Option<Sha256dHash>,
309311
},
310312
Watchtower {
311313
revocation_base_key: PublicKey,
@@ -433,6 +435,8 @@ impl ChannelMonitor {
433435
prev_latest_per_commitment_point: None,
434436
latest_per_commitment_point: None,
435437
funding_info: None,
438+
current_remote_commitment_txid: None,
439+
prev_remote_commitment_txid: None,
436440
},
437441
their_htlc_base_key: None,
438442
their_delayed_payment_base_key: None,
@@ -495,8 +499,24 @@ impl ChannelMonitor {
495499
return Err(MonitorUpdateError("Previous secret did not match new one"));
496500
}
497501
}
502+
if self.get_min_seen_secret() <= idx {
503+
return Ok(());
504+
}
498505
self.old_secrets[pos as usize] = (secret, idx);
499506

507+
// Prune HTLCs from the previous remote commitment tx so we don't generate failure/fulfill
508+
// events for now-revoked/fulfilled HTLCs.
509+
// TODO: We should probably consider whether we're really getting the next secret here.
510+
if let Storage::Local { ref mut prev_remote_commitment_txid, .. } = self.key_storage {
511+
if let Some(txid) = prev_remote_commitment_txid.take() {
512+
for &mut (_, ref mut htlc_data) in self.remote_claimable_outpoints.get_mut(&txid).unwrap() {
513+
// This potentially frees a Route-contained Vec, but ideally we'd convert the
514+
// HTLC data to an Option<Box<>> to make this free a bunch more memory
515+
htlc_data.take();
516+
}
517+
}
518+
}
519+
500520
if !self.payment_preimages.is_empty() {
501521
let local_signed_commitment_tx = self.current_local_signed_commitment_tx.as_ref().expect("Channel needs at least an initial commitment tx !");
502522
let prev_local_signed_commitment_tx = self.prev_local_signed_commitment_tx.as_ref();
@@ -544,14 +564,13 @@ impl ChannelMonitor {
544564
for &(ref htlc, _) in &htlc_outputs {
545565
self.remote_hash_commitment_number.insert(htlc.payment_hash, commitment_number);
546566
}
547-
// We prune old claimable outpoints, useless to pass backward state when remote commitment
548-
// tx get revoked, optimize for storage
549-
for (_, htlc_data) in self.remote_claimable_outpoints.iter_mut() {
550-
for &mut(_, ref mut source) in htlc_data.iter_mut() {
551-
source.take();
552-
}
567+
568+
let new_txid = unsigned_commitment_tx.txid();
569+
if let Storage::Local { ref mut current_remote_commitment_txid, ref mut prev_remote_commitment_txid, .. } = self.key_storage {
570+
*prev_remote_commitment_txid = current_remote_commitment_txid.take();
571+
*current_remote_commitment_txid = Some(new_txid);
553572
}
554-
self.remote_claimable_outpoints.insert(unsigned_commitment_tx.txid(), htlc_outputs);
573+
self.remote_claimable_outpoints.insert(new_txid, htlc_outputs);
555574
self.current_remote_commitment_number = commitment_number;
556575
//TODO: Merge this into the other per-remote-transaction output storage stuff
557576
match self.their_cur_revocation_points {
@@ -753,7 +772,7 @@ impl ChannelMonitor {
753772
U48(self.commitment_transaction_number_obscure_factor).write(writer)?;
754773

755774
match self.key_storage {
756-
Storage::Local { ref revocation_base_key, ref htlc_base_key, ref delayed_payment_base_key, ref payment_base_key, ref shutdown_pubkey, ref prev_latest_per_commitment_point, ref latest_per_commitment_point, ref funding_info } => {
775+
Storage::Local { ref revocation_base_key, ref htlc_base_key, ref delayed_payment_base_key, ref payment_base_key, ref shutdown_pubkey, ref prev_latest_per_commitment_point, ref latest_per_commitment_point, ref funding_info, current_remote_commitment_txid, prev_remote_commitment_txid } => {
757776
writer.write_all(&[0; 1])?;
758777
writer.write_all(&revocation_base_key[..])?;
759778
writer.write_all(&htlc_base_key[..])?;
@@ -782,6 +801,18 @@ impl ChannelMonitor {
782801
debug_assert!(false, "Try to serialize a useless Local monitor !");
783802
},
784803
}
804+
if let Some(ref txid) = current_remote_commitment_txid {
805+
writer.write_all(&[1; 1])?;
806+
writer.write_all(&txid[..])?;
807+
} else {
808+
writer.write_all(&[0; 1])?;
809+
}
810+
if let Some(ref txid) = prev_remote_commitment_txid {
811+
writer.write_all(&[1; 1])?;
812+
writer.write_all(&txid[..])?;
813+
} else {
814+
writer.write_all(&[0; 1])?;
815+
}
785816
},
786817
Storage::Watchtower { .. } => unimplemented!(),
787818
}
@@ -1169,6 +1200,27 @@ impl ChannelMonitor {
11691200
output: spend_tx.output[0].clone(),
11701201
});
11711202
txn_to_broadcast.push(spend_tx);
1203+
1204+
// TODO: We really should only fail backwards after our revocation claims have been
1205+
// confirmed, but we also need to do more other tracking of in-flight pre-confirm
1206+
// on-chain claims, so we can do that at the same time.
1207+
if let Storage::Local { ref current_remote_commitment_txid, ref prev_remote_commitment_txid, .. } = self.key_storage {
1208+
if let &Some(ref txid) = current_remote_commitment_txid {
1209+
for &(ref htlc, ref source_option) in self.remote_claimable_outpoints.get(&txid).unwrap() {
1210+
if let Some(ref source) = source_option.as_ref() {
1211+
htlc_updated.push(((*source).clone(), None, htlc.payment_hash.clone()));
1212+
}
1213+
}
1214+
}
1215+
if let &Some(ref txid) = prev_remote_commitment_txid {
1216+
for &(ref htlc, ref source_option) in self.remote_claimable_outpoints.get(&txid).unwrap() {
1217+
if let Some(ref source) = source_option.as_ref() {
1218+
htlc_updated.push(((*source).clone(), None, htlc.payment_hash.clone()));
1219+
}
1220+
}
1221+
}
1222+
}
1223+
// No need to check local commitment txn, symmetric HTLCSource must be present as per-htlc data on remote commitment tx
11721224
} else if let Some(per_commitment_data) = per_commitment_option {
11731225
// While this isn't useful yet, there is a potential race where if a counterparty
11741226
// revokes a state at the same time as the commitment transaction for that state is
@@ -1809,6 +1861,16 @@ impl<R: ::std::io::Read> ReadableArgs<R, Arc<Logger>> for (Sha256dHash, ChannelM
18091861
index: Readable::read(reader)?,
18101862
};
18111863
let funding_info = Some((outpoint, Readable::read(reader)?));
1864+
let current_remote_commitment_txid = match <u8 as Readable<R>>::read(reader)? {
1865+
0 => None,
1866+
1 => Some(Readable::read(reader)?),
1867+
_ => return Err(DecodeError::InvalidValue),
1868+
};
1869+
let prev_remote_commitment_txid = match <u8 as Readable<R>>::read(reader)? {
1870+
0 => None,
1871+
1 => Some(Readable::read(reader)?),
1872+
_ => return Err(DecodeError::InvalidValue),
1873+
};
18121874
Storage::Local {
18131875
revocation_base_key,
18141876
htlc_base_key,
@@ -1818,6 +1880,8 @@ impl<R: ::std::io::Read> ReadableArgs<R, Arc<Logger>> for (Sha256dHash, ChannelM
18181880
prev_latest_per_commitment_point,
18191881
latest_per_commitment_point,
18201882
funding_info,
1883+
current_remote_commitment_txid,
1884+
prev_remote_commitment_txid,
18211885
}
18221886
},
18231887
_ => return Err(DecodeError::InvalidValue),

0 commit comments

Comments
 (0)