Skip to content

Commit fcdffa7

Browse files
authored
fix: clean up key-wallet feature flags after no-std removal (#588)
* fix: move test-only `rand` helpers out of public API in `key-wallet` `generate_using_rng` and `generate_with_seed` were only used in tests but exposed as public API requiring `rand` at compile time. Move them to `mod tests` as private helpers and drop `rand` from the crate's default features. `rand` remains as optional dep for `bip38` and is added as a dev-dependency for test use. * fix: make `bip39/std` and `secp256k1/std` unconditional in `key-wallet` These were only enabled via default features, so `--no-default-features` would break compilation because `bip39` requires its `std` feature for `parse_in` and `to_seed`.
1 parent 2f5494f commit fcdffa7

2 files changed

Lines changed: 50 additions & 93 deletions

File tree

key-wallet/Cargo.toml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ readme = "README.md"
99
license = "CC0-1.0"
1010

1111
[features]
12-
default = ["secp256k1/std", "bip39/std", "getrandom", "rand", "manager"]
12+
default = ["getrandom", "manager"]
1313
manager = ["dep:tokio"]
1414
parallel-filters = ["manager", "dep:rayon"]
1515
serde = ["dep:serde", "dep:serde_json", "dashcore_hashes/serde", "secp256k1/serde", "dashcore/serde"]
@@ -23,8 +23,8 @@ test-utils = ["dashcore/test-utils"]
2323
internals = { path = "../internals", package = "dashcore-private" }
2424
dashcore_hashes = { path = "../hashes" }
2525
dashcore = { path="../dash" }
26-
secp256k1 = { version = "0.30.0", default-features = false, features = ["hashes", "recovery"] }
27-
bip39 = { version = "2.2.0", default-features = false, features = ["chinese-simplified", "chinese-traditional", "czech", "french", "italian", "japanese", "korean", "portuguese", "spanish", "zeroize"] }
26+
secp256k1 = { version = "0.30.0", default-features = false, features = ["hashes", "recovery", "std"] }
27+
bip39 = { version = "2.2.0", default-features = false, features = ["std", "chinese-simplified", "chinese-traditional", "czech", "french", "italian", "japanese", "korean", "portuguese", "spanish", "zeroize"] }
2828
serde = { version = "1.0", default-features = false, features = ["derive"], optional = true }
2929
base58ck = { version = "0.1.0", default-features = false }
3030
bitflags = { version = "2.6", default-features = false }
@@ -51,4 +51,5 @@ rayon = { version = "1.11", optional = true }
5151
dashcore = { path="../dash", features = ["test-utils"] }
5252
hex = "0.4"
5353
key-wallet = { path = ".", features = ["test-utils", "bip38", "serde", "bincode", "eddsa", "bls"] }
54+
rand = { version = "0.8", features = ["std", "std_rng"] }
5455
test-case = "3.3"

key-wallet/src/mnemonic.rs

Lines changed: 46 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ use crate::error::{Error, Result};
88
#[cfg(feature = "bincode")]
99
use bincode_derive::{Decode, Encode};
1010
use bip39 as bip39_crate;
11-
use rand::{RngCore, SeedableRng};
1211
#[cfg(feature = "serde")]
1312
use serde::{Deserialize, Serialize};
1413
use zeroize::{Zeroize, ZeroizeOnDrop};
@@ -140,77 +139,6 @@ impl Mnemonic {
140139
Err(Error::InvalidMnemonic("Mnemonic generation requires getrandom feature".into()))
141140
}
142141

143-
/// Generate a new mnemonic using a provided RNG
144-
///
145-
/// This allows using custom random number generators like StdRng, ChaChaRng, etc.
146-
///
147-
/// # Examples
148-
/// ```no_run
149-
/// use key_wallet::mnemonic::{Mnemonic, Language};
150-
/// use rand::rngs::StdRng;
151-
/// use rand::SeedableRng;
152-
///
153-
/// let mut rng = StdRng::from_entropy();
154-
/// let mnemonic = Mnemonic::generate_using_rng(12, Language::English, &mut rng).unwrap();
155-
/// ```
156-
pub fn generate_using_rng<R: RngCore>(
157-
word_count: usize,
158-
language: Language,
159-
rng: &mut R,
160-
) -> Result<Self> {
161-
// Validate word count and get entropy size
162-
let entropy_bytes = match word_count {
163-
12 => 16, // 128 bits / 8
164-
15 => 20, // 160 bits / 8
165-
18 => 24, // 192 bits / 8
166-
21 => 28, // 224 bits / 8
167-
24 => 32, // 256 bits / 8
168-
_ => return Err(Error::InvalidMnemonic("Invalid word count".into())),
169-
};
170-
171-
// Generate random entropy using provided RNG
172-
let mut entropy = vec![0u8; entropy_bytes];
173-
rng.fill_bytes(&mut entropy);
174-
175-
// Create mnemonic from entropy with specified language
176-
let mnemonic = bip39_crate::Mnemonic::from_entropy_in(language.into(), &entropy)
177-
.map_err(|e| Error::InvalidMnemonic(e.to_string()))?;
178-
179-
Ok(Self {
180-
inner: mnemonic,
181-
})
182-
}
183-
184-
/// Generate a new mnemonic from a u64 seed
185-
///
186-
/// This creates a deterministic mnemonic from a seed value.
187-
/// Uses StdRng seeded with the provided value.
188-
///
189-
/// # Warning
190-
/// This is deterministic - the same seed will always produce the same mnemonic.
191-
/// This should only be used for testing or when deterministic generation is specifically required.
192-
///
193-
/// # Examples
194-
/// ```no_run
195-
/// use key_wallet::mnemonic::{Mnemonic, Language};
196-
///
197-
/// let seed = 12345u64;
198-
/// let mnemonic = Mnemonic::generate_with_seed(12, Language::English, seed).unwrap();
199-
/// ```
200-
pub fn generate_with_seed(word_count: usize, language: Language, seed: u64) -> Result<Self> {
201-
use rand::rngs::StdRng;
202-
203-
// Create RNG from seed
204-
// We need to convert u64 to [u8; 32] for StdRng
205-
let mut seed_bytes = [0u8; 32];
206-
seed_bytes[..8].copy_from_slice(&seed.to_le_bytes());
207-
208-
let mut rng = StdRng::from_seed(seed_bytes);
209-
210-
// Use the RNG to generate the mnemonic
211-
Self::generate_using_rng(word_count, language, &mut rng)
212-
}
213-
214142
/// Create a mnemonic from a phrase
215143
pub fn from_phrase(phrase: &str, language: Language) -> Result<Self> {
216144
let mnemonic = bip39_crate::Mnemonic::parse_in(language.into(), phrase)
@@ -521,7 +449,36 @@ mod tests {
521449
assert_eq!(format!("{}", mnemonic), phrase);
522450
}
523451

524-
// Test mnemonic generation with custom RNG
452+
// Test helper: generate a mnemonic using a provided RNG
453+
fn generate_using_rng<R: rand::RngCore>(
454+
word_count: usize,
455+
language: Language,
456+
rng: &mut R,
457+
) -> Result<Mnemonic> {
458+
let entropy_bytes = match word_count {
459+
12 => 16,
460+
15 => 20,
461+
18 => 24,
462+
21 => 28,
463+
24 => 32,
464+
_ => return Err(Error::InvalidMnemonic("Invalid word count".into())),
465+
};
466+
467+
let mut entropy = vec![0u8; entropy_bytes];
468+
rng.fill_bytes(&mut entropy);
469+
Mnemonic::from_entropy(&entropy, language)
470+
}
471+
472+
// Test helper: generate a deterministic mnemonic from a u64 seed
473+
fn generate_with_seed(word_count: usize, language: Language, seed: u64) -> Result<Mnemonic> {
474+
use rand::SeedableRng;
475+
476+
let mut seed_bytes = [0u8; 32];
477+
seed_bytes[..8].copy_from_slice(&seed.to_le_bytes());
478+
let mut rng = rand::rngs::StdRng::from_seed(seed_bytes);
479+
generate_using_rng(word_count, language, &mut rng)
480+
}
481+
525482
#[test]
526483
fn test_generate_using_rng() {
527484
use rand::rngs::StdRng;
@@ -531,62 +488,62 @@ mod tests {
531488
let mut rng = StdRng::seed_from_u64(12345);
532489

533490
// Generate 12-word mnemonic
534-
let mnemonic = Mnemonic::generate_using_rng(12, Language::English, &mut rng).unwrap();
491+
let mnemonic = generate_using_rng(12, Language::English, &mut rng).unwrap();
535492
assert_eq!(mnemonic.word_count(), 12);
536493

537494
// Generate 24-word mnemonic
538495
let mut rng = StdRng::seed_from_u64(12345);
539-
let mnemonic24 = Mnemonic::generate_using_rng(24, Language::English, &mut rng).unwrap();
496+
let mnemonic24 = generate_using_rng(24, Language::English, &mut rng).unwrap();
540497
assert_eq!(mnemonic24.word_count(), 24);
541498

542499
// Test with different language
543500
let mut rng = StdRng::seed_from_u64(54321);
544-
let mnemonic_jp = Mnemonic::generate_using_rng(12, Language::Japanese, &mut rng).unwrap();
501+
let mnemonic_jp = generate_using_rng(12, Language::Japanese, &mut rng).unwrap();
545502
assert_eq!(mnemonic_jp.word_count(), 12);
546503

547504
// Test invalid word count
548505
let mut rng = StdRng::seed_from_u64(99999);
549-
assert!(Mnemonic::generate_using_rng(13, Language::English, &mut rng).is_err());
506+
assert!(generate_using_rng(13, Language::English, &mut rng).is_err());
550507
}
551508

552509
// Test deterministic mnemonic generation from seed
553510
#[test]
554511
fn test_generate_with_seed() {
555512
// Generate mnemonic from seed
556513
let seed = 42u64;
557-
let mnemonic1 = Mnemonic::generate_with_seed(12, Language::English, seed).unwrap();
558-
let mnemonic2 = Mnemonic::generate_with_seed(12, Language::English, seed).unwrap();
514+
let mnemonic1 = generate_with_seed(12, Language::English, seed).unwrap();
515+
let mnemonic2 = generate_with_seed(12, Language::English, seed).unwrap();
559516

560517
// Same seed should produce same mnemonic
561518
assert_eq!(mnemonic1.phrase(), mnemonic2.phrase());
562519
assert_eq!(mnemonic1.word_count(), 12);
563520

564521
// Different seed should produce different mnemonic
565-
let mnemonic3 = Mnemonic::generate_with_seed(12, Language::English, 43).unwrap();
522+
let mnemonic3 = generate_with_seed(12, Language::English, 43).unwrap();
566523
assert_ne!(mnemonic1.phrase(), mnemonic3.phrase());
567524

568525
// Test with different word counts
569-
let mnemonic_15 = Mnemonic::generate_with_seed(15, Language::English, seed).unwrap();
526+
let mnemonic_15 = generate_with_seed(15, Language::English, seed).unwrap();
570527
assert_eq!(mnemonic_15.word_count(), 15);
571528

572-
let mnemonic_18 = Mnemonic::generate_with_seed(18, Language::English, seed).unwrap();
529+
let mnemonic_18 = generate_with_seed(18, Language::English, seed).unwrap();
573530
assert_eq!(mnemonic_18.word_count(), 18);
574531

575-
let mnemonic_21 = Mnemonic::generate_with_seed(21, Language::English, seed).unwrap();
532+
let mnemonic_21 = generate_with_seed(21, Language::English, seed).unwrap();
576533
assert_eq!(mnemonic_21.word_count(), 21);
577534

578-
let mnemonic_24 = Mnemonic::generate_with_seed(24, Language::English, seed).unwrap();
535+
let mnemonic_24 = generate_with_seed(24, Language::English, seed).unwrap();
579536
assert_eq!(mnemonic_24.word_count(), 24);
580537

581538
// Test with different languages
582-
let mnemonic_fr = Mnemonic::generate_with_seed(12, Language::French, seed).unwrap();
539+
let mnemonic_fr = generate_with_seed(12, Language::French, seed).unwrap();
583540
assert_eq!(mnemonic_fr.word_count(), 12);
584541
// French mnemonic should be different from English even with same seed and entropy
585542
// (due to different word lists)
586543

587544
// Test invalid word count
588-
assert!(Mnemonic::generate_with_seed(10, Language::English, seed).is_err());
589-
assert!(Mnemonic::generate_with_seed(25, Language::English, seed).is_err());
545+
assert!(generate_with_seed(10, Language::English, seed).is_err());
546+
assert!(generate_with_seed(25, Language::English, seed).is_err());
590547
}
591548

592549
// Test that generate_with_seed is truly deterministic
@@ -596,9 +553,8 @@ mod tests {
596553

597554
for seed in test_seeds {
598555
// Generate multiple times with same seed
599-
let mnemonics: Vec<_> = (0..5)
600-
.map(|_| Mnemonic::generate_with_seed(12, Language::English, seed).unwrap())
601-
.collect();
556+
let mnemonics: Vec<_> =
557+
(0..5).map(|_| generate_with_seed(12, Language::English, seed).unwrap()).collect();
602558

603559
// All should be identical
604560
let first_phrase = mnemonics[0].phrase();

0 commit comments

Comments
 (0)