Skip to content

Commit a4e7142

Browse files
committed
Generate ClaimEvent for HolderHTLCOutput inputs from anchor channels
1 parent 61e7748 commit a4e7142

File tree

3 files changed

+128
-15
lines changed

3 files changed

+128
-15
lines changed

lightning/src/chain/channelmonitor.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2394,6 +2394,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
23942394
pending_htlcs,
23952395
}));
23962396
},
2397+
_ => {},
23972398
}
23982399
}
23992400
ret

lightning/src/chain/onchaintx.rs

Lines changed: 103 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ use bitcoin::secp256k1;
2424
use crate::ln::msgs::DecodeError;
2525
use crate::ln::PaymentPreimage;
2626
#[cfg(anchors)]
27-
use crate::ln::chan_utils;
27+
use crate::ln::chan_utils::{self, HTLCOutputInCommitment};
2828
use crate::ln::chan_utils::{ChannelTransactionParameters, HolderCommitmentTransaction};
2929
#[cfg(anchors)]
3030
use crate::chain::chaininterface::ConfirmationTarget;
@@ -174,6 +174,16 @@ impl Writeable for Option<Vec<Option<(usize, Signature)>>> {
174174
}
175175
}
176176

177+
#[cfg(anchors)]
178+
/// The claim commonly referred to as the pre-signed second-stage HTLC transaction.
179+
pub(crate) struct ExternalHTLCClaim {
180+
pub(crate) commitment_txid: Txid,
181+
pub(crate) per_commitment_number: u64,
182+
pub(crate) htlc: HTLCOutputInCommitment,
183+
pub(crate) preimage: Option<PaymentPreimage>,
184+
pub(crate) counterparty_sig: Signature,
185+
}
186+
177187
// Represents the different types of claims for which events are yielded externally to satisfy said
178188
// claims.
179189
#[cfg(anchors)]
@@ -185,6 +195,10 @@ pub(crate) enum ClaimEvent {
185195
commitment_tx: Transaction,
186196
anchor_output_idx: u32,
187197
},
198+
BumpHTLC {
199+
target_feerate_sat_per_1000_weight: u32,
200+
htlcs: Vec<ExternalHTLCClaim>,
201+
},
188202
}
189203

190204
/// Represents the different ways an output can be claimed (i.e., spent to an address under our
@@ -479,15 +493,33 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
479493
// didn't receive confirmation of it before, or not enough reorg-safe depth on top of it).
480494
let new_timer = Some(cached_request.get_height_timer(cur_height));
481495
if cached_request.is_malleable() {
496+
#[cfg(anchors)]
497+
{ // Attributes are not allowed on if expressions on our current MSRV of 1.41.
498+
if cached_request.requires_external_funding() {
499+
let target_feerate_sat_per_1000_weight = cached_request
500+
.compute_package_feerate(fee_estimator, ConfirmationTarget::HighPriority);
501+
let htlcs = cached_request.construct_malleable_package_with_external_funding(self);
502+
return Some((
503+
new_timer,
504+
target_feerate_sat_per_1000_weight as u64,
505+
OnchainClaim::Event(ClaimEvent::BumpHTLC {
506+
target_feerate_sat_per_1000_weight,
507+
htlcs,
508+
}),
509+
));
510+
}
511+
}
512+
482513
let predicted_weight = cached_request.package_weight(&self.destination_script);
483-
if let Some((output_value, new_feerate)) =
484-
cached_request.compute_package_output(predicted_weight, self.destination_script.dust_value().to_sat(), fee_estimator, logger) {
514+
if let Some((output_value, new_feerate)) = cached_request.compute_package_output(
515+
predicted_weight, self.destination_script.dust_value().to_sat(), fee_estimator, logger,
516+
) {
485517
assert!(new_feerate != 0);
486518

487519
let transaction = cached_request.finalize_malleable_package(self, output_value, self.destination_script.clone(), logger).unwrap();
488520
log_trace!(logger, "...with timer {} and feerate {}", new_timer.unwrap(), new_feerate);
489521
assert!(predicted_weight >= transaction.weight());
490-
return Some((new_timer, new_feerate, OnchainClaim::Tx(transaction)))
522+
return Some((new_timer, new_feerate, OnchainClaim::Tx(transaction)));
491523
}
492524
} else {
493525
// Untractable packages cannot have their fees bumped through Replace-By-Fee. Some
@@ -543,7 +575,7 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
543575
debug_assert!(false, "Only HolderFundingOutput inputs should be untractable and require external funding");
544576
None
545577
},
546-
});
578+
})
547579
}
548580
None
549581
}
@@ -631,10 +663,20 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
631663
#[cfg(anchors)]
632664
OnchainClaim::Event(claim_event) => {
633665
log_info!(logger, "Yielding onchain event to spend inputs {:?}", req.outpoints());
634-
let txid = match claim_event {
635-
ClaimEvent::BumpCommitment { ref commitment_tx, .. } => commitment_tx.txid(),
666+
let package_id = match claim_event {
667+
ClaimEvent::BumpCommitment { ref commitment_tx, .. } => commitment_tx.txid().into_inner(),
668+
ClaimEvent::BumpHTLC { ref htlcs, .. } => {
669+
// Use the same construction as a lightning channel id to generate
670+
// the package id for this request based on the first HTLC. It
671+
// doesn't matter what we use as long as it's unique per request.
672+
let mut package_id = [0; 32];
673+
package_id[..].copy_from_slice(&htlcs[0].commitment_txid[..]);
674+
let htlc_output_index = htlcs[0].htlc.transaction_output_index.unwrap();
675+
package_id[30] ^= ((htlc_output_index >> 8) & 0xff) as u8;
676+
package_id[31] ^= ((htlc_output_index >> 0) & 0xff) as u8;
677+
package_id
678+
},
636679
};
637-
let package_id = txid.into_inner();
638680
self.pending_claim_events.insert(package_id, claim_event);
639681
package_id
640682
},
@@ -677,14 +719,31 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
677719
// outpoints to know if transaction is the original claim or a bumped one issued
678720
// by us.
679721
let mut set_equality = true;
680-
if request.outpoints().len() != tx.input.len() {
681-
set_equality = false;
722+
if !request.requires_external_funding() || !request.is_malleable() {
723+
// If the claim does not require external funds to be allocated through
724+
// additional inputs we can simply check the inputs in order as they
725+
// cannot change under us.
726+
if request.outpoints().len() != tx.input.len() {
727+
set_equality = false;
728+
} else {
729+
for (claim_inp, tx_inp) in request.outpoints().iter().zip(tx.input.iter()) {
730+
if **claim_inp != tx_inp.previous_output {
731+
set_equality = false;
732+
}
733+
}
734+
}
682735
} else {
683-
for (claim_inp, tx_inp) in request.outpoints().iter().zip(tx.input.iter()) {
684-
if **claim_inp != tx_inp.previous_output {
685-
set_equality = false;
736+
// Otherwise, we'll do a linear search for each input (we don't expect
737+
// large input sets to exist) to ensure the request's input set is fully
738+
// spent to be resilient against the external claim reordering inputs.
739+
let mut spends_all_inputs = true;
740+
for request_input in request.outpoints() {
741+
if tx.input.iter().find(|input| input.previous_output == *request_input).is_none() {
742+
spends_all_inputs = false;
743+
break;
686744
}
687745
}
746+
set_equality = spends_all_inputs;
688747
}
689748

690749
macro_rules! clean_claim_request_after_safety_delay {
@@ -990,6 +1049,37 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
9901049
htlc_tx
9911050
}
9921051

1052+
#[cfg(anchors)]
1053+
pub(crate) fn generate_external_htlc_claim(
1054+
&mut self, outp: &::bitcoin::OutPoint, preimage: &Option<PaymentPreimage>
1055+
) -> Option<ExternalHTLCClaim> {
1056+
let find_htlc = |holder_commitment: &HolderCommitmentTransaction| -> Option<ExternalHTLCClaim> {
1057+
let trusted_tx = holder_commitment.trust();
1058+
if outp.txid != trusted_tx.txid() {
1059+
return None;
1060+
}
1061+
trusted_tx.htlcs().iter().enumerate()
1062+
.find(|(_, htlc)| if let Some(output_index) = htlc.transaction_output_index {
1063+
output_index == outp.vout
1064+
} else {
1065+
false
1066+
})
1067+
.map(|(htlc_idx, htlc)| {
1068+
let counterparty_htlc_sig = holder_commitment.counterparty_htlc_sigs[htlc_idx];
1069+
ExternalHTLCClaim {
1070+
commitment_txid: trusted_tx.txid(),
1071+
per_commitment_number: trusted_tx.commitment_number(),
1072+
htlc: htlc.clone(),
1073+
preimage: *preimage,
1074+
counterparty_sig: counterparty_htlc_sig,
1075+
}
1076+
})
1077+
};
1078+
// Check if the HTLC spends from the current holder commitment or the previous one otherwise.
1079+
find_htlc(&self.holder_commitment)
1080+
.or_else(|| self.prev_holder_commitment.as_ref().map(|c| find_htlc(c)).flatten())
1081+
}
1082+
9931083
pub(crate) fn opt_anchors(&self) -> bool {
9941084
self.channel_transaction_parameters.opt_anchors.is_some()
9951085
}

lightning/src/chain/package.rs

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ use crate::ln::chan_utils;
2626
use crate::ln::msgs::DecodeError;
2727
use crate::chain::chaininterface::{FeeEstimator, ConfirmationTarget, MIN_RELAY_FEE_SAT_PER_1000_WEIGHT};
2828
use crate::chain::keysinterface::Sign;
29+
#[cfg(anchors)]
30+
use crate::chain::onchaintx::ExternalHTLCClaim;
2931
use crate::chain::onchaintx::OnchainTxHandler;
3032
use crate::util::byte_utils;
3133
use crate::util::logger::Logger;
@@ -453,8 +455,13 @@ impl PackageSolvingData {
453455
}
454456
fn get_finalized_tx<Signer: Sign>(&self, outpoint: &BitcoinOutPoint, onchain_handler: &mut OnchainTxHandler<Signer>) -> Option<Transaction> {
455457
match self {
456-
PackageSolvingData::HolderHTLCOutput(ref outp) => { return onchain_handler.get_fully_signed_htlc_tx(outpoint, &outp.preimage); }
457-
PackageSolvingData::HolderFundingOutput(ref outp) => { return Some(onchain_handler.get_fully_signed_holder_tx(&outp.funding_redeemscript)); }
458+
PackageSolvingData::HolderHTLCOutput(ref outp) => {
459+
debug_assert!(!outp.opt_anchors());
460+
return onchain_handler.get_fully_signed_htlc_tx(outpoint, &outp.preimage);
461+
}
462+
PackageSolvingData::HolderFundingOutput(ref outp) => {
463+
return Some(onchain_handler.get_fully_signed_holder_tx(&outp.funding_redeemscript));
464+
}
458465
_ => { panic!("API Error!"); }
459466
}
460467
}
@@ -654,6 +661,21 @@ impl PackageTemplate {
654661
let output_weight = (8 + 1 + destination_script.len()) * WITNESS_SCALE_FACTOR;
655662
inputs_weight + witnesses_weight + transaction_weight + output_weight
656663
}
664+
#[cfg(anchors)]
665+
pub(crate) fn construct_malleable_package_with_external_funding<Signer: Sign>(
666+
&self, onchain_handler: &mut OnchainTxHandler<Signer>,
667+
) -> Vec<ExternalHTLCClaim> {
668+
debug_assert!(self.requires_external_funding());
669+
let mut htlcs = Vec::with_capacity(self.inputs.len());
670+
self.inputs.iter().for_each(|input| match input.1 {
671+
PackageSolvingData::HolderHTLCOutput(ref outp) => {
672+
debug_assert!(outp.opt_anchors());
673+
htlcs.push(onchain_handler.generate_external_htlc_claim(&input.0, &outp.preimage).unwrap()); // TODO
674+
}
675+
_ => debug_assert!(false, "Expected HolderHTLCOutputs to not be aggregated with other input types"),
676+
});
677+
htlcs
678+
}
657679
pub(crate) fn finalize_malleable_package<L: Deref, Signer: Sign>(
658680
&self, onchain_handler: &mut OnchainTxHandler<Signer>, value: u64, destination_script: Script, logger: &L
659681
) -> Option<Transaction> where L::Target: Logger {

0 commit comments

Comments
 (0)