Skip to content

Commit f40cace

Browse files
committed
Make payment_key derivation deterministic
Previously, `KeysManager::derive_channel_keys` would derive the channel's `payment_key` uniquely on a per-channel basis which would disallow users losing their `channel_keys_id` to recover funds. As it's no real necessity to have `payment_key` derivation depend on `channel_keys_id` we can allow for easier recovery of any non-HTLC encumbered funds if we make `payment_key` derivation deterministic. Here we do just this but also include a larger refactor introducing a `ChannelKeysDerivationParameters` struct holding a versioning field to be able to express this change in the derivation scheme. To this end we ensure that legacy channels will continue to use the old derivation scheme after upgrade, while new channels will derive the `payment_key` deterministicly.
1 parent 6f2ffc4 commit f40cace

14 files changed

+540
-223
lines changed

fuzz/src/chanmon_consistency.rs

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@ use lightning::offers::invoice_request::UnsignedInvoiceRequest;
6161
use lightning::onion_message::messenger::{Destination, MessageRouter, OnionMessagePath};
6262
use lightning::routing::router::{InFlightHtlcs, Path, Route, RouteHop, RouteParameters, Router};
6363
use lightning::sign::{
64-
EntropySource, InMemorySigner, KeyMaterial, NodeSigner, Recipient, SignerProvider,
64+
ChannelKeysDerivationParameters, EntropySource, InMemorySigner, KeyMaterial, NodeSigner,
65+
Recipient, SignerProvider,
6566
};
6667
use lightning::types::payment::{PaymentHash, PaymentPreimage, PaymentSecret};
6768
use lightning::util::config::UserConfig;
@@ -366,17 +367,23 @@ impl SignerProvider for KeyProvider {
366367
#[cfg(taproot)]
367368
type TaprootSigner = TestChannelSigner;
368369

369-
fn generate_channel_keys_id(
370+
fn generate_channel_keys_derivation_params(
370371
&self, _inbound: bool, _channel_value_satoshis: u64, _user_channel_id: u128,
371-
) -> [u8; 32] {
372+
) -> ChannelKeysDerivationParameters {
372373
let id = self.rand_bytes_id.fetch_add(1, atomic::Ordering::Relaxed) as u8;
373-
[id; 32]
374+
let channel_keys_id = [id; 32];
375+
ChannelKeysDerivationParameters {
376+
channel_keys_derivation_version: Some(1),
377+
channel_keys_id,
378+
}
374379
}
375380

376381
fn derive_channel_signer(
377-
&self, channel_value_satoshis: u64, channel_keys_id: [u8; 32],
382+
&self, channel_value_satoshis: u64,
383+
channel_keys_derivation_params: ChannelKeysDerivationParameters,
378384
) -> Self::EcdsaSigner {
379385
let secp_ctx = Secp256k1::signing_only();
386+
let channel_keys_id = channel_keys_derivation_params.channel_keys_id;
380387
let id = channel_keys_id[0];
381388
#[rustfmt::skip]
382389
let keys = InMemorySigner::new(
@@ -388,7 +395,7 @@ impl SignerProvider for KeyProvider {
388395
SecretKey::from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, self.node_secret[31]]).unwrap(),
389396
[id, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, self.node_secret[31]],
390397
channel_value_satoshis,
391-
channel_keys_id,
398+
channel_keys_derivation_params,
392399
channel_keys_id,
393400
);
394401
let revoked_commitment = self.make_enforcement_state_cell(keys.commitment_seed);
@@ -404,7 +411,9 @@ impl SignerProvider for KeyProvider {
404411
Ok(TestChannelSigner::new_with_revoked(inner, state, false))
405412
}
406413

407-
fn get_destination_script(&self, _channel_keys_id: [u8; 32]) -> Result<ScriptBuf, ()> {
414+
fn get_destination_script(
415+
&self, _channel_keys_derivation_params: ChannelKeysDerivationParameters,
416+
) -> Result<ScriptBuf, ()> {
408417
let secp_ctx = Secp256k1::signing_only();
409418
#[rustfmt::skip]
410419
let channel_monitor_claim_key = SecretKey::from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, self.node_secret[31]]).unwrap();

fuzz/src/full_stack.rs

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ use lightning::routing::router::{
5858
};
5959
use lightning::routing::utxo::UtxoLookup;
6060
use lightning::sign::{
61-
EntropySource, InMemorySigner, KeyMaterial, NodeSigner, Recipient, SignerProvider,
61+
ChannelKeysDerivationParameters, EntropySource, InMemorySigner, KeyMaterial, NodeSigner,
62+
Recipient, SignerProvider,
6263
};
6364
use lightning::types::payment::{PaymentHash, PaymentPreimage, PaymentSecret};
6465
use lightning::util::config::{ChannelConfig, UserConfig};
@@ -439,20 +440,26 @@ impl SignerProvider for KeyProvider {
439440
#[cfg(taproot)]
440441
type TaprootSigner = TestChannelSigner;
441442

442-
fn generate_channel_keys_id(
443+
fn generate_channel_keys_derivation_params(
443444
&self, inbound: bool, _channel_value_satoshis: u64, _user_channel_id: u128,
444-
) -> [u8; 32] {
445+
) -> ChannelKeysDerivationParameters {
445446
let ctr = self.counter.fetch_add(1, Ordering::Relaxed) as u8;
446447
self.signer_state
447448
.borrow_mut()
448449
.insert(ctr, (inbound, Arc::new(Mutex::new(EnforcementState::new()))));
449-
[ctr; 32]
450+
let channel_keys_id = [ctr; 32];
451+
ChannelKeysDerivationParameters {
452+
channel_keys_derivation_version: Some(1),
453+
channel_keys_id,
454+
}
450455
}
451456

452457
fn derive_channel_signer(
453-
&self, channel_value_satoshis: u64, channel_keys_id: [u8; 32],
458+
&self, channel_value_satoshis: u64,
459+
channel_keys_derivation_params: ChannelKeysDerivationParameters,
454460
) -> Self::EcdsaSigner {
455461
let secp_ctx = Secp256k1::signing_only();
462+
let channel_keys_id = channel_keys_derivation_params.channel_keys_id;
456463
let ctr = channel_keys_id[0];
457464
let (inbound, state) = self.signer_state.borrow().get(&ctr).unwrap().clone();
458465
TestChannelSigner::new_with_revoked(
@@ -489,7 +496,7 @@ impl SignerProvider for KeyProvider {
489496
0, 0, 0, 0, 0, 6, ctr,
490497
],
491498
channel_value_satoshis,
492-
channel_keys_id,
499+
channel_keys_derivation_params,
493500
channel_keys_id,
494501
)
495502
} else {
@@ -525,7 +532,7 @@ impl SignerProvider for KeyProvider {
525532
0, 0, 0, 0, 0, 12, ctr,
526533
],
527534
channel_value_satoshis,
528-
channel_keys_id,
535+
channel_keys_derivation_params,
529536
channel_keys_id,
530537
)
531538
},
@@ -541,7 +548,9 @@ impl SignerProvider for KeyProvider {
541548
Ok(TestChannelSigner::new_with_revoked(inner, state, false))
542549
}
543550

544-
fn get_destination_script(&self, _channel_keys_id: [u8; 32]) -> Result<ScriptBuf, ()> {
551+
fn get_destination_script(
552+
&self, _channel_keys_derivation_params: ChannelKeysDerivationParameters,
553+
) -> Result<ScriptBuf, ()> {
545554
let secp_ctx = Secp256k1::signing_only();
546555
let channel_monitor_claim_key = SecretKey::from_slice(
547556
&<Vec<u8>>::from_hex(

fuzz/src/onion_message.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,10 @@ use lightning::onion_message::messenger::{
2323
};
2424
use lightning::onion_message::offers::{OffersMessage, OffersMessageHandler};
2525
use lightning::onion_message::packet::OnionMessageContents;
26-
use lightning::sign::{EntropySource, KeyMaterial, NodeSigner, Recipient, SignerProvider};
26+
use lightning::sign::{
27+
ChannelKeysDerivationParameters, EntropySource, KeyMaterial, NodeSigner, Recipient,
28+
SignerProvider,
29+
};
2730
use lightning::types::features::InitFeatures;
2831
use lightning::util::logger::Logger;
2932
use lightning::util::ser::{Readable, Writeable, Writer};
@@ -258,14 +261,15 @@ impl SignerProvider for KeyProvider {
258261
#[cfg(taproot)]
259262
type TaprootSigner = TestChannelSigner;
260263

261-
fn generate_channel_keys_id(
264+
fn generate_channel_keys_derivation_params(
262265
&self, _inbound: bool, _channel_value_satoshis: u64, _user_channel_id: u128,
263-
) -> [u8; 32] {
266+
) -> ChannelKeysDerivationParameters {
264267
unreachable!()
265268
}
266269

267270
fn derive_channel_signer(
268-
&self, _channel_value_satoshis: u64, _channel_keys_id: [u8; 32],
271+
&self, _channel_value_satoshis: u64,
272+
_channel_keys_derivation_params: ChannelKeysDerivationParameters,
269273
) -> Self::EcdsaSigner {
270274
unreachable!()
271275
}
@@ -274,7 +278,9 @@ impl SignerProvider for KeyProvider {
274278
unreachable!()
275279
}
276280

277-
fn get_destination_script(&self, _channel_keys_id: [u8; 32]) -> Result<ScriptBuf, ()> {
281+
fn get_destination_script(
282+
&self, _channel_keys_derivation_params: ChannelKeysDerivationParameters,
283+
) -> Result<ScriptBuf, ()> {
278284
unreachable!()
279285
}
280286

lightning/src/chain/channelmonitor.rs

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ use crate::chain;
4343
use crate::chain::{BestBlock, WatchedOutput};
4444
use crate::chain::chaininterface::{BroadcasterInterface, ConfirmationTarget, FeeEstimator, LowerBoundedFeeEstimator};
4545
use crate::chain::transaction::{OutPoint, TransactionData};
46-
use crate::sign::{ChannelDerivationParameters, HTLCDescriptor, SpendableOutputDescriptor, StaticPaymentOutputDescriptor, DelayedPaymentOutputDescriptor, ecdsa::EcdsaChannelSigner, SignerProvider, EntropySource};
46+
use crate::sign::{ChannelDerivationParameters, ChannelKeysDerivationParameters, HTLCDescriptor, SpendableOutputDescriptor, StaticPaymentOutputDescriptor, DelayedPaymentOutputDescriptor, ecdsa::EcdsaChannelSigner, SignerProvider, EntropySource};
4747
use crate::chain::onchaintx::{ClaimEvent, FeerateStrategy, OnchainTxHandler};
4848
use crate::chain::package::{CounterpartyOfferedHTLCOutput, CounterpartyReceivedHTLCOutput, HolderFundingOutput, HolderHTLCOutput, PackageSolvingData, PackageTemplate, RevokedOutput, RevokedHTLCOutput};
4949
use crate::chain::Filter;
@@ -872,7 +872,7 @@ pub(crate) struct ChannelMonitorImpl<Signer: EcdsaChannelSigner> {
872872
counterparty_payment_script: ScriptBuf,
873873
shutdown_script: Option<ScriptBuf>,
874874

875-
channel_keys_id: [u8; 32],
875+
channel_keys_derivation_params: ChannelKeysDerivationParameters,
876876
holder_revocation_basepoint: RevocationBasepoint,
877877
channel_id: ChannelId,
878878
funding_info: (OutPoint, ScriptBuf),
@@ -1076,7 +1076,7 @@ impl<Signer: EcdsaChannelSigner> Writeable for ChannelMonitorImpl<Signer> {
10761076
None => ScriptBuf::new().write(writer)?,
10771077
}
10781078

1079-
self.channel_keys_id.write(writer)?;
1079+
self.channel_keys_derivation_params.channel_keys_id.write(writer)?;
10801080
self.holder_revocation_basepoint.write(writer)?;
10811081
writer.write_all(&self.funding_info.0.txid[..])?;
10821082
writer.write_all(&self.funding_info.0.index.to_be_bytes())?;
@@ -1237,6 +1237,7 @@ impl<Signer: EcdsaChannelSigner> Writeable for ChannelMonitorImpl<Signer> {
12371237
(21, self.balances_empty_height, option),
12381238
(23, self.holder_pays_commitment_tx_fee, option),
12391239
(25, self.payment_preimages, required),
1240+
(27, self.channel_keys_derivation_params.channel_keys_derivation_version, option),
12401241
});
12411242

12421243
Ok(())
@@ -1355,7 +1356,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
13551356
let counterparty_htlc_base_key = counterparty_channel_parameters.pubkeys.htlc_basepoint;
13561357
let counterparty_commitment_params = CounterpartyCommitmentParameters { counterparty_delayed_payment_base_key, counterparty_htlc_base_key, on_counterparty_tx_csv };
13571358

1358-
let channel_keys_id = keys.channel_keys_id();
1359+
let channel_keys_derivation_params = keys.channel_keys_derivation_params();
13591360
let holder_revocation_basepoint = keys.pubkeys().revocation_basepoint;
13601361

13611362
// block for Rust 1.34 compat
@@ -1379,7 +1380,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
13791380
};
13801381

13811382
let onchain_tx_handler = OnchainTxHandler::new(
1382-
channel_value_satoshis, channel_keys_id, destination_script.into(), keys,
1383+
channel_value_satoshis, channel_keys_derivation_params, destination_script.into(), keys,
13831384
channel_parameters.clone(), initial_holder_commitment_tx, secp_ctx
13841385
);
13851386

@@ -1395,7 +1396,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitor<Signer> {
13951396
counterparty_payment_script,
13961397
shutdown_script,
13971398

1398-
channel_keys_id,
1399+
channel_keys_derivation_params,
13991400
holder_revocation_basepoint,
14001401
channel_id,
14011402
funding_info,
@@ -3304,7 +3305,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
33043305
commitment_tx_fee_satoshis,
33053306
anchor_descriptor: AnchorDescriptor {
33063307
channel_derivation_parameters: ChannelDerivationParameters {
3307-
channel_keys_id: self.channel_keys_id,
3308+
channel_keys_derivation_params: self.channel_keys_derivation_params,
33083309
value_satoshis: self.channel_value_satoshis,
33093310
transaction_parameters: self.onchain_tx_handler.channel_transaction_parameters.clone(),
33103311
},
@@ -3328,7 +3329,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
33283329
for htlc in htlcs {
33293330
htlc_descriptors.push(HTLCDescriptor {
33303331
channel_derivation_parameters: ChannelDerivationParameters {
3331-
channel_keys_id: self.channel_keys_id,
3332+
channel_keys_derivation_params: self.channel_keys_derivation_params,
33323333
value_satoshis: self.channel_value_satoshis,
33333334
transaction_parameters: self.onchain_tx_handler.channel_transaction_parameters.clone(),
33343335
},
@@ -4609,7 +4610,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
46094610
spendable_outputs.push(SpendableOutputDescriptor::StaticOutput {
46104611
outpoint: OutPoint { txid: tx.compute_txid(), index: i as u16 },
46114612
output: outp.clone(),
4612-
channel_keys_id: Some(self.channel_keys_id),
4613+
channel_keys_derivation_params: Some(self.channel_keys_derivation_params),
46134614
});
46144615
}
46154616
if let Some(ref broadcasted_holder_revokable_script) = self.broadcasted_holder_revokable_script {
@@ -4620,7 +4621,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
46204621
to_self_delay: self.on_holder_tx_csv,
46214622
output: outp.clone(),
46224623
revocation_pubkey: broadcasted_holder_revokable_script.2,
4623-
channel_keys_id: self.channel_keys_id,
4624+
channel_keys_derivation_params: self.channel_keys_derivation_params,
46244625
channel_value_satoshis: self.channel_value_satoshis,
46254626
channel_transaction_parameters: Some(self.onchain_tx_handler.channel_transaction_parameters.clone()),
46264627
}));
@@ -4630,7 +4631,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
46304631
spendable_outputs.push(SpendableOutputDescriptor::StaticPaymentOutput(StaticPaymentOutputDescriptor {
46314632
outpoint: OutPoint { txid: tx.compute_txid(), index: i as u16 },
46324633
output: outp.clone(),
4633-
channel_keys_id: self.channel_keys_id,
4634+
channel_keys_derivation_params: self.channel_keys_derivation_params,
46344635
channel_value_satoshis: self.channel_value_satoshis,
46354636
channel_transaction_parameters: Some(self.onchain_tx_handler.channel_transaction_parameters.clone()),
46364637
}));
@@ -4639,7 +4640,7 @@ impl<Signer: EcdsaChannelSigner> ChannelMonitorImpl<Signer> {
46394640
spendable_outputs.push(SpendableOutputDescriptor::StaticOutput {
46404641
outpoint: OutPoint { txid: tx.compute_txid(), index: i as u16 },
46414642
output: outp.clone(),
4642-
channel_keys_id: Some(self.channel_keys_id),
4643+
channel_keys_derivation_params: Some(self.channel_keys_derivation_params),
46434644
});
46444645
}
46454646
}
@@ -4929,6 +4930,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
49294930
let mut channel_id = None;
49304931
let mut holder_pays_commitment_tx_fee = None;
49314932
let mut payment_preimages_with_info: Option<HashMap<_, _>> = None;
4933+
let mut channel_keys_derivation_version: Option<u8> = None;
49324934
read_tlv_fields!(reader, {
49334935
(1, funding_spend_confirmed, option),
49344936
(3, htlcs_resolved_on_chain, optional_vec),
@@ -4943,6 +4945,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
49434945
(21, balances_empty_height, option),
49444946
(23, holder_pays_commitment_tx_fee, option),
49454947
(25, payment_preimages_with_info, option),
4948+
(27, channel_keys_derivation_version, option),
49464949
});
49474950
if let Some(payment_preimages_with_info) = payment_preimages_with_info {
49484951
if payment_preimages_with_info.len() != payment_preimages.len() {
@@ -4982,6 +4985,11 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
49824985
chan_utils::get_to_countersignatory_with_anchors_redeemscript(&payment_basepoint).to_p2wsh();
49834986
}
49844987

4988+
let channel_keys_derivation_params = ChannelKeysDerivationParameters {
4989+
channel_keys_derivation_version,
4990+
channel_keys_id,
4991+
};
4992+
49854993
Ok((best_block.block_hash, ChannelMonitor::from_impl(ChannelMonitorImpl {
49864994
latest_update_id,
49874995
commitment_transaction_number_obscure_factor,
@@ -4991,7 +4999,7 @@ impl<'a, 'b, ES: EntropySource, SP: SignerProvider> ReadableArgs<(&'a ES, &'b SP
49914999
counterparty_payment_script,
49925000
shutdown_script,
49935001

4994-
channel_keys_id,
5002+
channel_keys_derivation_params,
49955003
holder_revocation_basepoint,
49965004
channel_id: channel_id.unwrap_or(ChannelId::v1_from_funding_outpoint(outpoint)),
49975005
funding_info,
@@ -5070,7 +5078,7 @@ mod tests {
50705078
use crate::chain::channelmonitor::{ChannelMonitor, WithChannelMonitor};
50715079
use crate::chain::package::{weight_offered_htlc, weight_received_htlc, weight_revoked_offered_htlc, weight_revoked_received_htlc, WEIGHT_REVOKED_OUTPUT};
50725080
use crate::chain::transaction::OutPoint;
5073-
use crate::sign::InMemorySigner;
5081+
use crate::sign::{InMemorySigner, ChannelKeysDerivationParameters, CHANNEL_KEYS_DERIVATION_VERSION};
50745082
use crate::ln::types::ChannelId;
50755083
use crate::types::payment::{PaymentPreimage, PaymentHash};
50765084
use crate::ln::channel_keys::{DelayedPaymentBasepoint, DelayedPaymentKey, HtlcBasepoint, RevocationBasepoint, RevocationKey};
@@ -5233,6 +5241,11 @@ mod tests {
52335241
}
52345242
}
52355243

5244+
let dummy_key_derivation_params = ChannelKeysDerivationParameters {
5245+
channel_keys_derivation_version: Some(CHANNEL_KEYS_DERIVATION_VERSION),
5246+
channel_keys_id: [0; 32],
5247+
};
5248+
52365249
let keys = InMemorySigner::new(
52375250
&secp_ctx,
52385251
SecretKey::from_slice(&[41; 32]).unwrap(),
@@ -5242,7 +5255,7 @@ mod tests {
52425255
SecretKey::from_slice(&[41; 32]).unwrap(),
52435256
[41; 32],
52445257
0,
5245-
[0; 32],
5258+
dummy_key_derivation_params,
52465259
[0; 32],
52475260
);
52485261

@@ -5485,6 +5498,11 @@ mod tests {
54855498

54865499
let dummy_key = PublicKey::from_secret_key(&secp_ctx, &SecretKey::from_slice(&[42; 32]).unwrap());
54875500

5501+
let dummy_key_derivation_params = ChannelKeysDerivationParameters {
5502+
channel_keys_derivation_version: Some(CHANNEL_KEYS_DERIVATION_VERSION),
5503+
channel_keys_id: [0; 32],
5504+
};
5505+
54885506
let keys = InMemorySigner::new(
54895507
&secp_ctx,
54905508
SecretKey::from_slice(&[41; 32]).unwrap(),
@@ -5494,7 +5512,7 @@ mod tests {
54945512
SecretKey::from_slice(&[41; 32]).unwrap(),
54955513
[41; 32],
54965514
0,
5497-
[0; 32],
5515+
dummy_key_derivation_params,
54985516
[0; 32],
54995517
);
55005518

0 commit comments

Comments
 (0)