Skip to content

Commit 68122bd

Browse files
committed
Set transaction locktime on malleable packages to discourage fee sniping
This only applies to all malleable packages on channels pre-dating anchors and malleables packages for counterparty commitments post-anchors. Malleables packages for holder commitments post-anchors should have their transaction locktime applied manually by the consumer of `BumpTransactionEvent::HTLCResolution` events.
1 parent 2ac0971 commit 68122bd

File tree

3 files changed

+9
-7
lines changed

3 files changed

+9
-7
lines changed

lightning/src/chain/onchaintx.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -558,7 +558,9 @@ impl<ChannelSigner: WriteableEcdsaChannelSigner> OnchainTxHandler<ChannelSigner>
558558
) {
559559
assert!(new_feerate != 0);
560560

561-
let transaction = cached_request.finalize_malleable_package(self, output_value, self.destination_script.clone(), logger).unwrap();
561+
let transaction = cached_request.finalize_malleable_package(
562+
cur_height, self, output_value, self.destination_script.clone(), logger
563+
).unwrap();
562564
log_trace!(logger, "...with timer {} and feerate {}", new_timer.unwrap(), new_feerate);
563565
assert!(predicted_weight >= transaction.weight());
564566
return Some((new_timer, new_feerate, OnchainClaim::Tx(transaction)));

lightning/src/chain/package.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -434,7 +434,6 @@ impl PackageSolvingData {
434434
let chan_keys = TxCreationKeys::derive_new(&onchain_handler.secp_ctx, &outp.per_commitment_point, &outp.counterparty_delayed_payment_base_key, &outp.counterparty_htlc_base_key, &onchain_handler.signer.pubkeys().revocation_basepoint, &onchain_handler.signer.pubkeys().htlc_basepoint);
435435
let witness_script = chan_utils::get_htlc_redeemscript_with_explicit_keys(&outp.htlc, onchain_handler.opt_anchors(), &chan_keys.broadcaster_htlc_key, &chan_keys.countersignatory_htlc_key, &chan_keys.revocation_key);
436436

437-
bumped_tx.lock_time = PackedLockTime(outp.htlc.cltv_expiry); // Right now we don't aggregate time-locked transaction, if we do we should set lock_time before to avoid breaking hash computation
438437
if let Ok(sig) = onchain_handler.signer.sign_counterparty_htlc_transaction(&bumped_tx, i, &outp.htlc.amount_msat / 1000, &outp.per_commitment_point, &outp.htlc, &onchain_handler.secp_ctx) {
439438
let mut ser_sig = sig.serialize_der().to_vec();
440439
ser_sig.push(EcdsaSighashType::All as u8);
@@ -708,12 +707,13 @@ impl PackageTemplate {
708707
htlcs
709708
}
710709
pub(crate) fn finalize_malleable_package<L: Deref, Signer: WriteableEcdsaChannelSigner>(
711-
&self, onchain_handler: &mut OnchainTxHandler<Signer>, value: u64, destination_script: Script, logger: &L
710+
&self, current_height: u32, onchain_handler: &mut OnchainTxHandler<Signer>, value: u64,
711+
destination_script: Script, logger: &L
712712
) -> Option<Transaction> where L::Target: Logger {
713713
debug_assert!(self.is_malleable());
714714
let mut bumped_tx = Transaction {
715715
version: 2,
716-
lock_time: PackedLockTime::ZERO,
716+
lock_time: PackedLockTime(self.package_locktime(current_height)),
717717
input: vec![],
718718
output: vec![TxOut {
719719
script_pubkey: destination_script,

lightning/src/ln/functional_tests.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2843,7 +2843,7 @@ fn test_htlc_on_chain_success() {
28432843
assert_eq!(commitment_spend.input.len(), 2);
28442844
assert_eq!(commitment_spend.input[0].witness.last().unwrap().len(), OFFERED_HTLC_SCRIPT_WEIGHT);
28452845
assert_eq!(commitment_spend.input[1].witness.last().unwrap().len(), OFFERED_HTLC_SCRIPT_WEIGHT);
2846-
assert_eq!(commitment_spend.lock_time.0, 0);
2846+
assert_eq!(commitment_spend.lock_time.0, nodes[1].best_block_info().1 + 1);
28472847
assert!(commitment_spend.output[0].script_pubkey.is_v0_p2wpkh()); // direct payment
28482848
// We don't bother to check that B can claim the HTLC output on its commitment tx here as
28492849
// we already checked the same situation with A.
@@ -4699,7 +4699,7 @@ fn test_onchain_to_onchain_claim() {
46994699
check_spends!(b_txn[0], commitment_tx[0]);
47004700
assert_eq!(b_txn[0].input[0].witness.clone().last().unwrap().len(), OFFERED_HTLC_SCRIPT_WEIGHT);
47014701
assert!(b_txn[0].output[0].script_pubkey.is_v0_p2wpkh()); // direct payment
4702-
assert_eq!(b_txn[0].lock_time.0, 0); // Success tx
4702+
assert_eq!(b_txn[0].lock_time.0, nodes[1].best_block_info().1 + 1); // Success tx
47034703

47044704
check_closed_broadcast!(nodes[1], true);
47054705
check_added_monitors!(nodes[1], 1);
@@ -6860,7 +6860,7 @@ fn do_test_sweep_outbound_htlc_failure_update(revoked: bool, local: bool) {
68606860
if !revoked {
68616861
assert_eq!(timeout_tx[0].input[0].witness.last().unwrap().len(), ACCEPTED_HTLC_SCRIPT_WEIGHT);
68626862
} else {
6863-
assert_eq!(timeout_tx[0].lock_time.0, 0);
6863+
assert_eq!(timeout_tx[0].lock_time.0, 12);
68646864
}
68656865
// We fail non-dust-HTLC 2 by broadcast of local timeout/revocation-claim tx
68666866
mine_transaction(&nodes[0], &timeout_tx[0]);

0 commit comments

Comments
 (0)