Skip to content

Commit 3034614

Browse files
authored
Merge pull request #699 from coreyphillips/feat/configurable-mnemonic-word-count
Add support for configurable BIP39 mnemonic word counts
2 parents f641845 + 0a860d4 commit 3034614

File tree

5 files changed

+74
-11
lines changed

5 files changed

+74
-11
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ reqwest = { version = "0.12", default-features = false, features = ["json", "rus
5050
rustls = { version = "0.23", default-features = false }
5151
rusqlite = { version = "0.31.0", features = ["bundled"] }
5252
bitcoin = "0.32.7"
53-
bip39 = "2.0.0"
53+
bip39 = { version = "2.0.0", features = ["rand"] }
5454
bip21 = { version = "0.5", features = ["std"], default-features = false }
5555

5656
base64 = { version = "0.22.1", default-features = false, features = ["std"] }

bindings/ldk_node.udl

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
namespace ldk_node {
2-
Mnemonic generate_entropy_mnemonic();
2+
Mnemonic generate_entropy_mnemonic(WordCount? word_count);
33
Config default_config();
44
};
55

@@ -47,6 +47,14 @@ dictionary LSPS2ServiceConfig {
4747
boolean client_trusts_lsp;
4848
};
4949

50+
enum WordCount {
51+
"Words12",
52+
"Words15",
53+
"Words18",
54+
"Words21",
55+
"Words24",
56+
};
57+
5058
enum LogLevel {
5159
"Gossip",
5260
"Trace",

src/io/utils.rs

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -47,25 +47,25 @@ use crate::io::{
4747
};
4848
use crate::logger::{log_error, LdkLogger, Logger};
4949
use crate::peer_store::PeerStore;
50-
use crate::types::{Broadcaster, DynStore, KeysManager, Sweeper};
50+
use crate::types::{Broadcaster, DynStore, KeysManager, Sweeper, WordCount};
5151
use crate::wallet::ser::{ChangeSetDeserWrapper, ChangeSetSerWrapper};
5252
use crate::{Error, EventQueue, NodeMetrics, PaymentDetails};
5353

5454
pub const EXTERNAL_PATHFINDING_SCORES_CACHE_KEY: &str = "external_pathfinding_scores_cache";
5555

56-
/// Generates a random [BIP 39] mnemonic.
56+
/// Generates a random [BIP 39] mnemonic with the specified word count.
57+
///
58+
/// If no word count is specified, defaults to 24 words (256-bit entropy).
5759
///
5860
/// The result may be used to initialize the [`Node`] entropy, i.e., can be given to
5961
/// [`Builder::set_entropy_bip39_mnemonic`].
6062
///
6163
/// [BIP 39]: https://github.com/bitcoin/bips/blob/master/bip-0039.mediawiki
6264
/// [`Node`]: crate::Node
6365
/// [`Builder::set_entropy_bip39_mnemonic`]: crate::Builder::set_entropy_bip39_mnemonic
64-
pub fn generate_entropy_mnemonic() -> Mnemonic {
65-
// bip39::Mnemonic supports 256 bit entropy max
66-
let mut entropy = [0; 32];
67-
OsRng.try_fill_bytes(&mut entropy).expect("Failed to generate entropy");
68-
Mnemonic::from_entropy(&entropy).unwrap()
66+
pub fn generate_entropy_mnemonic(word_count: Option<WordCount>) -> Mnemonic {
67+
let word_count = word_count.unwrap_or(WordCount::Words24).word_count();
68+
Mnemonic::generate(word_count).expect("Failed to generate mnemonic")
6969
}
7070

7171
pub(crate) fn read_or_generate_seed_file<L: Deref>(
@@ -627,9 +627,35 @@ mod tests {
627627

628628
#[test]
629629
fn mnemonic_to_entropy_to_mnemonic() {
630-
let mnemonic = generate_entropy_mnemonic();
631-
630+
// Test default (24 words)
631+
let mnemonic = generate_entropy_mnemonic(None);
632632
let entropy = mnemonic.to_entropy();
633633
assert_eq!(mnemonic, Mnemonic::from_entropy(&entropy).unwrap());
634+
assert_eq!(mnemonic.word_count(), 24);
635+
636+
// Test with different word counts
637+
let word_counts = [
638+
WordCount::Words12,
639+
WordCount::Words15,
640+
WordCount::Words18,
641+
WordCount::Words21,
642+
WordCount::Words24,
643+
];
644+
645+
for word_count in word_counts {
646+
let mnemonic = generate_entropy_mnemonic(Some(word_count));
647+
let entropy = mnemonic.to_entropy();
648+
assert_eq!(mnemonic, Mnemonic::from_entropy(&entropy).unwrap());
649+
650+
// Verify expected word count
651+
let expected_words = match word_count {
652+
WordCount::Words12 => 12,
653+
WordCount::Words15 => 15,
654+
WordCount::Words18 => 18,
655+
WordCount::Words21 => 21,
656+
WordCount::Words24 => 24,
657+
};
658+
assert_eq!(mnemonic.word_count(), expected_words);
659+
}
634660
}
635661
}

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ use types::{
156156
};
157157
pub use types::{
158158
ChannelDetails, CustomTlvRecord, DynStore, PeerDetails, SyncAndAsyncKVStore, UserChannelId,
159+
WordCount,
159160
};
160161
pub use {
161162
bip39, bitcoin, lightning, lightning_invoice, lightning_liquidity, lightning_types, tokio,

src/types.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,34 @@ use crate::logger::Logger;
3636
use crate::message_handler::NodeCustomMessageHandler;
3737
use crate::payment::PaymentDetails;
3838

39+
/// Supported BIP39 mnemonic word counts for entropy generation.
40+
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
41+
pub enum WordCount {
42+
/// 12-word mnemonic (128-bit entropy)
43+
Words12,
44+
/// 15-word mnemonic (160-bit entropy)
45+
Words15,
46+
/// 18-word mnemonic (192-bit entropy)
47+
Words18,
48+
/// 21-word mnemonic (224-bit entropy)
49+
Words21,
50+
/// 24-word mnemonic (256-bit entropy)
51+
Words24,
52+
}
53+
54+
impl WordCount {
55+
/// Returns the word count as a usize value.
56+
pub fn word_count(&self) -> usize {
57+
match self {
58+
WordCount::Words12 => 12,
59+
WordCount::Words15 => 15,
60+
WordCount::Words18 => 18,
61+
WordCount::Words21 => 21,
62+
WordCount::Words24 => 24,
63+
}
64+
}
65+
}
66+
3967
/// A supertrait that requires that a type implements both [`KVStore`] and [`KVStoreSync`] at the
4068
/// same time.
4169
pub trait SyncAndAsyncKVStore: KVStore + KVStoreSync {}

0 commit comments

Comments
 (0)