Skip to content

Commit 7e297de

Browse files
author
Antoine Riard
committed
Add RBF-bumping of preimage/timeout txn on remote HTLC outputs
Given they are only signed by us we can RBF at wish Fix tests broken by introduction of more txn broadcast (channel_monitor_network_test) Add locktime in RemoteHTLC as it's needed to generate timeout txn.
1 parent e1f05a6 commit 7e297de

File tree

2 files changed

+40
-7
lines changed

2 files changed

+40
-7
lines changed

lightning/src/ln/channelmonitor.rs

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,7 @@ enum InputMaterial {
387387
key: SecretKey,
388388
preimage: Option<PaymentPreimage>,
389389
amount: u64,
390+
locktime: u32,
390391
},
391392
LocalHTLC {
392393
script: Script,
@@ -1182,12 +1183,13 @@ impl ChannelMonitor {
11821183
}
11831184
writer.write_all(&byte_utils::be64_to_array(*amount))?;
11841185
},
1185-
&InputMaterial::RemoteHTLC { ref script, ref key, ref preimage, ref amount } => {
1186+
&InputMaterial::RemoteHTLC { ref script, ref key, ref preimage, ref amount, ref locktime } => {
11861187
writer.write_all(&[1; 1])?;
11871188
script.write(writer)?;
11881189
key.write(writer)?;
11891190
preimage.write(writer)?;
11901191
writer.write_all(&byte_utils::be64_to_array(*amount))?;
1192+
writer.write_all(&byte_utils::be32_to_array(*locktime))?;
11911193
},
11921194
&InputMaterial::LocalHTLC { ref script, ref sigs, ref preimage, ref amount } => {
11931195
writer.write_all(&[2; 1])?;
@@ -1722,7 +1724,7 @@ impl ChannelMonitor {
17221724
});
17231725
log_trace!(self, "Outpoint {}:{} is being being claimed, if it doesn't succeed, a bumped claiming txn is going to be broadcast at height {}", single_htlc_tx.input[0].previous_output.txid, single_htlc_tx.input[0].previous_output.vout, height_timer);
17241726
let mut per_input_material = HashMap::with_capacity(1);
1725-
per_input_material.insert(single_htlc_tx.input[0].previous_output, InputMaterial::RemoteHTLC { script: redeemscript, key: htlc_key, preimage: Some(*payment_preimage), amount: htlc.amount_msat / 1000 });
1727+
per_input_material.insert(single_htlc_tx.input[0].previous_output, InputMaterial::RemoteHTLC { script: redeemscript, key: htlc_key, preimage: Some(*payment_preimage), amount: htlc.amount_msat / 1000, locktime: 0 });
17261728
match self.pending_claim_requests.entry(single_htlc_tx.txid()) {
17271729
hash_map::Entry::Occupied(_) => {},
17281730
hash_map::Entry::Vacant(entry) => { entry.insert(ClaimTxBumpMaterial { height_timer, feerate_previous: used_feerate, soonest_timelock: htlc.cltv_expiry, per_input_material}); }
@@ -1763,7 +1765,7 @@ impl ChannelMonitor {
17631765
//TODO: track SpendableOutputDescriptor
17641766
log_trace!(self, "Outpoint {}:{} is being being claimed, if it doesn't succeed, a bumped claiming txn is going to be broadcast at height {}", timeout_tx.input[0].previous_output.txid, timeout_tx.input[0].previous_output.vout, height_timer);
17651767
let mut per_input_material = HashMap::with_capacity(1);
1766-
per_input_material.insert(timeout_tx.input[0].previous_output, InputMaterial::RemoteHTLC { script : redeemscript, key: htlc_key, preimage: None, amount: htlc.amount_msat / 1000 });
1768+
per_input_material.insert(timeout_tx.input[0].previous_output, InputMaterial::RemoteHTLC { script : redeemscript, key: htlc_key, preimage: None, amount: htlc.amount_msat / 1000, locktime: htlc.cltv_expiry });
17671769
match self.pending_claim_requests.entry(timeout_tx.txid()) {
17681770
hash_map::Entry::Occupied(_) => {},
17691771
hash_map::Entry::Vacant(entry) => { entry.insert(ClaimTxBumpMaterial { height_timer, feerate_previous: used_feerate, soonest_timelock: htlc.cltv_expiry, per_input_material }); }
@@ -1807,7 +1809,7 @@ impl ChannelMonitor {
18071809
for (input, info) in spend_tx.input.iter_mut().zip(inputs_info.iter()) {
18081810
let (redeemscript, htlc_key) = sign_input!(sighash_parts, input, info.1, (info.0).0.to_vec());
18091811
log_trace!(self, "Outpoint {}:{} is being being claimed, if it doesn't succeed, a bumped claiming txn is going to be broadcast at height {}", input.previous_output.txid, input.previous_output.vout, height_timer);
1810-
per_input_material.insert(input.previous_output, InputMaterial::RemoteHTLC { script: redeemscript, key: htlc_key, preimage: Some(*(info.0)), amount: info.1});
1812+
per_input_material.insert(input.previous_output, InputMaterial::RemoteHTLC { script: redeemscript, key: htlc_key, preimage: Some(*(info.0)), amount: info.1, locktime: 0});
18111813
}
18121814
match self.pending_claim_requests.entry(spend_tx.txid()) {
18131815
hash_map::Entry::Occupied(_) => {},
@@ -2626,7 +2628,10 @@ impl ChannelMonitor {
26262628
inputs_witnesses_weight += Self::get_witnesses_weight(if !is_htlc { &[InputDescriptors::RevokedOutput] } else if script.len() == OFFERED_HTLC_SCRIPT_WEIGHT { &[InputDescriptors::RevokedOfferedHTLC] } else if script.len() == ACCEPTED_HTLC_SCRIPT_WEIGHT { &[InputDescriptors::RevokedReceivedHTLC] } else { &[] });
26272629
amt += *amount;
26282630
},
2629-
&InputMaterial::RemoteHTLC { .. } => { },
2631+
&InputMaterial::RemoteHTLC { ref preimage, ref amount, .. } => {
2632+
inputs_witnesses_weight += Self::get_witnesses_weight(if preimage.is_some() { &[InputDescriptors::OfferedHTLC] } else { &[InputDescriptors::ReceivedHTLC] });
2633+
amt += *amount;
2634+
},
26302635
&InputMaterial::LocalHTLC { .. } => { return None; }
26312636
}
26322637
}
@@ -2662,7 +2667,21 @@ impl ChannelMonitor {
26622667
bumped_tx.input[i].witness.push(script.clone().into_bytes());
26632668
log_trace!(self, "Going to broadcast bumped Penalty Transaction {} claiming revoked {} output {} from {} with new feerate {}", bumped_tx.txid(), if !is_htlc { "to_local" } else if script.len() == OFFERED_HTLC_SCRIPT_WEIGHT { "offered" } else if script.len() == ACCEPTED_HTLC_SCRIPT_WEIGHT { "received" } else { "" }, outp.vout, outp.txid, new_feerate);
26642669
},
2665-
&InputMaterial::RemoteHTLC { .. } => {},
2670+
&InputMaterial::RemoteHTLC { ref script, ref key, ref preimage, ref amount, ref locktime } => {
2671+
if !preimage.is_some() { bumped_tx.lock_time = *locktime };
2672+
let sighash_parts = bip143::SighashComponents::new(&bumped_tx);
2673+
let sighash = hash_to_message!(&sighash_parts.sighash_all(&bumped_tx.input[i], &script, *amount)[..]);
2674+
let sig = self.secp_ctx.sign(&sighash, &key);
2675+
bumped_tx.input[i].witness.push(sig.serialize_der().to_vec());
2676+
bumped_tx.input[i].witness[0].push(SigHashType::All as u8);
2677+
if let &Some(preimage) = preimage {
2678+
bumped_tx.input[i].witness.push(preimage.clone().0.to_vec());
2679+
} else {
2680+
bumped_tx.input[i].witness.push(vec![0]);
2681+
}
2682+
bumped_tx.input[i].witness.push(script.clone().into_bytes());
2683+
log_trace!(self, "Going to broadcast bumped Claim Transaction {} claiming remote {} htlc output {} from {} with new feerate {}", bumped_tx.txid(), if preimage.is_some() { "offered" } else { "received" }, outp.vout, outp.txid, new_feerate);
2684+
},
26662685
&InputMaterial::LocalHTLC { .. } => {
26672686
//TODO : Given that Local Commitment Transaction and HTLC-Timeout/HTLC-Success are counter-signed by peer, we can't
26682687
// RBF them. Need a Lightning specs change and package relay modification :
@@ -2932,11 +2951,13 @@ impl<R: ::std::io::Read> ReadableArgs<R, Arc<Logger>> for (Sha256dHash, ChannelM
29322951
let key = Readable::read(reader)?;
29332952
let preimage = Readable::read(reader)?;
29342953
let amount = Readable::read(reader)?;
2954+
let locktime = Readable::read(reader)?;
29352955
InputMaterial::RemoteHTLC {
29362956
script,
29372957
key,
29382958
preimage,
2939-
amount
2959+
amount,
2960+
locktime
29402961
}
29412962
},
29422963
2 => {

lightning/src/ln/functional_tests.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1783,8 +1783,10 @@ fn channel_monitor_network_test() {
17831783
// nodes[3] gets the preimage, but nodes[2] already disconnected, resulting in a nodes[2]
17841784
// HTLC-Timeout and a nodes[3] claim against it (+ its own announces)
17851785
nodes[2].node.peer_disconnected(&nodes[3].node.get_our_node_id(), true);
1786+
let node2_commitment_txid;
17861787
{
17871788
let node_txn = test_txn_broadcast(&nodes[2], &chan_3, None, HTLCType::TIMEOUT);
1789+
node2_commitment_txid = node_txn[0].txid();
17881790

17891791
// Claim the payment on nodes[3], giving it knowledge of the preimage
17901792
claim_funds!(nodes[3], nodes[2], payment_preimage_1, 3_000_000);
@@ -1818,6 +1820,16 @@ fn channel_monitor_network_test() {
18181820
nodes[3].block_notifier.block_connected_checked(&header, i, &Vec::new()[..], &[0; 0]);
18191821
}
18201822

1823+
// Clear bumped claiming txn spending node 2 commitment tx. Bumped txn are generated after reaching some height timer.
1824+
{
1825+
let mut node_txn = nodes[3].tx_broadcaster.txn_broadcasted.lock().unwrap();
1826+
node_txn.retain(|tx| {
1827+
if tx.input[0].previous_output.txid == node2_commitment_txid {
1828+
false
1829+
} else { true }
1830+
});
1831+
}
1832+
18211833
let node_txn = test_txn_broadcast(&nodes[3], &chan_4, None, HTLCType::TIMEOUT);
18221834

18231835
// Claim the payment on nodes[4], giving it knowledge of the preimage

0 commit comments

Comments
 (0)