Skip to content

Commit d87566f

Browse files
committed
Track HTLCDescriptor in HolderHTLCOutput
The `ChannelMonitor` and `OnchainTxHandler` have historically been tied together, often tracking some of the same state twice. As we introduce support for splices in the `ChannelMonitor`, we'd like to avoid leaking some of those details to the `OnchainTxHandler`. Ultimately, the `OnchainTxHandler` should stand on its own and support claiming funds from multiple `ChannelMonitor`s, allowing us to save on fees by batching aggregatable claims across multiple in-flight closing channels. This commit tracks the `HTLCDescriptor` for the specific `FundingScope` the `HolderHTLCOutput` claim originated from. Previously, when claiming `HolderHTLCOutput`s, the `OnchainTxHandler` had to check which holder commitment the HTLC belonged to in order to produce an `HTLCDescriptor` for signing. We still maintain the legacy logic for existing claims which have not had an `HTLCDescriptor` written yet. Along the way, we refactor the claim process for such outputs to decouple them from the `OnchainTxHandler`. As a result, the `OnchainTxHandler` is only used to obtain references to the signer and secp256k1 context. Once splices are supported, we may run into cases where we are attempting to claim an output from a specific `FundingScope`, while also having an additional pending `FundingScope` for a splice. If the pending splice confirms over the output claim, we need to cancel the claim and re-offer it with the set of relevant parameters in the new `FundingScope`.
1 parent f6519fa commit d87566f

File tree

5 files changed

+319
-217
lines changed

5 files changed

+319
-217
lines changed

lightning/src/chain/channelmonitor.rs

+71-41
Original file line numberDiff line numberDiff line change
@@ -3562,29 +3562,12 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
35623562
} => {
35633563
let channel_id = self.channel_id;
35643564
let counterparty_node_id = self.counterparty_node_id;
3565-
let mut htlc_descriptors = Vec::with_capacity(htlcs.len());
3566-
for htlc in htlcs {
3567-
htlc_descriptors.push(HTLCDescriptor {
3568-
channel_derivation_parameters: ChannelDerivationParameters {
3569-
keys_id: self.channel_keys_id,
3570-
value_satoshis: self.funding.channel_parameters.channel_value_satoshis,
3571-
transaction_parameters: self.funding.channel_parameters.clone(),
3572-
},
3573-
commitment_txid: htlc.commitment_txid,
3574-
per_commitment_number: htlc.per_commitment_number,
3575-
per_commitment_point: htlc.per_commitment_point,
3576-
feerate_per_kw: 0,
3577-
htlc: htlc.htlc,
3578-
preimage: htlc.preimage,
3579-
counterparty_sig: htlc.counterparty_sig,
3580-
});
3581-
}
35823565
ret.push(Event::BumpTransaction(BumpTransactionEvent::HTLCResolution {
35833566
channel_id,
35843567
counterparty_node_id,
35853568
claim_id,
35863569
target_feerate_sat_per_1000_weight,
3587-
htlc_descriptors,
3570+
htlc_descriptors: htlcs,
35883571
tx_lock_time,
35893572
}));
35903573
}
@@ -3991,25 +3974,39 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
39913974
));
39923975

39933976
let txid = tx.txid();
3994-
for htlc in holder_tx.htlcs() {
3977+
for (idx, htlc) in holder_tx.htlcs().iter().enumerate() {
39953978
if let Some(transaction_output_index) = htlc.transaction_output_index {
3996-
let (htlc_output, counterparty_spendable_height) = if htlc.offered {
3997-
let htlc_output = HolderHTLCOutput::build_offered(
3998-
htlc.amount_msat, htlc.cltv_expiry, self.channel_type_features().clone()
3999-
);
4000-
(htlc_output, conf_height)
4001-
} else {
4002-
let payment_preimage = if let Some((preimage, _)) = self.payment_preimages.get(&htlc.payment_hash) {
4003-
preimage.clone()
3979+
let counterparty_sig =
3980+
if let Some(counterparty_sig) = holder_tx.counterparty_htlc_sigs.get(idx) {
3981+
*counterparty_sig
40043982
} else {
4005-
// We can't build an HTLC-Success transaction without the preimage
3983+
debug_assert!(false, "All non-dust HTLCs must have a corresponding counterparty signature");
40063984
continue;
40073985
};
4008-
let htlc_output = HolderHTLCOutput::build_accepted(
4009-
payment_preimage, htlc.amount_msat, self.channel_type_features().clone()
4010-
);
4011-
(htlc_output, htlc.cltv_expiry)
3986+
3987+
let (preimage, counterparty_spendable_height) = if htlc.offered {
3988+
(None, conf_height)
3989+
} else if let Some((preimage, _)) = self.payment_preimages.get(&htlc.payment_hash) {
3990+
(Some(*preimage), htlc.cltv_expiry)
3991+
} else {
3992+
// We can't build an HTLC-Success transaction without the preimage
3993+
continue;
40123994
};
3995+
3996+
let htlc_output = HolderHTLCOutput::build(HTLCDescriptor {
3997+
channel_derivation_parameters: ChannelDerivationParameters {
3998+
value_satoshis: self.funding.channel_parameters.channel_value_satoshis,
3999+
keys_id: self.channel_keys_id,
4000+
transaction_parameters: self.funding.channel_parameters.clone(),
4001+
},
4002+
commitment_txid: tx.txid(),
4003+
per_commitment_number: tx.commitment_number(),
4004+
per_commitment_point: tx.per_commitment_point(),
4005+
feerate_per_kw: tx.feerate_per_kw(),
4006+
htlc: htlc.clone(),
4007+
preimage,
4008+
counterparty_sig,
4009+
});
40134010
let htlc_package = PackageTemplate::build_package(
40144011
txid, transaction_output_index,
40154012
PackageSolvingData::HolderHTLCOutput(htlc_output),
@@ -4153,24 +4150,57 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
41534150
&mut self, logger: &WithChannelMonitor<L>
41544151
) -> Vec<Transaction> where L::Target: Logger {
41554152
log_debug!(logger, "Getting signed copy of latest holder commitment transaction!");
4156-
let commitment_tx = self.onchain_tx_handler.get_fully_signed_copy_holder_tx(&self.funding.redeem_script);
4153+
let commitment_tx = {
4154+
let sig = self.onchain_tx_handler.signer.unsafe_sign_holder_commitment(
4155+
&self.funding.channel_parameters, &self.funding.current_holder_commitment.tx,
4156+
&self.onchain_tx_handler.secp_ctx,
4157+
).expect("sign holder commitment");
4158+
self.funding.current_holder_commitment.tx.add_holder_sig(&self.funding.redeem_script, sig)
4159+
};
41574160
let txid = commitment_tx.compute_txid();
41584161
let mut holder_transactions = vec![commitment_tx];
41594162
// When anchor outputs are present, the HTLC transactions are only final once the commitment
41604163
// transaction confirms due to the CSV 1 encumberance.
41614164
if self.channel_type_features().supports_anchors_zero_fee_htlc_tx() {
41624165
return holder_transactions;
41634166
}
4164-
for htlc in self.funding.current_holder_commitment.tx.htlcs() {
4167+
for (idx, htlc) in self.funding.current_holder_commitment.tx.htlcs().iter().enumerate() {
41654168
if let Some(vout) = htlc.transaction_output_index {
4166-
let preimage = if !htlc.offered {
4167-
if let Some((preimage, _)) = self.payment_preimages.get(&htlc.payment_hash) { Some(preimage.clone()) } else {
4168-
// We can't build an HTLC-Success transaction without the preimage
4169+
let commitment = &self.funding.current_holder_commitment;
4170+
let trusted_tx = commitment.tx.trust();
4171+
let counterparty_sig =
4172+
if let Some(counterparty_sig) = commitment.tx.counterparty_htlc_sigs.get(idx) {
4173+
*counterparty_sig
4174+
} else {
4175+
debug_assert!(false, "All non-dust HTLCs must have a corresponding counterparty signature");
41694176
continue;
4170-
}
4171-
} else { None };
4172-
if let Some(htlc_tx) = self.onchain_tx_handler.get_maybe_signed_htlc_tx(
4173-
&::bitcoin::OutPoint { txid, vout }, &preimage
4177+
};
4178+
4179+
let preimage = if htlc.offered {
4180+
None
4181+
} else if let Some((preimage, _)) = self.payment_preimages.get(&htlc.payment_hash) {
4182+
Some(*preimage)
4183+
} else {
4184+
// We can't build an HTLC-Success transaction without the preimage
4185+
continue;
4186+
};
4187+
4188+
let htlc_output = HolderHTLCOutput::build(HTLCDescriptor {
4189+
channel_derivation_parameters: ChannelDerivationParameters {
4190+
value_satoshis: self.funding.channel_parameters.channel_value_satoshis,
4191+
keys_id: self.channel_keys_id,
4192+
transaction_parameters: self.funding.channel_parameters.clone(),
4193+
},
4194+
commitment_txid: trusted_tx.txid(),
4195+
per_commitment_number: trusted_tx.commitment_number(),
4196+
per_commitment_point: trusted_tx.per_commitment_point(),
4197+
feerate_per_kw: trusted_tx.feerate_per_kw(),
4198+
htlc: htlc.clone(),
4199+
preimage,
4200+
counterparty_sig,
4201+
});
4202+
if let Some(htlc_tx) = htlc_output.get_maybe_signed_htlc_tx(
4203+
&mut self.onchain_tx_handler, &::bitcoin::OutPoint { txid, vout },
41744204
) {
41754205
if htlc_tx.is_fully_signed() {
41764206
holder_transactions.push(htlc_tx.0);

lightning/src/chain/onchaintx.rs

+36-115
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,12 @@ use bitcoin::script::{Script, ScriptBuf};
2020
use bitcoin::hashes::{Hash, HashEngine};
2121
use bitcoin::hashes::sha256::Hash as Sha256;
2222
use bitcoin::hash_types::{Txid, BlockHash};
23-
use bitcoin::secp256k1::PublicKey;
2423
use bitcoin::secp256k1::{Secp256k1, ecdsa::Signature};
2524
use bitcoin::secp256k1;
2625

2726
use crate::chain::chaininterface::{ConfirmationTarget, compute_feerate_sat_per_1000_weight};
28-
use crate::sign::{ChannelDerivationParameters, HTLCDescriptor, EntropySource, SignerProvider, ecdsa::EcdsaChannelSigner};
27+
use crate::sign::{EntropySource, HTLCDescriptor, SignerProvider, ecdsa::EcdsaChannelSigner};
2928
use crate::ln::msgs::DecodeError;
30-
use crate::types::payment::PaymentPreimage;
3129
use crate::ln::chan_utils::{self, ChannelTransactionParameters, HTLCOutputInCommitment, HolderCommitmentTransaction};
3230
use crate::chain::ClaimId;
3331
use crate::chain::chaininterface::{FeeEstimator, BroadcasterInterface, LowerBoundedFeeEstimator};
@@ -173,17 +171,6 @@ impl Writeable for Option<Vec<Option<(usize, Signature)>>> {
173171
}
174172
}
175173

176-
/// The claim commonly referred to as the pre-signed second-stage HTLC transaction.
177-
#[derive(Clone, PartialEq, Eq)]
178-
pub(crate) struct ExternalHTLCClaim {
179-
pub(crate) commitment_txid: Txid,
180-
pub(crate) per_commitment_number: u64,
181-
pub(crate) htlc: HTLCOutputInCommitment,
182-
pub(crate) preimage: Option<PaymentPreimage>,
183-
pub(crate) counterparty_sig: Signature,
184-
pub(crate) per_commitment_point: PublicKey,
185-
}
186-
187174
// Represents the different types of claims for which events are yielded externally to satisfy said
188175
// claims.
189176
#[derive(Clone, PartialEq, Eq)]
@@ -202,7 +189,7 @@ pub(crate) enum ClaimEvent {
202189
/// resolved by broadcasting a transaction with sufficient fee to claim them.
203190
BumpHTLC {
204191
target_feerate_sat_per_1000_weight: u32,
205-
htlcs: Vec<ExternalHTLCClaim>,
192+
htlcs: Vec<HTLCDescriptor>,
206193
tx_lock_time: LockTime,
207194
},
208195
}
@@ -234,7 +221,7 @@ pub(crate) enum FeerateStrategy {
234221
#[derive(Clone)]
235222
pub struct OnchainTxHandler<ChannelSigner: EcdsaChannelSigner> {
236223
channel_value_satoshis: u64,
237-
channel_keys_id: [u8; 32],
224+
channel_keys_id: [u8; 32], // Deprecated as of 0.2.
238225
destination_script: ScriptBuf, // Deprecated as of 0.2.
239226
holder_commitment: HolderCommitmentTransaction,
240227
prev_holder_commitment: Option<HolderCommitmentTransaction>,
@@ -610,19 +597,16 @@ impl<ChannelSigner: EcdsaChannelSigner> OnchainTxHandler<ChannelSigner> {
610597
let target_feerate_sat_per_1000_weight = cached_request.compute_package_feerate(
611598
fee_estimator, conf_target, feerate_strategy,
612599
);
613-
if let Some(htlcs) = cached_request.construct_malleable_package_with_external_funding(self) {
614-
return Some((
615-
new_timer,
616-
target_feerate_sat_per_1000_weight as u64,
617-
OnchainClaim::Event(ClaimEvent::BumpHTLC {
618-
target_feerate_sat_per_1000_weight,
619-
htlcs,
620-
tx_lock_time: LockTime::from_consensus(cached_request.package_locktime(cur_height)),
621-
}),
622-
));
623-
} else {
624-
return None;
625-
}
600+
let htlcs = cached_request.construct_malleable_package_with_external_funding(self)?;
601+
return Some((
602+
new_timer,
603+
target_feerate_sat_per_1000_weight as u64,
604+
OnchainClaim::Event(ClaimEvent::BumpHTLC {
605+
target_feerate_sat_per_1000_weight,
606+
htlcs,
607+
tx_lock_time: LockTime::from_consensus(cached_request.package_locktime(cur_height)),
608+
}),
609+
));
626610
}
627611

628612
let predicted_weight = cached_request.package_weight(destination_script);
@@ -1219,87 +1203,14 @@ impl<ChannelSigner: EcdsaChannelSigner> OnchainTxHandler<ChannelSigner> {
12191203
self.prev_holder_commitment = Some(replace(&mut self.holder_commitment, tx));
12201204
}
12211205

1222-
#[cfg(any(test, feature="_test_utils", feature="unsafe_revoked_tx_signing"))]
1223-
pub(crate) fn get_fully_signed_copy_holder_tx(&mut self, funding_redeemscript: &Script) -> Transaction {
1224-
let sig = self.signer.unsafe_sign_holder_commitment(&self.channel_transaction_parameters, &self.holder_commitment, &self.secp_ctx).expect("sign holder commitment");
1225-
self.holder_commitment.add_holder_sig(funding_redeemscript, sig)
1226-
}
1227-
1228-
pub(crate) fn get_maybe_signed_htlc_tx(&mut self, outp: &::bitcoin::OutPoint, preimage: &Option<PaymentPreimage>) -> Option<MaybeSignedTransaction> {
1229-
let get_signed_htlc_tx = |holder_commitment: &HolderCommitmentTransaction| {
1230-
let trusted_tx = holder_commitment.trust();
1231-
if trusted_tx.txid() != outp.txid {
1232-
return None;
1233-
}
1234-
let (htlc_idx, htlc) = trusted_tx.htlcs().iter().enumerate()
1235-
.find(|(_, htlc)| htlc.transaction_output_index.unwrap() == outp.vout)
1236-
.unwrap();
1237-
let counterparty_htlc_sig = holder_commitment.counterparty_htlc_sigs[htlc_idx];
1238-
let mut htlc_tx = trusted_tx.build_unsigned_htlc_tx(
1239-
&self.channel_transaction_parameters.as_holder_broadcastable(), htlc_idx, preimage,
1240-
);
1241-
1242-
let htlc_descriptor = HTLCDescriptor {
1243-
channel_derivation_parameters: ChannelDerivationParameters {
1244-
value_satoshis: self.channel_value_satoshis,
1245-
keys_id: self.channel_keys_id,
1246-
transaction_parameters: self.channel_transaction_parameters.clone(),
1247-
},
1248-
commitment_txid: trusted_tx.txid(),
1249-
per_commitment_number: trusted_tx.commitment_number(),
1250-
per_commitment_point: trusted_tx.per_commitment_point(),
1251-
feerate_per_kw: trusted_tx.feerate_per_kw(),
1252-
htlc: htlc.clone(),
1253-
preimage: preimage.clone(),
1254-
counterparty_sig: counterparty_htlc_sig.clone(),
1255-
};
1256-
if let Ok(htlc_sig) = self.signer.sign_holder_htlc_transaction(&htlc_tx, 0, &htlc_descriptor, &self.secp_ctx) {
1257-
htlc_tx.input[0].witness = trusted_tx.build_htlc_input_witness(
1258-
htlc_idx, &counterparty_htlc_sig, &htlc_sig, preimage,
1259-
);
1260-
}
1261-
Some(MaybeSignedTransaction(htlc_tx))
1262-
};
1263-
1264-
// Check if the HTLC spends from the current holder commitment first, or the previous.
1265-
get_signed_htlc_tx(&self.holder_commitment)
1266-
.or_else(|| self.prev_holder_commitment.as_ref().and_then(|prev_holder_commitment| get_signed_htlc_tx(prev_holder_commitment)))
1267-
}
1268-
1269-
pub(crate) fn generate_external_htlc_claim(
1270-
&self, outp: &::bitcoin::OutPoint, preimage: &Option<PaymentPreimage>
1271-
) -> Option<ExternalHTLCClaim> {
1272-
let find_htlc = |holder_commitment: &HolderCommitmentTransaction| -> Option<ExternalHTLCClaim> {
1273-
let trusted_tx = holder_commitment.trust();
1274-
if outp.txid != trusted_tx.txid() {
1275-
return None;
1276-
}
1277-
trusted_tx.htlcs().iter().enumerate()
1278-
.find(|(_, htlc)| if let Some(output_index) = htlc.transaction_output_index {
1279-
output_index == outp.vout
1280-
} else {
1281-
false
1282-
})
1283-
.map(|(htlc_idx, htlc)| {
1284-
let counterparty_htlc_sig = holder_commitment.counterparty_htlc_sigs[htlc_idx];
1285-
ExternalHTLCClaim {
1286-
commitment_txid: trusted_tx.txid(),
1287-
per_commitment_number: trusted_tx.commitment_number(),
1288-
htlc: htlc.clone(),
1289-
preimage: *preimage,
1290-
counterparty_sig: counterparty_htlc_sig,
1291-
per_commitment_point: trusted_tx.per_commitment_point(),
1292-
}
1293-
})
1294-
};
1295-
// Check if the HTLC spends from the current holder commitment or the previous one otherwise.
1296-
find_htlc(&self.holder_commitment)
1297-
.or_else(|| self.prev_holder_commitment.as_ref().map(|c| find_htlc(c)).flatten())
1298-
}
1299-
13001206
pub(crate) fn channel_type_features(&self) -> &ChannelTypeFeatures {
13011207
&self.channel_transaction_parameters.channel_type_features
13021208
}
1209+
1210+
// Deprecated as of 0.2, only use in cases where it was not previously available.
1211+
pub(crate) fn channel_keys_id(&self) -> [u8; 32] {
1212+
self.channel_keys_id
1213+
}
13031214
}
13041215

13051216
#[cfg(test)]
@@ -1320,7 +1231,7 @@ mod tests {
13201231
};
13211232
use crate::ln::channel_keys::{DelayedPaymentBasepoint, HtlcBasepoint, RevocationBasepoint};
13221233
use crate::ln::functional_test_utils::create_dummy_block;
1323-
use crate::sign::InMemorySigner;
1234+
use crate::sign::{ChannelDerivationParameters, HTLCDescriptor, InMemorySigner};
13241235
use crate::types::payment::{PaymentHash, PaymentPreimage};
13251236
use crate::util::test_utils::{TestBroadcaster, TestFeeEstimator, TestLogger};
13261237

@@ -1425,17 +1336,27 @@ mod tests {
14251336
let logger = TestLogger::new();
14261337

14271338
// Request claiming of each HTLC on the holder's commitment, with current block height 1.
1428-
let holder_commit_txid = tx_handler.current_holder_commitment_tx().trust().txid();
1339+
let holder_commit = tx_handler.current_holder_commitment_tx();
1340+
let holder_commit_txid = holder_commit.trust().txid();
14291341
let mut requests = Vec::new();
1430-
for (htlc, _) in htlcs {
1342+
for (idx, htlc) in holder_commit.htlcs().iter().enumerate() {
14311343
requests.push(PackageTemplate::build_package(
14321344
holder_commit_txid,
14331345
htlc.transaction_output_index.unwrap(),
1434-
PackageSolvingData::HolderHTLCOutput(HolderHTLCOutput::build_offered(
1435-
htlc.amount_msat,
1436-
htlc.cltv_expiry,
1437-
ChannelTypeFeatures::only_static_remote_key(),
1438-
)),
1346+
PackageSolvingData::HolderHTLCOutput(HolderHTLCOutput::build(HTLCDescriptor {
1347+
channel_derivation_parameters: ChannelDerivationParameters {
1348+
value_satoshis: tx_handler.channel_value_satoshis,
1349+
keys_id: tx_handler.channel_keys_id,
1350+
transaction_parameters: tx_handler.channel_transaction_parameters.clone(),
1351+
},
1352+
commitment_txid: holder_commit_txid,
1353+
per_commitment_number: holder_commit.commitment_number(),
1354+
per_commitment_point: holder_commit.per_commitment_point(),
1355+
feerate_per_kw: holder_commit.feerate_per_kw(),
1356+
htlc: htlc.clone(),
1357+
preimage: None,
1358+
counterparty_sig: holder_commit.counterparty_htlc_sigs[idx].clone(),
1359+
})),
14391360
0,
14401361
));
14411362
}

0 commit comments

Comments
 (0)