Skip to content

Commit fda4235

Browse files
committed
Support scalar tweak to rotate holder funding key during splicing
A scalar tweak applied to the base funding key to obtain the channel's funding key used in the 2-of-2 multisig. This is used to derive additional keys from the same secret backing the base `funding_pubkey`, as we have to rotate keys for each successful splice attempt. The tweak is computed similar to existing tweaks used in [BOLT-3](https://github.com/lightning/bolts/blob/master/03-transactions.md#key-derivation), but rather than using the `per_commitment_point`, as we cannot guarantee that it will change across multiple splice attempts, we use the txid of the funding transaction the splice transaction is spending, henceforth known as the splice's parent funding txid. tweak = base_funding_key + SHA256(splice_parent_funding_txid || base_funding_pubkey)
1 parent a55149f commit fda4235

File tree

9 files changed

+295
-184
lines changed

9 files changed

+295
-184
lines changed

lightning/src/chain/channelmonitor.rs

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3463,8 +3463,10 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
34633463
let countersignatory_keys =
34643464
&self.onchain_tx_handler.channel_transaction_parameters.holder_pubkeys;
34653465

3466-
let broadcaster_funding_key = broadcaster_keys.funding_pubkey;
3467-
let countersignatory_funding_key = countersignatory_keys.funding_pubkey;
3466+
let broadcaster_funding_key =
3467+
broadcaster_keys.funding_pubkey(&self.onchain_tx_handler.secp_ctx);
3468+
let countersignatory_funding_key =
3469+
countersignatory_keys.funding_pubkey(&self.onchain_tx_handler.secp_ctx);
34683470
let keys = TxCreationKeys::from_channel_static_keys(&their_per_commitment_point,
34693471
&broadcaster_keys, &countersignatory_keys, &self.onchain_tx_handler.secp_ctx);
34703472
let channel_parameters =
@@ -5405,13 +5407,13 @@ mod tests {
54055407
[0; 32],
54065408
);
54075409

5408-
let counterparty_pubkeys = ChannelPublicKeys {
5409-
funding_pubkey: PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[44; 32]).unwrap()),
5410-
revocation_basepoint: RevocationBasepoint::from(PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[45; 32]).unwrap())),
5411-
payment_point: PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[46; 32]).unwrap()),
5412-
delayed_payment_basepoint: DelayedPaymentBasepoint::from(PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[47; 32]).unwrap())),
5413-
htlc_basepoint: HtlcBasepoint::from(PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[48; 32]).unwrap()))
5414-
};
5410+
let counterparty_pubkeys = ChannelPublicKeys::new(
5411+
PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[44; 32]).unwrap()), None,
5412+
RevocationBasepoint::from(PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[45; 32]).unwrap())),
5413+
PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[46; 32]).unwrap()),
5414+
DelayedPaymentBasepoint::from(PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[47; 32]).unwrap())),
5415+
HtlcBasepoint::from(PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[48; 32]).unwrap()))
5416+
);
54155417
let funding_outpoint = OutPoint { txid: Txid::all_zeros(), index: u16::MAX };
54165418
let channel_id = ChannelId::v1_from_funding_outpoint(funding_outpoint);
54175419
let channel_parameters = ChannelTransactionParameters {
@@ -5657,13 +5659,13 @@ mod tests {
56575659
[0; 32],
56585660
);
56595661

5660-
let counterparty_pubkeys = ChannelPublicKeys {
5661-
funding_pubkey: PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[44; 32]).unwrap()),
5662-
revocation_basepoint: RevocationBasepoint::from(PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[45; 32]).unwrap())),
5663-
payment_point: PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[46; 32]).unwrap()),
5664-
delayed_payment_basepoint: DelayedPaymentBasepoint::from(PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[47; 32]).unwrap())),
5665-
htlc_basepoint: HtlcBasepoint::from(PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[48; 32]).unwrap())),
5666-
};
5662+
let counterparty_pubkeys = ChannelPublicKeys::new(
5663+
PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[44; 32]).unwrap()), None,
5664+
RevocationBasepoint::from(PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[45; 32]).unwrap())),
5665+
PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[46; 32]).unwrap()),
5666+
DelayedPaymentBasepoint::from(PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[47; 32]).unwrap())),
5667+
HtlcBasepoint::from(PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[48; 32]).unwrap())),
5668+
);
56675669
let funding_outpoint = OutPoint { txid: Txid::all_zeros(), index: u16::MAX };
56685670
let channel_id = ChannelId::v1_from_funding_outpoint(funding_outpoint);
56695671
let channel_parameters = ChannelTransactionParameters {

lightning/src/chain/onchaintx.rs

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -668,7 +668,7 @@ impl<ChannelSigner: EcdsaChannelSigner> OnchainTxHandler<ChannelSigner> {
668668
}
669669

670670
// We'll locate an anchor output we can spend within the commitment transaction.
671-
let funding_pubkey = &self.channel_transaction_parameters.holder_pubkeys.funding_pubkey;
671+
let funding_pubkey = &self.channel_transaction_parameters.holder_pubkeys.funding_pubkey(&self.secp_ctx);
672672
match chan_utils::get_anchor_output(&tx.0, funding_pubkey) {
673673
// An anchor output was found, so we should yield a funding event externally.
674674
Some((idx, _)) => {
@@ -1320,28 +1320,20 @@ mod tests {
13201320
[0; 32],
13211321
[0; 32],
13221322
);
1323-
let counterparty_pubkeys = ChannelPublicKeys {
1324-
funding_pubkey: PublicKey::from_secret_key(
1325-
&secp_ctx,
1326-
&SecretKey::from_slice(&[44; 32]).unwrap(),
1323+
let counterparty_pubkeys = ChannelPublicKeys::new(
1324+
PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[44; 32]).unwrap()),
1325+
None,
1326+
RevocationBasepoint::from(
1327+
PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[45; 32]).unwrap()),
13271328
),
1328-
revocation_basepoint: RevocationBasepoint::from(PublicKey::from_secret_key(
1329-
&secp_ctx,
1330-
&SecretKey::from_slice(&[45; 32]).unwrap(),
1331-
)),
1332-
payment_point: PublicKey::from_secret_key(
1333-
&secp_ctx,
1334-
&SecretKey::from_slice(&[46; 32]).unwrap(),
1329+
PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[46; 32]).unwrap()),
1330+
DelayedPaymentBasepoint::from(
1331+
PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[47; 32]).unwrap()),
13351332
),
1336-
delayed_payment_basepoint: DelayedPaymentBasepoint::from(PublicKey::from_secret_key(
1337-
&secp_ctx,
1338-
&SecretKey::from_slice(&[47; 32]).unwrap(),
1339-
)),
1340-
htlc_basepoint: HtlcBasepoint::from(PublicKey::from_secret_key(
1341-
&secp_ctx,
1342-
&SecretKey::from_slice(&[48; 32]).unwrap(),
1343-
)),
1344-
};
1333+
HtlcBasepoint::from(
1334+
PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[48; 32]).unwrap()),
1335+
),
1336+
);
13451337
let funding_outpoint = OutPoint { txid: Txid::all_zeros(), index: u16::MAX };
13461338

13471339
// Use non-anchor channels so that HTLC-Timeouts are broadcast immediately instead of sent

lightning/src/events/bump_transaction.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ use bitcoin::constants::WITNESS_SCALE_FACTOR;
3838
use bitcoin::locktime::absolute::LockTime;
3939
use bitcoin::consensus::Encodable;
4040
use bitcoin::secp256k1;
41-
use bitcoin::secp256k1::{PublicKey, Secp256k1};
41+
use bitcoin::secp256k1::{PublicKey, Secp256k1, Verification};
4242
use bitcoin::secp256k1::ecdsa::Signature;
4343
use bitcoin::transaction::Version;
4444

@@ -61,9 +61,9 @@ pub struct AnchorDescriptor {
6161
impl AnchorDescriptor {
6262
/// Returns the UTXO to be spent by the anchor input, which can be obtained via
6363
/// [`Self::unsigned_tx_input`].
64-
pub fn previous_utxo(&self) -> TxOut {
64+
pub fn previous_utxo<C: Verification>(&self, secp: &Secp256k1<C>) -> TxOut {
6565
TxOut {
66-
script_pubkey: self.witness_script().to_p2wsh(),
66+
script_pubkey: self.witness_script(secp).to_p2wsh(),
6767
value: Amount::from_sat(ANCHOR_OUTPUT_VALUE_SATOSHI),
6868
}
6969
}
@@ -80,16 +80,16 @@ impl AnchorDescriptor {
8080
}
8181

8282
/// Returns the witness script of the anchor output in the commitment transaction.
83-
pub fn witness_script(&self) -> ScriptBuf {
83+
pub fn witness_script<C: Verification>(&self, secp: &Secp256k1<C>) -> ScriptBuf {
8484
let channel_params = self.channel_derivation_parameters.transaction_parameters.as_holder_broadcastable();
85-
chan_utils::get_anchor_redeemscript(&channel_params.broadcaster_pubkeys().funding_pubkey)
85+
chan_utils::get_anchor_redeemscript(&channel_params.broadcaster_pubkeys().funding_pubkey(secp))
8686
}
8787

8888
/// Returns the fully signed witness required to spend the anchor output in the commitment
8989
/// transaction.
90-
pub fn tx_input_witness(&self, signature: &Signature) -> Witness {
90+
pub fn tx_input_witness<C: Verification>(&self, secp: &Secp256k1<C>, signature: &Signature) -> Witness {
9191
let channel_params = self.channel_derivation_parameters.transaction_parameters.as_holder_broadcastable();
92-
chan_utils::build_anchor_input_witness(&channel_params.broadcaster_pubkeys().funding_pubkey, signature)
92+
chan_utils::build_anchor_input_witness(&channel_params.broadcaster_pubkeys().funding_pubkey(secp), signature)
9393
}
9494
}
9595

@@ -601,7 +601,7 @@ where
601601
// Our commitment transaction already has fees allocated to it, so we should take them into
602602
// account. We do so by pretending the commitment transaction's fee and weight are part of
603603
// the anchor input.
604-
let mut anchor_utxo = anchor_descriptor.previous_utxo();
604+
let mut anchor_utxo = anchor_descriptor.previous_utxo(&self.secp);
605605
let commitment_tx_fee_sat = Amount::from_sat(commitment_tx_fee_sat);
606606
anchor_utxo.value += commitment_tx_fee_sat;
607607
let starting_package_and_fixed_input_satisfaction_weight =
@@ -641,7 +641,7 @@ where
641641
// construct psbt
642642
let mut anchor_psbt = Psbt::from_unsigned_tx(anchor_tx).unwrap();
643643
// add witness_utxo to anchor input
644-
anchor_psbt.inputs[0].witness_utxo = Some(anchor_descriptor.previous_utxo());
644+
anchor_psbt.inputs[0].witness_utxo = Some(anchor_descriptor.previous_utxo(&self.secp));
645645
// add witness_utxo to remaining inputs
646646
for (idx, utxo) in coin_selection.confirmed_utxos.into_iter().enumerate() {
647647
// add 1 to skip the anchor input
@@ -679,7 +679,7 @@ where
679679
let signer = self.signer_provider.derive_channel_signer(anchor_descriptor.channel_derivation_parameters.keys_id);
680680
let channel_parameters = &anchor_descriptor.channel_derivation_parameters.transaction_parameters;
681681
let anchor_sig = signer.sign_holder_anchor_input(channel_parameters, &anchor_tx, 0, &self.secp)?;
682-
anchor_tx.input[0].witness = anchor_descriptor.tx_input_witness(&anchor_sig);
682+
anchor_tx.input[0].witness = anchor_descriptor.tx_input_witness(&self.secp, &anchor_sig);
683683

684684
#[cfg(debug_assertions)] {
685685
let signed_tx_weight = anchor_tx.weight().to_wu();

lightning/src/ln/chan_utils.rs

Lines changed: 72 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ use crate::util::transaction_utils;
3535

3636
use bitcoin::locktime::absolute::LockTime;
3737
use bitcoin::ecdsa::Signature as BitcoinSignature;
38-
use bitcoin::secp256k1::{SecretKey, PublicKey, Scalar};
38+
use bitcoin::secp256k1::{SecretKey, PublicKey, Scalar, Verification};
3939
use bitcoin::secp256k1::{Secp256k1, ecdsa::Signature, Message};
4040
use bitcoin::{secp256k1, Sequence, Witness};
4141

@@ -430,6 +430,24 @@ pub fn derive_private_revocation_key<T: secp256k1::Signing>(secp_ctx: &Secp256k1
430430
.expect("Addition only fails if the tweak is the inverse of the key. This is not possible when the tweak commits to the key.")
431431
}
432432

433+
/// Computes the tweak to apply to the base funding key of a channel.
434+
///
435+
/// The tweak is computed similar to existing tweaks used in
436+
/// [BOLT-3](https://github.com/lightning/bolts/blob/master/03-transactions.md#key-derivation), but
437+
/// rather than using the `per_commitment_point`, as we cannot guarantee that it will change across
438+
/// multiple splice attempts, we use the txid of the funding transaction the splice transaction is
439+
/// spending, henceforth known as the splice's parent funding txid.
440+
///
441+
/// tweak = base_funding_key + SHA256(splice_parent_funding_txid || base_funding_pubkey)
442+
//
443+
// TODO: Expose a helper on `FundingScope` that calls this.
444+
pub fn compute_funding_key_tweak(base_funding_pubkey: &PublicKey, splice_parent_funding_txid: &Txid) -> Scalar {
445+
let mut sha = Sha256::engine();
446+
sha.input(splice_parent_funding_txid.as_byte_array());
447+
sha.input(&base_funding_pubkey.serialize());
448+
Scalar::from_be_bytes(Sha256::from_engine(sha).to_byte_array()).unwrap()
449+
}
450+
433451
/// The set of public keys which are used in the creation of one commitment transaction.
434452
/// These are derived from the channel base keys and per-commitment data.
435453
///
@@ -470,7 +488,14 @@ impl_writeable_tlv_based!(TxCreationKeys, {
470488
pub struct ChannelPublicKeys {
471489
/// The public key which is used to sign all commitment transactions, as it appears in the
472490
/// on-chain channel lock-in 2-of-2 multisig output.
473-
pub funding_pubkey: PublicKey,
491+
funding_pubkey: PublicKey,
492+
/// A scalar tweak applied to the base funding key to obtain the channel's funding key used in
493+
/// the 2-of-2 multisig. This is used to derive additional keys from the same secret backing the
494+
/// base `funding_pubkey`, as we have to rotate keys for each successful splice attempt.
495+
///
496+
/// The tweak is computed as described in [`compute_funding_key_tweak`], and it must only be set
497+
/// for holder public keys, never for counterparty ones.
498+
funding_key_tweak: Option<Scalar>,
474499
/// The base point which is used (with [`RevocationKey::from_basepoint`]) to derive per-commitment
475500
/// revocation keys. This is combined with the per-commitment-secret generated by the
476501
/// counterparty to create a secret which the counterparty can reveal to revoke previous
@@ -489,12 +514,43 @@ pub struct ChannelPublicKeys {
489514
pub htlc_basepoint: HtlcBasepoint,
490515
}
491516

517+
impl ChannelPublicKeys {
518+
/// Constructs a new instance of [`ChannelPublicKeys`].
519+
pub fn new(
520+
funding_pubkey: PublicKey, funding_key_tweak: Option<Scalar>,
521+
revocation_basepoint: RevocationBasepoint, payment_point: PublicKey,
522+
delayed_payment_basepoint: DelayedPaymentBasepoint, htlc_basepoint: HtlcBasepoint,
523+
) -> Self {
524+
Self {
525+
funding_pubkey, funding_key_tweak, revocation_basepoint, payment_point,
526+
delayed_payment_basepoint, htlc_basepoint,
527+
}
528+
}
529+
530+
/// Returns the funding public key with the required tweak applied if one exists.
531+
pub fn funding_pubkey<C: secp256k1::Verification>(&self, secp: &Secp256k1<C>) -> PublicKey {
532+
self.funding_key_tweak
533+
.map(|tweak| {
534+
self.funding_pubkey.add_exp_tweak(secp, &tweak).expect(
535+
"Addition only fails if the tweak is the inverse of the key"
536+
)
537+
})
538+
.unwrap_or(self.funding_pubkey)
539+
}
540+
541+
/// Returns the tweak, if one exists, to apply to the funding key.
542+
pub fn funding_key_tweak(&self) -> Option<Scalar> {
543+
self.funding_key_tweak
544+
}
545+
}
546+
492547
impl_writeable_tlv_based!(ChannelPublicKeys, {
493548
(0, funding_pubkey, required),
494549
(2, revocation_basepoint, required),
495550
(4, payment_point, required),
496551
(6, delayed_payment_basepoint, required),
497552
(8, htlc_basepoint, required),
553+
(10, funding_key_tweak, option),
498554
});
499555

500556
impl TxCreationKeys {
@@ -931,10 +987,10 @@ impl ChannelTransactionParameters {
931987
}
932988
}
933989

934-
pub(crate) fn make_funding_redeemscript(&self) -> ScriptBuf {
990+
pub(crate) fn make_funding_redeemscript<C: Verification>(&self, secp: &Secp256k1<C>) -> ScriptBuf {
935991
make_funding_redeemscript(
936-
&self.holder_pubkeys.funding_pubkey,
937-
&self.counterparty_parameters.as_ref().unwrap().pubkeys.funding_pubkey
992+
&self.holder_pubkeys.funding_pubkey(secp),
993+
&self.counterparty_parameters.as_ref().unwrap().pubkeys.funding_pubkey(&secp)
938994
)
939995
}
940996

@@ -945,13 +1001,13 @@ impl ChannelTransactionParameters {
9451001

9461002
#[cfg(test)]
9471003
pub fn test_dummy(channel_value_satoshis: u64) -> Self {
948-
let dummy_keys = ChannelPublicKeys {
949-
funding_pubkey: PublicKey::from_slice(&[2; 33]).unwrap(),
950-
revocation_basepoint: PublicKey::from_slice(&[2; 33]).unwrap().into(),
951-
payment_point: PublicKey::from_slice(&[2; 33]).unwrap(),
952-
delayed_payment_basepoint: PublicKey::from_slice(&[2; 33]).unwrap().into(),
953-
htlc_basepoint: PublicKey::from_slice(&[2; 33]).unwrap().into(),
954-
};
1004+
let dummy_keys = ChannelPublicKeys::new(
1005+
PublicKey::from_slice(&[2; 33]).unwrap(), None,
1006+
PublicKey::from_slice(&[2; 33]).unwrap().into(),
1007+
PublicKey::from_slice(&[2; 33]).unwrap(),
1008+
PublicKey::from_slice(&[2; 33]).unwrap().into(),
1009+
PublicKey::from_slice(&[2; 33]).unwrap().into(),
1010+
);
9551011
Self {
9561012
holder_pubkeys: dummy_keys.clone(),
9571013
holder_selected_contest_delay: 42,
@@ -1136,13 +1192,10 @@ impl HolderCommitmentTransaction {
11361192
countersignatory_htlc_key: HtlcKey::from_basepoint(&secp_ctx, &HtlcBasepoint::from(dummy_key), &dummy_key),
11371193
broadcaster_delayed_payment_key: DelayedPaymentKey::from_basepoint(&secp_ctx, &DelayedPaymentBasepoint::from(dummy_key), &dummy_key),
11381194
};
1139-
let channel_pubkeys = ChannelPublicKeys {
1140-
funding_pubkey: dummy_key.clone(),
1141-
revocation_basepoint: RevocationBasepoint::from(dummy_key),
1142-
payment_point: dummy_key.clone(),
1143-
delayed_payment_basepoint: DelayedPaymentBasepoint::from(dummy_key.clone()),
1144-
htlc_basepoint: HtlcBasepoint::from(dummy_key.clone())
1145-
};
1195+
let channel_pubkeys = ChannelPublicKeys::new(
1196+
dummy_key.clone(), None, RevocationBasepoint::from(dummy_key), dummy_key.clone(),
1197+
DelayedPaymentBasepoint::from(dummy_key.clone()), HtlcBasepoint::from(dummy_key.clone())
1198+
);
11461199
let channel_parameters = ChannelTransactionParameters {
11471200
holder_pubkeys: channel_pubkeys.clone(),
11481201
holder_selected_contest_delay: 0,

0 commit comments

Comments
 (0)