Skip to content

Commit 7280be9

Browse files
committed
Properly handle funding key rotation during splices
When splicing, we're required by protocol to retain all the existing keys material except the funding key which we're allowed to rotate. In the original implementation we acknowledged that but figured we'd stick with a single `pubkey` method in the `ChannelSigner` anyway cause adding a specific method for it is annoying. Sadly, this was ultimately broken - in `FundingScope::for_splice`, we called the signer's `new_pubkeys` method (renamed from `pubkeys` after splicing initially landed), replacing all of the public keys the `Channel` would use rather than just the funding key. This can result in commitment signature mismatches if the signer changes any keys aside from the funding one. `InMemorySigner` did not do so, however, so we didn't notice the bug. Luckily-ish, in 189b8ac we started generating a fresh `remote_key` when splicing (at least when upgrading from 0.1 to 0.2 or when setting `KeysManager` to use v1 `remote_key` derivation). This breaks splicing cause we can't communicate the new `remote_key` to the counterparty during the splicing handshake. Ultimately this bug is because the API we had didn't communicate to the signer that we weren't allowed to change anything except the funding key, and allowed returning a `ChannelPublicKeys` which would break the channel. Here we fix this by renaming `new_pubkeys` `pubkeys` again (partially reverting 9d291e0 but keeping the changed requirements that `pubkeys` only be called once) and adding a new `ChannelSigner:new_funding_pubkey` method specifically for splicing. We also update `channel.rs` to correctly fetch the new funding pubkey before sending `splice_init`, storing it in the `PendingFunding` untl we build a `FundingScope`.
1 parent 0f11db0 commit 7280be9

File tree

7 files changed

+98
-66
lines changed

7 files changed

+98
-66
lines changed

lightning/src/chain/channelmonitor.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7009,7 +7009,7 @@ mod tests {
70097009
let funding_outpoint = OutPoint { txid: Txid::all_zeros(), index: u16::MAX };
70107010
let channel_id = ChannelId::v1_from_funding_outpoint(funding_outpoint);
70117011
let channel_parameters = ChannelTransactionParameters {
7012-
holder_pubkeys: keys.new_pubkeys(None, &secp_ctx),
7012+
holder_pubkeys: keys.pubkeys(&secp_ctx),
70137013
holder_selected_contest_delay: 66,
70147014
is_outbound_from_holder: true,
70157015
counterparty_parameters: Some(CounterpartyChannelTransactionParameters {
@@ -7272,7 +7272,7 @@ mod tests {
72727272
let funding_outpoint = OutPoint { txid: Txid::all_zeros(), index: u16::MAX };
72737273
let channel_id = ChannelId::v1_from_funding_outpoint(funding_outpoint);
72747274
let channel_parameters = ChannelTransactionParameters {
7275-
holder_pubkeys: keys.new_pubkeys(None, &secp_ctx),
7275+
holder_pubkeys: keys.pubkeys(&secp_ctx),
72767276
holder_selected_contest_delay: 66,
72777277
is_outbound_from_holder: true,
72787278
counterparty_parameters: Some(CounterpartyChannelTransactionParameters {

lightning/src/chain/onchaintx.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1339,7 +1339,7 @@ mod tests {
13391339
// Use non-anchor channels so that HTLC-Timeouts are broadcast immediately instead of sent
13401340
// to the user for external funding.
13411341
let chan_params = ChannelTransactionParameters {
1342-
holder_pubkeys: signer.new_pubkeys(None, &secp_ctx),
1342+
holder_pubkeys: signer.pubkeys(&secp_ctx),
13431343
holder_selected_contest_delay: 66,
13441344
is_outbound_from_holder: true,
13451345
counterparty_parameters: Some(CounterpartyChannelTransactionParameters {

lightning/src/ln/chan_utils.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1018,11 +1018,11 @@ pub struct ChannelTransactionParameters {
10181018
/// If a channel was funded with transaction A, and later spliced with transaction B, this field
10191019
/// tracks the txid of transaction A.
10201020
///
1021-
/// See [`compute_funding_key_tweak`] and [`ChannelSigner::new_pubkeys`] for more context on how
1021+
/// See [`compute_funding_key_tweak`] and [`ChannelSigner::pubkeys`] for more context on how
10221022
/// this may be used.
10231023
///
10241024
/// [`compute_funding_key_tweak`]: crate::sign::compute_funding_key_tweak
1025-
/// [`ChannelSigner::new_pubkeys`]: crate::sign::ChannelSigner::new_pubkeys
1025+
/// [`ChannelSigner::pubkeys`]: crate::sign::ChannelSigner::pubkeys
10261026
pub splice_parent_funding_txid: Option<Txid>,
10271027
/// This channel's type, as negotiated during channel open. For old objects where this field
10281028
/// wasn't serialized, it will default to static_remote_key at deserialization.
@@ -2245,8 +2245,8 @@ mod tests {
22452245
let counterparty_signer = keys_provider.derive_channel_signer(keys_provider.generate_channel_keys_id(true, 1));
22462246
let per_commitment_secret = SecretKey::from_slice(&<Vec<u8>>::from_hex("1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100").unwrap()[..]).unwrap();
22472247
let per_commitment_point = PublicKey::from_secret_key(&secp_ctx, &per_commitment_secret);
2248-
let holder_pubkeys = signer.new_pubkeys(None, &secp_ctx);
2249-
let counterparty_pubkeys = counterparty_signer.new_pubkeys(None, &secp_ctx).clone();
2248+
let holder_pubkeys = signer.pubkeys(&secp_ctx);
2249+
let counterparty_pubkeys = counterparty_signer.pubkeys(&secp_ctx).clone();
22502250
let channel_parameters = ChannelTransactionParameters {
22512251
holder_pubkeys: holder_pubkeys.clone(),
22522252
holder_selected_contest_delay: 0,

lightning/src/ln/channel.rs

Lines changed: 50 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2470,6 +2470,7 @@ impl FundingScope {
24702470
fn for_splice<SP: Deref>(
24712471
prev_funding: &Self, context: &ChannelContext<SP>, our_funding_contribution: SignedAmount,
24722472
their_funding_contribution: SignedAmount, counterparty_funding_pubkey: PublicKey,
2473+
our_new_holder_keys: ChannelPublicKeys,
24732474
) -> Self
24742475
where
24752476
SP::Target: SignerProvider,
@@ -2489,19 +2490,15 @@ impl FundingScope {
24892490
debug_assert!(post_value_to_self_msat.is_some());
24902491
let post_value_to_self_msat = post_value_to_self_msat.unwrap();
24912492

2492-
// Rotate the pubkeys using the prev_funding_txid as a tweak
2493-
let prev_funding_txid = prev_funding.get_funding_txid();
2494-
let holder_pubkeys = context.new_holder_pubkeys(prev_funding_txid);
2495-
24962493
let channel_parameters = &prev_funding.channel_transaction_parameters;
24972494
let mut post_channel_transaction_parameters = ChannelTransactionParameters {
2498-
holder_pubkeys,
2495+
holder_pubkeys: our_new_holder_keys,
24992496
holder_selected_contest_delay: channel_parameters.holder_selected_contest_delay,
25002497
// The 'outbound' attribute doesn't change, even if the splice initiator is the other node
25012498
is_outbound_from_holder: channel_parameters.is_outbound_from_holder,
25022499
counterparty_parameters: channel_parameters.counterparty_parameters.clone(),
25032500
funding_outpoint: None, // filled later
2504-
splice_parent_funding_txid: prev_funding_txid,
2501+
splice_parent_funding_txid: prev_funding.get_funding_txid(),
25052502
channel_type_features: channel_parameters.channel_type_features.clone(),
25062503
channel_value_satoshis: post_channel_value,
25072504
};
@@ -2625,13 +2622,17 @@ struct PendingFunding {
26252622

26262623
/// The funding txid used in the `splice_locked` received from the counterparty.
26272624
received_funding_txid: Option<Txid>,
2625+
2626+
/// The new funding key the signer provided us for use in the splice output.
2627+
new_holder_funding_key: PublicKey,
26282628
}
26292629

26302630
impl_writeable_tlv_based!(PendingFunding, {
26312631
(1, funding_negotiation, upgradable_option),
26322632
(3, negotiated_candidates, required_vec),
26332633
(5, sent_funding_txid, option),
26342634
(7, received_funding_txid, option),
2635+
(9, new_holder_funding_key, required),
26352636
});
26362637

26372638
enum FundingNegotiation {
@@ -3510,7 +3511,7 @@ where
35103511

35113512
// TODO(dual_funding): Checks for `funding_feerate_sat_per_1000_weight`?
35123513

3513-
let pubkeys = holder_signer.new_pubkeys(None, &secp_ctx);
3514+
let pubkeys = holder_signer.pubkeys(&secp_ctx);
35143515

35153516
let funding = FundingScope {
35163517
value_to_self_msat,
@@ -3748,7 +3749,7 @@ where
37483749
Err(_) => return Err(APIError::ChannelUnavailable { err: "Failed to get destination script".to_owned()}),
37493750
};
37503751

3751-
let pubkeys = holder_signer.new_pubkeys(None, &secp_ctx);
3752+
let pubkeys = holder_signer.pubkeys(&secp_ctx);
37523753
let temporary_channel_id = temporary_channel_id_fn.map(|f| f(&pubkeys))
37533754
.unwrap_or_else(|| ChannelId::temporary_from_entropy_source(entropy_source));
37543755

@@ -4111,16 +4112,6 @@ where
41114112
return &mut self.holder_signer;
41124113
}
41134114

4114-
/// Returns holder pubkeys to use for the channel.
4115-
fn new_holder_pubkeys(&self, prev_funding_txid: Option<Txid>) -> ChannelPublicKeys {
4116-
match &self.holder_signer {
4117-
ChannelSignerType::Ecdsa(ecdsa) => ecdsa.new_pubkeys(prev_funding_txid, &self.secp_ctx),
4118-
// TODO (taproot|arik)
4119-
#[cfg(taproot)]
4120-
_ => todo!(),
4121-
}
4122-
}
4123-
41244115
/// Only allowed immediately after deserialization if get_outbound_scid_alias returns 0,
41254116
/// indicating we were written by LDK prior to 0.0.106 which did not set outbound SCID aliases
41264117
/// or prior to any channel actions during `Channel` initialization.
@@ -11962,17 +11953,27 @@ where
1196211953
change_script,
1196311954
};
1196411955

11956+
// Rotate the funding pubkey using the prev_funding_txid as a tweak
11957+
let prev_funding_txid = self.funding.get_funding_txid();
11958+
let funding_pubkey = match (prev_funding_txid, &self.context.holder_signer) {
11959+
(None, _) => {
11960+
debug_assert!(false);
11961+
self.funding.get_holder_pubkeys().funding_pubkey
11962+
},
11963+
(Some(prev_funding_txid), ChannelSignerType::Ecdsa(ecdsa)) =>
11964+
ecdsa.new_funding_pubkey(prev_funding_txid, &self.context.secp_ctx),
11965+
#[cfg(taproot)]
11966+
_ => todo!(),
11967+
};
11968+
1196511969
self.pending_splice = Some(PendingFunding {
1196611970
funding_negotiation: Some(FundingNegotiation::AwaitingAck { context }),
1196711971
negotiated_candidates: vec![],
1196811972
sent_funding_txid: None,
1196911973
received_funding_txid: None,
11974+
new_holder_funding_key: funding_pubkey,
1197011975
});
1197111976

11972-
// Rotate the pubkeys using the prev_funding_txid as a tweak
11973-
let prev_funding_txid = self.funding.get_funding_txid();
11974-
let funding_pubkey = self.context.new_holder_pubkeys(prev_funding_txid).funding_pubkey;
11975-
1197611977
msgs::SpliceInit {
1197711978
channel_id: self.context.channel_id,
1197811979
funding_contribution_satoshis: adjusted_funding_contribution.to_sat(),
@@ -12056,12 +12057,28 @@ where
1205612057
self.validate_splice_contributions(our_funding_contribution, their_funding_contribution)
1205712058
.map_err(|e| ChannelError::WarnAndDisconnect(e))?;
1205812059

12060+
// Rotate the pubkeys using the prev_funding_txid as a tweak
12061+
let prev_funding_txid = self.funding.get_funding_txid();
12062+
let funding_pubkey = match (prev_funding_txid, &self.context.holder_signer) {
12063+
(None, _) => {
12064+
debug_assert!(false);
12065+
self.funding.get_holder_pubkeys().funding_pubkey
12066+
},
12067+
(Some(prev_funding_txid), ChannelSignerType::Ecdsa(ecdsa)) =>
12068+
ecdsa.new_funding_pubkey(prev_funding_txid, &self.context.secp_ctx),
12069+
#[cfg(taproot)]
12070+
_ => todo!(),
12071+
};
12072+
let mut new_keys = self.funding.get_holder_pubkeys().clone();
12073+
new_keys.funding_pubkey = funding_pubkey;
12074+
1205912075
Ok(FundingScope::for_splice(
1206012076
&self.funding,
1206112077
&self.context,
1206212078
our_funding_contribution,
1206312079
their_funding_contribution,
1206412080
msg.funding_pubkey,
12081+
new_keys,
1206512082
))
1206612083
}
1206712084

@@ -12206,9 +12223,9 @@ where
1220612223
// optimization, but for often-offline nodes it may be, as we may connect and immediately
1220712224
// go into splicing from both sides.
1220812225

12209-
let funding_pubkey = splice_funding.get_holder_pubkeys().funding_pubkey;
12210-
12226+
let new_funding_pubkey = splice_funding.get_holder_pubkeys().funding_pubkey;
1221112227
self.pending_splice = Some(PendingFunding {
12228+
new_holder_funding_key: new_funding_pubkey,
1221212229
funding_negotiation: Some(FundingNegotiation::ConstructingTransaction {
1221312230
funding: splice_funding,
1221412231
interactive_tx_constructor,
@@ -12221,7 +12238,7 @@ where
1222112238
Ok(msgs::SpliceAck {
1222212239
channel_id: self.context.channel_id,
1222312240
funding_contribution_satoshis: our_funding_contribution.to_sat(),
12224-
funding_pubkey,
12241+
funding_pubkey: new_funding_pubkey,
1222512242
require_confirmed_inputs: None,
1222612243
})
1222712244
}
@@ -12284,12 +12301,12 @@ where
1228412301
fn validate_splice_ack(&self, msg: &msgs::SpliceAck) -> Result<FundingScope, ChannelError> {
1228512302
// TODO(splicing): Add check that we are the splice (quiescence) initiator
1228612303

12287-
let funding_negotiation_context = match &self
12304+
let pending_splice = self
1228812305
.pending_splice
1228912306
.as_ref()
12290-
.ok_or(ChannelError::Ignore("Channel is not in pending splice".to_owned()))?
12291-
.funding_negotiation
12292-
{
12307+
.ok_or_else(|| ChannelError::Ignore("Channel is not in pending splice".to_owned()))?;
12308+
12309+
let funding_negotiation_context = match &pending_splice.funding_negotiation {
1229312310
Some(FundingNegotiation::AwaitingAck { context }) => context,
1229412311
Some(FundingNegotiation::ConstructingTransaction { .. })
1229512312
| Some(FundingNegotiation::AwaitingSignatures { .. }) => {
@@ -12309,12 +12326,16 @@ where
1230912326
self.validate_splice_contributions(our_funding_contribution, their_funding_contribution)
1231012327
.map_err(|e| ChannelError::WarnAndDisconnect(e))?;
1231112328

12329+
let mut new_keys = self.funding.get_holder_pubkeys().clone();
12330+
new_keys.funding_pubkey = pending_splice.new_holder_funding_key;
12331+
1231212332
Ok(FundingScope::for_splice(
1231312333
&self.funding,
1231412334
&self.context,
1231512335
our_funding_contribution,
1231612336
their_funding_contribution,
1231712337
msg.funding_pubkey,
12338+
new_keys,
1231812339
))
1231912340
}
1232012341

lightning/src/sign/mod.rs

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -272,12 +272,12 @@ pub enum SpendableOutputDescriptor {
272272
/// To derive the delayed payment key which is used to sign this input, you must pass the
273273
/// holder [`InMemorySigner::delayed_payment_base_key`] (i.e., the private key which
274274
/// corresponds to the [`ChannelPublicKeys::delayed_payment_basepoint`] in
275-
/// [`ChannelSigner::new_pubkeys`]) and the provided
275+
/// [`ChannelSigner::pubkeys`]) and the provided
276276
/// [`DelayedPaymentOutputDescriptor::per_commitment_point`] to
277277
/// [`chan_utils::derive_private_key`]. The DelayedPaymentKey can be generated without the
278278
/// secret key using [`DelayedPaymentKey::from_basepoint`] and only the
279279
/// [`ChannelPublicKeys::delayed_payment_basepoint`] which appears in
280-
/// [`ChannelSigner::new_pubkeys`].
280+
/// [`ChannelSigner::pubkeys`].
281281
///
282282
/// To derive the [`DelayedPaymentOutputDescriptor::revocation_pubkey`] provided here (which is
283283
/// used in the witness script generation), you must pass the counterparty
@@ -292,7 +292,7 @@ pub enum SpendableOutputDescriptor {
292292
/// [`chan_utils::get_revokeable_redeemscript`].
293293
DelayedPaymentOutput(DelayedPaymentOutputDescriptor),
294294
/// An output spendable exclusively by our payment key (i.e., the private key that corresponds
295-
/// to the `payment_point` in [`ChannelSigner::new_pubkeys`]). The output type depends on the
295+
/// to the `payment_point` in [`ChannelSigner::pubkeys`]). The output type depends on the
296296
/// channel type negotiated.
297297
///
298298
/// On an anchor outputs channel, the witness in the spending input is:
@@ -792,19 +792,25 @@ pub trait ChannelSigner {
792792
/// and pause future signing operations until this validation completes.
793793
fn validate_counterparty_revocation(&self, idx: u64, secret: &SecretKey) -> Result<(), ()>;
794794

795-
/// Returns a *new* set of holder channel public keys and basepoints. They may be the same as a
796-
/// previous value, but are also allowed to change arbitrarily. Signing methods must still
797-
/// support signing for any keys which have ever been returned. This should only be called
798-
/// either for new channels or new splices.
795+
/// Returns the holder channel public keys and basepoints. This should only be called once
796+
/// during channel creation and as such implementations are allowed undefined behavior if
797+
/// called more than once.
799798
///
800-
/// `splice_parent_funding_txid` can be used to compute a tweak to rotate the funding key in the
801-
/// 2-of-2 multisig script during a splice. See [`compute_funding_key_tweak`] for an example
802-
/// tweak and more details.
799+
/// This method is *not* asynchronous. Instead, the value must be computed locally or in
800+
/// advance and cached.
801+
fn pubkeys(&self, secp_ctx: &Secp256k1<secp256k1::All>) -> ChannelPublicKeys;
802+
803+
/// Returns a new funding pubkey (i.e. our public which is used in a 2-of-2 with the
804+
/// counterparty's key to to lock the funds on-chain) for a spliced channel.
805+
///
806+
/// `splice_parent_funding_txid` can be used to compute a tweak with which to rotate the base
807+
/// key (which will then be available later in signing operations via
808+
/// [`ChannelTransactionParameters::splice_parent_funding_txid`]).
803809
///
804810
/// This method is *not* asynchronous. Instead, the value must be cached locally.
805-
fn new_pubkeys(
806-
&self, splice_parent_funding_txid: Option<Txid>, secp_ctx: &Secp256k1<secp256k1::All>,
807-
) -> ChannelPublicKeys;
811+
fn new_funding_pubkey(
812+
&self, splice_parent_funding_txid: Txid, secp_ctx: &Secp256k1<secp256k1::All>,
813+
) -> PublicKey;
808814

809815
/// Returns an arbitrary identifier describing the set of keys which are provided back to you in
810816
/// some [`SpendableOutputDescriptor`] types. This should be sufficient to identify this
@@ -1457,17 +1463,13 @@ impl ChannelSigner for InMemorySigner {
14571463
Ok(())
14581464
}
14591465

1460-
fn new_pubkeys(
1461-
&self, splice_parent_funding_txid: Option<Txid>, secp_ctx: &Secp256k1<secp256k1::All>,
1462-
) -> ChannelPublicKeys {
1466+
fn pubkeys(&self, secp_ctx: &Secp256k1<secp256k1::All>) -> ChannelPublicKeys {
14631467
// Because splices always break downgrades, we go ahead and always use the new derivation
14641468
// here as its just much better.
1465-
let use_v2_derivation =
1466-
self.v2_remote_key_derivation || splice_parent_funding_txid.is_some();
14671469
let payment_key =
1468-
if use_v2_derivation { &self.payment_key_v2 } else { &self.payment_key_v1 };
1470+
if self.v2_remote_key_derivation { &self.payment_key_v2 } else { &self.payment_key_v1 };
14691471
let from_secret = |s: &SecretKey| PublicKey::from_secret_key(secp_ctx, s);
1470-
let mut pubkeys = ChannelPublicKeys {
1472+
let pubkeys = ChannelPublicKeys {
14711473
funding_pubkey: from_secret(&self.funding_key.0),
14721474
revocation_basepoint: RevocationBasepoint::from(from_secret(&self.revocation_base_key)),
14731475
payment_point: from_secret(payment_key),
@@ -1477,13 +1479,15 @@ impl ChannelSigner for InMemorySigner {
14771479
htlc_basepoint: HtlcBasepoint::from(from_secret(&self.htlc_base_key)),
14781480
};
14791481

1480-
if splice_parent_funding_txid.is_some() {
1481-
pubkeys.funding_pubkey =
1482-
self.funding_key(splice_parent_funding_txid).public_key(secp_ctx);
1483-
}
14841482
pubkeys
14851483
}
14861484

1485+
fn new_funding_pubkey(
1486+
&self, splice_parent_funding_txid: Txid, secp_ctx: &Secp256k1<secp256k1::All>,
1487+
) -> PublicKey {
1488+
self.funding_key(Some(splice_parent_funding_txid)).public_key(secp_ctx)
1489+
}
1490+
14871491
fn channel_keys_id(&self) -> [u8; 32] {
14881492
self.channel_keys_id
14891493
}

lightning/src/util/dyn_signer.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -174,9 +174,12 @@ delegate!(DynSigner, ChannelSigner,
174174
holder_tx: &HolderCommitmentTransaction,
175175
preimages: Vec<PaymentPreimage>
176176
) -> Result<(), ()>,
177-
fn new_pubkeys(,
178-
splice_parent_funding_txid: Option<Txid>, secp_ctx: &Secp256k1<secp256k1::All>
177+
fn pubkeys(,
178+
secp_ctx: &Secp256k1<secp256k1::All>
179179
) -> ChannelPublicKeys,
180+
fn new_funding_pubkey(,
181+
splice_parent_funding_txid: Txid, secp_ctx: &Secp256k1<secp256k1::All>
182+
) -> PublicKey,
180183
fn channel_keys_id(,) -> [u8; 32],
181184
fn validate_counterparty_revocation(, idx: u64, secret: &SecretKey) -> Result<(), ()>
182185
);

lightning/src/util/test_channel_signer.rs

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -221,10 +221,14 @@ impl ChannelSigner for TestChannelSigner {
221221
Ok(())
222222
}
223223

224-
fn new_pubkeys(
225-
&self, splice_parent_funding_txid: Option<Txid>, secp_ctx: &Secp256k1<secp256k1::All>,
226-
) -> ChannelPublicKeys {
227-
self.inner.new_pubkeys(splice_parent_funding_txid, secp_ctx)
224+
fn pubkeys(&self, secp_ctx: &Secp256k1<secp256k1::All>) -> ChannelPublicKeys {
225+
self.inner.pubkeys(secp_ctx)
226+
}
227+
228+
fn new_funding_pubkey(
229+
&self, splice_parent_funding_txid: Txid, secp_ctx: &Secp256k1<secp256k1::All>,
230+
) -> PublicKey {
231+
self.inner.new_funding_pubkey(splice_parent_funding_txid, secp_ctx)
228232
}
229233

230234
fn channel_keys_id(&self) -> [u8; 32] {

0 commit comments

Comments
 (0)