Skip to content

Commit 01d8c4e

Browse files
committed
Generate ClaimEvent for HolderHTLCOutput inputs from anchor channels
1 parent 9aa4ae8 commit 01d8c4e

File tree

4 files changed

+148
-15
lines changed

4 files changed

+148
-15
lines changed

lightning/src/chain/channelmonitor.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2371,6 +2371,7 @@ impl<Signer: Sign> ChannelMonitorImpl<Signer> {
23712371
pending_htlcs,
23722372
}));
23732373
},
2374+
_ => {},
23742375
}
23752376
}
23762377
ret

lightning/src/chain/onchaintx.rs

Lines changed: 93 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,18 @@ impl Writeable for Option<Vec<Option<(usize, Signature)>>> {
170170
}
171171
}
172172

173+
#[cfg(anchors)]
174+
pub(crate) enum ExternalHTLCClaim {
175+
/// The claim commonly referred to as the pre-signed second-stage HTLC transaction.
176+
SecondStage {
177+
amount: u64,
178+
per_commitment_number: u64,
179+
redeem_script: Script,
180+
preimage: Option<PaymentPreimage>,
181+
counterparty_sig: Signature,
182+
}
183+
}
184+
173185
// Represents the different types of claims for which events are yielded externally to satisfy said
174186
// claims.
175187
#[cfg(anchors)]
@@ -181,6 +193,11 @@ pub(crate) enum ClaimEvent {
181193
commitment_tx: Transaction,
182194
anchor_output_idx: u32,
183195
},
196+
BumpHTLC {
197+
target_feerate_sat_per_1000_weight: u32,
198+
tx_template: Transaction,
199+
htlcs: Vec<ExternalHTLCClaim>,
200+
},
184201
}
185202

186203
/// Represents the different ways an output can be claimed (i.e., spent to an address under our
@@ -472,15 +489,32 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
472489
// didn't receive confirmation of it before, or not enough reorg-safe depth on top of it).
473490
let new_timer = Some(cached_request.get_height_timer(cur_height));
474491
if cached_request.is_malleable() {
492+
#[cfg(anchors)]
493+
if cached_request.requires_external_funding() {
494+
let target_feerate_sat_per_1000_weight = cached_request
495+
.compute_package_feerate(fee_estimator, ConfirmationTarget::HighPriority);
496+
let (tx_template, htlcs) = cached_request.construct_malleable_package_with_external_funding(self);
497+
return Some((
498+
new_timer,
499+
target_feerate_sat_per_1000_weight as u64,
500+
OnchainClaim::Event(ClaimEvent::BumpHTLC {
501+
target_feerate_sat_per_1000_weight,
502+
tx_template,
503+
htlcs,
504+
}),
505+
));
506+
}
507+
475508
let predicted_weight = cached_request.package_weight(&self.destination_script);
476-
if let Some((output_value, new_feerate)) =
477-
cached_request.compute_package_output(predicted_weight, self.destination_script.dust_value().to_sat(), fee_estimator, logger) {
509+
if let Some((output_value, new_feerate)) = cached_request.compute_package_output(
510+
predicted_weight, self.destination_script.dust_value().to_sat(), fee_estimator, logger,
511+
) {
478512
assert!(new_feerate != 0);
479513

480514
let transaction = cached_request.finalize_malleable_package(self, output_value, self.destination_script.clone(), logger).unwrap();
481515
log_trace!(logger, "...with timer {} and feerate {}", new_timer.unwrap(), new_feerate);
482516
assert!(predicted_weight >= transaction.weight());
483-
return Some((new_timer, new_feerate, OnchainClaim::Tx(transaction)))
517+
return Some((new_timer, new_feerate, OnchainClaim::Tx(transaction)));
484518
}
485519
} else {
486520
// Untractable packages cannot have their fees bumped through Replace-By-Fee. Some
@@ -536,7 +570,7 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
536570
debug_assert!(false, "Only HolderFundingOutput inputs should be untractable and require external funding");
537571
None
538572
},
539-
});
573+
})
540574
}
541575
None
542576
}
@@ -621,6 +655,7 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
621655
log_info!(logger, "Yielding onchain event to spend inputs {:?}", req.outpoints());
622656
let txid = match claim_event {
623657
ClaimEvent::BumpCommitment { ref commitment_tx, .. } => commitment_tx.txid(),
658+
ClaimEvent::BumpHTLC { ref tx_template, .. } => tx_template.txid(),
624659
};
625660
self.pending_claim_events.insert(txid, claim_event);
626661
txid
@@ -646,14 +681,28 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
646681
// outpoints to know if transaction is the original claim or a bumped one issued
647682
// by us.
648683
let mut set_equality = true;
649-
if request.outpoints().len() != tx.input.len() {
650-
set_equality = false;
651-
} else {
652-
for (claim_inp, tx_inp) in request.outpoints().iter().zip(tx.input.iter()) {
653-
if **claim_inp != tx_inp.previous_output {
654-
set_equality = false;
684+
if !request.requires_external_funding() {
685+
if request.outpoints().len() != tx.input.len() {
686+
set_equality = false;
687+
} else {
688+
for (claim_inp, tx_inp) in request.outpoints().iter().zip(tx.input.iter()) {
689+
if **claim_inp != tx_inp.previous_output {
690+
set_equality = false;
691+
}
655692
}
656693
}
694+
} else {
695+
let request_inputs = request.outpoints();
696+
let mut input_set = HashSet::with_capacity(request_inputs.len());
697+
for input in request_inputs {
698+
let _ = input_set.insert(input);
699+
}
700+
for tx_input in &tx.input {
701+
let _ = input_set.remove(&tx_input.previous_output);
702+
}
703+
if !input_set.is_empty() {
704+
set_equality = false;
705+
}
657706
}
658707

659708
macro_rules! clean_claim_request_after_safety_delay {
@@ -956,6 +1005,40 @@ impl<ChannelSigner: Sign> OnchainTxHandler<ChannelSigner> {
9561005
htlc_tx
9571006
}
9581007

1008+
#[cfg(anchors)]
1009+
pub(crate) fn unsigned_htlc_tx(
1010+
&mut self, outp: &::bitcoin::OutPoint, preimage: &Option<PaymentPreimage>
1011+
) -> Option<(Transaction, ExternalHTLCClaim)> {
1012+
let find_htlc = |holder_commitment: &HolderCommitmentTransaction| -> Option<(Transaction, ExternalHTLCClaim)> {
1013+
let trusted_tx = holder_commitment.trust();
1014+
trusted_tx.htlcs().iter().enumerate()
1015+
.find(|(_, htlc)| if let Some(output_index) = htlc.transaction_output_index {
1016+
output_index == outp.vout
1017+
} else {
1018+
false
1019+
})
1020+
.map(|(htlc_idx, htlc)| {
1021+
let counterparty_htlc_sig = holder_commitment.counterparty_htlc_sigs[htlc_idx];
1022+
let htlc_tx = trusted_tx.unsigned_htlc_tx(
1023+
&self.channel_transaction_parameters.as_holder_broadcastable(), htlc_idx, preimage,
1024+
);
1025+
(htlc_tx, ExternalHTLCClaim::SecondStage {
1026+
amount: htlc.amount_msat / 1000,
1027+
per_commitment_number: trusted_tx.commitment_number(),
1028+
redeem_script: chan_utils::get_htlc_redeemscript_with_explicit_keys(
1029+
&htlc, self.opt_anchors(), &trusted_tx.keys().broadcaster_htlc_key,
1030+
&trusted_tx.keys().countersignatory_htlc_key, &trusted_tx.keys().revocation_key,
1031+
),
1032+
preimage: *preimage,
1033+
counterparty_sig: counterparty_htlc_sig,
1034+
})
1035+
})
1036+
};
1037+
// Check if the HTLC spends from the current holder commitment or the previous one otherwise.
1038+
find_htlc(&self.holder_commitment)
1039+
.or(self.prev_holder_commitment.as_ref().map(|c| find_htlc(c)).flatten())
1040+
}
1041+
9591042
pub(crate) fn opt_anchors(&self) -> bool {
9601043
self.channel_transaction_parameters.opt_anchors.is_some()
9611044
}

lightning/src/chain/package.rs

Lines changed: 34 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,31 @@ 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+
) -> (Transaction, Vec<ExternalHTLCClaim>) {
668+
debug_assert!(self.requires_external_funding());
669+
let mut aggregate_tx = Transaction {
670+
version: 2,
671+
lock_time: PackedLockTime(self.package_timelock()),
672+
input: Vec::with_capacity(self.inputs.len()),
673+
output: Vec::with_capacity(self.inputs.len()),
674+
};
675+
let mut htlcs = Vec::with_capacity(self.inputs.len());
676+
self.inputs.iter().for_each(|input| match input.1 {
677+
PackageSolvingData::HolderHTLCOutput(ref outp) => {
678+
debug_assert!(outp.opt_anchors());
679+
onchain_handler.unsigned_htlc_tx(&input.0, &outp.preimage).map(|(mut htlc_tx, htlc)| {
680+
aggregate_tx.input.push(htlc_tx.input.pop().unwrap());
681+
aggregate_tx.output.push(htlc_tx.output.pop().unwrap());
682+
htlcs.push(htlc)
683+
});
684+
}
685+
_ => debug_assert!(false, "Expected HolderHTLCOutputs to not be aggregated with other input types"),
686+
});
687+
(aggregate_tx, htlcs)
688+
}
657689
pub(crate) fn finalize_malleable_package<L: Deref, Signer: Sign>(
658690
&self, onchain_handler: &mut OnchainTxHandler<Signer>, value: u64, destination_script: Script, logger: &L
659691
) -> Option<Transaction> where L::Target: Logger {

lightning/src/ln/chan_utils.rs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1532,8 +1532,12 @@ impl<'a> TrustedCommitmentTransaction<'a> {
15321532
Ok(ret)
15331533
}
15341534

1535-
/// Gets a signed HTLC transaction given a preimage (for !htlc.offered) and the holder HTLC transaction signature.
1536-
pub(crate) fn get_signed_htlc_tx(&self, channel_parameters: &DirectedChannelTransactionParameters, htlc_index: usize, counterparty_signature: &Signature, signature: &Signature, preimage: &Option<PaymentPreimage>) -> Transaction {
1535+
/// Builds the unsigned HTLC transaction for a HTLC with output index `htlc_index` within
1536+
/// the commitment transaction.
1537+
pub(crate) fn unsigned_htlc_tx(
1538+
&self, channel_parameters: &DirectedChannelTransactionParameters, htlc_index: usize,
1539+
preimage: &Option<PaymentPreimage>
1540+
) -> Transaction {
15371541
let inner = self.inner;
15381542
let keys = &inner.keys;
15391543
let txid = inner.built.txid;
@@ -1543,8 +1547,21 @@ impl<'a> TrustedCommitmentTransaction<'a> {
15431547
if !this_htlc.offered && preimage.is_none() { unreachable!(); }
15441548
// Further, we should never be provided the preimage for an HTLC-Timeout transaction.
15451549
if this_htlc.offered && preimage.is_some() { unreachable!(); }
1550+
build_htlc_transaction(
1551+
&txid, inner.feerate_per_kw, channel_parameters.contest_delay(), &this_htlc,
1552+
self.opt_anchors(), &keys.broadcaster_delayed_payment_key, &keys.revocation_key,
1553+
)
1554+
}
15461555

1547-
let mut htlc_tx = build_htlc_transaction(&txid, inner.feerate_per_kw, channel_parameters.contest_delay(), &this_htlc, self.opt_anchors(), &keys.broadcaster_delayed_payment_key, &keys.revocation_key);
1556+
/// Gets a signed HTLC transaction given a preimage (for !htlc.offered) and the holder HTLC
1557+
/// transaction signature.
1558+
pub(crate) fn get_signed_htlc_tx(
1559+
&self, channel_parameters: &DirectedChannelTransactionParameters, htlc_index: usize,
1560+
counterparty_signature: &Signature, signature: &Signature, preimage: &Option<PaymentPreimage>
1561+
) -> Transaction {
1562+
let keys = &&self.inner.keys;
1563+
let this_htlc = &self.inner.htlcs[htlc_index];
1564+
let mut htlc_tx = self.unsigned_htlc_tx(channel_parameters, htlc_index, preimage);
15481565

15491566
let htlc_redeemscript = get_htlc_redeemscript_with_explicit_keys(&this_htlc, self.opt_anchors(), &keys.broadcaster_htlc_key, &keys.countersignatory_htlc_key, &keys.revocation_key);
15501567

0 commit comments

Comments
 (0)