Skip to content

Commit c8e493c

Browse files
author
Antoine Riard
committed
Watch outputs of revoked HTLC-transactions
Bumping of justice txn on revoked HTLC-Success/HTLC-timeout is triggered until our claim is confirmed onchain with at least ANTI_REORG_DELAY_SAFE. Before this patch, we weren't tracking them in check_spend_remote_htlc, leading us to infinite bumps. Fix #411
1 parent 83c9eb4 commit c8e493c

File tree

3 files changed

+17
-14
lines changed

3 files changed

+17
-14
lines changed

lightning/src/ln/channelmonitor.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1668,22 +1668,23 @@ impl<ChanSigner: ChannelKeys> ChannelMonitor<ChanSigner> {
16681668
}
16691669

16701670
/// Attempts to claim a remote HTLC-Success/HTLC-Timeout's outputs using the revocation key
1671-
fn check_spend_remote_htlc(&mut self, tx: &Transaction, commitment_number: u64, height: u32) -> Vec<ClaimRequest> {
1671+
fn check_spend_remote_htlc(&mut self, tx: &Transaction, commitment_number: u64, height: u32) -> (Vec<ClaimRequest>, (Sha256dHash, Vec<TxOut>)) {
16721672
//TODO: send back new outputs to guarantee pending_claim_request consistency
1673+
let htlc_txid = tx.txid(); //TODO: This is gonna be a performance bottleneck for watchtowers!
16731674
if tx.input.len() != 1 || tx.output.len() != 1 || tx.input[0].witness.len() != 5 {
1674-
return Vec::new()
1675+
return (Vec::new(), (htlc_txid, Vec::new()))
16751676
}
16761677

16771678
macro_rules! ignore_error {
16781679
( $thing : expr ) => {
16791680
match $thing {
16801681
Ok(a) => a,
1681-
Err(_) => return Vec::new()
1682+
Err(_) => return (Vec::new(), (htlc_txid, Vec::new()))
16821683
}
16831684
};
16841685
}
16851686

1686-
let secret = if let Some(secret) = self.get_secret(commitment_number) { secret } else { return Vec::new(); };
1687+
let secret = if let Some(secret) = self.get_secret(commitment_number) { secret } else { return (Vec::new(), (htlc_txid, Vec::new())); };
16871688
let per_commitment_key = ignore_error!(SecretKey::from_slice(&secret));
16881689
let per_commitment_point = PublicKey::from_secret_key(&self.secp_ctx, &per_commitment_key);
16891690
let (revocation_pubkey, revocation_key) = match self.key_storage {
@@ -1694,16 +1695,15 @@ impl<ChanSigner: ChannelKeys> ChannelMonitor<ChanSigner> {
16941695
Storage::Watchtower { .. } => { unimplemented!() }
16951696
};
16961697
let delayed_key = match self.their_delayed_payment_base_key {
1697-
None => return Vec::new(),
1698+
None => return (Vec::new(), (htlc_txid, Vec::new())),
16981699
Some(their_delayed_payment_base_key) => ignore_error!(chan_utils::derive_public_key(&self.secp_ctx, &per_commitment_point, &their_delayed_payment_base_key)),
16991700
};
17001701
let redeemscript = chan_utils::get_revokeable_redeemscript(&revocation_pubkey, self.our_to_self_delay, &delayed_key);
1701-
let htlc_txid = tx.txid(); //TODO: This is gonna be a performance bottleneck for watchtowers!
17021702

17031703
log_trace!(self, "Remote HTLC broadcast {}:{}", htlc_txid, 0);
17041704
let witness_data = InputMaterial::Revoked { witness_script: redeemscript, pubkey: Some(revocation_pubkey), key: revocation_key, is_htlc: false, amount: tx.output[0].value };
17051705
let claimable_outpoints = vec!(ClaimRequest { absolute_timelock: height + self.our_to_self_delay as u32, aggregable: true, outpoint: BitcoinOutPoint { txid: htlc_txid, vout: 0}, witness_data });
1706-
claimable_outpoints
1706+
(claimable_outpoints, (htlc_txid, tx.output.clone()))
17071707
}
17081708

17091709
fn broadcast_by_local_state(&self, local_tx: &LocalSignedTx, delayed_payment_base_key: &SecretKey) -> (Vec<Transaction>, Vec<SpendableOutputDescriptor>, Vec<TxOut>) {
@@ -2019,8 +2019,9 @@ impl<ChanSigner: ChannelKeys> ChannelMonitor<ChanSigner> {
20192019
}
20202020
} else {
20212021
if let Some(&(commitment_number, _)) = self.remote_commitment_txn_on_chain.get(&prevout.txid) {
2022-
let mut new_outpoints = self.check_spend_remote_htlc(&tx, commitment_number, height);
2022+
let (mut new_outpoints, new_outputs) = self.check_spend_remote_htlc(&tx, commitment_number, height);
20232023
claimable_outpoints.append(&mut new_outpoints);
2024+
watch_outputs.push(new_outputs);
20242025
}
20252026
}
20262027
}

lightning/src/ln/functional_test_utils.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1097,9 +1097,9 @@ pub fn test_txn_broadcast<'a, 'b, 'c>(node: &Node<'a, 'b, 'c>, chan: &(msgs::Cha
10971097
/// HTLC transaction.
10981098
pub fn test_revoked_htlc_claim_txn_broadcast<'a, 'b, 'c>(node: &Node<'a, 'b, 'c>, revoked_tx: Transaction, commitment_revoked_tx: Transaction) {
10991099
let mut node_txn = node.tx_broadcaster.txn_broadcasted.lock().unwrap();
1100-
// We should issue a 2nd transaction if one htlc is dropped from initial claiming tx
1101-
// but sometimes not as feerate is too-low
1102-
if node_txn.len() != 1 && node_txn.len() != 2 { assert!(false); }
1100+
// We may issue multiple claiming transaction on revoked outputs due to block rescan
1101+
// for revoked htlc outputs
1102+
if node_txn.len() != 1 && node_txn.len() != 2 && node_txn.len() != 3 { assert!(false); }
11031103
node_txn.retain(|tx| {
11041104
if tx.input.len() == 1 && tx.input[0].previous_output.txid == revoked_tx.txid() {
11051105
check_spends!(tx, revoked_tx);

lightning/src/ln/functional_tests.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2127,8 +2127,10 @@ fn test_justice_tx() {
21272127
test_txn_broadcast(&nodes[1], &chan_5, None, HTLCType::NONE);
21282128

21292129
nodes[0].block_notifier.block_connected(&Block { header, txdata: vec![revoked_local_txn[0].clone()] }, 1);
2130+
// Verify broadcast of revoked HTLC-timeout
21302131
let node_txn = test_txn_broadcast(&nodes[0], &chan_5, Some(revoked_local_txn[0].clone()), HTLCType::TIMEOUT);
21312132
header = BlockHeader { version: 0x20000000, prev_blockhash: header.bitcoin_hash(), merkle_root: Default::default(), time: 42, bits: 42, nonce: 42 };
2133+
// Broadcast revoked HTLC-timeout on node 1
21322134
nodes[1].block_notifier.block_connected(&Block { header, txdata: vec![node_txn[1].clone()] }, 1);
21332135
test_revoked_htlc_claim_txn_broadcast(&nodes[1], node_txn[1].clone(), revoked_local_txn[0].clone());
21342136
}
@@ -4182,7 +4184,7 @@ fn test_static_spendable_outputs_justice_tx_revoked_htlc_timeout_tx() {
41824184
check_closed_broadcast!(nodes[1], false);
41834185

41844186
let node_txn = nodes[1].tx_broadcaster.txn_broadcasted.lock().unwrap();
4185-
assert_eq!(node_txn.len(), 4 ); // ChannelMonitor: justice tx on revoked commitment, justice tx on revoked HTLC-timeout, adjusted justice tx, ChannelManager: local commitment tx
4187+
assert_eq!(node_txn.len(), 5); // ChannelMonitor: justice tx on revoked commitment, justice tx on revoked HTLC-timeout, adjusted justice tx, bumped justice tx, ChannelManager: local commitment tx
41864188
assert_eq!(node_txn[2].input.len(), 1);
41874189
check_spends!(node_txn[2], revoked_htlc_txn[0]);
41884190

@@ -4233,7 +4235,7 @@ fn test_static_spendable_outputs_justice_tx_revoked_htlc_success_tx() {
42334235

42344236
// Check A's ChannelMonitor was able to generate the right spendable output descriptor
42354237
let spend_txn = check_spendable_outputs!(nodes[0], 1);
4236-
assert_eq!(spend_txn.len(), 4);
4238+
assert_eq!(spend_txn.len(), 5); // Duplicated SpendableOutput due to block rescan after revoked htlc output tracking
42374239
assert_eq!(spend_txn[0], spend_txn[2]);
42384240
check_spends!(spend_txn[0], revoked_local_txn[0]); // spending to_remote output from revoked local tx
42394241
check_spends!(spend_txn[1], node_txn[0]); // spending justice tx output from revoked local tx htlc received output
@@ -6964,7 +6966,7 @@ fn test_bump_penalty_txn_on_revoked_htlcs() {
69646966
connect_blocks(&nodes[0].block_notifier, 20, 145, true, header_145.bitcoin_hash());
69656967
{
69666968
let mut node_txn = nodes[0].tx_broadcaster.txn_broadcasted.lock().unwrap();
6967-
assert_eq!(node_txn.len(), 1); //TODO: fix check_spend_remote_htlc lack of watch output
6969+
assert_eq!(node_txn.len(), 0); // Spending of revoked htlc output by claiming transaction remove request as expected
69686970
node_txn.clear();
69696971
}
69706972
check_closed_broadcast!(nodes[0], false);

0 commit comments

Comments
 (0)