Skip to content

Commit ba328d9

Browse files
valentinewallaceAntoine Riard
and
Antoine Riard
committed
Claim HTLC output on-chain if preimage is recv'd after force-close
If we receive a preimage for an outgoing HTLC on a force-closed channel, we need to claim the HTLC output on-chain. Co-authored-by: Antoine Riard <[email protected]> Co-authored-by: Valentine Wallace <[email protected]>
1 parent 78723db commit ba328d9

File tree

5 files changed

+223
-30
lines changed

5 files changed

+223
-30
lines changed

fuzz/src/chanmon_consistency.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ impl chain::Watch for TestChainMonitor {
128128
};
129129
let mut deserialized_monitor = <(BlockHash, channelmonitor::ChannelMonitor<EnforcingChannelKeys>)>::
130130
read(&mut Cursor::new(&map_entry.get().1)).unwrap().1;
131-
deserialized_monitor.update_monitor(&update, &&TestBroadcaster {}, &self.logger).unwrap();
131+
deserialized_monitor.update_monitor(&update, &&TestBroadcaster{}, &&FuzzEstimator{}, &self.logger).unwrap();
132132
let mut ser = VecWriter(Vec::new());
133133
deserialized_monitor.serialize_for_disk(&mut ser).unwrap();
134134
map_entry.insert((update.update_id, ser.0));

lightning/src/chain/chainmonitor.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ where C::Target: chain::Filter,
198198
},
199199
Some(orig_monitor) => {
200200
log_trace!(self.logger, "Updating Channel Monitor for channel {}", log_funding_info!(orig_monitor));
201-
let update_res = orig_monitor.update_monitor(&update, &self.broadcaster, &self.logger);
201+
let update_res = orig_monitor.update_monitor(&update, &self.broadcaster, &self.fee_estimator, &self.logger);
202202
if let Err(e) = &update_res {
203203
log_error!(self.logger, "Failed to update channel monitor: {:?}", e);
204204
}

lightning/src/chain/channelmonitor.rs

Lines changed: 71 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1150,6 +1150,25 @@ impl<ChanSigner: ChannelKeys> ChannelMonitor<ChanSigner> {
11501150
L::Target: Logger,
11511151
{
11521152
self.payment_preimages.insert(payment_hash.clone(), payment_preimage.clone());
1153+
// If the channel is force closed, try to claim the output from this preimage
1154+
if self.lockdown_from_offchain || self.holder_tx_signed {
1155+
if let Some(txid) = self.current_counterparty_commitment_txid {
1156+
if let Some(commitment_number) = self.counterparty_commitment_txn_on_chain.get(&txid) {
1157+
let (htlc_claim_reqs, set_script) = self.get_counterparty_htlc_output_claim_reqs(*commitment_number, txid, None);
1158+
if set_script {
1159+
self.counterparty_payment_script = {
1160+
// Note that the Network here is ignored as we immediately drop the address for the
1161+
// script_pubkey version
1162+
let payment_hash160 = WPubkeyHash::hash(&self.keys.pubkeys().payment_point.serialize());
1163+
Builder::new().push_opcode(opcodes::all::OP_PUSHBYTES_0).push_slice(&payment_hash160[..]).into_script()
1164+
};
1165+
}
1166+
for req in htlc_claim_reqs {
1167+
self.onchain_tx_handler.claim_counterparty_htlc(req, broadcaster, fee_estimator, logger);
1168+
}
1169+
}
1170+
}
1171+
}
11531172
}
11541173

11551174
pub(crate) fn broadcast_latest_holder_commitment_txn<B: Deref, L: Deref>(&mut self, broadcaster: &B, logger: &L)
@@ -1166,11 +1185,16 @@ impl<ChanSigner: ChannelKeys> ChannelMonitor<ChanSigner> {
11661185
/// itself.
11671186
///
11681187
/// panics if the given update is not the next update by update_id.
1169-
pub fn update_monitor<B: Deref, L: Deref>(&mut self, updates: &ChannelMonitorUpdate, broadcaster: &B, logger: &L) -> Result<(), MonitorUpdateError>
1170-
where B::Target: BroadcasterInterface,
1171-
L::Target: Logger,
1188+
pub fn update_monitor<B: Deref, F: Deref, L: Deref>(&mut self, updates: &ChannelMonitorUpdate, broadcaster: &B, fee_estimator: &F, logger: &L) -> Result<(), MonitorUpdateError>
1189+
where B::Target: BroadcasterInterface,
1190+
F::Target: FeeEstimator,
1191+
L::Target: Logger,
11721192
{
1173-
if self.latest_update_id + 1 != updates.update_id {
1193+
// ChannelMonitor updates may be applied after force close if we receive a
1194+
// preimage for a broadcasted counterparty HTLC output that we'd like to
1195+
// claim on-chain. If this is the case, we no longer have guaranteed access
1196+
// to the monitor's update ID, so we use a sentinel value instead.
1197+
if updates.update_id != std::u64::MAX && self.latest_update_id + 1 != updates.update_id {
11741198
panic!("Attempted to apply ChannelMonitorUpdates out of order, check the update_id before passing an update to update_monitor!");
11751199
}
11761200
for update in updates.updates.iter() {
@@ -1438,39 +1462,61 @@ impl<ChanSigner: ChannelKeys> ChannelMonitor<ChanSigner> {
14381462
check_htlc_fails!(txid, "previous", 'prev_loop);
14391463
}
14401464

1465+
let (htlc_claim_reqs, set_script) = self.get_counterparty_htlc_output_claim_reqs(commitment_number, commitment_txid, Some(tx));
1466+
if set_script {
1467+
self.counterparty_payment_script = {
1468+
// Note that the Network here is ignored as we immediately drop the address for the
1469+
// script_pubkey version
1470+
let payment_hash160 = WPubkeyHash::hash(&self.keys.pubkeys().payment_point.serialize());
1471+
Builder::new().push_opcode(opcodes::all::OP_PUSHBYTES_0).push_slice(&payment_hash160[..]).into_script()
1472+
};
1473+
}
1474+
for req in htlc_claim_reqs {
1475+
claimable_outpoints.push(req);
1476+
}
1477+
1478+
}
1479+
(claimable_outpoints, (commitment_txid, watch_outputs))
1480+
}
1481+
1482+
fn get_counterparty_htlc_output_claim_reqs(&self, commitment_number: u64, commitment_txid: Txid, tx: Option<&Transaction>) -> (Vec<ClaimRequest>, bool) {
1483+
let mut claims = Vec::new();
1484+
let mut set_counterparty_payment_script = false;
1485+
if let Some(htlc_outputs) = self.counterparty_claimable_outpoints.get(&commitment_txid) {
14411486
if let Some(revocation_points) = self.their_cur_revocation_points {
14421487
let revocation_point_option =
14431488
if revocation_points.0 == commitment_number { Some(&revocation_points.1) }
1444-
else if let Some(point) = revocation_points.2.as_ref() {
1445-
if revocation_points.0 == commitment_number + 1 { Some(point) } else { None }
1446-
} else { None };
1489+
else if let Some(point) = revocation_points.2.as_ref() {
1490+
if revocation_points.0 == commitment_number + 1 { Some(point) } else { None }
1491+
} else { None };
14471492
if let Some(revocation_point) = revocation_point_option {
1448-
self.counterparty_payment_script = {
1449-
// Note that the Network here is ignored as we immediately drop the address for the
1450-
// script_pubkey version
1451-
let payment_hash160 = WPubkeyHash::hash(&self.keys.pubkeys().payment_point.serialize());
1452-
Builder::new().push_opcode(opcodes::all::OP_PUSHBYTES_0).push_slice(&payment_hash160[..]).into_script()
1453-
};
1493+
set_counterparty_payment_script = true;
14541494

14551495
// Then, try to find htlc outputs
1456-
for (_, &(ref htlc, _)) in per_commitment_data.iter().enumerate() {
1496+
for (_, &(ref htlc, _)) in htlc_outputs.iter().enumerate() {
14571497
if let Some(transaction_output_index) = htlc.transaction_output_index {
1458-
if transaction_output_index as usize >= tx.output.len() ||
1459-
tx.output[transaction_output_index as usize].value != htlc.amount_msat / 1000 {
1460-
return (claimable_outpoints, (commitment_txid, watch_outputs)); // Corrupted per_commitment_data, fuck this user
1498+
if let Some(transaction) = tx {
1499+
if transaction_output_index as usize >= transaction.output.len() ||
1500+
transaction.output[transaction_output_index as usize].value != htlc.amount_msat / 1000 {
1501+
return (claims, set_counterparty_payment_script) // Corrupted per_commitment_data, fuck this user
1502+
}
14611503
}
1462-
let preimage = if htlc.offered { if let Some(p) = self.payment_preimages.get(&htlc.payment_hash) { Some(*p) } else { None } } else { None };
1504+
let preimage = if htlc.offered {
1505+
if let Some(p) = self.payment_preimages.get(&htlc.payment_hash) {
1506+
Some(*p)
1507+
} else { None }
1508+
} else { None };
14631509
let aggregable = if !htlc.offered { false } else { true };
14641510
if preimage.is_some() || !htlc.offered {
14651511
let witness_data = InputMaterial::CounterpartyHTLC { per_commitment_point: *revocation_point, counterparty_delayed_payment_base_key: self.counterparty_tx_cache.counterparty_delayed_payment_base_key, counterparty_htlc_base_key: self.counterparty_tx_cache.counterparty_htlc_base_key, preimage, htlc: htlc.clone() };
1466-
claimable_outpoints.push(ClaimRequest { absolute_timelock: htlc.cltv_expiry, aggregable, outpoint: BitcoinOutPoint { txid: commitment_txid, vout: transaction_output_index }, witness_data });
1512+
claims.push(ClaimRequest { absolute_timelock: htlc.cltv_expiry, aggregable, outpoint: BitcoinOutPoint { txid: commitment_txid, vout: transaction_output_index }, witness_data });
14671513
}
14681514
}
14691515
}
14701516
}
14711517
}
14721518
}
1473-
(claimable_outpoints, (commitment_txid, watch_outputs))
1519+
(claims, set_counterparty_payment_script)
14741520
}
14751521

14761522
/// Attempts to claim a counterparty HTLC-Success/HTLC-Timeout's outputs using the revocation key
@@ -2499,16 +2545,18 @@ mod tests {
24992545
use ln::onchaintx::{OnchainTxHandler, InputDescriptors};
25002546
use ln::chan_utils;
25012547
use ln::chan_utils::{HTLCOutputInCommitment, HolderCommitmentTransaction};
2502-
use util::test_utils::TestLogger;
2548+
use util::test_utils::{TestLogger, TestBroadcaster, TestFeeEstimator};
25032549
use bitcoin::secp256k1::key::{SecretKey,PublicKey};
25042550
use bitcoin::secp256k1::Secp256k1;
2505-
use std::sync::Arc;
2551+
use std::sync::{Arc, Mutex};
25062552
use chain::keysinterface::InMemoryChannelKeys;
25072553

25082554
#[test]
25092555
fn test_prune_preimages() {
25102556
let secp_ctx = Secp256k1::new();
25112557
let logger = Arc::new(TestLogger::new());
2558+
let broadcaster = Arc::new(TestBroadcaster{txn_broadcasted: Mutex::new(Vec::new())});
2559+
let fee_estimator = Arc::new(TestFeeEstimator { sat_per_kw: 253 });
25122560

25132561
let dummy_key = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
25142562
let dummy_tx = Transaction { version: 0, lock_time: 0, input: Vec::new(), output: Vec::new() };
@@ -2584,7 +2632,7 @@ mod tests {
25842632
monitor.provide_latest_counterparty_commitment_tx_info(&dummy_tx, preimages_slice_to_htlc_outputs!(preimages[17..20]), 281474976710653, dummy_key, &logger);
25852633
monitor.provide_latest_counterparty_commitment_tx_info(&dummy_tx, preimages_slice_to_htlc_outputs!(preimages[18..20]), 281474976710652, dummy_key, &logger);
25862634
for &(ref preimage, ref hash) in preimages.iter() {
2587-
monitor.provide_payment_preimage(hash, preimage);
2635+
monitor.provide_payment_preimage(hash, preimage, &broadcaster, &fee_estimator, &logger);
25882636
}
25892637

25902638
// Now provide a secret, pruning preimages 10-15

lightning/src/ln/functional_tests.rs

Lines changed: 117 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3523,7 +3523,7 @@ fn test_force_close_fail_back() {
35233523
{
35243524
let mut monitors = nodes[2].chain_monitor.chain_monitor.monitors.lock().unwrap();
35253525
monitors.get_mut(&OutPoint{ txid: Txid::from_slice(&payment_event.commitment_msg.channel_id[..]).unwrap(), index: 0 }).unwrap()
3526-
.provide_payment_preimage(&our_payment_hash, &our_payment_preimage);
3526+
.provide_payment_preimage(&our_payment_hash, &our_payment_preimage, &node_cfgs[2].tx_broadcaster, &node_cfgs[2].fee_estimator, &&logger);
35273527
}
35283528
connect_block(&nodes[2], &block, 1);
35293529
let node_txn = nodes[2].tx_broadcaster.txn_broadcasted.lock().unwrap();
@@ -8503,3 +8503,119 @@ fn test_htlc_no_detection() {
85038503
connect_blocks(&nodes[0], ANTI_REORG_DELAY - 1, 201, true, header_201.block_hash());
85048504
expect_payment_failed!(nodes[0], our_payment_hash, true);
85058505
}
8506+
8507+
#[test]
8508+
fn test_unordered_onchain_htlc_settlement() {
8509+
// If we route an HTLC, then learn the HTLC's preimage after the upstream
8510+
// channel has been force-closed by our counterparty, we must claim that HTLC
8511+
// on-chain. (Given an HTLC forwarded from Alice --> Bob --> Carol, Alice
8512+
// would be the upstream node, and Carol the downstream.)
8513+
//
8514+
// Steps of the test:
8515+
// 1) Alice sends a HTLC to Carol through Bob.
8516+
// 2) Carol doesn't settle the HTLC.
8517+
// 3) Alice force-closes her channel with Bob.
8518+
// 4) Bob sees the Alice's commitment on his chain. An offered output is present but can't
8519+
// be claimed as Bob doesn't have yet knowledge of the preimage.
8520+
// 5) Carol release the preimage to Bob off-chain.
8521+
// 6) Bob claims the offered output on Alice's commitment.
8522+
// TODO: reverse the test with Bob's commitment reaching the chain instead of Alice's one.
8523+
8524+
let chanmon_cfgs = create_chanmon_cfgs(3);
8525+
let node_cfgs = create_node_cfgs(3, &chanmon_cfgs);
8526+
let node_chanmgrs = create_node_chanmgrs(3, &node_cfgs, &[None, None, None]);
8527+
let nodes = create_network(3, &node_cfgs, &node_chanmgrs);
8528+
8529+
// Create some initial channels
8530+
let chan_ab = create_announced_chan_between_nodes_with_value(&nodes, 0, 1, 100000, 10001, InitFeatures::known(), InitFeatures::known());
8531+
create_announced_chan_between_nodes_with_value(&nodes, 1, 2, 100000, 10001, InitFeatures::known(), InitFeatures::known());
8532+
8533+
// Steps (1) and (2):
8534+
// Send an HTLC Alice --> Bob --> Carol, but Carol doesn't settle the HTLC back.
8535+
let (payment_preimage, _payment_hash) = route_payment(&nodes[0], &vec!(&nodes[1], &nodes[2]), 3_000_000);
8536+
8537+
// Check that Alice's commitment transaction now contains an output for this HTLC.
8538+
let alice_txn = get_local_commitment_txn!(nodes[0], chan_ab.2);
8539+
check_spends!(alice_txn[0], chan_ab.3);
8540+
assert_eq!(alice_txn[0].output.len(), 2);
8541+
check_spends!(alice_txn[1], alice_txn[0]); // 2nd transaction is a non-final HTLC-timeout
8542+
assert_eq!(alice_txn[1].input[0].witness.last().unwrap().len(), OFFERED_HTLC_SCRIPT_WEIGHT);
8543+
assert_eq!(alice_txn.len(), 2);
8544+
8545+
// Steps (3) and (4):
8546+
// Broadcast Alice's commitment transaction and check that Bob responds by (1) broadcasting
8547+
// a channel update and (2) adding a new ChannelMonitor.
8548+
let header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42};
8549+
connect_block(&nodes[1], &Block { header, txdata: vec![alice_txn[0].clone()]}, 1);
8550+
check_closed_broadcast!(nodes[1], false);
8551+
check_added_monitors!(nodes[1], 1);
8552+
{
8553+
let mut bob_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap(); // ChannelManager : 1 (commitment tx)
8554+
assert_eq!(bob_txn.len(), 1);
8555+
check_spends!(bob_txn[0], chan_ab.3);
8556+
bob_txn.clear();
8557+
}
8558+
8559+
// Step (5):
8560+
// Carol then claims the funds and sends an update_fulfill message to Bob, and they go
8561+
// through the process of removing the HTLC from their commitment transactions.
8562+
assert!(nodes[2].node.claim_funds(payment_preimage, &None, 3_000_000));
8563+
check_added_monitors!(nodes[2], 1);
8564+
let carol_updates = get_htlc_update_msgs!(nodes[2], nodes[1].node.get_our_node_id());
8565+
assert!(carol_updates.update_add_htlcs.is_empty());
8566+
assert!(carol_updates.update_fail_htlcs.is_empty());
8567+
assert!(carol_updates.update_fail_malformed_htlcs.is_empty());
8568+
assert!(carol_updates.update_fee.is_none());
8569+
assert_eq!(carol_updates.update_fulfill_htlcs.len(), 1);
8570+
8571+
nodes[1].node.handle_update_fulfill_htlc(&nodes[2].node.get_our_node_id(), &carol_updates.update_fulfill_htlcs[0]);
8572+
nodes[1].node.handle_commitment_signed(&nodes[2].node.get_our_node_id(), &carol_updates.commitment_signed);
8573+
// One monitor update for the preimage, one monitor update for the new commitment tx info
8574+
check_added_monitors!(nodes[1], 2);
8575+
8576+
let events = nodes[1].node.get_and_clear_pending_msg_events();
8577+
assert_eq!(events.len(), 2);
8578+
let bob_revocation = match events[0] {
8579+
MessageSendEvent::SendRevokeAndACK { ref node_id, ref msg } => {
8580+
assert_eq!(*node_id, nodes[2].node.get_our_node_id());
8581+
(*msg).clone()
8582+
},
8583+
_ => panic!("Unexpected event"),
8584+
};
8585+
let bob_updates = match events[1] {
8586+
MessageSendEvent::UpdateHTLCs { ref node_id, ref updates } => {
8587+
assert_eq!(*node_id, nodes[2].node.get_our_node_id());
8588+
(*updates).clone()
8589+
},
8590+
_ => panic!("Unexpected event"),
8591+
};
8592+
8593+
nodes[2].node.handle_revoke_and_ack(&nodes[1].node.get_our_node_id(), &bob_revocation);
8594+
check_added_monitors!(nodes[2], 1);
8595+
nodes[2].node.handle_commitment_signed(&nodes[1].node.get_our_node_id(), &bob_updates.commitment_signed);
8596+
check_added_monitors!(nodes[2], 1);
8597+
8598+
let events = nodes[2].node.get_and_clear_pending_msg_events();
8599+
assert_eq!(events.len(), 1);
8600+
let carol_revocation = match events[0] {
8601+
MessageSendEvent::SendRevokeAndACK { ref node_id, ref msg } => {
8602+
assert_eq!(*node_id, nodes[1].node.get_our_node_id());
8603+
(*msg).clone()
8604+
},
8605+
_ => panic!("Unexpected event"),
8606+
};
8607+
nodes[1].node.handle_revoke_and_ack(&nodes[2].node.get_our_node_id(), &carol_revocation);
8608+
check_added_monitors!(nodes[1], 1);
8609+
8610+
let header = BlockHeader { version: 0x20000000, prev_blockhash: Default::default(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
8611+
connect_block(&nodes[1], &Block { header, txdata: vec![] }, 1);
8612+
// Step (6):
8613+
// Finally, check that Bob broadcasted a preimage-claiming transaction for the HTLC
8614+
// output on Alice's broadcasted commitment transaction.
8615+
{
8616+
let bob_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap().clone(); // ChannelMonitor : 1 (htlc-preimage tx)
8617+
assert_eq!(bob_txn.len(), 1);
8618+
check_spends!(bob_txn[0], alice_txn[0]);
8619+
assert_eq!(bob_txn[0].input[0].witness.last().unwrap().len(), OFFERED_HTLC_SCRIPT_WEIGHT);
8620+
}
8621+
}

0 commit comments

Comments
 (0)