Skip to content

Commit b7d5502

Browse files
committed
seed: derive LN node key pair directly w/o LDK keys manager
1 parent 905b985 commit b7d5502

File tree

4 files changed

+55
-7
lines changed

4 files changed

+55
-7
lines changed

common/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ edition = "2021"
1313
[dependencies]
1414
# --- BITCOIN --- #
1515

16-
bitcoin = { version = "0.28.1", features = ["use-serde"] }
16+
bitcoin = { version = "0.28.1", features = ["std", "use-serde"] }
1717
bitcoin-bech32 = "0.12"
1818

1919
# --- LIGHTNING --- #

common/src/root_seed.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,14 @@ use std::fmt;
22
use std::str::FromStr;
33

44
use anyhow::format_err;
5+
use bitcoin::secp256k1::Secp256k1;
6+
use bitcoin::util::bip32::{ChildNumber, ExtendedPrivKey};
7+
use bitcoin::{KeyPair, Network};
58
use rand_core::{CryptoRng, RngCore};
69
use secrecy::{ExposeSecret, Secret, SecretVec};
710
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
811

12+
use crate::rng::Crng;
913
use crate::{ed25519, hex};
1014

1115
/// The user's root seed from which we derive all child secrets.
@@ -92,6 +96,30 @@ impl RootSeed {
9296
ed25519::from_seed(seed.expose_secret())
9397
}
9498

99+
/// Derive the lightning node key pair directly, without needing to derive
100+
/// all the other auxiliary node secrets.
101+
pub fn derive_node_key_pair<R: Crng>(&self, rng: &mut R) -> KeyPair {
102+
// NOTE: this doesn't affect the output; this randomizes the SECP256K1
103+
// context for sidechannel resistance.
104+
let mut secp_randomize = [0u8; 32];
105+
rng.fill_bytes(&mut secp_randomize);
106+
let mut secp_ctx = Secp256k1::new();
107+
secp_ctx.seeded_randomize(&secp_randomize);
108+
109+
let master_secret =
110+
ExtendedPrivKey::new_master(Network::Testnet, self.expose_secret())
111+
.expect("should never fail; the sizes match up");
112+
let child_number = ChildNumber::from_hardened_idx(0)
113+
.expect("should never fail; index is in range");
114+
let node_sk = master_secret
115+
.ckd_priv(&secp_ctx, child_number)
116+
.expect("should never fail")
117+
.private_key;
118+
let node_key_pair = KeyPair::from_secret_key(&secp_ctx, node_sk);
119+
120+
node_key_pair
121+
}
122+
95123
#[cfg(test)]
96124
fn as_bytes(&self) -> &[u8] {
97125
self.0.expose_secret().as_slice()
@@ -195,6 +223,7 @@ impl proptest::arbitrary::Arbitrary for RootSeed {
195223

196224
proptest::arbitrary::any::<[u8; 32]>()
197225
.prop_map(|buf| Self::new(Secret::new(buf)))
226+
.no_shrink()
198227
.boxed()
199228
}
200229
}

node/src/lexe/keys_manager.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,3 +114,23 @@ impl LexeKeysManager {
114114
)
115115
}
116116
}
117+
118+
#[cfg(test)]
119+
mod test {
120+
use common::rng::SysRng;
121+
122+
use super::*;
123+
124+
#[test]
125+
fn test_derive_node_sk_equiv() {
126+
// TODO(phlip9): use test rng
127+
let mut rng = SysRng::new();
128+
let root_seed = RootSeed::from_rng(&mut rng);
129+
let node_key_pair = root_seed.derive_node_key_pair(&mut rng);
130+
let node_pk = PublicKey::from(&node_key_pair);
131+
132+
let keys_manager =
133+
LexeKeysManager::init(&mut rng, &node_pk, &root_seed).unwrap();
134+
assert_eq!(node_pk, keys_manager.derive_pk(&mut rng));
135+
}
136+
}

node/src/provision.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ use warp::reject::Reject;
4545
use warp::{Filter, Rejection, Reply};
4646

4747
use crate::attest;
48-
use crate::lexe::keys_manager::LexeKeysManager;
4948
use crate::types::ApiClientType;
5049

5150
const PROVISION_TIMEOUT: Duration = Duration::from_secs(10);
@@ -116,10 +115,10 @@ fn verify_provision_request<R: Crng>(
116115
) -> Result<(UserPk, PublicKey, ProvisionedSecrets)> {
117116
ensure!(req.user_pk == expected_user_id);
118117

119-
// TODO(phlip9): derive just the node pk without all the extra junk
120-
// that gets derived constructing a whole KeysManager
121-
let _keys_manager =
122-
LexeKeysManager::init(rng, &req.node_pk, &req.root_seed)?;
118+
let derived_node_pk =
119+
PublicKey::from(req.root_seed.derive_node_key_pair(rng));
120+
ensure!(req.node_pk == derived_node_pk);
121+
123122
Ok((
124123
req.user_pk,
125124
req.node_pk,
@@ -264,7 +263,7 @@ pub async fn provision(
264263
match tokio::time::timeout(PROVISION_TIMEOUT, shutdown_rx.recv()).await
265264
{
266265
Ok(_) => {
267-
debug!("received shutdown; done provisioning");
266+
info!("received shutdown; done provisioning");
268267
}
269268
Err(_) => {
270269
warn!(

0 commit comments

Comments
 (0)