feat: BIP-39 master seed for unified key derivation
Replace fragmented random key generation with a single 24-word BIP-39 mnemonic that deterministically derives all node keys: Ed25519 (DID), secp256k1 (Nostr/Bitcoin), BIP-84 xprv (Bitcoin Core), and LND aezeed entropy. New onboarding flow: seed generate → word verification → identity naming. Restore path enabled via 24-word entry. Includes seed RPC handlers, mock backend support, LND/Bitcoin Core wallet-from-seed integration, and UI polish across settings and discover views. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -64,6 +64,45 @@ impl NodeIdentity {
|
||||
})
|
||||
}
|
||||
|
||||
/// Create node identity from a BIP-39 master seed (deterministic derivation).
|
||||
/// Writes derived key to disk in the same format as load_or_create.
|
||||
pub async fn from_seed(identity_dir: &Path, seed: &crate::seed::MasterSeed) -> Result<Self> {
|
||||
fs::create_dir_all(identity_dir)
|
||||
.await
|
||||
.context("Failed to create identity directory")?;
|
||||
|
||||
let signing_key = crate::seed::derive_node_ed25519(seed)?;
|
||||
let key_path = identity_dir.join(NODE_KEY_FILE);
|
||||
let pub_path = identity_dir.join(NODE_KEY_PUB_FILE);
|
||||
|
||||
fs::write(&key_path, signing_key.to_bytes())
|
||||
.await
|
||||
.context("Failed to write node key")?;
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
fs::set_permissions(&key_path, std::fs::Permissions::from_mode(0o600))
|
||||
.await
|
||||
.context("Failed to set key permissions")?;
|
||||
}
|
||||
fs::write(&pub_path, signing_key.verifying_key().as_bytes())
|
||||
.await
|
||||
.context("Failed to write node public key")?;
|
||||
|
||||
let pubkey_hex = hex::encode(signing_key.verifying_key().as_bytes());
|
||||
tracing::info!("Derived node identity from seed (pubkey: {}...)", &pubkey_hex[..16]);
|
||||
|
||||
Ok(Self {
|
||||
signing_key,
|
||||
_identity_dir: identity_dir.to_path_buf(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Check if a node key already exists on disk.
|
||||
pub fn key_exists(identity_dir: &Path) -> bool {
|
||||
identity_dir.join(NODE_KEY_FILE).exists()
|
||||
}
|
||||
|
||||
/// Access the signing key (for key derivation, e.g. mesh encryption).
|
||||
pub fn signing_key(&self) -> &SigningKey {
|
||||
&self.signing_key
|
||||
@@ -115,6 +154,11 @@ impl NodeIdentity {
|
||||
.map_err(|e| anyhow::anyhow!("Invalid pubkey hex: {}", e))
|
||||
}
|
||||
|
||||
/// Generate a W3C DID Document for this identity.
|
||||
#[allow(dead_code)]
|
||||
pub fn did_document(&self) -> Result<serde_json::Value> {
|
||||
did_document_from_pubkey_hex(&self.pubkey_hex())
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert Ed25519 pubkey (hex) to did:key format.
|
||||
|
||||
Reference in New Issue
Block a user