lightning_signer/signer/
derive.rs

1use alloc::boxed::Box;
2
3use bitcoin::bip32::{ChildNumber, Xpriv, Xpub};
4use bitcoin::hashes::sha256::Hash as Sha256;
5use bitcoin::hashes::{Hash, HashEngine};
6use bitcoin::secp256k1::{PublicKey, SecretKey};
7use bitcoin::{secp256k1, secp256k1::Secp256k1, Network};
8
9use crate::channel::ChannelId;
10use crate::util::byte_utils;
11use crate::util::crypto_utils::{hkdf_sha256, hkdf_sha256_keys};
12
13/// Derive keys for nodes and channels
14pub trait KeyDerive {
15    /// Derive master key
16    fn master_key(&self, seed: &[u8]) -> Xpriv;
17    /// Derive node key
18    fn node_keys(
19        &self,
20        seed: &[u8],
21        secp_ctx: &Secp256k1<secp256k1::All>,
22    ) -> (PublicKey, SecretKey);
23    /// Derive LDK keys_id from the channel_id and a seed base
24    /// The seed_base
25    fn keys_id(&self, channel_id: ChannelId, channel_seed_base: &[u8; 32]) -> [u8; 32] {
26        hkdf_sha256(channel_seed_base, "per-peer seed".as_bytes(), channel_id.as_slice())
27    }
28
29    /// A base for channel keys
30    fn channels_seed(&self, seed: &[u8]) -> [u8; 32] {
31        hkdf_sha256(seed, "peer seed".as_bytes(), &[])
32    }
33
34    /// Derive channel keys.
35    /// funding_key, revocation_base_key, htlc_base_key, payment_key, delayed_payment_base_key, commitment_seed
36    fn channel_keys(
37        &self,
38        seed: &[u8],
39        keys_id: &[u8; 32],
40        basepoint_index: u32,
41        master_key: &Xpriv,
42        secp_ctx: &Secp256k1<secp256k1::All>,
43    ) -> (SecretKey, SecretKey, SecretKey, SecretKey, SecretKey, [u8; 32]);
44}
45
46/// CLN compatible derivation
47#[derive(Clone, Debug)]
48pub struct NativeKeyDerive {
49    network: Network,
50}
51
52impl NativeKeyDerive {
53    /// network value.
54    pub fn new(network: Network) -> Self {
55        Self { network }
56    }
57}
58
59impl KeyDerive for NativeKeyDerive {
60    fn master_key(&self, seed: &[u8]) -> Xpriv {
61        let master_seed = hkdf_sha256(seed, "bip32 seed".as_bytes(), &[]);
62        Xpriv::new_master(self.network, &master_seed).expect("Your RNG is busted")
63    }
64
65    fn node_keys(
66        &self,
67        seed: &[u8],
68        secp_ctx: &Secp256k1<secp256k1::All>,
69    ) -> (PublicKey, SecretKey) {
70        let node_private_bytes = hkdf_sha256(seed, "nodeid".as_bytes(), &[]);
71        let node_secret_key = SecretKey::from_slice(&node_private_bytes).unwrap();
72        let node_id = PublicKey::from_secret_key(&secp_ctx, &node_secret_key);
73        (node_id, node_secret_key)
74    }
75
76    fn channel_keys(
77        &self,
78        _seed: &[u8],
79        keys_id: &[u8; 32],
80        _basepoint_index: u32,
81        _master_key: &Xpriv,
82        _secp_ctx: &Secp256k1<secp256k1::All>,
83    ) -> (SecretKey, SecretKey, SecretKey, SecretKey, SecretKey, [u8; 32]) {
84        let hkdf_info = "c-lightning";
85        let keys_buf: [u8; 192] = hkdf_sha256_keys(keys_id, hkdf_info.as_bytes(), &[]);
86
87        // unwraps below are safe because the keys_buf is 192 bytes long
88        let mut ndx = 0;
89        let funding_key = SecretKey::from_slice(&keys_buf[ndx..ndx + 32]).unwrap();
90        ndx += 32;
91        let revocation_base_key = SecretKey::from_slice(&keys_buf[ndx..ndx + 32]).unwrap();
92        ndx += 32;
93        let htlc_base_key = SecretKey::from_slice(&keys_buf[ndx..ndx + 32]).unwrap();
94        ndx += 32;
95        let payment_key = SecretKey::from_slice(&keys_buf[ndx..ndx + 32]).unwrap();
96        ndx += 32;
97        let delayed_payment_base_key = SecretKey::from_slice(&keys_buf[ndx..ndx + 32]).unwrap();
98        ndx += 32;
99        let commitment_seed = keys_buf[ndx..ndx + 32].try_into().unwrap();
100        (
101            funding_key,
102            revocation_base_key,
103            htlc_base_key,
104            payment_key,
105            delayed_payment_base_key,
106            commitment_seed,
107        )
108    }
109}
110
111/// LDK compatible derivation
112pub struct LdkKeyDerive {
113    network: Network,
114}
115
116impl KeyDerive for LdkKeyDerive {
117    fn master_key(&self, seed: &[u8]) -> Xpriv {
118        Xpriv::new_master(self.network, &seed).expect("Your RNG is busted")
119    }
120
121    fn node_keys(
122        &self,
123        seed: &[u8],
124        secp_ctx: &Secp256k1<secp256k1::All>,
125    ) -> (PublicKey, SecretKey) {
126        let master = self.master_key(seed);
127        let node_secret_key = master
128            .derive_priv(&secp_ctx, &[ChildNumber::from_hardened_idx(0).unwrap()])
129            .expect("Your RNG is busted")
130            .private_key;
131        let node_id = PublicKey::from_secret_key(&secp_ctx, &node_secret_key);
132        (node_id, node_secret_key)
133    }
134
135    fn channel_keys(
136        &self,
137        seed: &[u8],
138        keys_id: &[u8; 32],
139        _basepoint_index: u32,
140        master_key: &Xpriv,
141        secp_ctx: &Secp256k1<secp256k1::All>,
142    ) -> (SecretKey, SecretKey, SecretKey, SecretKey, SecretKey, [u8; 32]) {
143        let chan_id = byte_utils::slice_to_be64(&keys_id[0..8]);
144        assert!(chan_id <= core::u32::MAX as u64); // Otherwise the params field wasn't created by us
145        let mut unique_start = Sha256::engine();
146        unique_start.input(keys_id);
147        unique_start.input(seed);
148
149        let channel_master_key = master_key
150            .derive_priv(&secp_ctx, &[ChildNumber::from_hardened_idx(3).unwrap()])
151            .expect("Your RNG is busted");
152
153        // We only seriously intend to rely on the channel_master_key for true secure
154        // entropy, everything else just ensures uniqueness. We rely on the unique_start (ie
155        // starting_time provided in the constructor) to be unique.
156        let child_privkey = channel_master_key
157            .derive_priv(
158                secp_ctx,
159                &[ChildNumber::from_hardened_idx(chan_id as u32).expect("key space exhausted")],
160            )
161            .expect("Your RNG is busted");
162        unique_start.input(child_privkey.private_key.as_ref());
163
164        let channel_seed = Sha256::from_engine(unique_start).to_byte_array();
165
166        let commitment_seed = {
167            let mut sha = Sha256::engine();
168            sha.input(&channel_seed);
169            sha.input(&b"commitment seed"[..]);
170            Sha256::from_engine(sha).to_byte_array()
171        };
172        macro_rules! key_step {
173            ($info: expr, $prev_key: expr) => {{
174                let mut sha = Sha256::engine();
175                sha.input(&channel_seed);
176                sha.input(&$prev_key[..]);
177                sha.input(&$info[..]);
178                SecretKey::from_slice(Sha256::from_engine(sha).as_ref()).unwrap()
179            }};
180        }
181        let funding_key = key_step!(b"funding key", commitment_seed);
182        let revocation_base_key = key_step!(b"revocation base key", funding_key);
183        let payment_key = key_step!(b"payment key", revocation_base_key);
184        let delayed_payment_base_key = key_step!(b"delayed payment base key", payment_key);
185        let htlc_base_key = key_step!(b"HTLC base key", delayed_payment_base_key);
186        (
187            funding_key,
188            revocation_base_key,
189            htlc_base_key,
190            payment_key,
191            delayed_payment_base_key,
192            commitment_seed,
193        )
194    }
195
196    fn keys_id(&self, channel_id: ChannelId, channel_seed_base: &[u8; 32]) -> [u8; 32] {
197        let mut res =
198            hkdf_sha256(channel_seed_base, "per-peer seed".as_bytes(), channel_id.as_slice());
199        // The stock KeysManager requires the first four bytes of the keys ID to be zero,
200        // and the byte after that to be 127 or less.  The big-endian interpretation is used as
201        // a derivation index, and it must be less than 2^31.
202        res[0] = 0;
203        res[1] = 0;
204        res[2] = 0;
205        res[3] = 0;
206        res[4] &= 0x7f;
207        res
208    }
209}
210
211/// LND compatible derivation
212pub struct LndKeyDerive {
213    network: Network,
214}
215
216impl KeyDerive for LndKeyDerive {
217    fn master_key(&self, seed: &[u8]) -> Xpriv {
218        Xpriv::new_master(self.network, seed).expect("Your RNG is busted")
219    }
220
221    fn node_keys(
222        &self,
223        seed: &[u8],
224        secp_ctx: &Secp256k1<secp256k1::All>,
225    ) -> (PublicKey, SecretKey) {
226        let key_family_node_key = 6;
227        let index = 0;
228        let master = self.master_key(seed);
229        derive_key_lnd(secp_ctx, self.network, &master, key_family_node_key, index)
230    }
231
232    fn channel_keys(
233        &self,
234        _seed: &[u8],
235        keys_id: &[u8; 32],
236        basepoint_index: u32,
237        master_key: &Xpriv,
238        _secp_ctx: &Secp256k1<secp256k1::All>,
239    ) -> (SecretKey, SecretKey, SecretKey, SecretKey, SecretKey, [u8; 32]) {
240        let hkdf_info = "c-lightning";
241        let keys_buf: [u8; 192] = hkdf_sha256_keys(keys_id, hkdf_info.as_bytes(), &[]);
242
243        // unwraps below are safe because the keys_buf is 192 bytes long
244        let mut ndx = 0;
245        ndx += 32;
246        ndx += 32;
247        ndx += 32;
248        ndx += 32;
249        ndx += 32;
250        let commitment_seed = keys_buf[ndx..ndx + 32].try_into().unwrap();
251
252        let secp_ctx = Secp256k1::new();
253
254        // These need to match the constants defined in lnd/keychain/derivation.go
255        // KeyFamilyMultiSig KeyFamily = 0
256        // KeyFamilyRevocationBase = 1
257        // KeyFamilyHtlcBase KeyFamily = 2
258        // KeyFamilyPaymentBase KeyFamily = 3
259        // KeyFamilyDelayBase KeyFamily = 4
260        let (_, funding_key) =
261            derive_key_lnd(&secp_ctx, self.network, master_key, 0, basepoint_index);
262        let (_, revocation_base_key) =
263            derive_key_lnd(&secp_ctx, self.network, master_key, 1, basepoint_index);
264        let (_, htlc_base_key) =
265            derive_key_lnd(&secp_ctx, self.network, master_key, 2, basepoint_index);
266        let (_, payment_key) =
267            derive_key_lnd(&secp_ctx, self.network, master_key, 3, basepoint_index);
268        let (_, delayed_payment_base_key) =
269            derive_key_lnd(&secp_ctx, self.network, master_key, 4, basepoint_index);
270        (
271            funding_key,
272            revocation_base_key,
273            htlc_base_key,
274            payment_key,
275            delayed_payment_base_key,
276            commitment_seed,
277        )
278    }
279}
280
281/// Construct a key deriver based on the style
282pub fn key_derive(style: KeyDerivationStyle, network: Network) -> Box<dyn KeyDerive> {
283    match style {
284        KeyDerivationStyle::Native => Box::new(NativeKeyDerive { network }),
285        KeyDerivationStyle::Ldk => Box::new(LdkKeyDerive { network }),
286        KeyDerivationStyle::Lnd => Box::new(LndKeyDerive { network }),
287    }
288}
289
290/// The key derivation style
291///
292/// NOTE - This enum should be kept in sync with the grpc definition in `remotesigner.proto`
293/// and `convert_node_config` in `driver.rs`
294#[derive(Clone, Copy, Debug)]
295pub enum KeyDerivationStyle {
296    /// Our preferred style, C-lightning compatible
297    Native = 1,
298    /// LDK compatible
299    Ldk = 2,
300    /// The LND style
301    Lnd = 3,
302}
303
304impl TryFrom<u8> for KeyDerivationStyle {
305    type Error = ();
306
307    fn try_from(v: u8) -> Result<Self, Self::Error> {
308        use KeyDerivationStyle::{Ldk, Lnd, Native};
309        match v {
310            x if x == Native as u8 => Ok(Native),
311            x if x == Ldk as u8 => Ok(Ldk),
312            x if x == Lnd as u8 => Ok(Lnd),
313            _ => Err(()),
314        }
315    }
316}
317
318impl core::fmt::Display for KeyDerivationStyle {
319    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
320        f.pad(match *self {
321            KeyDerivationStyle::Native => "native",
322            KeyDerivationStyle::Ldk => "ldk",
323            KeyDerivationStyle::Lnd => "lnd",
324        })
325    }
326}
327
328impl core::str::FromStr for KeyDerivationStyle {
329    type Err = ();
330    #[inline]
331    fn from_str(s: &str) -> Result<Self, Self::Err> {
332        match s {
333            "native" => Ok(KeyDerivationStyle::Native),
334            "ldk" => Ok(KeyDerivationStyle::Ldk),
335            "lnd" => Ok(KeyDerivationStyle::Lnd),
336            _ => Err(()),
337        }
338    }
339}
340
341impl KeyDerivationStyle {
342    pub(crate) fn get_key_path_len(&self) -> Option<usize> {
343        match self {
344            // CLN uses a single BIP32 chain for both external
345            // and internal (change) addresses.
346            KeyDerivationStyle::Native => Some(1),
347            // LDK can use any BIP32 chain for both external
348            // and internal addresses based on the implementation.
349            KeyDerivationStyle::Ldk => None,
350            // lnd uses two BIP32 branches, one for external and one
351            // for internal (change) addresses.
352            KeyDerivationStyle::Lnd => Some(2),
353        }
354    }
355
356    pub(crate) fn get_account_extended_key(
357        &self,
358        secp_ctx: &Secp256k1<secp256k1::All>,
359        network: Network,
360        seed: &[u8],
361    ) -> Xpriv {
362        match self {
363            KeyDerivationStyle::Native => get_account_extended_key_native(secp_ctx, network, seed),
364            KeyDerivationStyle::Ldk => get_account_extended_key_native(secp_ctx, network, seed),
365            KeyDerivationStyle::Lnd => get_account_extended_key_lnd(secp_ctx, network, seed),
366        }
367    }
368}
369
370// This function will panic if the Xpriv::new_master fails.
371// Only use where failure is an option (ie, startup).
372pub(crate) fn get_account_extended_key_native(
373    secp_ctx: &Secp256k1<secp256k1::All>,
374    network: Network,
375    node_seed: &[u8],
376) -> Xpriv {
377    let bip32_seed = hkdf_sha256(node_seed, "bip32 seed".as_bytes(), &[]);
378    let master = Xpriv::new_master(network, &bip32_seed).unwrap();
379    master
380        .derive_priv(&secp_ctx, &[ChildNumber::from_normal_idx(0).unwrap()])
381        .unwrap()
382        .derive_priv(&secp_ctx, &[ChildNumber::from_normal_idx(0).unwrap()])
383        .unwrap()
384}
385
386// This function will panic if the Xpriv::new_master fails.
387// Only use where failure is an option (ie, startup).
388pub(crate) fn get_account_extended_key_lnd(
389    secp_ctx: &Secp256k1<secp256k1::All>,
390    network: Network,
391    node_seed: &[u8],
392) -> Xpriv {
393    // Must match btcsuite/btcwallet/waddrmgr/scoped_manager.go
394    let master = Xpriv::new_master(network, node_seed).unwrap();
395    let purpose = 84;
396    let cointype = 0;
397    let account = 0;
398    master
399        .derive_priv(&secp_ctx, &[ChildNumber::from_hardened_idx(purpose).unwrap()])
400        .unwrap()
401        .derive_priv(&secp_ctx, &[ChildNumber::from_hardened_idx(cointype).unwrap()])
402        .unwrap()
403        .derive_priv(&secp_ctx, &[ChildNumber::from_hardened_idx(account).unwrap()])
404        .unwrap()
405}
406
407pub(crate) fn derive_key_lnd(
408    secp_ctx: &Secp256k1<secp256k1::All>,
409    network: Network,
410    master: &Xpriv,
411    key_family: u32,
412    index: u32,
413) -> (PublicKey, SecretKey) {
414    let bip43purpose = 1017;
415    #[rustfmt::skip]
416    let coin_type = match network {
417        Network::Bitcoin => 0,
418        Network::Testnet => 1,
419        Network::Regtest => 1,
420        Network::Signet => 1,
421        _ => unreachable!(),
422    };
423    let branch = 0;
424    let node_ext_prv = master
425        .derive_priv(&secp_ctx, &[ChildNumber::from_hardened_idx(bip43purpose).unwrap()])
426        .unwrap()
427        .derive_priv(&secp_ctx, &[ChildNumber::from_hardened_idx(coin_type).unwrap()])
428        .unwrap()
429        .derive_priv(&secp_ctx, &[ChildNumber::from_hardened_idx(key_family).unwrap()])
430        .unwrap()
431        .derive_priv(&secp_ctx, &[ChildNumber::from_normal_idx(branch).unwrap()])
432        .unwrap()
433        .derive_priv(&secp_ctx, &[ChildNumber::from_normal_idx(index).unwrap()])
434        .unwrap();
435    let node_ext_pub = &Xpub::from_priv(&secp_ctx, &node_ext_prv);
436    (node_ext_pub.public_key, node_ext_prv.private_key)
437}
438
439#[cfg(test)]
440mod tests {
441    use super::*;
442    use bitcoin::Network::Testnet;
443    use hex;
444
445    struct ExpectedValues {
446        master_key: &'static str,
447        node_secret_key: &'static str,
448        node_id: &'static str,
449        channels_seed: &'static str,
450        keys_id: &'static str,
451        funding_key: &'static str,
452        commitment_seed: &'static str,
453        account_extended_key: &'static str,
454    }
455
456    #[test]
457    fn test_key_path_len() {
458        assert_eq!(Some(1), KeyDerivationStyle::Native.get_key_path_len());
459        assert_eq!(Some(2), KeyDerivationStyle::Lnd.get_key_path_len());
460        assert_eq!(None, KeyDerivationStyle::Ldk.get_key_path_len());
461    }
462
463    #[test]
464    fn test_derivation() {
465        let tests = [
466            (
467                KeyDerivationStyle::Native,
468                ExpectedValues {
469                    master_key: "tprv8ZgxMBicQKsPfBjs724zXetsAfo8GqUTLKFgWsb92txrGoW9De1DTABH7htVkp1jS9ZhNws7do3UNPZreZru8MNXvWDrTxoecc2wnTYrb4S",
470                    node_secret_key: "aae7ec5943df6bf7f26773729b3ac9a12ee428bbc4e6e2fbd27f3cf78cfe6d86",
471                    node_id: "026f61d7ee82f937f9697f4f3e44bfaaa25849cc4f526b3a57326130eba6346002",
472                    channels_seed: "7e273adccc072169f5a1cb1aee23a2d6986b6cbbeff4f0995d2762ac7c0b2511",
473                    keys_id: "36814b08b8410cf33c02217de2c24f800b46642355d5c4be3a78c7d5d924af38",
474                    funding_key: "b70812d9d05617ac829ffa666bd0a7ddbbd5303b562fa2ead2a842440333eff4",
475                    commitment_seed: "fd8c46daa8eda9211b6cf4461003c8cc6aaa98024b438779f08254e4e67ae60c",
476                    account_extended_key: "tprv8eoZddcAfpUQZNYYcJEVkpyfpss9vmBPvFMQ3CZrAxhoKpc6cM36XQJZ8jRbSuH7bouYBbKL6iQ5F4W3H2sh6NobCi4A9CJ3LJEUfDvmKib",
477                },
478            ),
479            (
480                KeyDerivationStyle::Ldk,
481                ExpectedValues {
482                    master_key: "tprv8ZgxMBicQKsPdDdJFAqvG3mt4VqsVV125X4vsor5NxK366upt6qvovLQqaCi5SJiCE1aLkt3HtxsnTpzeGu27kPC5RUCr4h3oPBPYnAvhdE",
483                    node_secret_key: "31bbbef9e06c9ffe3fec8fa24030bccd561ca8e92dded97af7cea6ca3ac85a84",
484                    node_id: "0355f8d2238a322d16b602bd0ceaad5b01019fb055971eaadcc9b29226a4da6c23",
485                    channels_seed: "7e273adccc072169f5a1cb1aee23a2d6986b6cbbeff4f0995d2762ac7c0b2511",
486                    keys_id: "0000000038410cf33c02217de2c24f800b46642355d5c4be3a78c7d5d924af38",
487                    funding_key: "6c0816a87b3a49abcf9fb4d8f2dfa8fd422a26c80ed1c8ca125a5ef2b028308b",
488                    commitment_seed: "4589ed83ea68e56ce041918fdd17bd3bb47ca8bcc992f8be1495da0a05a54ba5",
489                    account_extended_key: "tprv8eoZddcAfpUQZNYYcJEVkpyfpss9vmBPvFMQ3CZrAxhoKpc6cM36XQJZ8jRbSuH7bouYBbKL6iQ5F4W3H2sh6NobCi4A9CJ3LJEUfDvmKib",
490                },
491            ),
492            (
493                KeyDerivationStyle::Lnd,
494                ExpectedValues {
495                    master_key: "tprv8ZgxMBicQKsPdDdJFAqvG3mt4VqsVV125X4vsor5NxK366upt6qvovLQqaCi5SJiCE1aLkt3HtxsnTpzeGu27kPC5RUCr4h3oPBPYnAvhdE",
496                    node_secret_key: "a0794f0889ab261bd7ebdd8f33bfcea8497a0c429c58bde6e60ef157923fa787",
497                    node_id: "02be197c34dccb4c23a6312404b78f8570519105f79dea0bdc947200354b6d1d34",
498                    channels_seed: "7e273adccc072169f5a1cb1aee23a2d6986b6cbbeff4f0995d2762ac7c0b2511",
499                    keys_id: "36814b08b8410cf33c02217de2c24f800b46642355d5c4be3a78c7d5d924af38",
500                    funding_key: "78575e487b25b2cb527a0a67596841e4663e3b37c1da8015f290d1b951d701c2",
501                    commitment_seed: "fd8c46daa8eda9211b6cf4461003c8cc6aaa98024b438779f08254e4e67ae60c",
502                    account_extended_key: "tprv8fwV3nqr6mWFtQMxEmSGN9gbgxaoNzRms4dVeFSp3nG8chPHTmHA6razFaCUrtStcczbFDpazwBnsLkQ2uXK7rR9SxW3L92E7k6ZwTiMpwZ",
503                },
504            ),
505        ];
506
507        for (style, expected) in tests {
508            let secp_ctx = Secp256k1::new();
509            let seed = [0x01; 32];
510            let channel_id = ChannelId::new(&[0x01; 32]);
511            let channel_seed_base = [0x02; 32];
512            let keys_id = [0u8; 32];
513            let derive = key_derive(style, Testnet);
514
515            // Test master_key
516            let master_key = derive.master_key(&seed);
517            assert_eq!(
518                master_key.to_string(),
519                expected.master_key,
520                "master_key mismatch for {}",
521                style
522            );
523
524            // Test node_keys
525            let (node_id, node_secret_key) = derive.node_keys(&seed, &secp_ctx);
526            assert_eq!(
527                hex::encode(node_secret_key.secret_bytes()),
528                expected.node_secret_key,
529                "node_secret_key mismatch for {}",
530                style
531            );
532            assert_eq!(node_id.to_string(), expected.node_id, "node_id mismatch for {}", style);
533
534            // Test channels_seed
535            let channels_seed = derive.channels_seed(&seed);
536            assert_eq!(
537                hex::encode(channels_seed),
538                expected.channels_seed,
539                "channels_seed mismatch for {}",
540                style
541            );
542
543            // Test keys_id
544            let keys_id_result = derive.keys_id(channel_id.clone(), &channel_seed_base);
545            assert_eq!(
546                hex::encode(keys_id_result),
547                expected.keys_id,
548                "keys_id mismatch for {}",
549                style
550            );
551
552            // Test channel_keys
553            let (funding_key, _, _, _, _, commitment_seed) =
554                derive.channel_keys(&seed, &keys_id, 0, &master_key, &secp_ctx);
555            assert_eq!(
556                hex::encode(funding_key.secret_bytes()),
557                expected.funding_key,
558                "funding_key mismatch for {}",
559                style
560            );
561            assert_eq!(
562                hex::encode(commitment_seed),
563                expected.commitment_seed,
564                "commitment_seed mismatch for {}",
565                style
566            );
567
568            // Test get_account_extended_key
569            let account_key = style.get_account_extended_key(&secp_ctx, Testnet, &seed);
570            assert_eq!(
571                account_key.to_string(),
572                expected.account_extended_key,
573                "account_extended_key mismatch for {}",
574                style
575            );
576        }
577    }
578}