diff --git a/Cargo.lock b/Cargo.lock index fa2f824c..628f1a94 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6728,6 +6728,7 @@ dependencies = [ "prost-types 0.11.8", "rand 0.8.5", "regex 1.9.3", + "schnorrkel", "scrypt", "serde", "serde_json", diff --git a/token-core/tcx-migration/src/migration.rs b/token-core/tcx-migration/src/migration.rs index 223448e8..0266c94e 100644 --- a/token-core/tcx-migration/src/migration.rs +++ b/token-core/tcx-migration/src/migration.rs @@ -118,11 +118,14 @@ impl LegacyKeystore { let unlocker = self.crypto.use_key(&key)?; let sec_key_bytes = unlocker.plaintext()?; let sec_key = Secp256k1PrivateKey::from_slice(&sec_key_bytes)?; + let pub_key = TypedPublicKey::Secp256k1(sec_key.public_key()); let coin_info = coin_info_from_param("ETHEREUM", "", "", CurveType::SECP256k1.as_str())?; + let calc_address = EthAddress::from_public_key(&pub_key, &coin_info)?.to_string(); - let calc_addr_bytes = &Vec::from_hex(calc_address)?; - let addr_bytes = &Vec::from_hex(&self.address)?; + let calc_addr_bytes = &Vec::from_hex_auto(calc_address)?; + let addr_bytes = &Vec::from_hex_auto(&self.address)?; + if calc_addr_bytes == addr_bytes { Ok(()) } else { diff --git a/token-core/tcx-primitive/src/sr25519.rs b/token-core/tcx-primitive/src/sr25519.rs index 7f3aedd7..833e4591 100644 --- a/token-core/tcx-primitive/src/sr25519.rs +++ b/token-core/tcx-primitive/src/sr25519.rs @@ -35,8 +35,8 @@ impl TraitPrivateKey for Sr25519PrivateKey { // MiniSecretKey::from_bytes(data).expect("32 bytes can always build a key; qed"); // // let kp = mini_key.expand_to_keypair(ExpansionMode::Ed25519); - let pk = SecretKey::from_ed25519_bytes(data).map_err(|_| KeyError::InvalidSr25519Key)?; - // let pk = SecretKey::from_bytes(data).map_err(|_| KeyError::InvalidSr25519Key)?; + // let pk = SecretKey::from_ed25519_bytes(data).map_err(|_| KeyError::InvalidSr25519Key)?; + let pk = SecretKey::from_bytes(data).map_err(|_| KeyError::InvalidSr25519Key)?; Ok(Sr25519PrivateKey(Pair::from(pk))) } diff --git a/token-core/tcx-proto/src/api.proto b/token-core/tcx-proto/src/api.proto index cb1b2d5c..56ee7881 100644 --- a/token-core/tcx-proto/src/api.proto +++ b/token-core/tcx-proto/src/api.proto @@ -58,8 +58,9 @@ message SignResultPoc { } message PublicKeyDerivation { - string path = 1; - string curve = 2; + string chainType = 1; + string path = 2; + string curve = 3; } message GetPublicKeysParam { diff --git a/token-core/tcx-proto/src/params.proto b/token-core/tcx-proto/src/params.proto index 6315b03d..a74a31b8 100644 --- a/token-core/tcx-proto/src/params.proto +++ b/token-core/tcx-proto/src/params.proto @@ -119,16 +119,6 @@ message ImportPrivateKeyParam { bool overwrite = 5; } -// FUNCTION: private_key_store_export(PrivateKeyStoreExportParam): ExportResult -// -// export the private key from a private key keystore -message PrivateKeyStoreExportParam { - string id = 1; - string password = 2; - string chainType = 3; - string network = 4; -} - // FUNCTION: keystore_common_exists(KeystoreCommonExistsParam): ExistsKeystoreResult // // Check is there a keystore was generate by the special privateKey or mnemonic @@ -205,20 +195,6 @@ message DeriveSubAccountsResult { repeated AccountResponse accounts = 1; } - -message PublicKeyParam { - string id = 1; - string chainType = 2; - string address = 3; -} - -message PublicKeyResult { - string id = 1; - string chainType = 2; - string address = 3; - string publicKey = 4; -} - message RemoveWalletParam { string id = 1; string password = 2; diff --git a/token-core/tcx-substrate/Cargo.toml b/token-core/tcx-substrate/Cargo.toml index de644ea2..9d4102f7 100644 --- a/token-core/tcx-substrate/Cargo.toml +++ b/token-core/tcx-substrate/Cargo.toml @@ -33,6 +33,7 @@ serde = { version = "1.0.147", features = ["derive"] } base64 = "0.13.1" scrypt = { version = "0.10.0", default-features = false } regex = "1.7.0" +schnorrkel = "0.9.1" [dev-dependencies] diff --git a/token-core/tcx-substrate/src/keystore.rs b/token-core/tcx-substrate/src/keystore.rs index 1052fad2..82abf929 100644 --- a/token-core/tcx-substrate/src/keystore.rs +++ b/token-core/tcx-substrate/src/keystore.rs @@ -1,19 +1,22 @@ use crate::SubstrateAddress; use rand::Rng; use serde::{de, Deserialize, Deserializer, Serialize}; +use sp_core::Pair; use std::convert::TryInto; -use tcx_keystore::Address; +use tcx_keystore::{tcx_ensure, Address}; use byteorder::LittleEndian; use byteorder::{ReadBytesExt, WriteBytesExt}; use regex::Regex; +use schnorrkel::{SecretKey, SECRET_KEY_LENGTH}; use serde::__private::{fmt, PhantomData}; use std::io::Cursor; use std::time::{SystemTime, UNIX_EPOCH}; use tcx_common::{random_u8_32, FromHex, ToHex}; use tcx_constants::{CoinInfo, Result}; use tcx_primitive::{ - DeterministicPrivateKey, PrivateKey, PublicKey, Sr25519PrivateKey, TypedPublicKey, + DeterministicPrivateKey, PrivateKey, PublicKey, Sr25519PrivateKey, Sr25519PublicKey, + TypedPublicKey, }; use xsalsa20poly1305::aead::{generic_array::GenericArray, Aead}; use xsalsa20poly1305::{KeyInit, XSalsa20Poly1305}; @@ -321,19 +324,19 @@ fn password_to_key(password_bytes: &[u8]) -> [u8; 32] { } pub fn decode_substrate_keystore(keystore: &SubstrateKeystore, password: &str) -> Result> { - let (secret_key, pub_key) = keystore.decrypt(password)?; - dbg!(secret_key.to_hex()); - // let priv_key = if secret_key.len() == 32 { - // Sr25519PrivateKey::from_seed(&secret_key) - // } else { - // Sr25519PrivateKey::from_slice(&secret_key) - // }?; - let priv_key = Sr25519PrivateKey::from_slice(&secret_key)?; - // if priv_key.public_key().to_bytes() != pub_key { - // return Err(Error::KeystorePublicKeyUnmatch.into()); - // } - dbg!("decode_substrate_keystore success"); - dbg!(priv_key.to_bytes().to_0x_hex()); + let (secret_key_bytes, pub_key) = keystore.decrypt(password)?; + tcx_ensure!( + secret_key_bytes.len() == SECRET_KEY_LENGTH, + format_err!("secret from substrate keystore must be 64 bytes") + ); + let secret_key = SecretKey::from_ed25519_bytes(&secret_key_bytes) + .map_err(|_| format_err!("secret key from_ed25519_bytes error"))?; + let priv_key = Sr25519PrivateKey::from_slice(&secret_key.to_bytes())?; + + if priv_key.public_key().to_bytes() != pub_key { + return Err(Error::KeystorePublicKeyUnmatch.into()); + } + Ok(priv_key.to_bytes()) } @@ -342,10 +345,18 @@ pub fn encode_substrate_keystore( prv_key: &[u8], coin: &CoinInfo, ) -> Result { - let pk = Sr25519PrivateKey::from_slice(prv_key)?; - let pub_key = pk.public_key(); + let sec_key = SecretKey::from_bytes(prv_key) + .map_err(|_| format_err!("construct secret key error when encoded_substrate_keystore"))?; + let pair = sp_core::sr25519::Pair::from(sec_key.clone()); + let pub_key = Sr25519PublicKey(pair.public()); let addr = SubstrateAddress::from_public_key(&TypedPublicKey::Sr25519(pub_key.clone()), &coin)?; - SubstrateKeystore::new(password, prv_key, &pub_key.to_bytes(), &addr.to_string()) + let ed25519_prv_key_bytes = sec_key.to_ed25519_bytes(); + SubstrateKeystore::new( + password, + &ed25519_prv_key_bytes, + &pub_key.to_bytes(), + &addr.to_string(), + ) } #[cfg(test)] diff --git a/token-core/tcx/src/api.rs b/token-core/tcx/src/api.rs index c7e672ba..0be14112 100644 --- a/token-core/tcx/src/api.rs +++ b/token-core/tcx/src/api.rs @@ -84,8 +84,10 @@ pub struct SignResultPoc { #[derive(Clone, PartialEq, ::prost::Message)] pub struct PublicKeyDerivation { #[prost(string, tag = "1")] - pub path: ::prost::alloc::string::String, + pub chain_type: ::prost::alloc::string::String, #[prost(string, tag = "2")] + pub path: ::prost::alloc::string::String, + #[prost(string, tag = "3")] pub curve: ::prost::alloc::string::String, } #[allow(clippy::derive_partial_eq_without_eq)] @@ -387,21 +389,6 @@ pub struct ImportPrivateKeyParam { #[prost(bool, tag = "5")] pub overwrite: bool, } -/// FUNCTION: private_key_store_export(PrivateKeyStoreExportParam): ExportResult -/// -/// export the private key from a private key keystore -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct PrivateKeyStoreExportParam { - #[prost(string, tag = "1")] - pub id: ::prost::alloc::string::String, - #[prost(string, tag = "2")] - pub password: ::prost::alloc::string::String, - #[prost(string, tag = "3")] - pub chain_type: ::prost::alloc::string::String, - #[prost(string, tag = "4")] - pub network: ::prost::alloc::string::String, -} #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct ExistsMnemonicParam { @@ -510,28 +497,6 @@ pub struct DeriveSubAccountsResult { } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] -pub struct PublicKeyParam { - #[prost(string, tag = "1")] - pub id: ::prost::alloc::string::String, - #[prost(string, tag = "2")] - pub chain_type: ::prost::alloc::string::String, - #[prost(string, tag = "3")] - pub address: ::prost::alloc::string::String, -} -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct PublicKeyResult { - #[prost(string, tag = "1")] - pub id: ::prost::alloc::string::String, - #[prost(string, tag = "2")] - pub chain_type: ::prost::alloc::string::String, - #[prost(string, tag = "3")] - pub address: ::prost::alloc::string::String, - #[prost(string, tag = "4")] - pub public_key: ::prost::alloc::string::String, -} -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] pub struct RemoveWalletParam { #[prost(string, tag = "1")] pub id: ::prost::alloc::string::String, diff --git a/token-core/tcx/src/handler.rs b/token-core/tcx/src/handler.rs index 023400d8..a04121e1 100644 --- a/token-core/tcx/src/handler.rs +++ b/token-core/tcx/src/handler.rs @@ -1,3 +1,4 @@ +use base58::ToBase58; use bytes::BytesMut; use prost::Message; use serde_json::Value; @@ -8,8 +9,11 @@ use std::str::FromStr; use tcx_eos::address::EosAddress; use tcx_keystore::keystore::IdentityNetwork; -use tcx_common::{FromHex, ToHex}; -use tcx_primitive::{private_key_without_version, Secp256k1PrivateKey, TypedPrivateKey}; +use tcx_common::{sha256d, FromHex, ToHex}; +use tcx_primitive::{ + private_key_without_version, PublicKey, Secp256k1PrivateKey, Secp256k1PublicKey, + TypedPrivateKey, TypedPublicKey, +}; use tcx_btc_kin::WIFDisplay; use tcx_keystore::{ @@ -410,7 +414,6 @@ pub(crate) fn import_private_key_internal( source: meta_source, ..Metadata::default() }; - dbg!("private key to store keystore: {}", &private_key); let pk_store = PrivateKeystore::from_private_key(&private_key, ¶m.password, meta)?; let mut keystore = Keystore::PrivateKey(pk_store); @@ -552,30 +555,25 @@ pub(crate) fn export_private_key(data: &[u8]) -> Result> { let mut guard = KeystoreGuard::unlock_by_password(keystore, ¶m.password)?; let curve = CurveType::from_str(¶m.curve); - dbg!(&curve); - let pk_bytes = guard + let private_key_bytes = guard .keystore_mut() .get_private_key(curve, ¶m.path)? .to_bytes(); - let pk_hex = pk_bytes.to_hex(); - dbg!("pk_hex: {}", &pk_hex); // private_key prefix is only about chain type and network // TODO: add export_pk to macro - let value = if ["TRON", "POLKADOT", "KUSAMA"].contains(¶m.chain_type.as_str()) { - Ok(pk_hex.to_string()) + let value = if ["TRON", "POLKADOT", "KUSAMA", "ETHEREUM"].contains(¶m.chain_type.as_str()) { + Ok(private_key_bytes.to_0x_hex()) } else if "FILECOIN".contains(¶m.chain_type.as_str()) { - Ok(KeyInfo::from_private_key(curve, &Vec::from_hex(pk_hex)?)? + Ok(KeyInfo::from_private_key(curve, &private_key_bytes)? .to_json()? .to_hex()) } else if "TEZOS".contains(¶m.chain_type.as_str()) { - Ok(build_tezos_base58_private_key(pk_hex.as_str())?) + Ok(build_tezos_base58_private_key(&private_key_bytes.to_hex())?) } else { // private_key prefix is only about chain type and network let coin_info = coin_info_from_param(¶m.chain_type, ¶m.network, "", "")?; - - let bytes = Vec::from_hex(pk_hex.to_string())?; - let typed_pk = TypedPrivateKey::from_slice(CurveType::SECP256k1, &bytes)?; + let typed_pk = TypedPrivateKey::from_slice(CurveType::SECP256k1, &private_key_bytes)?; typed_pk.fmt(&coin_info) }?; // TODO: add eos export support @@ -675,44 +673,6 @@ fn exists_key_hash(key_hash: &str) -> Result> { encode_message(result) } -// pub(crate) fn keystore_common_exists(data: &[u8]) -> Result> { -// let param: KeystoreCommonExistsParam = -// KeystoreCommonExistsParam::decode(data).expect("keystore_common_exists params"); -// let key_hash: String; -// if param.r#type == KeyType::Mnemonic as i32 { -// let mnemonic: &str = ¶m -// .value -// .split_whitespace() -// .collect::>() -// .join(" "); -// key_hash = key_hash_from_mnemonic(mnemonic)?; -// } else { -// if param.encoding.eq("TEZOS") { -// key_hash = key_hash_from_tezos_format_pk(¶m.value)?; -// } else { -// key_hash = key_hash_from_any_format_pk(¶m.value)?; -// } -// } -// let map = &mut KEYSTORE_MAP.write(); - -// let founded: Option<&Keystore> = map -// .values() -// .find(|keystore| keystore.key_hash() == key_hash); -// let result: ExistsKeystoreResult; -// if let Some(ks) = founded { -// result = ExistsKeystoreResult { -// is_exists: true, -// id: ks.id(), -// } -// } else { -// result = ExistsKeystoreResult { -// is_exists: false, -// id: "".to_owned(), -// } -// } -// encode_message(result) -// } - pub(crate) fn sign_tx(data: &[u8]) -> Result> { let param: SignParam = SignParam::decode(data).expect("SignTxParam"); @@ -770,7 +730,7 @@ pub(crate) fn get_public_keys(data: &[u8]) -> Result> { }?; let mut guard = KeystoreGuard::unlock_by_password(keystore, ¶m.password)?; - let public_keys = param + let public_keys: Vec> = param .derivations .iter() .map(|derivation| { @@ -778,11 +738,44 @@ pub(crate) fn get_public_keys(data: &[u8]) -> Result> { .keystore_mut() .get_public_key(CurveType::from_str(&derivation.curve), &derivation.path) .expect("PublicKeyProcessed"); - public_key.to_bytes().to_hex() + public_key.to_bytes() }) .collect(); - encode_message(GetPublicKeysResult { public_keys }) + let mut public_key_strs: Vec = vec![]; + for idx in 0..param.derivations.len() { + let pub_key = public_keys[idx].to_vec(); + let public_key_str_ret: Result = match param.derivations[idx].chain_type.as_str() { + "TEZOS" => { + let edpk_prefix: Vec = vec![0x0D, 0x0F, 0x25, 0xD9]; + let to_hash = [edpk_prefix, pub_key].concat(); + let hashed = sha256d(&to_hash); + let hash_with_checksum = [to_hash, hashed[0..4].to_vec()].concat(); + let edpk = hash_with_checksum.to_base58(); + Ok(edpk) + } + "EOS" => { + let secp256k1_pub_key = Secp256k1PublicKey::from_slice(&pub_key)?; + let typed_pub_key = TypedPublicKey::Secp256k1(secp256k1_pub_key); + let eos_addr = EosAddress::from_public_key( + &typed_pub_key, + &CoinInfo { + coin: "EOS".to_string(), + curve: CurveType::SECP256k1, + ..Default::default() + }, + )?; + Ok(eos_addr.to_string()) + } + _ => Ok(pub_key.to_0x_hex()), + }; + let pub_key_str = public_key_str_ret?; + public_key_strs.push(pub_key_str); + } + + encode_message(GetPublicKeysResult { + public_keys: public_key_strs, + }) } pub(crate) fn get_extended_public_keys(data: &[u8]) -> Result> { @@ -881,7 +874,6 @@ pub(crate) fn import_json(data: &[u8]) -> Result> { password_hint: "".to_string(), overwrite: param.overwrite, }; - dbg!(&pk_import_param); let mut ret = import_private_key_internal(&pk_import_param, Some(Source::SubstrateKeystore))?; ret.suggest_chain_types = vec!["KUSAMA".to_string(), "POLKADOT".to_string()]; @@ -922,17 +914,34 @@ pub(crate) fn export_json(data: &[u8]) -> Result> { // !!! Warning !!! HDKeystore only can export raw sr25519 key, // but polkadotjs fixtures needs a Ed25519 expanded secret key. - if keystore.derivable() { + if ["POLKADOT".to_string(), "KUSAMA".to_string()].contains(¶m.chain_type) + || keystore.derivable() + { return Err(format_err!("{}", "hd_wallet_cannot_export_keystore")); } meta = keystore.meta().clone(); } - let ret = export_private_key(data)?; + let curve = if ["POLKADOT".to_string(), "KUSAMA".to_string()].contains(¶m.chain_type) { + CurveType::SubSr25519.as_str().to_string() + } else { + CurveType::SECP256k1.as_str().to_string() + }; + + let export_pivate_key_param = ExportPrivateKeyParam { + id: param.id.to_string(), + password: param.password.to_string(), + chain_type: param.chain_type.to_string(), + network: "".to_string(), + curve: curve, + path: param.path.to_string(), + }; + + let ret = export_private_key(&encode_message(export_pivate_key_param)?)?; let export_result: ExportPrivateKeyResult = ExportPrivateKeyResult::decode(ret.as_slice())?; let private_key = export_result.private_key; - dbg!(&private_key); let private_key_bytes = Vec::from_hex_auto(private_key)?; + let coin = coin_info_from_param(¶m.chain_type, "", "", "")?; let json_str = match param.chain_type.as_str() { "KUSAMA" | "SUBSTRATE" => { @@ -960,8 +969,6 @@ pub(crate) fn export_json(data: &[u8]) -> Result> { pub(crate) fn exists_json(data: &[u8]) -> Result> { let param: ExistsJsonParam = ExistsJsonParam::decode(data)?; - let error = key_info_from_substrate_keystore(¶m.json, ¶m.password).err(); - dbg!(error); let sec_key_hex = if let Ok(parse_v3_result) = key_info_from_v3(¶m.json, ¶m.password) { let (sec_key_bytes, _) = parse_v3_result; sec_key_bytes.to_hex() @@ -974,7 +981,6 @@ pub(crate) fn exists_json(data: &[u8]) -> Result> { return Err(format_err!("decrypt_json_error")); }; - dbg!("sec_key_hex: {}", &sec_key_hex); let exists_param = ExistsPrivateKeyParam { private_key: sec_key_hex, }; diff --git a/token-core/tcx/src/lib.rs b/token-core/tcx/src/lib.rs index 0dbdc369..a79f7619 100644 --- a/token-core/tcx/src/lib.rs +++ b/token-core/tcx/src/lib.rs @@ -174,21 +174,20 @@ mod tests { use crate::api::{ sign_param, CreateKeystoreParam, DeriveAccountsParam, DeriveAccountsResult, DeriveSubAccountsParam, DeriveSubAccountsResult, DerivedKeyResult, ExistsJsonParam, - ExistsKeystoreResult, ExistsMnemonicParam, ExistsPrivateKeyParam, ExportJsonResult, - ExportMnemonicResult, ExportPrivateKeyParam, ExportPrivateKeyResult, GeneralResult, - GetPublicKeysParam, GetPublicKeysResult, ImportJsonParam, ImportMnemonicParam, - ImportPrivateKeyParam, ImportPrivateKeyResult, InitTokenCoreXParam, + ExistsKeystoreResult, ExistsMnemonicParam, ExistsPrivateKeyParam, ExportJsonParam, + ExportJsonResult, ExportMnemonicResult, ExportPrivateKeyParam, ExportPrivateKeyResult, + GeneralResult, GetPublicKeysParam, GetPublicKeysResult, ImportJsonParam, + ImportMnemonicParam, ImportPrivateKeyParam, ImportPrivateKeyResult, InitTokenCoreXParam, KeystoreCommonAccountsParam, KeystoreResult, MnemonicToPublicKeyParam, - MnemonicToPublicKeyResult, PrivateKeyStoreExportParam, PublicKeyDerivation, PublicKeyParam, - PublicKeyResult, SignHashesParam, SignHashesResult, SignParam, WalletKeyParam, - ZksyncPrivateKeyFromSeedParam, ZksyncPrivateKeyFromSeedResult, + MnemonicToPublicKeyResult, PublicKeyDerivation, SignHashesParam, SignHashesResult, + SignParam, WalletKeyParam, ZksyncPrivateKeyFromSeedParam, ZksyncPrivateKeyFromSeedResult, ZksyncPrivateKeyToPubkeyHashParam, ZksyncPrivateKeyToPubkeyHashResult, ZksyncSignMusigParam, ZksyncSignMusigResult, }; use crate::handler::import_mnemonic; use crate::handler::{encode_message, import_private_key}; use prost::Message; - use tcx_constants::{sample_key, CurveType}; + use tcx_constants::{sample_key, CurveType, TEST_PRIVATE_KEY}; use tcx_constants::{TEST_MNEMONIC, TEST_PASSWORD}; use tcx_keystore::Keystore; @@ -999,17 +998,21 @@ mod tests { export_result.private_key ); - let param: PublicKeyParam = PublicKeyParam { + let param: GetPublicKeysParam = GetPublicKeysParam { id: import_result.id.to_string(), - chain_type: "TEZOS".to_string(), - address: "tz1QSHaKpTFhgHLbqinyYRjxD5sLcbfbzhxy".to_string(), + password: TEST_PASSWORD.to_string(), + derivations: vec![PublicKeyDerivation { + chain_type: "TEZOS".to_string(), + path: "".to_string(), + curve: CurveType::ED25519.as_str().to_string(), + }], }; - let ret_bytes = call_api("get_public_key", param).unwrap(); - let public_key_result: PublicKeyResult = - PublicKeyResult::decode(ret_bytes.as_slice()).unwrap(); + let ret_bytes = call_api("get_public_keys", param).unwrap(); + let public_key_result: GetPublicKeysResult = + GetPublicKeysResult::decode(ret_bytes.as_slice()).unwrap(); assert_eq!( "edpkvQtuhdZQmjdjVfaY9Kf4hHfrRJYugaJErkCGvV3ER1S7XWsrrj", - public_key_result.public_key + public_key_result.public_keys[0] ); remove_created_wallet(&import_result.id); }) @@ -1986,17 +1989,21 @@ mod tests { let (wallet, _acc_rsp) = import_and_derive(derivation); - let param: PublicKeyParam = PublicKeyParam { + let param: GetPublicKeysParam = GetPublicKeysParam { id: wallet.id.to_string(), - chain_type: "EOS".to_string(), - address: "".to_string(), + password: TEST_PASSWORD.to_string(), + derivations: vec![PublicKeyDerivation { + chain_type: "EOS".to_string(), + path: "m/44'/194'/0'/0/0".to_string(), + curve: "SECP256k1".to_string(), + }], }; let ret_bytes = call_api("get_public_key", param).unwrap(); - let public_key_result: PublicKeyResult = - PublicKeyResult::decode(ret_bytes.as_slice()).unwrap(); + let public_key_result: GetPublicKeysResult = + GetPublicKeysResult::decode(ret_bytes.as_slice()).unwrap(); assert_eq!( "EOS88XhiiP7Cu5TmAUJqHbyuhyYgd6sei68AU266PyetDDAtjmYWF", - public_key_result.public_key + public_key_result.public_keys[0] ); }) } @@ -2248,7 +2255,7 @@ mod tests { network: "".to_string(), seg_wit: "".to_string(), chain_id: "".to_string(), - curve: "".to_string(), + curve: "SubSr25519".to_string(), bech32_prefix: "".to_string(), }; @@ -2264,7 +2271,7 @@ mod tests { assert_eq!( accounts.accounts[0].address, - "JHBkzZJnLZ3S3HLvxjpFAjd6ywP7WAk5miL7MwVCn9a7jHS" + "FDS7ZJpJg4R7Kd2hzfsEc6mtW5iknjZ3UazX76EsnbH74v8" ); let export_param = ExportPrivateKeyParam { @@ -2272,7 +2279,7 @@ mod tests { password: TEST_PASSWORD.to_string(), chain_type: "KUSAMA".to_string(), network: "".to_string(), - curve: "".to_string(), + curve: "SubSr25519".to_string(), path: "".to_string(), }; let ret = call_api("export_json", export_param).unwrap(); @@ -2282,9 +2289,9 @@ mod tests { assert!(keystore.validate().is_ok()); assert_eq!( keystore.address, - "JHBkzZJnLZ3S3HLvxjpFAjd6ywP7WAk5miL7MwVCn9a7jHS" + "FDS7ZJpJg4R7Kd2hzfsEc6mtW5iknjZ3UazX76EsnbH74v8" ); - assert_eq!(keystore.meta.name, "i_can_save_name"); + assert_eq!(keystore.meta.name, "test account"); assert!(keystore.meta.when_created > 1594102917); // assert_eq!(keystore_ret.fixtures, ""); @@ -3173,9 +3180,10 @@ mod tests { overwrite: true, network: "TESTNET".to_string(), }; - let ret = call_api("hd_store_import", param).unwrap(); + let ret = call_api("import_mnemonic", param).unwrap(); let import_result: KeystoreResult = KeystoreResult::decode(ret.as_slice()).unwrap(); let derivations = vec![PublicKeyDerivation { + chain_type: "ETHEREUM2".to_string(), path: "m/12381/3600/0/0/0".to_string(), curve: "BLS".to_string(), }]; @@ -3203,7 +3211,7 @@ mod tests { overwrite: true, network: "MAINNET".to_string(), }; - let ret = call_api("hd_store_import", param).unwrap(); + let ret = call_api("import_mnemonic", param).unwrap(); let import_result: KeystoreResult = KeystoreResult::decode(ret.as_slice()).unwrap(); let data_to_sign = vec![DataToSign { hash: "3e0658d8284d8f50c0aa8fa6cdbd1bde0eb370d4b3489a26c83763671ace8b1c" @@ -3221,6 +3229,22 @@ mod tests { let result = SignHashesResult::decode(result_bytes.as_slice()).unwrap(); assert_eq!(result.signatures.get(0).unwrap(), "0x8fa5d4dfe4766de7896f0e32c5bee9baae47aaa843cf5f1a2587dd9aaedf8a8c4400cb31bdcb1e90ddfe6d309e57841204dbf53704e4c4da3a9d25e9b4a09dac31a3221a7aac76f58ca21854173303cf58f039770a9e2307966e89faf0e5e79e"); + let data_to_sign = vec![DataToSign { + hash: "3e0658d8284d8f50c0aa8fa6cdbd1bde0eb370d4b3489a26c83763671ace8b1c" + .to_string(), + path: "m/44'/60'/0'/0/0".to_string(), + curve: "SECP256k1".to_string(), + sig_alg: "ECDSA".to_string(), + }]; + let param = SignHashesParam { + id: import_result.id.to_string(), + password: TEST_PASSWORD.to_string(), + data_to_sign, + }; + let result_bytes = call_api("sign_hashes", param).unwrap(); + let result = SignHashesResult::decode(result_bytes.as_slice()).unwrap(); + assert_eq!(result.signatures.get(0).unwrap(), "0x80c4f5c9299d21dc62a91e6bd1868cda545e31cadbf0eff35f802a4509cecea2618e5b352843ac4f487d2b43ebd55cdf7ad0b78ca81a96504744cd4209ce343d00"); + remove_created_wallet(&import_result.id); }) } @@ -3604,19 +3628,144 @@ mod tests { } #[test] - pub fn test_import_private_key_integration() { + pub fn test_import_hex_private_key() { run_test(|| { - let bytes = &Vec::::from_0x_hex("0x0a12696d706f72745f707269766174655f6b6579126f0a196170692e496d706f7274507269766174654b6579506172616d12520a4063323761303639373562656434386165353262366565353461646637663366366335646136356566366166316538616464623231663866366461303366303634120831323334313233341a0022002801").unwrap(); - let action: TcxAction = TcxAction::decode(bytes.as_slice()).unwrap(); - let bytes = action.param.unwrap().value; - let mut import_private_key: ImportPrivateKeyParam = - ImportPrivateKeyParam::decode(bytes.as_slice()).unwrap(); - import_private_key.private_key = - "0xc27a06975bed48ae52b6ee54adf7f3f6c5da65ef6af1e8addb21f8f6da03f064".to_string(); - let ret = call_api("import_private_key", import_private_key).unwrap(); + let param: ExistsPrivateKeyParam = ExistsPrivateKeyParam { + private_key: TEST_PRIVATE_KEY.to_string(), + }; + let ret = call_api("exists_private_key", param).unwrap(); + let exists_private_key_result: ExistsKeystoreResult = + ExistsKeystoreResult::decode(ret.as_slice()).unwrap(); + assert!(!exists_private_key_result.is_exists); + + let param: ImportPrivateKeyParam = ImportPrivateKeyParam { + private_key: TEST_PRIVATE_KEY.to_string(), + password: TEST_PASSWORD.to_string(), + name: "import_private_key_wallet".to_string(), + password_hint: "".to_string(), + overwrite: true, + }; + let ret = call_api("import_private_key", param).unwrap(); let import_result: ImportPrivateKeyResult = ImportPrivateKeyResult::decode(ret.as_slice()).unwrap(); - assert_eq!(import_result.id, ""); + assert_eq!( + vec!["ETHEREUM".to_string(), "TRON".to_string(),], + import_result.suggest_chain_types + ); + assert_eq!("SECP256k1", import_result.suggest_curve); + assert_eq!("", import_result.suggest_network); + assert_eq!("PRIVATE", import_result.source); + + let param: ExistsPrivateKeyParam = ExistsPrivateKeyParam { + private_key: TEST_PRIVATE_KEY.to_string(), + }; + let ret = call_api("exists_private_key", param).unwrap(); + let exists_private_key_result: ExistsKeystoreResult = + ExistsKeystoreResult::decode(ret.as_slice()).unwrap(); + assert!(exists_private_key_result.is_exists); + assert_eq!(exists_private_key_result.id, import_result.id); + + let param: ExportPrivateKeyParam = ExportPrivateKeyParam { + id: import_result.id.to_string(), + password: TEST_PASSWORD.to_string(), + chain_type: "ETHEREUM".to_string(), + network: "".to_string(), + curve: CurveType::SECP256k1.as_str().to_string(), + path: "".to_string(), + }; + let ret = call_api("export_private_key", param).unwrap(); + let export_result: ExportPrivateKeyResult = + ExportPrivateKeyResult::decode(ret.as_slice()).unwrap(); + assert_eq!(export_result.id, import_result.id); + assert_eq!(export_result.private_key, TEST_PRIVATE_KEY); + + let param: ExportJsonParam = ExportJsonParam { + id: import_result.id.to_string(), + password: TEST_PASSWORD.to_string(), + chain_type: "ETHEREUM".to_string(), + path: "".to_string(), + }; + let ret = call_api("export_json", param).unwrap(); + let export_result: ExportJsonResult = ExportJsonResult::decode(ret.as_slice()).unwrap(); + assert_eq!(export_result.id, import_result.id); + assert!(export_result + .json + .contains("0x6031564e7b2F5cc33737807b2E58DaFF870B590b")); + + // assert_eq!(export_result.js, TEST_PRIVATE_KEY); + }) + } + + #[test] + pub fn test_import_json() { + run_test(|| { + let json = r#"{ + "version": 3, + "id": "5c24e96a-8fd8-4872-9702-3fd2fc9166cd", + "crypto": { + "cipher": "aes-128-ctr", + "cipherparams": { "iv": "56ed1daad9226d7edd75e8ab34e32309" }, + "ciphertext": "95cae71ef4d76c3def64bf77d267608a823fc65cda6254ea24d1cbbe09de6b6b", + "kdf": "pbkdf2", + "kdfparams": { + "c": 262144, + "prf": "hmac-sha256", + "dklen": 32, + "salt": "63c89a7275a65bd659a937fe374c668e5aa3b05a9b0ef3ec9178aa9182f42666" + }, + "mac": "2adc6da2f5f183e528a063b36ebeddaf0d3a90269ef797b99dc143d58ba3bb58" + }, + "address": "0x6031564e7b2F5cc33737807b2E58DaFF870B590b" + } + "#; + let param: ExistsJsonParam = ExistsJsonParam { + json: json.to_string(), + password: TEST_PASSWORD.to_string(), + }; + let ret = call_api("exists_json", param).unwrap(); + let exists_private_key_result: ExistsKeystoreResult = + ExistsKeystoreResult::decode(ret.as_slice()).unwrap(); + assert!(!exists_private_key_result.is_exists); + + let param: ImportJsonParam = ImportJsonParam { + password: TEST_PASSWORD.to_string(), + json: json.to_string(), + overwrite: true, + }; + let ret = call_api("import_json", param).unwrap(); + let import_result: ImportPrivateKeyResult = + ImportPrivateKeyResult::decode(ret.as_slice()).unwrap(); + assert_eq!( + vec!["ETHEREUM".to_string()], + import_result.suggest_chain_types + ); + assert_eq!("SECP256k1", import_result.suggest_curve); + assert_eq!("", import_result.suggest_network); + assert_eq!("KEYSTORE_V3", import_result.source); + + let param: ExistsJsonParam = ExistsJsonParam { + password: TEST_PASSWORD.to_string(), + json: json.to_string(), + }; + let ret = call_api("exists_json", param).unwrap(); + let exists_private_key_result: ExistsKeystoreResult = + ExistsKeystoreResult::decode(ret.as_slice()).unwrap(); + assert!(exists_private_key_result.is_exists); + assert_eq!(exists_private_key_result.id, import_result.id); + + let param: ExportJsonParam = ExportJsonParam { + id: import_result.id.to_string(), + password: TEST_PASSWORD.to_string(), + chain_type: "ETHEREUM".to_string(), + path: "".to_string(), + }; + let ret = call_api("export_json", param).unwrap(); + let export_result: ExportJsonResult = ExportJsonResult::decode(ret.as_slice()).unwrap(); + assert_eq!(export_result.id, import_result.id); + assert!(export_result + .json + .to_string() + .contains("0x6031564e7b2F5cc33737807b2E58DaFF870B590b")); }) } // #[test]