Skip to content

Commit d3181c5

Browse files
committed
Let ChannelSigner set htlc tx script pubkey
This allows the htlc tx output to easily be changed according to the features of the channel, or the evolution of the LN specification. The output could even be set to completely arbitrary scripts if compatibility with the formal LN spec is not required. Builders of htlc transactions now ask a `ChannelSigner` for the appropriate revokeable script pubkey to use, and then pass it to the htlc transaction constructors.
1 parent 1155fa0 commit d3181c5

File tree

7 files changed

+54
-59
lines changed

7 files changed

+54
-59
lines changed

lightning/src/chain/onchaintx.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1210,8 +1210,9 @@ impl<ChannelSigner: EcdsaChannelSigner> OnchainTxHandler<ChannelSigner> {
12101210
.find(|(_, htlc)| htlc.transaction_output_index.unwrap() == outp.vout)
12111211
.unwrap();
12121212
let counterparty_htlc_sig = holder_commitment.counterparty_htlc_sigs[htlc_idx];
1213+
let revokeable_spk = self.signer.get_revokeable_spk(true, holder_commitment.commitment_number(), &holder_commitment.per_commitment_point(), &self.secp_ctx);
12131214
let mut htlc_tx = trusted_tx.build_unsigned_htlc_tx(
1214-
&self.channel_transaction_parameters.as_holder_broadcastable(), htlc_idx, preimage,
1215+
htlc_idx, preimage, revokeable_spk,
12151216
);
12161217

12171218
let htlc_descriptor = HTLCDescriptor {
@@ -1295,7 +1296,7 @@ mod tests {
12951296
};
12961297
use crate::ln::channel_keys::{DelayedPaymentBasepoint, HtlcBasepoint, RevocationBasepoint};
12971298
use crate::ln::functional_test_utils::create_dummy_block;
1298-
use crate::sign::InMemorySigner;
1299+
use crate::sign::{ChannelSigner, InMemorySigner};
12991300
use crate::types::payment::{PaymentHash, PaymentPreimage};
13001301
use crate::util::test_utils::{TestBroadcaster, TestFeeEstimator, TestLogger};
13011302

@@ -1307,7 +1308,7 @@ mod tests {
13071308
#[test]
13081309
fn test_broadcast_height() {
13091310
let secp_ctx = Secp256k1::new();
1310-
let signer = InMemorySigner::new(
1311+
let mut signer = InMemorySigner::new(
13111312
&secp_ctx,
13121313
SecretKey::from_slice(&[41; 32]).unwrap(),
13131314
SecretKey::from_slice(&[41; 32]).unwrap(),
@@ -1356,6 +1357,7 @@ mod tests {
13561357
funding_outpoint: Some(funding_outpoint),
13571358
channel_type_features: ChannelTypeFeatures::only_static_remote_key(),
13581359
};
1360+
signer.provide_channel_parameters(&chan_params);
13591361

13601362
// Create an OnchainTxHandler for a commitment containing HTLCs with CLTV expiries of 0, 1,
13611363
// and 2 blocks.

lightning/src/events/bump_transaction.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ use crate::ln::chan_utils::{
2626
};
2727
use crate::prelude::*;
2828
use crate::sign::{
29-
ChannelDerivationParameters, HTLCDescriptor, SignerProvider, P2WPKH_WITNESS_WEIGHT
29+
ChannelDerivationParameters, ChannelSigner, HTLCDescriptor, SignerProvider, P2WPKH_WITNESS_WEIGHT,
3030
};
3131
use crate::sign::ecdsa::EcdsaChannelSigner;
3232
use crate::sync::Mutex;
@@ -728,6 +728,7 @@ where
728728
output: vec![],
729729
};
730730
let mut must_spend = Vec::with_capacity(htlc_descriptors.len());
731+
let mut signers_and_revokeable_spks = BTreeMap::new();
731732
for htlc_descriptor in htlc_descriptors {
732733
let htlc_input = htlc_descriptor.unsigned_tx_input();
733734
must_spend.push(Input {
@@ -740,7 +741,13 @@ where
740741
},
741742
});
742743
htlc_tx.input.push(htlc_input);
743-
let htlc_output = htlc_descriptor.tx_output(&self.secp);
744+
let revokeable_spk = signers_and_revokeable_spks.entry(htlc_descriptor.channel_derivation_parameters.keys_id)
745+
.or_insert_with(|| {
746+
let signer = htlc_descriptor.derive_channel_signer(&self.signer_provider);
747+
let revokeable_spk = signer.get_revokeable_spk(true, htlc_descriptor.per_commitment_number, &htlc_descriptor.per_commitment_point, &self.secp);
748+
(signer, revokeable_spk)
749+
}).1.clone();
750+
let htlc_output = htlc_descriptor.tx_output(revokeable_spk);
744751
htlc_tx.output.push(htlc_output);
745752
}
746753

@@ -789,10 +796,9 @@ where
789796
log_debug!(self.logger, "Signing HTLC transaction {}", htlc_psbt.unsigned_tx.compute_txid());
790797
htlc_tx = self.utxo_source.sign_psbt(htlc_psbt)?;
791798

792-
let mut signers = BTreeMap::new();
793799
for (idx, htlc_descriptor) in htlc_descriptors.iter().enumerate() {
794-
let signer = signers.entry(htlc_descriptor.channel_derivation_parameters.keys_id)
795-
.or_insert_with(|| htlc_descriptor.derive_channel_signer(&self.signer_provider));
800+
// Unwrap because we derived the corresponding signers for all htlc descriptors further above
801+
let signer = &signers_and_revokeable_spks.get(&htlc_descriptor.channel_derivation_parameters.keys_id).unwrap().0;
796802
let htlc_sig = signer.sign_holder_htlc_transaction(&htlc_tx, idx, htlc_descriptor, &self.secp)?;
797803
let witness_script = htlc_descriptor.witness_script(&self.secp);
798804
htlc_tx.input[idx].witness = htlc_descriptor.tx_input_witness(&htlc_sig, &witness_script);

lightning/src/ln/chan_utils.rs

Lines changed: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -704,13 +704,12 @@ pub(crate) fn make_funding_redeemscript_from_slices(broadcaster_funding_key: &[u
704704
///
705705
/// Panics if htlc.transaction_output_index.is_none() (as such HTLCs do not appear in the
706706
/// commitment transaction).
707-
pub fn build_htlc_transaction(commitment_txid: &Txid, feerate_per_kw: u32, contest_delay: u16, htlc: &HTLCOutputInCommitment, channel_type_features: &ChannelTypeFeatures, broadcaster_delayed_payment_key: &DelayedPaymentKey, revocation_key: &RevocationKey) -> Transaction {
708-
let txins= vec![build_htlc_input(commitment_txid, htlc, channel_type_features)];
707+
pub fn build_htlc_transaction(commitment_txid: &Txid, feerate_per_kw: u32, htlc: &HTLCOutputInCommitment, channel_type_features: &ChannelTypeFeatures, revokeable_spk: ScriptBuf) -> Transaction {
708+
let txins = vec![build_htlc_input(commitment_txid, htlc, channel_type_features)];
709709

710710
let mut txouts: Vec<TxOut> = Vec::new();
711711
txouts.push(build_htlc_output(
712-
feerate_per_kw, contest_delay, htlc, channel_type_features,
713-
broadcaster_delayed_payment_key, revocation_key
712+
feerate_per_kw, htlc, channel_type_features, revokeable_spk,
714713
));
715714

716715
Transaction {
@@ -734,7 +733,7 @@ pub(crate) fn build_htlc_input(commitment_txid: &Txid, htlc: &HTLCOutputInCommit
734733
}
735734

736735
pub(crate) fn build_htlc_output(
737-
feerate_per_kw: u32, contest_delay: u16, htlc: &HTLCOutputInCommitment, channel_type_features: &ChannelTypeFeatures, broadcaster_delayed_payment_key: &DelayedPaymentKey, revocation_key: &RevocationKey
736+
feerate_per_kw: u32, htlc: &HTLCOutputInCommitment, channel_type_features: &ChannelTypeFeatures, revokeable_spk: ScriptBuf
738737
) -> TxOut {
739738
let weight = if htlc.offered {
740739
htlc_timeout_tx_weight(channel_type_features)
@@ -749,7 +748,7 @@ pub(crate) fn build_htlc_output(
749748
};
750749

751750
TxOut {
752-
script_pubkey: get_revokeable_redeemscript(revocation_key, contest_delay, broadcaster_delayed_payment_key).to_p2wsh(),
751+
script_pubkey: revokeable_spk,
753752
value: output_value,
754753
}
755754
}
@@ -1740,8 +1739,7 @@ impl<'a> TrustedCommitmentTransaction<'a> {
17401739
///
17411740
/// This function is only valid in the holder commitment context, it always uses EcdsaSighashType::All.
17421741
pub fn get_htlc_sigs<T: secp256k1::Signing, ES: Deref>(
1743-
&self, htlc_base_key: &SecretKey, channel_parameters: &DirectedChannelTransactionParameters,
1744-
entropy_source: &ES, secp_ctx: &Secp256k1<T>,
1742+
&self, htlc_base_key: &SecretKey, entropy_source: &ES, secp_ctx: &Secp256k1<T>, revokeable_spk: ScriptBuf,
17451743
) -> Result<Vec<Signature>, ()> where ES::Target: EntropySource {
17461744
let inner = self.inner;
17471745
let keys = &inner.keys;
@@ -1751,7 +1749,7 @@ impl<'a> TrustedCommitmentTransaction<'a> {
17511749

17521750
for this_htlc in inner.htlcs.iter() {
17531751
assert!(this_htlc.transaction_output_index.is_some());
1754-
let htlc_tx = build_htlc_transaction(&txid, inner.feerate_per_kw, channel_parameters.contest_delay(), &this_htlc, &self.channel_type_features, &keys.broadcaster_delayed_payment_key, &keys.revocation_key);
1752+
let htlc_tx = build_htlc_transaction(&txid, inner.feerate_per_kw, &this_htlc, &self.channel_type_features, revokeable_spk.clone());
17551753

17561754
let htlc_redeemscript = get_htlc_redeemscript_with_explicit_keys(&this_htlc, &self.channel_type_features, &keys.broadcaster_htlc_key, &keys.countersignatory_htlc_key, &keys.revocation_key);
17571755

@@ -1762,11 +1760,7 @@ impl<'a> TrustedCommitmentTransaction<'a> {
17621760
}
17631761

17641762
/// Builds the second-level holder HTLC transaction for the HTLC with index `htlc_index`.
1765-
pub(crate) fn build_unsigned_htlc_tx(
1766-
&self, channel_parameters: &DirectedChannelTransactionParameters, htlc_index: usize,
1767-
preimage: &Option<PaymentPreimage>,
1768-
) -> Transaction {
1769-
let keys = &self.inner.keys;
1763+
pub(crate) fn build_unsigned_htlc_tx(&self, htlc_index: usize, preimage: &Option<PaymentPreimage>, revokeable_spk: ScriptBuf) -> Transaction {
17701764
let this_htlc = &self.inner.htlcs[htlc_index];
17711765
assert!(this_htlc.transaction_output_index.is_some());
17721766
// if we don't have preimage for an HTLC-Success, we can't generate an HTLC transaction.
@@ -1775,8 +1769,8 @@ impl<'a> TrustedCommitmentTransaction<'a> {
17751769
if this_htlc.offered && preimage.is_some() { unreachable!(); }
17761770

17771771
build_htlc_transaction(
1778-
&self.inner.built.txid, self.inner.feerate_per_kw, channel_parameters.contest_delay(), &this_htlc,
1779-
&self.channel_type_features, &keys.broadcaster_delayed_payment_key, &keys.revocation_key
1772+
&self.inner.built.txid, self.inner.feerate_per_kw, &this_htlc,
1773+
&self.channel_type_features, revokeable_spk,
17801774
)
17811775
}
17821776

lightning/src/ln/channel.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5130,11 +5130,11 @@ impl<SP: Deref> Channel<SP> where
51305130

51315131
let mut nondust_htlc_sources = Vec::with_capacity(htlcs_cloned.len());
51325132
let mut htlcs_and_sigs = Vec::with_capacity(htlcs_cloned.len());
5133+
let revokeable_spk = self.context.holder_signer.as_ref().get_revokeable_spk(true, commitment_stats.tx.commitment_number(), &commitment_stats.tx.per_commitment_point(), &self.context.secp_ctx);
51335134
for (idx, (htlc, mut source_opt)) in htlcs_cloned.drain(..).enumerate() {
51345135
if let Some(_) = htlc.transaction_output_index {
51355136
let htlc_tx = chan_utils::build_htlc_transaction(&commitment_txid, commitment_stats.feerate_per_kw,
5136-
self.context.get_counterparty_selected_contest_delay().unwrap(), &htlc, &self.context.channel_type,
5137-
&keys.broadcaster_delayed_payment_key, &keys.revocation_key);
5137+
&htlc, &self.context.channel_type, revokeable_spk.clone());
51385138

51395139
let htlc_redeemscript = chan_utils::get_htlc_redeemscript(&htlc, &self.context.channel_type, &keys);
51405140
let htlc_sighashtype = if self.context.channel_type.supports_anchors_zero_fee_htlc_tx() { EcdsaSighashType::SinglePlusAnyoneCanPay } else { EcdsaSighashType::All };
@@ -8103,6 +8103,7 @@ impl<SP: Deref> Channel<SP> where
81038103
).map_err(|_| ChannelError::Ignore("Failed to get signatures for new commitment_signed".to_owned()))?;
81048104
signature = res.0;
81058105
htlc_signatures = res.1;
8106+
let revokeable_spk = ecdsa.get_revokeable_spk(false, commitment_stats.tx.commitment_number(), &commitment_stats.tx.per_commitment_point(), &self.context.secp_ctx);
81068107

81078108
log_trace!(logger, "Signed remote commitment tx {} (txid {}) with redeemscript {} -> {} in channel {}",
81088109
encode::serialize_hex(&commitment_stats.tx.trust().built_transaction().transaction),
@@ -8111,7 +8112,7 @@ impl<SP: Deref> Channel<SP> where
81118112

81128113
for (ref htlc_sig, ref htlc) in htlc_signatures.iter().zip(htlcs) {
81138114
log_trace!(logger, "Signed remote HTLC tx {} with redeemscript {} with pubkey {} -> {} in channel {}",
8114-
encode::serialize_hex(&chan_utils::build_htlc_transaction(&counterparty_commitment_txid, commitment_stats.feerate_per_kw, self.context.get_holder_selected_contest_delay(), htlc, &self.context.channel_type, &counterparty_keys.broadcaster_delayed_payment_key, &counterparty_keys.revocation_key)),
8115+
encode::serialize_hex(&chan_utils::build_htlc_transaction(&counterparty_commitment_txid, commitment_stats.feerate_per_kw, htlc, &self.context.channel_type, revokeable_spk.clone())),
81158116
encode::serialize_hex(&chan_utils::get_htlc_redeemscript(&htlc, &self.context.channel_type, &counterparty_keys)),
81168117
log_bytes!(counterparty_keys.broadcaster_htlc_key.to_public_key().serialize()),
81178118
log_bytes!(htlc_sig.serialize_compact()[..]), &self.context.channel_id());
@@ -11017,9 +11018,8 @@ mod tests {
1101711018
let remote_signature = Signature::from_der(&<Vec<u8>>::from_hex($counterparty_htlc_sig_hex).unwrap()[..]).unwrap();
1101811019

1101911020
let ref htlc = htlcs[$htlc_idx];
11020-
let mut htlc_tx = chan_utils::build_htlc_transaction(&unsigned_tx.txid, chan.context.feerate_per_kw,
11021-
chan.context.get_counterparty_selected_contest_delay().unwrap(),
11022-
&htlc, $opt_anchors, &keys.broadcaster_delayed_payment_key, &keys.revocation_key);
11021+
let revokeable_spk = signer.get_revokeable_spk(true, holder_commitment_tx.commitment_number(), &holder_commitment_tx.per_commitment_point(), &secp_ctx);
11022+
let mut htlc_tx = chan_utils::build_htlc_transaction(&unsigned_tx.txid, chan.context.feerate_per_kw, &htlc, $opt_anchors, revokeable_spk);
1102311023
let htlc_redeemscript = chan_utils::get_htlc_redeemscript(&htlc, $opt_anchors, &keys);
1102411024
let htlc_sighashtype = if $opt_anchors.supports_anchors_zero_fee_htlc_tx() { EcdsaSighashType::SinglePlusAnyoneCanPay } else { EcdsaSighashType::All };
1102511025
let htlc_sighash = Message::from_digest(sighash::SighashCache::new(&htlc_tx).p2wsh_signature_hash(0, &htlc_redeemscript, htlc.to_bitcoin_amount(), htlc_sighashtype).unwrap().as_raw_hash().to_byte_array());

lightning/src/ln/monitor_tests.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@
99

1010
//! Further functional tests which test blockchain reorganizations.
1111
12-
use crate::sign::{ecdsa::EcdsaChannelSigner, OutputSpender, SpendableOutputDescriptor};
12+
use alloc::collections::BTreeMap;
13+
14+
use crate::sign::{ecdsa::EcdsaChannelSigner, ChannelSigner, OutputSpender, SpendableOutputDescriptor};
1315
use crate::chain::channelmonitor::{ANTI_REORG_DELAY, LATENCY_GRACE_PERIOD_BLOCKS, Balance, BalanceSource, ChannelMonitorUpdateStep};
1416
use crate::chain::transaction::OutPoint;
1517
use crate::chain::chaininterface::{ConfirmationTarget, LowerBoundedFeeEstimator, compute_feerate_sat_per_1000_weight};
@@ -2901,6 +2903,7 @@ fn test_anchors_aggregated_revoked_htlc_tx() {
29012903
}],
29022904
};
29032905
let mut descriptors = Vec::with_capacity(4);
2906+
let mut revokeable_spks = BTreeMap::new();
29042907
for event in events {
29052908
// We don't use the `BumpTransactionEventHandler` here because it does not support
29062909
// creating one transaction from multiple `HTLCResolution` events.
@@ -2909,7 +2912,12 @@ fn test_anchors_aggregated_revoked_htlc_tx() {
29092912
for htlc_descriptor in &htlc_descriptors {
29102913
assert!(!htlc_descriptor.htlc.offered);
29112914
htlc_tx.input.push(htlc_descriptor.unsigned_tx_input());
2912-
htlc_tx.output.push(htlc_descriptor.tx_output(&secp));
2915+
let revokeable_spk = revokeable_spks.entry(htlc_descriptor.channel_derivation_parameters.keys_id)
2916+
.or_insert_with(|| {
2917+
let signer = htlc_descriptor.derive_channel_signer(&nodes[1].keys_manager);
2918+
signer.get_revokeable_spk(true, htlc_descriptor.per_commitment_number, &htlc_descriptor.per_commitment_point, &secp)
2919+
}).clone();
2920+
htlc_tx.output.push(htlc_descriptor.tx_output(revokeable_spk));
29132921
}
29142922
descriptors.append(&mut htlc_descriptors);
29152923
htlc_tx.lock_time = tx_lock_time;

lightning/src/sign/mod.rs

Lines changed: 10 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -631,30 +631,12 @@ impl HTLCDescriptor {
631631

632632
/// Returns the delayed output created as a result of spending the HTLC output in the commitment
633633
/// transaction.
634-
pub fn tx_output<C: secp256k1::Signing + secp256k1::Verification>(
635-
&self, secp: &Secp256k1<C>,
636-
) -> TxOut {
637-
let channel_params =
638-
self.channel_derivation_parameters.transaction_parameters.as_holder_broadcastable();
639-
let broadcaster_keys = channel_params.broadcaster_pubkeys();
640-
let counterparty_keys = channel_params.countersignatory_pubkeys();
641-
let broadcaster_delayed_key = DelayedPaymentKey::from_basepoint(
642-
secp,
643-
&broadcaster_keys.delayed_payment_basepoint,
644-
&self.per_commitment_point,
645-
);
646-
let counterparty_revocation_key = &RevocationKey::from_basepoint(
647-
&secp,
648-
&counterparty_keys.revocation_basepoint,
649-
&self.per_commitment_point,
650-
);
634+
pub fn tx_output(&self, revokeable_spk: ScriptBuf) -> TxOut {
651635
chan_utils::build_htlc_output(
652636
self.feerate_per_kw,
653-
channel_params.contest_delay(),
654637
&self.htlc,
655-
channel_params.channel_type_features(),
656-
&broadcaster_delayed_key,
657-
&counterparty_revocation_key,
638+
&self.channel_derivation_parameters.transaction_parameters.channel_type_features,
639+
revokeable_spk,
658640
)
659641
}
660642

@@ -1468,19 +1450,21 @@ impl EcdsaChannelSigner for InMemorySigner {
14681450
let commitment_txid = built_tx.txid;
14691451

14701452
let mut htlc_sigs = Vec::with_capacity(commitment_tx.htlcs().len());
1453+
let revokeable_spk = self.get_revokeable_spk(
1454+
false,
1455+
trusted_tx.commitment_number(),
1456+
&trusted_tx.per_commitment_point(),
1457+
secp_ctx,
1458+
);
14711459
for htlc in commitment_tx.htlcs() {
14721460
let channel_parameters = self.get_channel_parameters().expect(MISSING_PARAMS_ERR);
1473-
let holder_selected_contest_delay =
1474-
self.holder_selected_contest_delay().expect(MISSING_PARAMS_ERR);
14751461
let chan_type = &channel_parameters.channel_type_features;
14761462
let htlc_tx = chan_utils::build_htlc_transaction(
14771463
&commitment_txid,
14781464
commitment_tx.feerate_per_kw(),
1479-
holder_selected_contest_delay,
14801465
htlc,
14811466
chan_type,
1482-
&keys.broadcaster_delayed_payment_key,
1483-
&keys.revocation_key,
1467+
revokeable_spk.clone(),
14841468
);
14851469
let htlc_redeemscript = chan_utils::get_htlc_redeemscript(&htlc, chan_type, &keys);
14861470
let htlc_sighashtype = if chan_type.supports_anchors_zero_fee_htlc_tx() {

lightning/src/util/test_channel_signer.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,8 @@ impl EcdsaChannelSigner for TestChannelSigner {
308308
}
309309
}
310310
assert_eq!(htlc_tx.input[input], htlc_descriptor.unsigned_tx_input());
311-
assert_eq!(htlc_tx.output[input], htlc_descriptor.tx_output(secp_ctx));
311+
let revokeable_spk = self.get_revokeable_spk(true, htlc_descriptor.per_commitment_number, &htlc_descriptor.per_commitment_point, secp_ctx);
312+
assert_eq!(htlc_tx.output[input], htlc_descriptor.tx_output(revokeable_spk));
312313
{
313314
let witness_script = htlc_descriptor.witness_script(secp_ctx);
314315
let sighash_type = if self.channel_type_features().supports_anchors_zero_fee_htlc_tx() {

0 commit comments

Comments
 (0)