Skip to content

Commit

Permalink
Merge pull request #227 from EspressoSystems/ma/mnemonic-to-keystore
Browse files Browse the repository at this point in the history
Create keystore from mnemonic
  • Loading branch information
sveitser authored Sep 21, 2023
2 parents e88b5d7 + fb5f4bc commit 2d344de
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 28 deletions.
11 changes: 5 additions & 6 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 keygen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ edition = "2021"

[dependencies]
clap = { version = "4.3.9", features = ["derive", "env"] }
coins-bip32 = "0.8.7"
eth-keystore = { version = "0.5.0", features = ["geth-compat"] }
ethers = "2.0.7"
rand = "0.8.5"
tempfile = "3.8.0"
119 changes: 97 additions & 22 deletions keygen/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,45 +1,120 @@
use clap::Parser;
use std::path::PathBuf;
use coins_bip32::{path::DerivationPath, Bip32Error};
use eth_keystore::encrypt_key;
use ethers::{
prelude::k256::ecdsa::SigningKey,
signers::{
coins_bip39::{English, Mnemonic},
WalletError,
},
utils::secret_key_to_address,
};
use std::{
path::{Path, PathBuf},
str::FromStr,
};

use ethers::{prelude::k256::ecdsa::SigningKey, utils::secret_key_to_address};
const TEST_MNEMONIC: &str = "test test test test test test test test test test test junk";

#[derive(Parser, Clone, Debug)]
struct Options {
#[clap(long, env = "ESPRESSO_ZKEVM_KEYSTORE_DIR", default_value = "./")]
keystore_dir: PathBuf,

#[clap(
long,
env = "ESPRESSO_ZKEVM_KEYSTORE_PASSWORD",
default_value = "testonly"
)]
password: String,

#[clap(long, env = "ESPRESSO_ZKEVM_KEYSTORE_PATH")]
path: PathBuf,

#[clap(
long,
env = "ESPRESSO_ZKEVM_KEYSTORE_NAME",
default_value = "aggregator.keystore"
env = "ESPRESSO_ZKEVM_KEYSTORE_MNEMONIC",
default_value = TEST_MNEMONIC,
)]
filename: String,
mnemonic: String,

#[clap(long, env = "ESPRESSO_ZKEVM_KEYSTORE_INDEX", default_value = "0")]
index: u32,
}

fn mnemonic_to_key(
mnemonic: &Mnemonic<English>,
derivation_path: &DerivationPath,
) -> Result<SigningKey, WalletError> {
let derived_priv_key = mnemonic.derive_key(derivation_path, None /* password */)?;
let key: &coins_bip32::prelude::SigningKey = derived_priv_key.as_ref();
Ok(SigningKey::from_bytes(&key.to_bytes())?)
}

fn create_key_store(
path: &Path,
mnemonic: &str,
index: u32,
password: &str,
) -> Result<(), Bip32Error> {
let name = path.file_name().unwrap().to_str().unwrap();
let dir = path.parent().unwrap().to_path_buf();
let mnemonic = Mnemonic::<English>::new_from_phrase(mnemonic).unwrap();
let derivation_path = DerivationPath::from_str(&format!("m/44'/60'/0'/0/{}", index)).unwrap();
let signer = mnemonic_to_key(&mnemonic, &derivation_path).unwrap();
let address = secret_key_to_address(&signer);

let mut rng = rand::thread_rng();
let sign_key_bytes = signer.to_bytes();
encrypt_key(dir.clone(), &mut rng, sign_key_bytes, password, Some(name)).unwrap();
println!(
"New Keystore with address {:?} created at {:?}",
address, path
);

Ok(())
}

fn main() {
let opt = Options::parse();
let dir = opt.keystore_dir;
let name = opt.filename;
let password = opt.password;
create_key_store(&opt.path, &opt.mnemonic, opt.index, &opt.password).unwrap();
}

let mut rng = rand::thread_rng();
let (secret, _) = eth_keystore::new(dir.clone(), &mut rng, password, Some(&name)).unwrap();
let signer = SigningKey::from_bytes(secret.as_slice().into()).unwrap();
let address = secret_key_to_address(&signer);
#[cfg(test)]
mod tests {
use super::*;
use eth_keystore::decrypt_key;
use ethers::{signers::coins_bip39::English, types::H160, utils::hex};
use std::str::FromStr;

let mut full_path: PathBuf = dir;
full_path.push(name);
#[test]
fn test_mnemonic_to_key() {
let expected_address: H160 = "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266"
.parse()
.unwrap();
let expected_private_key =
hex::decode("ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80")
.unwrap();

println!(
"New Keystore with address {:?} created at {}",
address,
full_path.display()
)
// Derive known key in memory
let mnemonic = Mnemonic::<English>::new_from_phrase(TEST_MNEMONIC).unwrap();
let derivation_path =
DerivationPath::from_str(&format!("m/44'/60'/0'/0/{}", 0u32)).unwrap();
let signer = mnemonic_to_key(&mnemonic, &derivation_path).unwrap();
let signing_key_bytes = signer.to_bytes().to_vec();

// Check the derived key matches the expected key
assert_eq!(signing_key_bytes, expected_private_key);

// Check the address of the key matches the expected address of the first account
let address = secret_key_to_address(&signer);
assert_eq!(address, expected_address);

// Create a keystore and check the decrypted key matches the key derived in memory
let name = "test.keystore";
let tmpdir = tempfile::tempdir().unwrap();
let dir = tmpdir.path().to_path_buf();
let path = dir.join(name);
create_key_store(&path, TEST_MNEMONIC, 0, "testonly").unwrap();

let decrypted_key_bytes = decrypt_key(path, "testonly").unwrap();
assert_eq!(decrypted_key_bytes, expected_private_key);
}
}

0 comments on commit 2d344de

Please sign in to comment.