Skip to content

Commit e3a4624

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 ff59871 commit e3a4624

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,
@@ -1175,12 +1176,13 @@ impl ChannelMonitor {
11751176
}
11761177
writer.write_all(&byte_utils::be64_to_array(*amount))?;
11771178
},
1178-
&InputMaterial::RemoteHTLC { ref script, ref key, ref preimage, ref amount } => {
1179+
&InputMaterial::RemoteHTLC { ref script, ref key, ref preimage, ref amount, ref locktime } => {
11791180
writer.write_all(&[1; 1])?;
11801181
script.write(writer)?;
11811182
key.write(writer)?;
11821183
preimage.write(writer)?;
11831184
writer.write_all(&byte_utils::be64_to_array(*amount))?;
1185+
writer.write_all(&byte_utils::be32_to_array(*locktime))?;
11841186
},
11851187
&InputMaterial::LocalHTLC { ref script, ref sigs, ref preimage, ref amount } => {
11861188
writer.write_all(&[2; 1])?;
@@ -1708,7 +1710,7 @@ impl ChannelMonitor {
17081710
});
17091711
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);
17101712
let mut per_input_material = HashMap::with_capacity(1);
1711-
per_input_material.insert(single_htlc_tx.input[0].previous_output.vout, InputMaterial::RemoteHTLC { script: redeemscript, key: htlc_key, preimage: Some(*payment_preimage), amount: htlc.amount_msat / 1000 });
1713+
per_input_material.insert(single_htlc_tx.input[0].previous_output.vout, InputMaterial::RemoteHTLC { script: redeemscript, key: htlc_key, preimage: Some(*payment_preimage), amount: htlc.amount_msat / 1000, locktime: 0 });
17121714
match self.our_claim_txn_waiting_first_conf.entry(single_htlc_tx.input[0].previous_output.clone()) {
17131715
hash_map::Entry::Occupied(_) => {},
17141716
hash_map::Entry::Vacant(entry) => { entry.insert(ClaimTxBumpMaterial { height_timer, first_seen_height: height, feerate_previous: used_feerate, soonest_timelock: htlc.cltv_expiry, per_input_material}); }
@@ -1749,7 +1751,7 @@ impl ChannelMonitor {
17491751
//TODO: track SpendableOutputDescriptor
17501752
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);
17511753
let mut per_input_material = HashMap::with_capacity(1);
1752-
per_input_material.insert(timeout_tx.input[0].previous_output.vout, InputMaterial::RemoteHTLC { script : redeemscript, key: htlc_key, preimage: None, amount: htlc.amount_msat / 1000 });
1754+
per_input_material.insert(timeout_tx.input[0].previous_output.vout, InputMaterial::RemoteHTLC { script : redeemscript, key: htlc_key, preimage: None, amount: htlc.amount_msat / 1000, locktime: htlc.cltv_expiry });
17531755
match self.our_claim_txn_waiting_first_conf.entry(timeout_tx.input[0].previous_output.clone()) {
17541756
hash_map::Entry::Occupied(_) => {},
17551757
hash_map::Entry::Vacant(entry) => { entry.insert(ClaimTxBumpMaterial { height_timer, first_seen_height: height, feerate_previous: used_feerate, soonest_timelock: htlc.cltv_expiry, per_input_material }); }
@@ -1793,7 +1795,7 @@ impl ChannelMonitor {
17931795
for (input, info) in spend_tx.input.iter_mut().zip(inputs_info.iter()) {
17941796
let (redeemscript, htlc_key) = sign_input!(sighash_parts, input, info.1, (info.0).0.to_vec());
17951797
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);
1796-
per_input_material.insert(input.previous_output.vout, InputMaterial::RemoteHTLC { script: redeemscript, key: htlc_key, preimage: Some(*(info.0)), amount: info.1});
1798+
per_input_material.insert(input.previous_output.vout, InputMaterial::RemoteHTLC { script: redeemscript, key: htlc_key, preimage: Some(*(info.0)), amount: info.1, locktime: 0});
17971799
}
17981800
match self.our_claim_txn_waiting_first_conf.entry(spend_tx.input[0].previous_output.clone()) {
17991801
hash_map::Entry::Occupied(_) => {},
@@ -2600,7 +2602,10 @@ impl ChannelMonitor {
26002602
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 { &[] });
26012603
amt += *amount;
26022604
},
2603-
&InputMaterial::RemoteHTLC { .. } => { return None; },
2605+
&InputMaterial::RemoteHTLC { ref preimage, ref amount, .. } => {
2606+
inputs_witnesses_weight += Self::get_witnesses_weight(if preimage.is_some() { &[InputDescriptors::OfferedHTLC] } else { &[InputDescriptors::ReceivedHTLC] });
2607+
amt += *amount;
2608+
},
26042609
&InputMaterial::LocalHTLC { .. } => { return None; }
26052610
}
26062611
}
@@ -2637,7 +2642,21 @@ impl ChannelMonitor {
26372642
bumped_tx.input[i].witness.push(script.clone().into_bytes());
26382643
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 { "" }, vout, outp.txid, new_feerate);
26392644
},
2640-
&InputMaterial::RemoteHTLC { .. } => {},
2645+
&InputMaterial::RemoteHTLC { ref script, ref key, ref preimage, ref amount, ref locktime } => {
2646+
if !preimage.is_some() { bumped_tx.lock_time = *locktime };
2647+
let sighash_parts = bip143::SighashComponents::new(&bumped_tx);
2648+
let sighash = hash_to_message!(&sighash_parts.sighash_all(&bumped_tx.input[i], &script, *amount)[..]);
2649+
let sig = self.secp_ctx.sign(&sighash, &key);
2650+
bumped_tx.input[i].witness.push(sig.serialize_der().to_vec());
2651+
bumped_tx.input[i].witness[0].push(SigHashType::All as u8);
2652+
if let &Some(preimage) = preimage {
2653+
bumped_tx.input[i].witness.push(preimage.clone().0.to_vec());
2654+
} else {
2655+
bumped_tx.input[i].witness.push(vec![0]);
2656+
}
2657+
bumped_tx.input[i].witness.push(script.clone().into_bytes());
2658+
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" }, vout, outp.txid, new_feerate);
2659+
},
26412660
&InputMaterial::LocalHTLC { .. } => {
26422661
//TODO : Given that Local Commitment Transaction and HTLC-Timeout/HTLC-Success are counter-signed by peer, we can't
26432662
// RBF them. Need a Lightning specs change and package relay modification :
@@ -2908,11 +2927,13 @@ impl<R: ::std::io::Read> ReadableArgs<R, Arc<Logger>> for (Sha256dHash, ChannelM
29082927
let key = Readable::read(reader)?;
29092928
let preimage = Readable::read(reader)?;
29102929
let amount = Readable::read(reader)?;
2930+
let locktime = Readable::read(reader)?;
29112931
InputMaterial::RemoteHTLC {
29122932
script,
29132933
key,
29142934
preimage,
2915-
amount
2935+
amount,
2936+
locktime
29162937
}
29172938
},
29182939
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)