Skip to content

Commit

Permalink
Implement recovering account from derive path.
Browse files Browse the repository at this point in the history
  • Loading branch information
stevenatcrypto committed Nov 6, 2022
1 parent 1a57b2f commit ac06cb6
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 0 deletions.
50 changes: 50 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 sdk/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@ aptos-rest-client = { path = "../crates/aptos-rest-client" }
aptos-types = { path = "../types" }
bcs = { git = "https://github.com/aptos-labs/bcs", rev = "2cde3e8446c460cb17b0c1d6bac7e27e964ac169" }
cached-packages = { path = "../aptos-move/framework/cached-packages" }
ed25519-dalek-bip32 = "0.2.0"
move-core-types = { workspace = true }
rand_core = "0.5.1"
serde = { version = "1.0.137", features = ["derive"] }
tiny-bip39 = "0.8.2"

# Used by the examples.
[dev-dependencies]
Expand Down
52 changes: 52 additions & 0 deletions sdk/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,12 @@ use crate::{
},
};

use anyhow::Result;
use aptos_types::event::EventKey;
pub use aptos_types::*;
use bip39::{Language, Mnemonic, Seed};
use ed25519_dalek_bip32::{DerivationPath, ExtendedSecretKey};
use std::str::FromStr;

/// LocalAccount represents an account on the Aptos blockchain. Internally it
/// holds the private / public key pair and the address of the account. You can
Expand Down Expand Up @@ -42,6 +46,29 @@ impl LocalAccount {
}
}

/// Recover an account from derive path (e.g. m/44'/637'/0'/0'/0') and mnemonic phrase,
pub fn from_derive_path(
derive_path: &str,
mnemonic_phrase: &str,
sequence_number: u64,
) -> Result<Self> {
let derive_path = DerivationPath::from_str(derive_path)?;
let mnemonic = Mnemonic::from_phrase(mnemonic_phrase, Language::English)?;
// TODO: Make `password` as an optional argument.
let seed = Seed::new(&mnemonic, "");
let key = ExtendedSecretKey::from_seed(seed.as_bytes())?
.derive(&derive_path)?
.secret_key;
let key = AccountKey::from(Ed25519PrivateKey::try_from(key.as_bytes().as_ref())?);
let address = key.authentication_key().derived_address();

Ok(Self {
address,
key,
sequence_number,
})
}

/// Generate a new account locally. Note: This function does not actually
/// create an account on the Aptos blockchain, it just generates a new
/// account locally.
Expand Down Expand Up @@ -183,3 +210,28 @@ impl From<Ed25519PrivateKey> for AccountKey {
Self::from_private_key(private_key)
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_recover_account_from_derive_path() {
// Same constants in test cases of TypeScript
// https://github.com/aptos-labs/aptos-core/blob/main/ecosystem/typescript/sdk/src/aptos_account.test.ts
let derive_path = "m/44'/637'/0'/0'/0'";
let mnemonic_phrase =
"shoot island position soft burden budget tooth cruel issue economy destroy above";
let expected_address = "0x7968dab936c1bad187c60ce4082f307d030d780e91e694ae03aef16aba73f30";

// Validate if the expected address.
let account = LocalAccount::from_derive_path(derive_path, mnemonic_phrase, 0).unwrap();
assert_eq!(account.address().to_hex_literal(), expected_address);

// Return an error for empty derive path.
assert!(LocalAccount::from_derive_path("", mnemonic_phrase, 0).is_err());

// Return an error for empty mnemonic phrase.
assert!(LocalAccount::from_derive_path(derive_path, "", 0).is_err());
}
}

0 comments on commit ac06cb6

Please sign in to comment.