Skip to content

Commit 59b1445

Browse files
author
Antoine Riard
committed
Implement timeout tx claiming on remote received HTLC outputs
Harden htlc_on_chain_timeout test in consequence
1 parent d7f04d8 commit 59b1445

File tree

2 files changed

+60
-4
lines changed

2 files changed

+60
-4
lines changed

src/ln/channelmanager.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3632,13 +3632,23 @@ mod tests {
36323632
Event::BroadcastChannelUpdate { msg: msgs::ChannelUpdate { .. } } => {},
36333633
_ => panic!("Unexpected event"),
36343634
}
3635-
let node_txn = nodes[2].tx_broadcaster.txn_broadcasted.lock().unwrap().clone();
3635+
let mut funding_tx_map = HashMap::new();
3636+
funding_tx_map.insert(chan_2.3.txid(), chan_2.3.clone());
3637+
commitment_tx[0].verify(&funding_tx_map).unwrap();
36363638

36373639
// Broadcast timeout transaction by B on received output fron C's commitment tx on B's chain
36383640
// Verify that B's ChannelManager is able to detect that HTLC is timeout by its own tx and react backward in consequence
3639-
nodes[1].chain_monitor.block_connected_with_filtering(&Block { header, txdata: node_txn}, 200);
3640-
let node_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap().clone(); //TODO: shouldn't be a HTLC-Timeout but actually that's one... => implement lacking parts in check_spend_remote_transaction
3641-
nodes[1].chain_monitor.block_connected_with_filtering(&Block { header, txdata: node_txn}, 1);
3641+
nodes[1].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![commitment_tx[0].clone()]}, 200);
3642+
let node_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap().clone();
3643+
assert_eq!(node_txn.len(), 8); // ChannelManager : 2 (commitment tx, HTLC-Timeout), ChannelMonitor : 6 (commitment tx, HTLC-Timeout, timeout tx) * 2 (block-rescan)
3644+
assert_eq!(node_txn[2].input[0].previous_output.txid, node_txn[1].txid());
3645+
assert_eq!(node_txn[2].clone().input[0].witness.last().unwrap().len(), 133);
3646+
3647+
let mut commitment_tx_map = HashMap::new();
3648+
commitment_tx_map.insert(commitment_tx[0].txid(), commitment_tx[0].clone());
3649+
node_txn[0].verify(&commitment_tx_map).unwrap();
3650+
3651+
nodes[1].chain_monitor.block_connected_with_filtering(&Block { header, txdata: vec![node_txn[0].clone()]}, 1);
36423652
let events = nodes[1].node.get_and_clear_pending_events();
36433653
assert_eq!(events.len(), 2);
36443654
match events[0] {

src/ln/channelmonitor.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -939,6 +939,29 @@ impl ChannelMonitor {
939939
}
940940
}
941941

942+
macro_rules! sign_input_timeout {
943+
($sighash_parts: expr, $input: expr, $amount: expr) => {
944+
{
945+
let (sig, redeemscript) = match self.key_storage {
946+
KeyStorage::PrivMode { ref htlc_base_key, .. } => {
947+
let htlc = &per_commitment_option.unwrap()[$input.sequence as usize];
948+
let redeemscript = chan_utils::get_htlc_redeemscript_with_explicit_keys(htlc, &a_htlc_key, &b_htlc_key, &revocation_pubkey);
949+
let sighash = ignore_error!(Message::from_slice(&$sighash_parts.sighash_all(&$input, &redeemscript, $amount)[..]));
950+
let htlc_key = ignore_error!(chan_utils::derive_private_key(&self.secp_ctx, revocation_point, &htlc_base_key));
951+
(self.secp_ctx.sign(&sighash, &htlc_key), redeemscript)
952+
},
953+
KeyStorage::SigsMode { .. } => {
954+
unimplemented!();
955+
}
956+
};
957+
$input.witness.push(sig.serialize_der(&self.secp_ctx).to_vec());
958+
$input.witness[0].push(SigHashType::All as u8);
959+
$input.witness.push(vec![0]);
960+
$input.witness.push(redeemscript.into_bytes());
961+
}
962+
}
963+
}
964+
942965
for (idx, htlc) in per_commitment_data.iter().enumerate() {
943966
if let Some(payment_preimage) = self.payment_preimages.get(&htlc.payment_hash) {
944967
let input = TxIn {
@@ -969,6 +992,29 @@ impl ChannelMonitor {
969992
txn_to_broadcast.push(single_htlc_tx);
970993
}
971994
}
995+
if !htlc.offered {
996+
let input = TxIn {
997+
previous_output: BitcoinOutPoint {
998+
txid: commitment_txid,
999+
vout: htlc.transaction_output_index,
1000+
},
1001+
script_sig: Script::new(),
1002+
sequence: idx as u32,
1003+
witness: Vec::new(),
1004+
};
1005+
let mut timeout_tx = Transaction {
1006+
version: 2,
1007+
lock_time: htlc.cltv_expiry,
1008+
input: vec![input],
1009+
output: vec!(TxOut {
1010+
script_pubkey: self.destination_script.clone(),
1011+
value: htlc.amount_msat / 1000,
1012+
}),
1013+
};
1014+
let sighash_parts = bip143::SighashComponents::new(&timeout_tx);
1015+
sign_input_timeout!(sighash_parts, timeout_tx.input[0], htlc.amount_msat / 1000);
1016+
txn_to_broadcast.push(timeout_tx);
1017+
}
9721018
}
9731019

9741020
if inputs.is_empty() { return (txn_to_broadcast, (commitment_txid, watch_outputs)); } // Nothing to be done...probably a false positive/local tx

0 commit comments

Comments
 (0)