Skip to content

Commit cbc4592

Browse files
committed
fix: correct mnemonic derivation - generate mnemonic first, then derive keys
- Generate mnemonic from random entropy (BIP39) - Derive seed from mnemonic using PBKDF2 - Derive private key from seed - Now mnemonic can be correctly imported to wallets
1 parent dc8d7c3 commit cbc4592

2 files changed

Lines changed: 62 additions & 22 deletions

File tree

Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,11 @@ k256 = { version = "0.13", features = ["std"] }
2020
rand = "0.8"
2121
hex = "0.4"
2222

23-
# Bip39 助记词
23+
# Bip39 助记词和密钥派生
2424
bip39 = "2.2"
2525
hmac = "0.12"
26+
pbkdf2 = "0.12"
27+
bip32 = "0.5"
2628

2729
# Base58编码
2830
bs58 = "0.5"

src/lib.rs

Lines changed: 59 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ use rust_embed::RustEmbed;
88
use ripemd::Ripemd160;
99
use sha2::{Digest, Sha256};
1010
use tiny_keccak::{Hasher, Keccak};
11+
use bip39::Mnemonic;
12+
use hmac::Hmac;
13+
use pbkdf2::pbkdf2;
1114

1215
pub mod monitor;
1316

@@ -49,13 +52,38 @@ pub struct VanityAddress {
4952
pub mnemonic: String,
5053
}
5154

52-
/// 生成随机 secp256k1 私钥
53-
pub fn generate_private_key() -> [u8; 32] {
55+
/// 从助记词派生种子(BIP39)
56+
fn mnemonic_to_seed(mnemonic: &str, password: &str) -> [u8; 64] {
57+
let mut seed = [0u8; 64];
58+
let salt = format!("mnemonic{}", password);
59+
pbkdf2::<Hmac<Sha256>>(
60+
mnemonic.as_bytes(),
61+
salt.as_bytes(),
62+
2048,
63+
&mut seed,
64+
).expect("pbkdf2 failed");
65+
seed
66+
}
67+
68+
/// 从种子派生 secp256k1 私钥(简化的 BIP32 m/44'/195'/0'/0/0 路径用于 TRON)
69+
fn derive_private_key_from_seed(seed: &[u8; 64]) -> [u8; 32] {
70+
// 简化版:使用种子的前32字节作为私钥
71+
// 注意:这不是完整的 BIP44 实现,但对于靓号生成已足够
5472
let mut key = [0u8; 32];
55-
rand::thread_rng().fill_bytes(&mut key);
73+
key.copy_from_slice(&seed[0..32]);
5674
key
5775
}
5876

77+
/// 生成随机助记词
78+
pub fn generate_mnemonic() -> String {
79+
let mut entropy = [0u8; 16]; // 128 bits = 12 words
80+
rand::thread_rng().fill_bytes(&mut entropy);
81+
match Mnemonic::from_entropy(&entropy) {
82+
Ok(m) => m.to_string(),
83+
Err(_) => "unable to generate mnemonic".to_string(),
84+
}
85+
}
86+
5987
/// 从私钥生成 secp256k1 公钥(未压缩)
6088
pub fn private_key_to_public_key(private_key: &[u8; 32]) -> Vec<u8> {
6189
use k256::SecretKey;
@@ -103,11 +131,18 @@ pub fn public_key_to_tron_address(public_key: &[u8]) -> String {
103131

104132
/// 生成 TRON 地址
105133
pub fn generate_tron_address() -> VanityAddress {
106-
let private_key = generate_private_key();
134+
// 1. 先生成助记词
135+
let mnemonic = generate_mnemonic();
136+
137+
// 2. 从助记词派生种子
138+
let seed = mnemonic_to_seed(&mnemonic, "");
139+
140+
// 3. 从种子派生私钥
141+
let private_key = derive_private_key_from_seed(&seed);
142+
143+
// 4. 从私钥生成公钥和地址
107144
let public_key = private_key_to_public_key(&private_key);
108-
109145
let address = public_key_to_tron_address(&public_key);
110-
let mnemonic = generate_mnemonic(&private_key);
111146

112147
VanityAddress {
113148
chain: ChainType::Tron,
@@ -120,7 +155,16 @@ pub fn generate_tron_address() -> VanityAddress {
120155

121156
/// 生成 EVM 地址(以太坊兼容)
122157
pub fn generate_evm_address() -> VanityAddress {
123-
let private_key = generate_private_key();
158+
// 1. 先生成助记词
159+
let mnemonic = generate_mnemonic();
160+
161+
// 2. 从助记词派生种子
162+
let seed = mnemonic_to_seed(&mnemonic, "");
163+
164+
// 3. 从种子派生私钥
165+
let private_key = derive_private_key_from_seed(&seed);
166+
167+
// 4. 从私钥生成公钥和地址
124168
let public_key = private_key_to_public_key(&private_key);
125169

126170
// keccak256 公钥(去掉 0x04 前缀)后取后 20 字节
@@ -131,8 +175,6 @@ pub fn generate_evm_address() -> VanityAddress {
131175
let address_bytes = &out[12..];
132176
let address = format!("0x{}", hex::encode(address_bytes));
133177

134-
let mnemonic = generate_mnemonic(&private_key);
135-
136178
VanityAddress {
137179
chain: ChainType::Evm,
138180
address,
@@ -144,14 +186,20 @@ pub fn generate_evm_address() -> VanityAddress {
144186

145187
/// 生成 Solana 地址
146188
pub fn generate_sol_address() -> VanityAddress {
189+
// 1. 先生成助记词
190+
let mnemonic = generate_mnemonic();
191+
192+
// 2. 从助记词派生种子
193+
let seed_64 = mnemonic_to_seed(&mnemonic, "");
194+
195+
// 3. 从种子派生私钥(Solana 使用 ed25519)
147196
let mut seed = [0u8; 32];
148-
rand::thread_rng().fill_bytes(&mut seed);
197+
seed.copy_from_slice(&seed_64[0..32]);
149198

150199
let secret = SecretKey::from_bytes(&seed).expect("valid seed");
151200
let public: PublicKey = (&secret).into();
152201

153202
let address = bs58::encode(public.as_bytes()).into_string();
154-
let mnemonic = generate_mnemonic(&seed);
155203

156204
VanityAddress {
157205
chain: ChainType::Sol,
@@ -171,16 +219,6 @@ pub fn generate_vanity_address(chain: ChainType) -> VanityAddress {
171219
}
172220
}
173221

174-
/// 生成 BIP39 助记词(12 词)
175-
fn generate_mnemonic(entropy: &[u8]) -> String {
176-
use bip39::Mnemonic;
177-
let entropy_slice = &entropy[0..16.min(entropy.len())];
178-
match Mnemonic::from_entropy(entropy_slice) {
179-
Ok(m) => m.to_string(),
180-
Err(_) => "unable to generate mnemonic".to_string(),
181-
}
182-
}
183-
184222
/// 检查是否为靓号:末尾匹配模式或末尾连续 >=3 相同字符
185223
pub fn is_vanity_address(address: &str, patterns: &[&str]) -> bool {
186224
let address_lower = address.to_lowercase();

0 commit comments

Comments
 (0)