@@ -74,19 +74,36 @@ pub fn generate_entropy_mnemonic(word_count: Option<WordCount>) -> Mnemonic {
7474/// This is the same key that would be used by a [`Node`] built with this mnemonic via
7575/// [`Builder::set_entropy_bip39_mnemonic`].
7676///
77+ /// The derivation follows LDK's KeysManager behavior:
78+ /// 1. BIP39 seed (64 bytes) → BIP32 master key (32 bytes)
79+ /// 2. Those 32 bytes as new seed → BIP32 master → derive m/0' → node_secret
80+ ///
7781/// [`Node`]: crate::Node
7882/// [`Builder::set_entropy_bip39_mnemonic`]: crate::Builder::set_entropy_bip39_mnemonic
7983pub fn derive_node_secret_from_mnemonic (
8084 mnemonic : String , passphrase : Option < String > ,
8185) -> Result < Vec < u8 > , Error > {
82- let parsed_mnemonic = Mnemonic :: parse ( & mnemonic) . map_err ( |_| Error :: InvalidMnemonic ) ?;
86+ use bitcoin:: bip32:: ChildNumber ;
87+ use bitcoin:: secp256k1:: Secp256k1 ;
8388
89+ let parsed_mnemonic = Mnemonic :: parse ( & mnemonic) . map_err ( |_| Error :: InvalidMnemonic ) ?;
8490 let seed = parsed_mnemonic. to_seed ( passphrase. as_deref ( ) . unwrap_or ( "" ) ) ;
8591
92+ // First BIP32 derivation: 64-byte BIP39 seed → 32-byte master private key
8693 let xpriv =
8794 Xpriv :: new_master ( Network :: Bitcoin , & seed) . map_err ( |_| Error :: InvalidMnemonic ) ?;
88-
89- Ok ( xpriv. private_key . secret_bytes ( ) . to_vec ( ) )
95+ let ldk_seed: [ u8 ; 32 ] = xpriv. private_key . secret_bytes ( ) ;
96+
97+ // Second BIP32 derivation: KeysManager treats the 32-byte key as a new seed
98+ // and derives node_secret at path m/0'
99+ let secp = Secp256k1 :: new ( ) ;
100+ let keys_master =
101+ Xpriv :: new_master ( Network :: Bitcoin , & ldk_seed) . map_err ( |_| Error :: InvalidMnemonic ) ?;
102+ let node_secret_xpriv = keys_master
103+ . derive_priv ( & secp, & [ ChildNumber :: from_hardened_idx ( 0 ) . unwrap ( ) ] )
104+ . map_err ( |_| Error :: InvalidMnemonic ) ?;
105+
106+ Ok ( node_secret_xpriv. private_key . secret_bytes ( ) . to_vec ( ) )
90107}
91108
92109pub ( crate ) fn read_or_generate_seed_file < L : Deref > (
@@ -645,6 +662,7 @@ pub(crate) fn read_bdk_wallet_change_set(
645662#[ cfg( test) ]
646663mod tests {
647664 use super :: * ;
665+ use lightning:: sign:: KeysManager as LdkKeysManager ;
648666
649667 #[ test]
650668 fn mnemonic_to_entropy_to_mnemonic ( ) {
@@ -679,4 +697,46 @@ mod tests {
679697 assert_eq ! ( mnemonic. word_count( ) , expected_words) ;
680698 }
681699 }
700+
701+ #[ test]
702+ fn derive_node_secret_matches_keys_manager ( ) {
703+ // Standard test mnemonic (BIP39 test vector)
704+ let mnemonic =
705+ "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about" ;
706+
707+ // Derive using our function
708+ let derived_secret =
709+ derive_node_secret_from_mnemonic ( mnemonic. to_string ( ) , None ) . unwrap ( ) ;
710+
711+ // Derive using LDK's KeysManager (same flow as Builder)
712+ let parsed = Mnemonic :: parse ( mnemonic) . unwrap ( ) ;
713+ let seed = parsed. to_seed ( "" ) ;
714+ let xpriv = Xpriv :: new_master ( Network :: Bitcoin , & seed) . unwrap ( ) ;
715+ let ldk_seed: [ u8 ; 32 ] = xpriv. private_key . secret_bytes ( ) ;
716+
717+ let keys_manager = LdkKeysManager :: new ( & ldk_seed, 0 , 0 , false ) ;
718+ let expected_secret = keys_manager. get_node_secret_key ( ) ;
719+
720+ assert_eq ! ( derived_secret, expected_secret. secret_bytes( ) . to_vec( ) ) ;
721+ }
722+
723+ #[ test]
724+ fn derive_node_secret_with_passphrase ( ) {
725+ let mnemonic =
726+ "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about" ;
727+ let passphrase = Some ( "test_passphrase" . to_string ( ) ) ;
728+
729+ let derived_secret =
730+ derive_node_secret_from_mnemonic ( mnemonic. to_string ( ) , passphrase) . unwrap ( ) ;
731+
732+ let parsed = Mnemonic :: parse ( mnemonic) . unwrap ( ) ;
733+ let seed = parsed. to_seed ( "test_passphrase" ) ;
734+ let xpriv = Xpriv :: new_master ( Network :: Bitcoin , & seed) . unwrap ( ) ;
735+ let ldk_seed: [ u8 ; 32 ] = xpriv. private_key . secret_bytes ( ) ;
736+
737+ let keys_manager = LdkKeysManager :: new ( & ldk_seed, 0 , 0 , false ) ;
738+ let expected_secret = keys_manager. get_node_secret_key ( ) ;
739+
740+ assert_eq ! ( derived_secret, expected_secret. secret_bytes( ) . to_vec( ) ) ;
741+ }
682742}
0 commit comments