Skip to content

Commit 72ca0a9

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 cbac0e7 commit 72ca0a9

File tree

7 files changed

+51
-57
lines changed

7 files changed

+51
-57
lines changed

lightning/src/chain/onchaintx.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1192,8 +1192,9 @@ impl<ChannelSigner: EcdsaChannelSigner> OnchainTxHandler<ChannelSigner> {
11921192
.find(|(_, htlc)| htlc.transaction_output_index.unwrap() == outp.vout)
11931193
.unwrap();
11941194
let counterparty_htlc_sig = holder_commitment.counterparty_htlc_sigs[htlc_idx];
1195+
let revokeable_spk = self.signer.get_revokeable_spk(true, holder_commitment.commitment_number(), &holder_commitment.per_commitment_point(), &self.secp_ctx);
11951196
let mut htlc_tx = trusted_tx.build_unsigned_htlc_tx(
1196-
&self.channel_transaction_parameters.as_holder_broadcastable(), htlc_idx, preimage,
1197+
htlc_idx, preimage, revokeable_spk,
11971198
);
11981199

11991200
let htlc_descriptor = HTLCDescriptor {

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
@@ -5122,11 +5122,11 @@ impl<SP: Deref> Channel<SP> where
51225122

51235123
let mut nondust_htlc_sources = Vec::with_capacity(htlcs_cloned.len());
51245124
let mut htlcs_and_sigs = Vec::with_capacity(htlcs_cloned.len());
5125+
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);
51255126
for (idx, (htlc, mut source_opt)) in htlcs_cloned.drain(..).enumerate() {
51265127
if let Some(_) = htlc.transaction_output_index {
51275128
let htlc_tx = chan_utils::build_htlc_transaction(&commitment_txid, commitment_stats.feerate_per_kw,
5128-
self.context.get_counterparty_selected_contest_delay().unwrap(), &htlc, &self.context.channel_type,
5129-
&keys.broadcaster_delayed_payment_key, &keys.revocation_key);
5129+
&htlc, &self.context.channel_type, revokeable_spk.clone());
51305130

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

80858086
log_trace!(logger, "Signed remote commitment tx {} (txid {}) with redeemscript {} -> {} in channel {}",
80868087
encode::serialize_hex(&commitment_stats.tx.trust().built_transaction().transaction),
@@ -8089,7 +8090,7 @@ impl<SP: Deref> Channel<SP> where
80898090

80908091
for (ref htlc_sig, ref htlc) in htlc_signatures.iter().zip(htlcs) {
80918092
log_trace!(logger, "Signed remote HTLC tx {} with redeemscript {} with pubkey {} -> {} in channel {}",
8092-
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)),
8093+
encode::serialize_hex(&chan_utils::build_htlc_transaction(&counterparty_commitment_txid, commitment_stats.feerate_per_kw, htlc, &self.context.channel_type, revokeable_spk.clone())),
80938094
encode::serialize_hex(&chan_utils::get_htlc_redeemscript(&htlc, &self.context.channel_type, &counterparty_keys)),
80948095
log_bytes!(counterparty_keys.broadcaster_htlc_key.to_public_key().serialize()),
80958096
log_bytes!(htlc_sig.serialize_compact()[..]), &self.context.channel_id());
@@ -10993,9 +10994,8 @@ mod tests {
1099310994
let remote_signature = Signature::from_der(&<Vec<u8>>::from_hex($counterparty_htlc_sig_hex).unwrap()[..]).unwrap();
1099410995

1099510996
let ref htlc = htlcs[$htlc_idx];
10996-
let mut htlc_tx = chan_utils::build_htlc_transaction(&unsigned_tx.txid, chan.context.feerate_per_kw,
10997-
chan.context.get_counterparty_selected_contest_delay().unwrap(),
10998-
&htlc, $opt_anchors, &keys.broadcaster_delayed_payment_key, &keys.revocation_key);
10997+
let revokeable_spk = signer.get_revokeable_spk(true, holder_commitment_tx.commitment_number(), &holder_commitment_tx.per_commitment_point(), &secp_ctx);
10998+
let mut htlc_tx = chan_utils::build_htlc_transaction(&unsigned_tx.txid, chan.context.feerate_per_kw, &htlc, $opt_anchors, revokeable_spk);
1099910999
let htlc_redeemscript = chan_utils::get_htlc_redeemscript(&htlc, $opt_anchors, &keys);
1100011000
let htlc_sighashtype = if $opt_anchors.supports_anchors_zero_fee_htlc_tx() { EcdsaSighashType::SinglePlusAnyoneCanPay } else { EcdsaSighashType::All };
1100111001
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};
@@ -2827,6 +2829,7 @@ fn test_anchors_aggregated_revoked_htlc_tx() {
28272829
}],
28282830
};
28292831
let mut descriptors = Vec::with_capacity(4);
2832+
let mut revokeable_spks = BTreeMap::new();
28302833
for event in events {
28312834
// We don't use the `BumpTransactionEventHandler` here because it does not support
28322835
// creating one transaction from multiple `HTLCResolution` events.
@@ -2835,7 +2838,12 @@ fn test_anchors_aggregated_revoked_htlc_tx() {
28352838
for htlc_descriptor in &htlc_descriptors {
28362839
assert!(!htlc_descriptor.htlc.offered);
28372840
htlc_tx.input.push(htlc_descriptor.unsigned_tx_input());
2838-
htlc_tx.output.push(htlc_descriptor.tx_output(&secp));
2841+
let revokeable_spk = revokeable_spks.entry(htlc_descriptor.channel_derivation_parameters.keys_id)
2842+
.or_insert_with(|| {
2843+
let signer = htlc_descriptor.derive_channel_signer(&nodes[1].keys_manager);
2844+
signer.get_revokeable_spk(true, htlc_descriptor.per_commitment_number, &htlc_descriptor.per_commitment_point, &secp)
2845+
}).clone();
2846+
htlc_tx.output.push(htlc_descriptor.tx_output(revokeable_spk));
28392847
}
28402848
descriptors.append(&mut htlc_descriptors);
28412849
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
@@ -637,30 +637,12 @@ impl HTLCDescriptor {
637637

638638
/// Returns the delayed output created as a result of spending the HTLC output in the commitment
639639
/// transaction.
640-
pub fn tx_output<C: secp256k1::Signing + secp256k1::Verification>(
641-
&self, secp: &Secp256k1<C>,
642-
) -> TxOut {
643-
let channel_params =
644-
self.channel_derivation_parameters.transaction_parameters.as_holder_broadcastable();
645-
let broadcaster_keys = channel_params.broadcaster_pubkeys();
646-
let counterparty_keys = channel_params.countersignatory_pubkeys();
647-
let broadcaster_delayed_key = DelayedPaymentKey::from_basepoint(
648-
secp,
649-
&broadcaster_keys.delayed_payment_basepoint,
650-
&self.per_commitment_point,
651-
);
652-
let counterparty_revocation_key = &RevocationKey::from_basepoint(
653-
&secp,
654-
&counterparty_keys.revocation_basepoint,
655-
&self.per_commitment_point,
656-
);
640+
pub fn tx_output(&self, revokeable_spk: ScriptBuf) -> TxOut {
657641
chan_utils::build_htlc_output(
658642
self.feerate_per_kw,
659-
channel_params.contest_delay(),
660643
&self.htlc,
661-
channel_params.channel_type_features(),
662-
&broadcaster_delayed_key,
663-
&counterparty_revocation_key,
644+
&self.channel_derivation_parameters.transaction_parameters.channel_type_features,
645+
revokeable_spk,
664646
)
665647
}
666648

@@ -1474,19 +1456,21 @@ impl EcdsaChannelSigner for InMemorySigner {
14741456
let commitment_txid = built_tx.txid;
14751457

14761458
let mut htlc_sigs = Vec::with_capacity(commitment_tx.htlcs().len());
1459+
let revokeable_spk = self.get_revokeable_spk(
1460+
false,
1461+
trusted_tx.commitment_number(),
1462+
&trusted_tx.per_commitment_point(),
1463+
secp_ctx,
1464+
);
14771465
for htlc in commitment_tx.htlcs() {
14781466
let channel_parameters = self.get_channel_parameters().expect(MISSING_PARAMS_ERR);
1479-
let holder_selected_contest_delay =
1480-
self.holder_selected_contest_delay().expect(MISSING_PARAMS_ERR);
14811467
let chan_type = &channel_parameters.channel_type_features;
14821468
let htlc_tx = chan_utils::build_htlc_transaction(
14831469
&commitment_txid,
14841470
commitment_tx.feerate_per_kw(),
1485-
holder_selected_contest_delay,
14861471
htlc,
14871472
chan_type,
1488-
&keys.broadcaster_delayed_payment_key,
1489-
&keys.revocation_key,
1473+
revokeable_spk.clone(),
14901474
);
14911475
let htlc_redeemscript = chan_utils::get_htlc_redeemscript(&htlc, chan_type, &keys);
14921476
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)