Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ ark-std = { version = "0.5", features = ["parallel"] }
ark-test-curves = { version = "0.5", features = ["parallel", "asm"] }
base64 = "0.21.5"
bcs = "0.1.3"
bip39 = { version = "2.1", features = ["std"] }
bitvec = "1.0.0"
blake2 = "0.10.0"
bs58 = "0.5.0"
Expand All @@ -52,6 +53,7 @@ elf = "0.7.2"
env_logger = "0.11.1"
getrandom = { version = "0.2.15", features = ["js"] }
hex = { version = "0.4", features = ["serde"] }
hmac = "0.12.1"
iai = "0.1"
itertools = "0.12.1"
js-sys = "=0.3.64"
Expand Down
2 changes: 2 additions & 0 deletions signer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@ path = "src/lib.rs"
[dependencies]
ark-ec.workspace = true
ark-ff.workspace = true
bip39.workspace = true
bitvec.workspace = true
blake2.workspace = true
bs58.workspace = true
hex.workspace = true
hmac.workspace = true
mina-curves.workspace = true
mina-hasher.workspace = true
num-bigint.workspace = true
Expand Down
31 changes: 31 additions & 0 deletions signer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,37 @@ The scalar field is larger than the base field by exactly
Both fields are approximately 2^254, making signatures 64 bytes total (32 bytes
for `rx` + 32 bytes for `s`).

## BIP39 Mnemonic Support

The `mina_signer` crate now supports deriving Mina keypairs from BIP39 mnemonic seed phrases. This enables:

* Generating and using 12-24 word mnemonic phrases
* BIP32 hierarchical deterministic (HD) wallet derivation
* Ledger hardware wallet compatibility (using path `m/44'/12586'/account'/0/0`)
* Multiple account derivation from a single mnemonic

### Quick Example

```rust
use mina_signer::bip39::Bip39;

# fn main() -> Result<(), Box<dyn std::error::Error>> {
// Generate a new 24-word mnemonic
let mnemonic = Bip39::generate_mnemonic(24)?;

// Derive a keypair using BIP32 (Ledger-compatible)
let keypair = Bip39::mnemonic_to_keypair_bip32(&mnemonic, None, 0)?;
let address = keypair.public.into_address();

// Derive multiple accounts
let account1 = Bip39::mnemonic_to_keypair_bip32(&mnemonic, None, 1)?;
# Ok(())
# }
```

For a complete example with multiple derivation methods, see:
`cargo run --example bip39_derivation`

## Signer interface

The `mina_signer` crate currently supports creating both legacy and current signers.
Expand Down
99 changes: 99 additions & 0 deletions signer/examples/bip39_derivation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
//! Comprehensive example of BIP39 mnemonic seed phrase support for Mina
//!
//! This example demonstrates:
//! 1. Generating BIP39 mnemonic phrases
//! 2. Deriving Mina keypairs from mnemonics
//! 3. Using BIP32 hierarchical deterministic derivation (Ledger-compatible)
//! 4. Deriving multiple accounts from a single mnemonic
//!
//! Run with:
//! ```
//! cargo run --example bip39_derivation
//! ```

use mina_signer::bip39::{Bip39, MINA_COIN_TYPE};

fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("=== Mina BIP39 Derivation Example ===\n");

// Example 1: Generate a new mnemonic
println!("1. Generate a new 24-word mnemonic:");
let mnemonic = Bip39::generate_mnemonic(24)?;
println!(" Mnemonic: {}\n", mnemonic);

// Example 2: Use a known test mnemonic
let test_mnemonic = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
println!("2. Using test mnemonic for reproducible results:");
println!(" Mnemonic: {}\n", test_mnemonic);

// Example 3: Simple derivation (non-BIP32)
println!("3. Simple derivation (non-BIP32 hierarchical):");
let keypair_simple = Bip39::mnemonic_to_keypair(test_mnemonic, None)?;
println!(" Secret key: {}", keypair_simple.secret.to_hex());
println!(" Address: {}\n", keypair_simple.public.into_address());

// Example 4: BIP32 hierarchical derivation (Ledger-compatible)
println!("4. BIP32 hierarchical derivation (Ledger-compatible):");
println!(" Path: m/44'/{}'/<account>'/0/0", MINA_COIN_TYPE);
let keypair_bip32 = Bip39::mnemonic_to_keypair_bip32(test_mnemonic, None, 0)?;
println!(" Account 0:");
println!(" Secret key: {}", keypair_bip32.secret.to_hex());
println!(" Address: {}\n", keypair_bip32.public.into_address());

// Example 5: Multiple accounts from the same mnemonic
println!("5. Derive multiple accounts (BIP32 HD wallet):");
for account in 0..3 {
let keypair = Bip39::mnemonic_to_keypair_bip32(test_mnemonic, None, account)?;
println!(" Account {}: {}", account, keypair.public.into_address());
}
println!();

// Example 6: Using a passphrase for additional security
println!("6. Derivation with optional passphrase:");
let keypair_no_pass = Bip39::mnemonic_to_keypair_bip32(test_mnemonic, None, 0)?;
let keypair_with_pass =
Bip39::mnemonic_to_keypair_bip32(test_mnemonic, Some("my-secret-passphrase"), 0)?;

println!(
" Without passphrase: {}",
keypair_no_pass.public.into_address()
);
println!(
" With passphrase: {}",
keypair_with_pass.public.into_address()
);
println!(" (Different passphrases produce different keys)\n");

// Example 7: Working with seeds directly
println!("7. Advanced: Working with seeds directly:");
let seed = Bip39::mnemonic_to_seed(test_mnemonic, None)?;
println!(" Seed length: {} bytes", seed.len());
println!(" Seed (hex): {}...", hex::encode(&seed[..16]));

// Derive from seed
let keypair_from_seed = Bip39::seed_to_keypair_bip32(&seed, 0)?;
println!(
" Derived address: {}\n",
keypair_from_seed.public.into_address()
);

// Example 8: Non-BIP32 account indexing
println!("8. Simple account indexing (non-BIP32):");
for account_index in 0..3 {
let keypair = Bip39::mnemonic_to_keypair_with_index(test_mnemonic, None, account_index)?;
println!(
" Account {}: {}",
account_index,
keypair.public.into_address()
);
}

println!("\n=== Best Practices ===");
println!("1. Use BIP32 derivation (mnemonic_to_keypair_bip32) for Ledger compatibility");
println!("2. Store mnemonics securely - they provide access to all derived accounts");
println!("3. Use passphrases for an additional layer of security");
println!("4. BIP44 path for Mina: m/44'/12586'/<account>'/0/0");
println!("5. Never share your mnemonic or secret keys");

Ok(())
}
Loading
Loading