diff --git a/Cargo.lock b/Cargo.lock index 0e721e6f..971c1422 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5962,6 +5962,7 @@ dependencies = [ "parking_lot", "serde", "serde_json", + "tcx-common", ] [[package]] diff --git a/Makefile b/Makefile index 95760337..997445a2 100644 --- a/Makefile +++ b/Makefile @@ -8,4 +8,4 @@ build-tcx: cd token-core; cargo build test-tcx: - cd token-core; cargo test + cd token-core; KDF_ROUNDS=1 cargo test diff --git a/token-core/tcx-btc-kin/src/signer.rs b/token-core/tcx-btc-kin/src/signer.rs index bc53dc7b..078945f5 100644 --- a/token-core/tcx-btc-kin/src/signer.rs +++ b/token-core/tcx-btc-kin/src/signer.rs @@ -452,6 +452,7 @@ mod tests { TEST_PASSWORD, CurveType::SECP256k1, Metadata::default(), + None, ) .unwrap(); keystore.unlock_by_password(TEST_PASSWORD).unwrap(); @@ -623,6 +624,7 @@ mod tests { TEST_PASSWORD, CurveType::SECP256k1, Metadata::default(), + None, ) .unwrap(); ks.unlock_by_password(TEST_PASSWORD).unwrap(); @@ -1377,6 +1379,7 @@ mod tests { TEST_PASSWORD, CurveType::SECP256k1, Metadata::default(), + None, ) .unwrap(); let _coin_info = coin_info_from_param("LITECOIN", "TESTNET", "NONE", "").unwrap(); diff --git a/token-core/tcx-ckb/src/signer.rs b/token-core/tcx-ckb/src/signer.rs index 8404742a..56fe7844 100644 --- a/token-core/tcx-ckb/src/signer.rs +++ b/token-core/tcx-ckb/src/signer.rs @@ -428,6 +428,7 @@ mod tests { TEST_PASSWORD, CurveType::SECP256k1, Metadata::default(), + None, ) .unwrap(); ks.unlock_by_password(TEST_PASSWORD).unwrap(); diff --git a/token-core/tcx-constants/Cargo.toml b/token-core/tcx-constants/Cargo.toml index 88902adf..d3ddb1b2 100644 --- a/token-core/tcx-constants/Cargo.toml +++ b/token-core/tcx-constants/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +tcx-common = { path = "../tcx-common" } failure = "=0.1.8" lazy_static = "=1.4.0" serde_json = "=1.0.89" diff --git a/token-core/tcx-constants/src/coin_info.rs b/token-core/tcx-constants/src/coin_info.rs index 549f5ad6..df3da905 100644 --- a/token-core/tcx-constants/src/coin_info.rs +++ b/token-core/tcx-constants/src/coin_info.rs @@ -1,13 +1,31 @@ use crate::curve::CurveType; use crate::Result; use failure::format_err; +use tcx_common::FromHex; use parking_lot::RwLock; -// pub enum Coin { -// Ethereum { path: String, chain_id: i32 }, -// } - +pub fn get_xpub_prefix(network: &str, derivation_path: &str) -> Vec { + if derivation_path.starts_with("m/49'") { + if network == "MAINNET" { + Vec::from_hex("049d7cb2").unwrap() + } else { + Vec::from_hex("044a5262").unwrap() + } + } else if derivation_path.starts_with("m/84'") { + if network == "MAINNET" { + Vec::from_hex("04b24746").unwrap() + } else { + Vec::from_hex("045f1cf6").unwrap() + } + } else { + if network == "MAINNET" { + Vec::from_hex("0488b21e").unwrap() + } else { + Vec::from_hex("043587cf").unwrap() + } + } +} /// Blockchain basic config #[derive(Clone)] pub struct CoinInfo { diff --git a/token-core/tcx-crypto/src/crypto.rs b/token-core/tcx-crypto/src/crypto.rs index 31009acc..86d85015 100644 --- a/token-core/tcx-crypto/src/crypto.rs +++ b/token-core/tcx-crypto/src/crypto.rs @@ -231,7 +231,7 @@ impl Crypto { match key { Key::Password(password) => { let derived_key = self.derive_key(password)?; - + dbg!(derived_key.to_0x_hex()); if !self.mac.is_empty() && !self.verify_derived_key(&derived_key) { return Err(Error::PasswordIncorrect.into()); } @@ -242,7 +242,7 @@ impl Crypto { }) } Key::DerivedKey(derived_key_hex) => { - let derived_key = Vec::from_hex(derived_key_hex)?; + let derived_key = Vec::from_hex_auto(derived_key_hex)?; if !self.verify_derived_key(&derived_key) { return Err(Error::PasswordIncorrect.into()); } diff --git a/token-core/tcx-eth/src/signer.rs b/token-core/tcx-eth/src/signer.rs index 2698275a..68bc6c22 100644 --- a/token-core/tcx-eth/src/signer.rs +++ b/token-core/tcx-eth/src/signer.rs @@ -173,9 +173,14 @@ mod test { use tcx_keystore::{Keystore, MessageSigner, Metadata, SignatureParameters, TransactionSigner}; fn private_key_store(key: &str) -> Keystore { - let mut ks = - Keystore::from_private_key(key, "imToken1", CurveType::SECP256k1, Metadata::default()) - .unwrap(); + let mut ks = Keystore::from_private_key( + key, + "imToken1", + CurveType::SECP256k1, + Metadata::default(), + None, + ) + .unwrap(); ks.unlock_by_password("imToken1").unwrap(); ks } diff --git a/token-core/tcx-filecoin/src/signer.rs b/token-core/tcx-filecoin/src/signer.rs index 25568172..0e317e95 100644 --- a/token-core/tcx-filecoin/src/signer.rs +++ b/token-core/tcx-filecoin/src/signer.rs @@ -126,6 +126,7 @@ mod tests { "Password", CurveType::SECP256k1, Metadata::default(), + None, ) .unwrap(); ks.unlock_by_password("Password").unwrap(); @@ -172,6 +173,7 @@ mod tests { "Password", CurveType::SECP256k1, Metadata::default(), + None, ) .unwrap(); ks.unlock_by_password("Password").unwrap(); @@ -218,6 +220,7 @@ mod tests { "Password", CurveType::SECP256k1, Metadata::default(), + None, ) .unwrap(); ks.unlock_by_password("Password").unwrap(); diff --git a/token-core/tcx-keystore/src/identity.rs b/token-core/tcx-keystore/src/identity.rs index 36f0e04c..c2e282bc 100644 --- a/token-core/tcx-keystore/src/identity.rs +++ b/token-core/tcx-keystore/src/identity.rs @@ -392,6 +392,7 @@ mod test { &PASSWORD, tcx_constants::CurveType::SECP256k1, meta, + None, ) .unwrap(); let identity = &keystore.store().identity; @@ -410,9 +411,14 @@ mod test { fn test_create_identity_from_private_key_testnet() { let mut meta = Metadata::default(); meta.network = IdentityNetwork::Testnet; - let keystore = - PrivateKeystore::from_private_key(&PRIVATE_KEY, &PASSWORD, CurveType::SECP256k1, meta) - .unwrap(); + let keystore = PrivateKeystore::from_private_key( + &PRIVATE_KEY, + &PASSWORD, + CurveType::SECP256k1, + meta, + None, + ) + .unwrap(); let identity = &keystore.store().identity; assert_eq!( diff --git a/token-core/tcx-keystore/src/keystore/guard.rs b/token-core/tcx-keystore/src/keystore/guard.rs index ed70e7e6..7d7855f0 100644 --- a/token-core/tcx-keystore/src/keystore/guard.rs +++ b/token-core/tcx-keystore/src/keystore/guard.rs @@ -82,6 +82,7 @@ mod tests { TEST_PASSWORD, CurveType::SECP256k1, Metadata::default(), + None, ) .unwrap(); let derived_key = ks.get_derived_key(&TEST_PASSWORD).unwrap(); diff --git a/token-core/tcx-keystore/src/keystore/hd.rs b/token-core/tcx-keystore/src/keystore/hd.rs index 55f34c90..0f7086b7 100644 --- a/token-core/tcx-keystore/src/keystore/hd.rs +++ b/token-core/tcx-keystore/src/keystore/hd.rs @@ -8,7 +8,7 @@ use super::{transform_mnemonic_error, Account, Address, Error, Metadata, Result, use std::collections::{hash_map::Entry, HashMap}; use tcx_common::{FromHex, ToHex}; -use tcx_constants::{CoinInfo, CurveType}; +use tcx_constants::{coin_info::get_xpub_prefix, CoinInfo, CurveType}; use tcx_crypto::{Crypto, Key}; use tcx_primitive::{ generate_mnemonic, get_account_path, Bip32DeterministicPrivateKey, Derive, @@ -176,34 +176,13 @@ impl HdKeystore { meta, identity, curve: None, + enc_original: None, }, cache: None, }) } - pub fn get_ext_version(network: &str, derivation_path: &str) -> Vec { - if derivation_path.starts_with("m/49'") { - if network == "MAINNET" { - Vec::from_hex("049d7cb2").unwrap() - } else { - Vec::from_hex("044a5262").unwrap() - } - } else if derivation_path.starts_with("m/84'") { - if network == "MAINNET" { - Vec::from_hex("04b24746").unwrap() - } else { - Vec::from_hex("045f1cf6").unwrap() - } - } else { - if network == "MAINNET" { - Vec::from_hex("0488b21e").unwrap() - } else { - Vec::from_hex("043587cf").unwrap() - } - } - } - pub fn derive_coin(&mut self, coin_info: &CoinInfo) -> Result { let cache = self.cache.as_ref().ok_or(Error::KeystoreLocked)?; @@ -218,7 +197,7 @@ impl HdKeystore { _ => root .derive(&get_account_path(&coin_info.derivation_path)?)? .deterministic_public_key() - .to_ss58check_with_version(&Self::get_ext_version( + .to_ss58check_with_version(&get_xpub_prefix( &coin_info.network, &coin_info.derivation_path, )), @@ -288,6 +267,7 @@ mod tests { timestamp: metadata_default_time(), source: Source::Mnemonic, network: IdentityNetwork::Mainnet, + identified_chain_types: None, }; assert_eq!(meta.name, expected.name); diff --git a/token-core/tcx-keystore/src/keystore/mod.rs b/token-core/tcx-keystore/src/keystore/mod.rs index a98e2b2e..c2c283ac 100644 --- a/token-core/tcx-keystore/src/keystore/mod.rs +++ b/token-core/tcx-keystore/src/keystore/mod.rs @@ -19,7 +19,7 @@ pub use self::{ }; use crate::identity::Identity; -use tcx_crypto::{Crypto, Key}; +use tcx_crypto::{Crypto, EncPair, Key}; use tcx_primitive::{Derive, TypedDeterministicPublicKey, TypedPrivateKey, TypedPublicKey}; #[derive(Debug, Clone, Serialize, Deserialize)] @@ -32,7 +32,8 @@ pub struct Store { pub identity: Identity, #[serde(skip_serializing_if = "Option::is_none")] pub curve: Option, - + #[serde(skip_serializing_if = "Option::is_none")] + pub enc_original: Option, #[serde(rename = "imTokenMeta")] pub meta: Metadata, } @@ -183,6 +184,8 @@ pub struct Metadata { pub source: Source, #[serde(default = "metadata_default_network")] pub network: IdentityNetwork, + #[serde(skip_serializing_if = "Option::is_none")] + pub identified_chain_types: Option>, } fn metadata_default_time() -> i64 { @@ -207,6 +210,7 @@ impl Default for Metadata { timestamp: metadata_default_time(), source: Source::Mnemonic, network: IdentityNetwork::Mainnet, + identified_chain_types: None, } } } @@ -223,12 +227,14 @@ impl Keystore { password: &str, curve: CurveType, meta: Metadata, + original: Option, ) -> Result { Ok(Keystore::PrivateKey(PrivateKeystore::from_private_key( private_key, password, curve, meta, + original, )?)) } @@ -842,6 +848,7 @@ pub(crate) mod tests { TEST_PASSWORD, CurveType::SECP256k1, meta, + None, ) .unwrap(); let keystore = PrivateKey(pk_store); @@ -927,6 +934,7 @@ pub(crate) mod tests { TEST_PASSWORD, CurveType::SECP256k1, Metadata::default(), + None, ) .unwrap(); diff --git a/token-core/tcx-keystore/src/keystore/private.rs b/token-core/tcx-keystore/src/keystore/private.rs index ba8efcfe..882f411f 100644 --- a/token-core/tcx-keystore/src/keystore/private.rs +++ b/token-core/tcx-keystore/src/keystore/private.rs @@ -91,6 +91,7 @@ impl PrivateKeystore { password: &str, curve: CurveType, meta: Metadata, + original: Option, ) -> Result { let key_data: Vec = Vec::from_hex_auto(private_key)?; let fingerprint = fingerprint_from_private_key(&key_data)?; @@ -98,6 +99,9 @@ impl PrivateKeystore { let unlocker = crypto.use_key(&Key::Password(password.to_string()))?; let identity = Identity::from_private_key(private_key, &unlocker, &meta.network)?; + let enc_original = + original.and_then(|ori| unlocker.encrypt_with_random_iv(ori.as_bytes()).ok()); + let store = Store { source_fingerprint: fingerprint, crypto, @@ -106,6 +110,7 @@ impl PrivateKeystore { version: PrivateKeystore::VERSION, identity, curve: Some(curve), + enc_original: enc_original, }; Ok(PrivateKeystore { @@ -169,6 +174,7 @@ mod tests { TEST_PASSWORD, CurveType::SECP256k1, meta, + None, ) .unwrap(); @@ -191,6 +197,7 @@ mod tests { TEST_PASSWORD, CurveType::SECP256k1, Metadata::default(), + None, ) .unwrap(); @@ -229,6 +236,7 @@ mod tests { TEST_PASSWORD, CurveType::SECP256k1, Metadata::default(), + None, ) .unwrap(); keystore diff --git a/token-core/tcx-migration/src/keystore_upgrade.rs b/token-core/tcx-migration/src/keystore_upgrade.rs index d6b08b9a..2e8d5655 100644 --- a/token-core/tcx-migration/src/keystore_upgrade.rs +++ b/token-core/tcx-migration/src/keystore_upgrade.rs @@ -10,7 +10,7 @@ use tcx_keystore::{ mnemonic_to_seed, HdKeystore, Keystore, PrivateKeystore, Result, Source, }; -pub(crate) fn mapping_curve_name(old_curve_name: &str) -> String { +pub fn mapping_curve_name(old_curve_name: &str) -> String { let new_curve_name = match old_curve_name { "ED25519" => CurveType::ED25519, "SECP256k1" => CurveType::SECP256k1, @@ -38,12 +38,12 @@ impl KeystoreUpgrade { && self.json["imTokenMeta"].is_object() } - pub fn upgrade(&self, key: &Key) -> Result { + pub fn upgrade(&self, key: &Key, identity_network: &IdentityNetwork) -> Result { let version = self.json["version"].as_i64().unwrap_or(0); let mut json = self.json.clone(); - let source = json["imTokenMeta"]["source"].as_str().unwrap_or(""); + let source: &str = json["imTokenMeta"]["source"].as_str().unwrap_or(""); json["imTokenMeta"]["source"] = match (version, source) { (11000, "NEW_IDENTITY") => json!(Source::NewMnemonic.to_string()), (11000, "RECOVERED_IDENTITY") => json!(Source::Mnemonic.to_string()), @@ -74,11 +74,6 @@ impl KeystoreUpgrade { }?; json["sourceFingerprint"] = json!(fingerprint); - let identity_network = match json["meta"]["network"].as_str().unwrap_or("") { - "TESTNET" => IdentityNetwork::Testnet, - _ => IdentityNetwork::Mainnet, - }; - match version { 11001 => { json["version"] = json!(PrivateKeystore::VERSION); @@ -100,6 +95,11 @@ impl KeystoreUpgrade { .expect("activeAccounts need contains curve"); let new_curve_name = mapping_curve_name(&old_curve_name); json["curve"] = json!(new_curve_name); + let chain_type = account_json["coin"] + .as_str() + .expect("activeAccounts need contains chainType"); + json["imTokenMeta"]["identifiedChainTypes"] = json!(vec![chain_type]); + // tcx pk keystore has lost the original private key } } 11000 => { @@ -125,7 +125,7 @@ mod tests { use tcx_constants::CurveType; use tcx_crypto::Error::PasswordIncorrect; use tcx_crypto::Key; - use tcx_keystore::{Keystore, Source}; + use tcx_keystore::{keystore::IdentityNetwork, Keystore, Source}; fn pk_json(version: i64, source: &str, name: &str) -> Value { json!( @@ -177,7 +177,7 @@ mod tests { let upgrade_keystore = super::KeystoreUpgrade::new(hd_json(t.0, t.1, "Unknown")); let key = Key::DerivedKey(hd_derived_key()); - let upgraded = upgrade_keystore.upgrade(&key); + let upgraded = upgrade_keystore.upgrade(&key, &IdentityNetwork::Mainnet); assert!(!upgrade_keystore.need_upgrade()); assert_eq!(upgraded.err().unwrap().to_string(), "invalid version"); @@ -190,7 +190,9 @@ mod tests { super::KeystoreUpgrade::new(hd_json(11000, "NEW_IDENTITY", "Unknown")); let key = Key::Password("imtoken1".to_owned()); - let upgraded = upgrade_keystore.upgrade(&key).unwrap(); + let upgraded = upgrade_keystore + .upgrade(&key, &IdentityNetwork::Testnet) + .unwrap(); assert!(upgrade_keystore.need_upgrade()); assert_eq!(upgraded.store().version, 12000); @@ -203,7 +205,9 @@ mod tests { super::KeystoreUpgrade::new(hd_json(11000, "NEW_IDENTITY", "Unknown")); let key = Key::DerivedKey("9e6c4391999f0f578105284fe25eb9cf1b60ab75473ac155c83091ae36bf2bb64eb6ec5cbe39dab3a100f87442ee580619700291c15e84961fec2a259c808e69".to_owned()); - let upgraded = upgrade_keystore.upgrade(&key).unwrap(); + let upgraded = upgrade_keystore + .upgrade(&key, &IdentityNetwork::Testnet) + .unwrap(); assert!(upgrade_keystore.need_upgrade()); assert_eq!(upgraded.store().version, 12000); @@ -225,7 +229,9 @@ mod tests { let upgrade_keystore = super::KeystoreUpgrade::new(hd_json(11000, t.0, "Unknown")); let key = Key::DerivedKey(hd_derived_key()); - let upgraded = upgrade_keystore.upgrade(&key).unwrap(); + let upgraded = upgrade_keystore + .upgrade(&key, &IdentityNetwork::Testnet) + .unwrap(); assert!(upgrade_keystore.need_upgrade()); assert_eq!(upgraded.store().version, 12000); @@ -233,6 +239,31 @@ mod tests { } } + #[test] + fn test_upgrade_hd_identity_network() { + let upgrade_keystore = + super::KeystoreUpgrade::new(hd_json(11000, "NEW_IDENTITY", "Unknown")); + let key = Key::DerivedKey(hd_derived_key()); + + let upgraded = upgrade_keystore + .upgrade(&key, &IdentityNetwork::Testnet) + .unwrap(); + + assert_eq!( + upgraded.identity().identifier, + "im18MDKM8hcTykvMmhLnov9m2BaFqsdjoA7cwNg" + ); + + let upgraded = upgrade_keystore + .upgrade(&key, &IdentityNetwork::Mainnet) + .unwrap(); + + assert_eq!( + upgraded.identity().identifier, + "im14x5GXsdME4JsrHYe2wvznqRz4cUhx2pA4HPf" + ); + } + #[test] fn test_private_keystore_upgrade() { let tests = [ @@ -248,7 +279,9 @@ mod tests { let upgrade_keystore = super::KeystoreUpgrade::new(pk_json(11001, t.0, "vvvvvv")); let key = Key::DerivedKey(private_derived_key()); - let upgraded = upgrade_keystore.upgrade(&key).unwrap(); + let upgraded = upgrade_keystore + .upgrade(&key, &IdentityNetwork::Testnet) + .unwrap(); assert!(upgrade_keystore.need_upgrade()); assert_eq!(upgraded.store().version, 12001); @@ -256,12 +289,36 @@ mod tests { } } + #[test] + fn test_upgrade_pk_identity_network() { + let upgrade_keystore = super::KeystoreUpgrade::new(pk_json(11001, "WIF", "vvvvvv")); + let key = Key::DerivedKey(private_derived_key()); + + let upgraded = upgrade_keystore + .upgrade(&key, &IdentityNetwork::Testnet) + .unwrap(); + + assert_eq!( + upgraded.identity().identifier, + "im18MDUZKTuAALuXb1Wait1XBb984rdjVpFeBgu" + ); + + let upgraded = upgrade_keystore + .upgrade(&key, &IdentityNetwork::Mainnet) + .unwrap(); + + assert_eq!( + upgraded.identity().identifier, + "im14x5UPbCXmU2HMQ8jfeKcCDrQYhDppRYaa5C6" + ); + } + #[test] fn test_invalid_version() { let upgrade_keystore = super::KeystoreUpgrade::new(pk_json(11002, "PRIVATE", "SSS")); let key = Key::DerivedKey(hd_derived_key()); - let upgraded = upgrade_keystore.upgrade(&key); + let upgraded = upgrade_keystore.upgrade(&key, &IdentityNetwork::Testnet); assert!(upgraded.is_err()); } @@ -327,7 +384,7 @@ mod tests { let upgrade_keystore = super::KeystoreUpgrade::new(pk_json(11001, "PRIVATE", "vvvvvv")); let key = Key::Password("imtoken2".to_owned()); - let upgraded = upgrade_keystore.upgrade(&key); + let upgraded = upgrade_keystore.upgrade(&key, &IdentityNetwork::Testnet); assert_eq!( upgraded.err().unwrap().to_string(), PasswordIncorrect.to_string() @@ -393,30 +450,40 @@ mod tests { let upgrade_keystore = super::KeystoreUpgrade::new(pk_json_with_bls(11001, "PRIVATE", "vvvvvv")); - let upgraded = upgrade_keystore.upgrade(&key).unwrap(); + let upgraded = upgrade_keystore + .upgrade(&key, &IdentityNetwork::Testnet) + .unwrap(); assert_eq!(upgraded.store().curve, Some(CurveType::BLS)); let upgrade_keystore = super::KeystoreUpgrade::new(pk_json_with_ed25519(11001, "PRIVATE", "vvvvvv")); - let upgraded = upgrade_keystore.upgrade(&key).unwrap(); + let upgraded = upgrade_keystore + .upgrade(&key, &IdentityNetwork::Testnet) + .unwrap(); assert_eq!(upgraded.store().curve, Some(CurveType::ED25519)); let upgrade_keystore = super::KeystoreUpgrade::new(pk_json_with_subsr25519(11001, "PRIVATE", "vvvvvv")); - let upgraded = upgrade_keystore.upgrade(&key).unwrap(); + let upgraded = upgrade_keystore + .upgrade(&key, &IdentityNetwork::Testnet) + .unwrap(); assert_eq!(upgraded.store().curve, Some(CurveType::SR25519)); let upgrade_keystore = super::KeystoreUpgrade::new(pk_json_with_secp256k1(11001, "PRIVATE", "vvvvvv")); - let upgraded = upgrade_keystore.upgrade(&key).unwrap(); + let upgraded = upgrade_keystore + .upgrade(&key, &IdentityNetwork::Testnet) + .unwrap(); assert_eq!(upgraded.store().curve, Some(CurveType::SECP256k1)); let upgrade_keystore = super::KeystoreUpgrade::new(hd_json(11000, "MNEMONIC", "vvvvvv")); - let upgraded = upgrade_keystore.upgrade(&key).unwrap(); + let upgraded = upgrade_keystore + .upgrade(&key, &IdentityNetwork::Testnet) + .unwrap(); assert_eq!(upgraded.store().curve, None); } } diff --git a/token-core/tcx-migration/src/migration.rs b/token-core/tcx-migration/src/migration.rs index 32e5c4c5..ca469b7c 100644 --- a/token-core/tcx-migration/src/migration.rs +++ b/token-core/tcx-migration/src/migration.rs @@ -54,15 +54,17 @@ impl OldMetadata { IdentityNetwork::Mainnet }; - let source = self - .source - .clone() - .map_or(Source::Mnemonic, |source| match source.as_str() { - "RECOVER_IDENTITY" => Source::Mnemonic, - "NEW_IDENTITY" => Source::NewMnemonic, - "KEYSTORE" => Source::KeystoreV3, - _ => Source::from_str(&source).unwrap_or(Source::Mnemonic), - }); + let (source, identified_chain_types) = + self.source + .clone() + .map_or((Source::Mnemonic, None), |source| match source.as_str() { + "RECOVER_IDENTITY" => (Source::Mnemonic, None), + "NEW_IDENTITY" => (Source::NewMnemonic, None), + "KEYSTORE" => (Source::KeystoreV3, Some(vec!["ETHEREUM".to_string()])), + "PRIVATE" => (Source::Private, Some(vec!["ETHEREUM".to_string()])), + "WIF" => (Source::Wif, Some(vec!["BITCOIN".to_string()])), + _ => (Source::from_str(&source).unwrap_or(Source::Mnemonic), None), + }); Metadata { name: self.name.clone(), @@ -70,10 +72,19 @@ impl OldMetadata { timestamp, source, network, + identified_chain_types, } } } +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct EOSKeyPath { + pub derived_mode: String, + pub path: String, + pub public_key: String, +} + #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct LegacyKeystore { @@ -82,11 +93,15 @@ pub struct LegacyKeystore { pub crypto: Crypto, #[serde(skip_serializing_if = "Option::is_none")] pub enc_mnemonic: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub xpub: Option, pub address: Option, #[serde(skip_serializing_if = "Option::is_none")] pub mnemonic_path: Option, #[serde(skip_serializing_if = "Option::is_none")] pub im_token_meta: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub key_path_privates: Option>, } impl LegacyKeystore { @@ -105,6 +120,8 @@ impl LegacyKeystore { address: Some(address), mnemonic_path: None, im_token_meta: None, + xpub: None, + key_path_privates: None, }) } @@ -145,16 +162,16 @@ impl LegacyKeystore { Ok(keystore) } - pub fn migrate(&self, key: &Key) -> Result { + pub fn migrate(&self, key: &Key, identity_network: &IdentityNetwork) -> Result { let keystore = if self.has_mnemonic() { - self.migrate_to_hd(key)? + self.migrate_to_hd(key, identity_network)? } else { - self.migrate_to_private(key)? + self.migrate_to_private(key, identity_network)? }; Ok(keystore) } - fn migrate_to_hd(&self, key: &Key) -> Result { + fn migrate_to_hd(&self, key: &Key, identity_network: &IdentityNetwork) -> Result { let unlocker = self.crypto.use_key(key)?; let mnemonic_data = unlocker.decrypt_enc_pair( self.enc_mnemonic @@ -171,7 +188,7 @@ impl LegacyKeystore { .expect("migration to hd need imTokenMeta") .to_metadata(); - let identity = Identity::from_seed(&seed, &unlocker, &meta.network)?; + let identity = Identity::from_seed(&seed, &unlocker, &identity_network)?; let mut store = Store { id: self.id.to_string(), version: HdKeystore::VERSION, @@ -180,6 +197,7 @@ impl LegacyKeystore { identity, meta, curve: None, + enc_original: None, }; let derived_key = unlocker.derived_key(); @@ -193,29 +211,40 @@ impl LegacyKeystore { Ok(keystore) } - fn migrate_to_private(&self, key: &Key) -> Result { + fn migrate_to_private( + &self, + key: &Key, + identity_network: &IdentityNetwork, + ) -> Result { let unlocker = self.crypto.use_key(key)?; - let mut private_key = unlocker.plaintext()?; + let decrypted = unlocker.plaintext()?; // Note legacy keystore only contains k1 curve let curve = CurveType::SECP256k1; + let is_wif = decrypted.len() != 32; - if private_key.len() != 32 { - private_key = - Secp256k1PrivateKey::from_wif(&String::from_utf8_lossy(&private_key))?.to_bytes() - } + let (private_key, original) = if is_wif { + ( + Secp256k1PrivateKey::from_wif(&String::from_utf8_lossy(&decrypted))?.to_bytes(), + decrypted, + ) + } else { + (decrypted.clone(), decrypted.to_hex().as_bytes().to_vec()) + }; let fingerprint = fingerprint_from_private_key(&private_key)?; let im_token_meta = self .im_token_meta .as_ref() .expect("migrate to private need imTokenMeta"); - let network = im_token_meta - .network - .clone() - .and_then(|net| IdentityNetwork::from_str(&net).ok()) - .unwrap_or(IdentityNetwork::Mainnet); - let identity = Identity::from_private_key(&private_key.to_hex(), &unlocker, &network)?; + let identity = + Identity::from_private_key(&private_key.to_hex(), &unlocker, &identity_network)?; + + let enc_original = if is_wif { + Some(unlocker.encrypt_with_random_iv(&original)?) + } else { + None + }; let mut store = Store { id: self.id.to_string(), version: PrivateKeystore::VERSION, @@ -224,6 +253,7 @@ impl LegacyKeystore { meta: im_token_meta.to_metadata(), identity, curve: Some(curve), + enc_original: enc_original, }; let unlocker = self.crypto.use_key(key)?; @@ -238,25 +268,6 @@ impl LegacyKeystore { Ok(keystore) } - - pub fn migrate_identity_wallets( - &self, - key: &Key, - new_keystore: Option, - ) -> Result { - let mut keystore = self.migrate_to_hd(key)?; - keystore.unlock(key)?; - - // TODO: merge with exists wallet - if let Some(_exists_keystore) = new_keystore { - - // exists_keystore.store_mut().crypto = keystore.store().crypto.clone(); - // exists_keystore.store_mut().id = keystore.id().to_string(); - // keystore.store_mut().id = wallet_id.to_string(); - } - - Ok(keystore) - } } #[cfg(test)] @@ -269,7 +280,8 @@ mod tests { use tcx_eos::address::{EosAddress, EosPublicKeyEncoder}; use tcx_eth::address::EthAddress; use tcx_keystore::{ - Keystore, KeystoreGuard, Metadata, PrivateKeystore, PublicKeyEncoder, Source, + keystore::IdentityNetwork, Keystore, KeystoreGuard, Metadata, PrivateKeystore, + PublicKeyEncoder, Source, }; use super::LegacyKeystore; @@ -320,7 +332,7 @@ mod tests { let (keystore_str, derived_key) = v3_eos_private_key(); let ks = LegacyKeystore::from_json_str(keystore_str).unwrap(); let key = Key::DerivedKey(derived_key.to_owned()); - let mut keystore = ks.migrate(&key).unwrap(); + let mut keystore = ks.migrate(&key, &IdentityNetwork::Testnet).unwrap(); let coin_info = CoinInfo { coin: "EOS".to_string(), @@ -346,7 +358,10 @@ mod tests { let (keystore_str, derived_key) = v44_bitcoin_mnemonic_1(); let ks = LegacyKeystore::from_json_str(keystore_str).unwrap(); let keystore = ks - .migrate(&Key::DerivedKey(derived_key.to_string())) + .migrate( + &Key::DerivedKey(derived_key.to_string()), + &IdentityNetwork::Testnet, + ) .unwrap(); let unlocker1 = ks @@ -366,7 +381,12 @@ mod tests { fn test_bitcoin_with_password() { let (keystore_str, _) = v44_bitcoin_mnemonic_1(); let ks = LegacyKeystore::from_json_str(keystore_str).unwrap(); - let mut keystore = ks.migrate(&Key::Password("imtoken1".to_string())).unwrap(); + let mut keystore = ks + .migrate( + &Key::Password("imtoken1".to_string()), + &IdentityNetwork::Testnet, + ) + .unwrap(); assert_eq!(keystore.derivable(), true); assert_eq!(keystore.id(), "02a55ab6-554a-4e78-bc26-6a7acced7e5e"); @@ -392,7 +412,7 @@ mod tests { //password Insecure Pa55w0rd let key = Key::DerivedKey(derived_key.to_owned()); - let mut keystore = ks.migrate(&key).unwrap(); + let mut keystore = ks.migrate(&key, &IdentityNetwork::Testnet).unwrap(); assert_eq!(keystore.derivable(), false); assert_eq!(keystore.id(), "045861fe-0e9b-4069-92aa-0ac03cad55e0"); @@ -416,7 +436,7 @@ mod tests { let ks = LegacyKeystore::from_json_str(keystore_str).unwrap(); //password imtoken1 let key = Key::DerivedKey(derived_key.to_owned()); - let mut keystore = ks.migrate(&key).unwrap(); + let mut keystore = ks.migrate(&key, &IdentityNetwork::Testnet).unwrap(); assert_eq!(keystore.derivable(), true); assert_eq!(keystore.id(), "175169f7-5a35-4df7-93c1-1ff612168e71"); @@ -439,7 +459,7 @@ mod tests { let ks = LegacyKeystore::from_json_str(keystore_str).unwrap(); //password imtoken1 let key = Key::DerivedKey(derived_key.to_owned()); - let mut keystore = ks.migrate(&key).unwrap(); + let mut keystore = ks.migrate(&key, &IdentityNetwork::Testnet).unwrap(); assert_eq!(keystore.derivable(), true); assert_eq!(keystore.id(), "5991857a-2488-4546-b730-463a5f84ea6a"); diff --git a/token-core/tcx-migration/tests/migrate_test.rs b/token-core/tcx-migration/tests/migrate_test.rs index ca8db3de..a9355a9f 100644 --- a/token-core/tcx-migration/tests/migrate_test.rs +++ b/token-core/tcx-migration/tests/migrate_test.rs @@ -8,6 +8,7 @@ use tcx_migration::migration::LegacyKeystore; mod tests { use serde_json::Value; use tcx_crypto::Key; + use tcx_keystore::keystore::IdentityNetwork; use tcx_keystore::Source; use tcx_migration::keystore_upgrade::KeystoreUpgrade; use tcx_migration::migration::LegacyKeystore; @@ -364,7 +365,9 @@ mod tests { let key = Key::DerivedKey(t.1.to_owned()); - let mut keystore = keystore_upgrade.upgrade(&key).unwrap(); + let mut keystore = keystore_upgrade + .upgrade(&key, &IdentityNetwork::Testnet) + .unwrap(); keystore.unlock(&key); assert_eq!(keystore.meta().source, t.2); @@ -376,7 +379,7 @@ mod tests { let key = Key::DerivedKey(t.1.to_owned()); - let mut keystore = legacy.migrate(&key).unwrap(); + let mut keystore = legacy.migrate(&key, &IdentityNetwork::Testnet).unwrap(); keystore.unlock(&key); assert_eq!(keystore.meta().source, t.2); diff --git a/token-core/tcx-proto/build.rs b/token-core/tcx-proto/build.rs index e535d106..334de988 100644 --- a/token-core/tcx-proto/build.rs +++ b/token-core/tcx-proto/build.rs @@ -14,10 +14,6 @@ fn main() { ) .unwrap(); - // // tcx-chain - // env::set_var("OUT_DIR", "../tcx-chain/src"); - // prost_build::compile_protos(&["src/tron.proto"], &["src/"]).unwrap(); - // tcx-atom env::set_var("OUT_DIR", "../tcx-atom/src"); prost_build::compile_protos(&["src/atom.proto"], &["src/"]).unwrap(); @@ -54,10 +50,6 @@ fn main() { env::set_var("OUT_DIR", "../tcx-tezos/src"); prost_build::compile_protos(&["src/tezos.proto"], &["src/"]).unwrap(); - //tcx-eth2 - env::set_var("OUT_DIR", "../tcx-eth2/src"); - prost_build::compile_protos(&["src/eth2.proto"], &["src/"]).unwrap(); - //tcx-eth env::set_var("OUT_DIR", "../tcx-eth/src"); prost_build::compile_protos(&["src/eth.proto"], &["src/"]).unwrap(); diff --git a/token-core/tcx-proto/src/params.proto b/token-core/tcx-proto/src/params.proto index fd618c38..f8fe6b05 100644 --- a/token-core/tcx-proto/src/params.proto +++ b/token-core/tcx-proto/src/params.proto @@ -49,9 +49,9 @@ message ImportPrivateKeyResult { string ipfsId = 4; string source = 5; int64 createdAt = 6; - repeated string suggestChainTypes = 7; - string suggestNetwork = 8; - string suggestCurve = 9; + repeated string identifiedChainTypes = 7; + string identifiedNetwork = 8; + string identifiedCurve = 9; string sourceFingerprint = 10; } @@ -83,6 +83,7 @@ message AccountResponse { string publicKey = 5; string extendedPublicKey = 6; string encryptedExtendedPublicKey = 7; + string segWit = 8; } message DeriveAccountsResult { @@ -243,11 +244,6 @@ message MnemonicToPublicKeyResult { string publicKey = 1; } -message ScanKeystoresResult { - repeated KeystoreResult hdKeystores = 1; - repeated ImportPrivateKeyResult privateKeyKeystores = 2; -} - message MigrateKeystoreParam { string id = 1; oneof key { @@ -256,8 +252,24 @@ message MigrateKeystoreParam { } } -message MigrateKeystoreResult { - bool isExisted = 1; - string existedId = 2; - KeystoreResult keystore = 3; -} \ No newline at end of file + message MigrateKeystoreResult { + bool isExisted = 1; + string existedId = 2; + KeystoreResult keystore = 3; + } + + message LegacyKeystoreResult { + string id = 1; + string name = 2; + string source = 3; + int64 createdAt = 4; + repeated AccountResponse accounts = 5; +} + + message ScanLegacyKeystoresResult { + string identifier = 1; + string ipfsId = 2; + string network = 3; + string source = 4; + repeated LegacyKeystoreResult keystores = 5; + } diff --git a/token-core/tcx-proto/src/wallet_api.proto b/token-core/tcx-proto/src/wallet_api.proto deleted file mode 100644 index 97d6e892..00000000 --- a/token-core/tcx-proto/src/wallet_api.proto +++ /dev/null @@ -1,92 +0,0 @@ -syntax = "proto3"; -package wallet_api; - - -message CreateIdentityParam { - string name = 1; - string password = 2; - optional string passwordHint = 3; - string network = 4; - string segWit = 5; -} - -message CreateIdentityResult { - string identifier = 1; - string ipfsId = 2; - repeated Wallet wallets = 3; -} - -message Wallet{ - string id = 1; - string address = 2; - uint64 createdAt = 3; - string source = 4; - string chainType = 5; - optional ExternalAddress externalAddress = 6; - optional string encXPub = 7; - optional string segWit = 8; -} - -message ExternalAddress { - string address = 1; - string type = 2; - string derivedPath = 3; -} - -message GetCurrentIdentityResult { - string identifier = 1; - string ipfsId = 2; - repeated IMTKeystore wallets = 3; - Metadata metadata = 4; -} - -message Metadata { - string name = 1; - optional string passwordHint = 2; - string chainType = 3; - uint64 timestamp = 4; - string network = 5; - repeated string backup = 6; - string source = 7; - optional string mode = 8; - optional string walletType = 9; - optional string segWit = 10; -} - -message IMTKeystore { - string id = 1; - uint32 version = 2; - string address = 3; - optional string mnemonicPath = 4; - Metadata metadata = 5; -} - -message ExportIdentityParam { - string identifier = 1; - string password = 2; -} - -message ExportIdentityResult { - string identifier = 1; - string mnemonic = 2; -} - -message RecoverIdentityParam { - string name = 1; - string mnemonic = 2; - string password = 3; - optional string passwordHint = 4; - string network = 5; - string segWit = 6; -} - -message RecoverIdentityResult { - string identifier = 1; - string mnemonic = 2; - string ipfsId = 3; - repeated Wallet wallets = 4; -} - - - - diff --git a/token-core/tcx/src/api.rs b/token-core/tcx/src/api.rs index 4b4e6580..60e76056 100644 --- a/token-core/tcx/src/api.rs +++ b/token-core/tcx/src/api.rs @@ -294,11 +294,11 @@ pub struct ImportPrivateKeyResult { #[prost(int64, tag = "6")] pub created_at: i64, #[prost(string, repeated, tag = "7")] - pub suggest_chain_types: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + pub identified_chain_types: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, #[prost(string, tag = "8")] - pub suggest_network: ::prost::alloc::string::String, + pub identified_network: ::prost::alloc::string::String, #[prost(string, tag = "9")] - pub suggest_curve: ::prost::alloc::string::String, + pub identified_curve: ::prost::alloc::string::String, #[prost(string, tag = "10")] pub source_fingerprint: ::prost::alloc::string::String, } @@ -360,6 +360,8 @@ pub struct AccountResponse { pub extended_public_key: ::prost::alloc::string::String, #[prost(string, tag = "7")] pub encrypted_extended_public_key: ::prost::alloc::string::String, + #[prost(string, tag = "8")] + pub seg_wit: ::prost::alloc::string::String, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -592,14 +594,6 @@ pub struct MnemonicToPublicKeyResult { } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] -pub struct ScanKeystoresResult { - #[prost(message, repeated, tag = "1")] - pub hd_keystores: ::prost::alloc::vec::Vec, - #[prost(message, repeated, tag = "2")] - pub private_key_keystores: ::prost::alloc::vec::Vec, -} -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] pub struct MigrateKeystoreParam { #[prost(string, tag = "1")] pub id: ::prost::alloc::string::String, @@ -629,6 +623,34 @@ pub struct MigrateKeystoreResult { } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] +pub struct LegacyKeystoreResult { + #[prost(string, tag = "1")] + pub id: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub name: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + pub source: ::prost::alloc::string::String, + #[prost(int64, tag = "4")] + pub created_at: i64, + #[prost(message, repeated, tag = "5")] + pub accounts: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ScanLegacyKeystoresResult { + #[prost(string, tag = "1")] + pub identifier: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub ipfs_id: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + pub network: ::prost::alloc::string::String, + #[prost(string, tag = "4")] + pub source: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "5")] + pub keystores: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] pub struct VerifyDerivedKeyParam { #[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 2c54637e..a9a17548 100644 --- a/token-core/tcx/src/handler.rs +++ b/token-core/tcx/src/handler.rs @@ -1,5 +1,6 @@ use bytes::BytesMut; use prost::Message; +use serde::{Deserialize, Serialize}; use serde_json::Value; use std::fs; use std::io::Read; @@ -37,10 +38,10 @@ use crate::api::{ ExportMnemonicResult, ExportPrivateKeyParam, ExportPrivateKeyResult, GeneralResult, GetExtendedPublicKeysParam, GetExtendedPublicKeysResult, GetPublicKeysParam, GetPublicKeysResult, ImportJsonParam, ImportMnemonicParam, ImportPrivateKeyParam, - ImportPrivateKeyResult, KeystoreResult, MigrateKeystoreParam, MigrateKeystoreResult, - MnemonicToPublicKeyParam, MnemonicToPublicKeyResult, ScanKeystoresResult, - SignAuthenticationMessageParam, SignAuthenticationMessageResult, SignHashesParam, - SignHashesResult, WalletKeyParam, + ImportPrivateKeyResult, KeystoreResult, LegacyKeystoreResult, MigrateKeystoreParam, + MigrateKeystoreResult, MnemonicToPublicKeyParam, MnemonicToPublicKeyResult, + ScanLegacyKeystoresResult, SignAuthenticationMessageParam, SignAuthenticationMessageResult, + SignHashesParam, SignHashesResult, WalletKeyParam, }; use crate::api::{InitTokenCoreXParam, SignParam}; use crate::error_handling::Result; @@ -55,18 +56,18 @@ use crate::IS_DEBUG; use base58::FromBase58; use tcx_keystore::tcx_ensure; -use tcx_constants::coin_info::coin_info_from_param; +use tcx_constants::coin_info::{coin_info_from_param, get_xpub_prefix}; use tcx_constants::{CoinInfo, CurveType}; use tcx_crypto::aes::cbc::encrypt_pkcs7; use tcx_crypto::KDF_ROUNDS; use tcx_eth::transaction::{EthRecoverAddressInput, EthRecoverAddressOutput}; use tcx_keystore::{MessageSigner, TransactionSigner}; -use tcx_migration::keystore_upgrade::KeystoreUpgrade; +use tcx_migration::keystore_upgrade::{mapping_curve_name, KeystoreUpgrade}; use tcx_primitive::{Bip32DeterministicPublicKey, Ss58Codec}; use tcx_substrate::{decode_substrate_keystore, encode_substrate_keystore, SubstrateKeystore}; -use tcx_migration::migration::LegacyKeystore; +use tcx_migration::migration::{LegacyKeystore, NumberOrNumberStr}; use tcx_primitive::TypedDeterministicPublicKey; use tcx_tezos::{build_tezos_base58_private_key, parse_tezos_private_key}; @@ -104,7 +105,7 @@ fn derive_account(keystore: &mut Keystore, derivation: &Derivation) -> Result Result { +pub fn encrypt_xpub(xpub: &str) -> Result { let key = tcx_crypto::XPUB_COMMON_KEY_128.read(); let iv = tcx_crypto::XPUB_COMMON_IV.read(); let key_bytes = Vec::from_hex(&*key)?; @@ -140,6 +141,7 @@ fn fingerprint_from_tezos_format_pk(pk: &str) -> Result { fn import_private_key_internal( param: &ImportPrivateKeyParam, source: Option, + original: Option, ) -> Result { let mut founded_id: Option = None; { @@ -168,14 +170,27 @@ fn import_private_key_internal( } else { decoded_ret.source }; + + let original = if let Some(original) = original { + original + } else { + param.private_key.to_string() + }; + let meta = Metadata { name: param.name.to_string(), password_hint: param.password_hint.to_string(), source: meta_source, + identified_chain_types: Some(decoded_ret.chain_types.clone()), ..Metadata::default() }; - let pk_store = - PrivateKeystore::from_private_key(&private_key, ¶m.password, decoded_ret.curve, meta)?; + let pk_store = PrivateKeystore::from_private_key( + &private_key, + ¶m.password, + decoded_ret.curve, + meta, + Some(original), + )?; let mut keystore = Keystore::PrivateKey(pk_store); @@ -194,9 +209,9 @@ fn import_private_key_internal( created_at: meta.timestamp, identifier: identity.identifier.to_string(), ipfs_id: identity.ipfs_id.to_string(), - suggest_chain_types: decoded_ret.chain_types.to_owned(), - suggest_network: decoded_ret.network.to_string(), - suggest_curve: decoded_ret.curve.as_str().to_string(), + identified_chain_types: decoded_ret.chain_types.to_owned(), + identified_network: decoded_ret.network.to_string(), + identified_curve: decoded_ret.curve.as_str().to_string(), source_fingerprint: keystore.fingerprint().to_string(), }; cache_keystore(keystore); @@ -359,15 +374,12 @@ pub fn init_token_core_x(data: &[u8]) -> Result<()> { Ok(()) } -pub(crate) fn scan_keystores() -> Result { +pub(crate) fn scan_keystores() -> Result<()> { clean_keystore(); let file_dir = WALLET_FILE_DIR.read(); let p = Path::new(file_dir.as_str()); let walk_dir = std::fs::read_dir(p).expect("read dir"); - let mut hd_keystores: Vec = Vec::new(); - let mut private_key_keystores: Vec = Vec::new(); - for entry in walk_dir { let entry = entry.expect("DirEntry"); let fp = entry.path(); @@ -391,63 +403,30 @@ pub(crate) fn scan_keystores() -> Result { if version == HdKeystore::VERSION || version == PrivateKeystore::VERSION { let keystore = Keystore::from_json(&contents)?; - - if version == HdKeystore::VERSION { - let keystore_result = KeystoreResult { - id: keystore.id(), - name: keystore.meta().name.to_string(), - identifier: keystore.identity().identifier.to_string(), - ipfs_id: keystore.identity().ipfs_id.to_string(), - source: keystore.meta().source.to_string(), - created_at: keystore.meta().timestamp, - source_fingerprint: keystore.fingerprint().to_string(), - }; - hd_keystores.push(keystore_result); - } else { - let curve = keystore - .get_curve() - .expect("pk keystore must contains curve"); - let kestore_result = ImportPrivateKeyResult { - id: keystore.id(), - name: keystore.meta().name.to_string(), - identifier: keystore.identity().identifier.to_string(), - ipfs_id: keystore.identity().ipfs_id.to_string(), - source: keystore.meta().source.to_string(), - created_at: keystore.meta().timestamp, - source_fingerprint: keystore.fingerprint().to_string(), - suggest_chain_types: curve_to_chain_type(&curve), - suggest_network: keystore.meta().network.to_string(), - suggest_curve: curve.as_str().to_string(), - }; - private_key_keystores.push(kestore_result); - } cache_keystore(keystore); } } - Ok(ScanKeystoresResult { - hd_keystores, - private_key_keystores, - }) + Ok(()) } -fn curve_to_chain_type(curve: &CurveType) -> Vec { - match curve { - CurveType::SECP256k1 => vec![ - "BITCOIN".to_string(), - "BITCOINCASH".to_string(), - "LITECOIN".to_string(), - "FILECOIN".to_string(), - "EOS".to_string(), - "TRON".to_string(), - "COSMOS".to_string(), - ], - CurveType::ED25519 => vec!["TEZOS".to_string()], - CurveType::SR25519 => vec!["KUSAMA".to_string(), "POLKADOT".to_string()], - CurveType::BLS => vec!["FILECOIN".to_string()], - _ => vec![], - } -} +// fn curve_to_chain_type(curve: &CurveType) -> Vec { +// match curve { +// CurveType::SECP256k1 => vec![ +// "BITCOIN".to_string(), +// "BITCOINCASH".to_string(), +// "LITECOIN".to_string(), +// "FILECOIN".to_string(), +// "EOS".to_string(), +// "TRON".to_string(), +// "COSMOS".to_string(), +// ], +// CurveType::ED25519 => vec!["TEZOS".to_string()], +// CurveType::SR25519 => vec!["KUSAMA".to_string(), "POLKADOT".to_string()], +// CurveType::BLS => vec!["FILECOIN".to_string()], +// _ => vec![], +// } +// } pub(crate) fn create_keystore(data: &[u8]) -> Result> { let param: CreateKeystoreParam = @@ -571,6 +550,7 @@ pub(crate) fn derive_accounts(data: &[u8]) -> Result> { } else { encrypt_xpub(&account.ext_pub_key.to_string()) }?; + let account_rsp = AccountResponse { chain_type: derivation.chain_type.to_owned(), address: account.address.to_owned(), @@ -579,6 +559,7 @@ pub(crate) fn derive_accounts(data: &[u8]) -> Result> { public_key: encode_public_key_internal(&account.public_key, &coin_info)?, extended_public_key: account.ext_pub_key.to_string(), encrypted_extended_public_key: enc_xpub, + seg_wit: derivation.seg_wit.to_string(), }; account_responses.push(account_rsp); } @@ -619,7 +600,7 @@ pub(crate) fn import_private_key(data: &[u8]) -> Result> { let param: ImportPrivateKeyParam = ImportPrivateKeyParam::decode(data).expect("import_private_key param"); - let rsp = import_private_key_internal(¶m, None)?; + let rsp = import_private_key_internal(¶m, None, None)?; let ret = encode_message(rsp)?; Ok(ret) @@ -901,10 +882,14 @@ pub(crate) fn import_json(data: &[u8]) -> Result> { password_hint: "".to_string(), overwrite: param.overwrite, }; - let mut ret = import_private_key_internal(&pk_import_param, Some(Source::KeystoreV3))?; - ret.suggest_chain_types = vec!["ETHEREUM".to_string()]; - ret.suggest_curve = CurveType::SECP256k1.as_str().to_string(); - ret.suggest_network = "".to_string(); + let mut ret = import_private_key_internal( + &pk_import_param, + Some(Source::KeystoreV3), + Some(param.json.to_string()), + )?; + ret.identified_chain_types = vec!["ETHEREUM".to_string()]; + ret.identified_curve = CurveType::SECP256k1.as_str().to_string(); + ret.identified_network = "".to_string(); encode_message(ret) } else if let Ok(parse_substrate_result) = key_info_from_substrate_keystore(¶m.json, ¶m.password) @@ -917,11 +902,14 @@ pub(crate) fn import_json(data: &[u8]) -> Result> { password_hint: "".to_string(), overwrite: param.overwrite, }; - let mut ret = - import_private_key_internal(&pk_import_param, Some(Source::SubstrateKeystore))?; - ret.suggest_chain_types = vec!["KUSAMA".to_string(), "POLKADOT".to_string()]; - ret.suggest_curve = CurveType::SR25519.as_str().to_string(); - ret.suggest_network = "".to_string(); + let mut ret = import_private_key_internal( + &pk_import_param, + Some(Source::SubstrateKeystore), + Some(param.json.to_string()), + )?; + ret.identified_chain_types = vec!["KUSAMA".to_string(), "POLKADOT".to_string()]; + ret.identified_curve = CurveType::SR25519.as_str().to_string(); + ret.identified_network = "".to_string(); return encode_message(ret); } else { return Err(format_err!("unsupport_chain")); @@ -1133,6 +1121,7 @@ pub(crate) fn derive_sub_accounts(data: &[u8]) -> Result> { encrypted_extended_public_key: enc_xpub, public_key: encode_public_key_internal(&acc.public_key, &coin_info)?, curve: param.curve.to_string(), + seg_wit: param.seg_wit.to_string(), }; Ok(acc_rsp) }) @@ -1145,89 +1134,6 @@ pub(crate) fn derive_sub_accounts(data: &[u8]) -> Result> { encode_message(DeriveSubAccountsResult { accounts }) } -pub(crate) fn migrate_keystore(data: &[u8]) -> Result> { - let param: MigrateKeystoreParam = - MigrateKeystoreParam::decode(data).expect("param: MigrateKeystoreParam"); - let legacy_file_dir = { - let dir = LEGACY_WALLET_FILE_DIR.read(); - dir.to_string() - }; - let mut file_path = format!("{}/{}.json", legacy_file_dir, param.id); - let path = Path::new(&file_path); - if !path.exists() { - file_path = format!("{}/{}", legacy_file_dir, param.id); - } - let json_str = fs::read_to_string(file_path)?; - let json = serde_json::from_str::(&json_str)?; - - let key = match param.key.clone().unwrap() { - migrate_keystore_param::Key::Password(password) => tcx_crypto::Key::Password(password), - migrate_keystore_param::Key::DerivedKey(derived_key) => { - tcx_crypto::Key::DerivedKey(derived_key) - } - }; - - let keystore; - - if let Some(version) = json["version"].as_i64() { - match version { - 11000 | 11001 => { - let keystore_upgrade = KeystoreUpgrade::new(json); - keystore = keystore_upgrade.upgrade(&key)?; - } - _ => { - let legacy_keystore = LegacyKeystore::from_json_str(&json_str)?; - keystore = legacy_keystore.migrate(&key)?; - } - } - - let mut is_existed = false; - let mut existed_id = "".to_string(); - let fingerprint = keystore.fingerprint(); - { - let keystore_map = KEYSTORE_MAP.read(); - let existed_ks: Vec<&Keystore> = keystore_map - .values() - .filter(|ks| ks.fingerprint() == fingerprint) - .collect(); - if existed_ks.len() > 0 { - is_existed = true; - existed_id = existed_ks[0].id().to_string(); - } - } - if is_existed { - return encode_message(MigrateKeystoreResult { - is_existed: true, - existed_id, - keystore: None, - }); - } else { - let identity = keystore.identity(); - - let keystore_result = KeystoreResult { - id: keystore.id(), - name: keystore.meta().name, - source: keystore.meta().source.to_string(), - created_at: keystore.meta().timestamp, - identifier: identity.identifier.to_string(), - ipfs_id: identity.ipfs_id.to_string(), - source_fingerprint: keystore.fingerprint().to_string(), - }; - - let ret = encode_message(MigrateKeystoreResult { - is_existed: false, - existed_id: "".to_string(), - keystore: Some(keystore_result), - }); - flush_keystore(&keystore)?; - cache_keystore(keystore); - return ret; - } - } else { - Err(format_err!("invalid version in keystore")) - } -} - pub(crate) fn mnemonic_to_public(data: &[u8]) -> Result> { let param = MnemonicToPublicKeyParam::decode(data)?; let public_key = tcx_primitive::mnemonic_to_public(¶m.mnemonic, ¶m.path, ¶m.curve)?; @@ -1261,7 +1167,10 @@ mod tests { use tcx_constants::CurveType; use tcx_keystore::Source; - use crate::{api::ImportPrivateKeyResult, filemanager::WALLET_FILE_DIR}; + use crate::{ + api::ImportPrivateKeyResult, + filemanager::{LEGACY_WALLET_FILE_DIR, WALLET_FILE_DIR}, + }; use super::{decode_private_key, scan_keystores}; use serial_test::serial; @@ -1338,108 +1247,4 @@ mod tests { assert_eq!(decoded.network, "".to_string()); assert_eq!(decoded.source, Source::Wif); } - - #[test] - fn test_scan_keystores() { - *WALLET_FILE_DIR.write() = "../test-data/scan-keystores-fixtures/".to_string(); - let result = scan_keystores().unwrap(); - assert_eq!(result.hd_keystores.len(), 1); - let hd = result.hd_keystores.first().unwrap(); - assert_eq!(hd.id, "1055741c-2904-4973-b7ee-4b69bfd8bcc6"); - assert_eq!(hd.identifier, "im14x5GXsdME4JsrHYe2wvznqRz4cUhx2pA4HPf"); - assert_eq!(hd.ipfs_id, "QmWqwovhrZBMmo32BzY83ZMEBQaP7YRMqXNmMc8mgrpzs6"); - assert_eq!( - hd.source_fingerprint, - "0x1468dba9c246fe22183c056540ab4d8b04553217" - ); - assert_eq!(hd.created_at, 1704252563); - assert_eq!(hd.source, "MNEMONIC"); - assert_eq!(hd.name, "test-wallet"); - assert_eq!(result.private_key_keystores.len(), 4); - - let founded_pk_stores: Vec<&ImportPrivateKeyResult> = result - .private_key_keystores - .iter() - .filter(|x| x.id == "7e1c2c55-5b7f-4a5a-8061-c42b594ceb2f") - .collect(); - let pk = founded_pk_stores.first().unwrap(); - assert_eq!(pk.identifier, "im14x5LYRt5YsM5iTr2xd6dQ75euijaoDs3nRB2"); - assert_eq!(pk.ipfs_id, "QmSpWyzy5gkYyJiagFHzfkJwqzDdcjCA3qeu8T3JFd54vZ"); - assert_eq!( - pk.source_fingerprint, - "0xc7b60806a2af1e89f107b9410da3ab8a825fe5a2" - ); - assert_eq!(pk.created_at, 1704251911); - assert_eq!(pk.source, "PRIVATE"); - assert_eq!(pk.name, "test_filecoin_import_private_key"); - assert_eq!(pk.suggest_curve, "bls12-381"); - - let founded_pk_stores: Vec<&ImportPrivateKeyResult> = result - .private_key_keystores - .iter() - .filter(|x| x.id == "1233134b-8377-4fb0-b06f-56062e858708") - .collect(); - let pk = founded_pk_stores.first().unwrap(); - assert_eq!(pk.identifier, "im14x5JEvG1gEwF9ukFv5EsVyQ47V3BegEA3hVa"); - assert_eq!(pk.ipfs_id, "QmZ86PZ1ipPUz8BzoJCSqRpkmmkph59xDzavvfkvzHbfqv"); - assert_eq!( - pk.source_fingerprint, - "0x60c8e832975b284511e4be509e1486020b3d058a" - ); - assert_eq!(pk.created_at, 1704252030); - assert_eq!(pk.source, "SUBSTRATE_KEYSTORE"); - assert_eq!(pk.name, "test_64bytes_import_private_key"); - assert_eq!(pk.suggest_curve, "sr25519"); - - let founded_pk_stores: Vec<&ImportPrivateKeyResult> = result - .private_key_keystores - .iter() - .filter(|x| x.id == "beb68589-0f0f-41e2-94d9-d78f10a72dec") - .collect(); - let pk = founded_pk_stores.first().unwrap(); - assert_eq!(pk.identifier, "im14x5UPbCXmU2HMQ8jfeKcCDrQYhDppRYaa5C6"); - assert_eq!(pk.ipfs_id, "QmczBPUeohPPaE8UnPiESyynPwffBqrn4RqrU6nPJw95VT"); - assert_eq!( - pk.source_fingerprint, - "0xe6cfaab9a59ba187f0a45db0b169c21bb48f09b3" - ); - assert_eq!(pk.created_at, 1704252158); - assert_eq!(pk.source, "PRIVATE"); - assert_eq!(pk.name, "test_filecoin_import_private_key"); - assert_eq!(pk.suggest_curve, "secp256k1"); - - let founded_pk_stores: Vec<&ImportPrivateKeyResult> = result - .private_key_keystores - .iter() - .filter(|x| x.id == "beb68589-0f0f-41e2-94d9-d78f10a72dec") - .collect(); - let pk = founded_pk_stores.first().unwrap(); - assert_eq!(pk.identifier, "im14x5UPbCXmU2HMQ8jfeKcCDrQYhDppRYaa5C6"); - assert_eq!(pk.ipfs_id, "QmczBPUeohPPaE8UnPiESyynPwffBqrn4RqrU6nPJw95VT"); - assert_eq!( - pk.source_fingerprint, - "0xe6cfaab9a59ba187f0a45db0b169c21bb48f09b3" - ); - assert_eq!(pk.created_at, 1704252158); - assert_eq!(pk.source, "PRIVATE"); - assert_eq!(pk.name, "test_filecoin_import_private_key"); - assert_eq!(pk.suggest_curve, "secp256k1"); - - let founded_pk_stores: Vec<&ImportPrivateKeyResult> = result - .private_key_keystores - .iter() - .filter(|x| x.id == "efcfffb2-9b63-418b-a9d0-ec3600012284") - .collect(); - let pk = founded_pk_stores.first().unwrap(); - assert_eq!(pk.identifier, "im14x5AU2zU5oRyNdGgNbemdP39ATmu16eVgPFQ"); - assert_eq!(pk.ipfs_id, "Qmb8K5w1fzdTbjTiATvSecNgZYvbMJ6gJB9JPG254aEY8F"); - assert_eq!( - pk.source_fingerprint, - "0x6bd7cc4e20a7de71296b81758d29447dfde9a388" - ); - assert_eq!(pk.created_at, 1704252267); - assert_eq!(pk.source, "PRIVATE"); - assert_eq!(pk.name, "test_tezos_import_private_key_export"); - assert_eq!(pk.suggest_curve, "ed25519"); - } } diff --git a/token-core/tcx/src/lib.rs b/token-core/tcx/src/lib.rs index 8a5db988..14142af8 100644 --- a/token-core/tcx/src/lib.rs +++ b/token-core/tcx/src/lib.rs @@ -13,6 +13,7 @@ use crate::api::{GeneralResult, TcxAction}; pub mod error_handling; pub mod handler; +pub mod migration; use failure::Error; use std::result; @@ -22,9 +23,10 @@ use crate::handler::{ encode_message, encrypt_data_to_ipfs, eth_recover_address, exists_json, exists_mnemonic, exists_private_key, export_json, export_mnemonic, export_private_key, get_derived_key, get_extended_public_keys, get_public_keys, import_json, import_mnemonic, import_private_key, - migrate_keystore, mnemonic_to_public, sign_authentication_message, sign_hashes, sign_message, - sign_tx, unlock_then_crash, verify_password, + mnemonic_to_public, sign_authentication_message, sign_hashes, sign_message, sign_tx, + unlock_then_crash, verify_password, }; +use crate::migration::{migrate_keystore, scan_legacy_keystores}; mod filemanager; // mod identity; @@ -72,9 +74,9 @@ pub unsafe extern "C" fn call_tcx_api(hex_str: *const c_char) -> *const c_char { handler::init_token_core_x(&action.param.unwrap().value).unwrap(); Ok(vec![]) }), - "scan_keystores" => landingpad(|| { - handler::scan_keystores().unwrap(); - Ok(vec![]) + "scan_legacy_keystores" => landingpad(|| { + let ret = migration::scan_legacy_keystores()?; + encode_message(ret) }), "create_keystore" => landingpad(|| create_keystore(&action.param.unwrap().value)), "import_mnemonic" => landingpad(|| import_mnemonic(&action.param.unwrap().value)), @@ -2424,7 +2426,6 @@ mod tests { }) } - // TODO: private key store need know private key curve #[test] #[serial] pub fn test_import_multi_curve() { @@ -3564,10 +3565,10 @@ mod tests { ImportPrivateKeyResult::decode(ret.as_slice()).unwrap(); assert_eq!( vec!["ETHEREUM".to_string(), "TRON".to_string(),], - import_result.suggest_chain_types + import_result.identified_chain_types ); - assert_eq!("secp256k1", import_result.suggest_curve); - assert_eq!("", import_result.suggest_network); + assert_eq!("secp256k1", import_result.identified_curve); + assert_eq!("", import_result.identified_network); assert_eq!("PRIVATE", import_result.source); let param: ExistsPrivateKeyParam = ExistsPrivateKeyParam { @@ -3652,10 +3653,10 @@ mod tests { ImportPrivateKeyResult::decode(ret.as_slice()).unwrap(); assert_eq!( vec!["ETHEREUM".to_string()], - import_result.suggest_chain_types + import_result.identified_chain_types ); - assert_eq!("secp256k1", import_result.suggest_curve); - assert_eq!("", import_result.suggest_network); + assert_eq!("secp256k1", import_result.identified_curve); + assert_eq!("", import_result.identified_network); assert_eq!("KEYSTORE_V3", import_result.source); let param: ExistsJsonParam = ExistsJsonParam { @@ -3770,11 +3771,11 @@ mod tests { assert_eq!(keystore.id, "0a2756cd-ff70-437b-9bdb-ad46b8bb0819"); assert_eq!( keystore.identifier, - "im14x5GXsdME4JsrHYe2wvznqRz4cUhx2pA4HPf" + "im18MDKM8hcTykvMmhLnov9m2BaFqsdjoA7cwNg" ); assert_eq!( keystore.ipfs_id, - "QmWqwovhrZBMmo32BzY83ZMEBQaP7YRMqXNmMc8mgrpzs6" + "QmSTTidyfa4np9ak9BZP38atuzkCHy4K59oif23f4dNAGU" ); assert_eq!(keystore.created_at, 1703213098); assert_eq!(keystore.source, "MNEMONIC"); @@ -3871,7 +3872,7 @@ mod tests { let result: MigrateKeystoreResult = MigrateKeystoreResult::decode(ret.as_slice()).unwrap(); assert_eq!(result.keystore.unwrap().source, "PRIVATE"); - fs::remove_dir_all("../test-data/walletsV2").unwrap(); + // fs::remove_dir_all("../test-data/walletsV2").unwrap(); } #[test] @@ -4056,4 +4057,191 @@ mod tests { assert_eq!(keystore.id(), "4b07b86f-cc3f-4bdd-b156-a69d5cbd4bca"); fs::remove_dir_all("../test-data/walletsV2").unwrap(); } + + #[test] + #[serial] + pub fn test_identified_network_flush() { + let _ = fs::remove_dir_all("../test-data/walletsV2"); + let param = InitTokenCoreXParam { + file_dir: "../test-data".to_string(), + xpub_common_key: "B888D25EC8C12BD5043777B1AC49F872".to_string(), + xpub_common_iv: "9C0C30889CBCC5E01AB5B2BB88715799".to_string(), + is_debug: true, + }; + + handler::init_token_core_x(&encode_message(param).unwrap()).expect("should init tcx"); + + let param: MigrateKeystoreParam = MigrateKeystoreParam { + id: "4b07b86f-cc3f-4bdd-b156-a69d5cbd4bca".to_string(), + key: Some(migrate_keystore_param::Key::DerivedKey( + "1a60471067b6c6a3202e0014de2ce9b2d45fd73e2289b3cc3d8e5b58fe99ff242fd61e9fe63e75abbdc0ed87a50756cc10c57daf1d6297b99ec9a3b174eee017".to_string(), + )), + }; + let _ = call_api("migrate_keystore", param).unwrap(); + // let result: MigrateKeystoreResult = MigrateKeystoreResult::decode(ret.as_slice()).unwrap(); + let json = fs::read_to_string(format!( + "../test-data/walletsV2/4b07b86f-cc3f-4bdd-b156-a69d5cbd4bca.json" + )) + .unwrap(); + let mut keystore = Keystore::from_json(&json).unwrap(); + assert_eq!( + keystore.fingerprint(), + "0x8b650646c72d8ec3f2a6da9f76dfe624a862c578" + ); + + keystore.unlock_by_password(TEST_PASSWORD).unwrap(); + assert_eq!( + keystore.export().unwrap(), + "685634d212eabe016a1cb09d9f1ea1ea757ebe590b9a097d7b1c9379ad280171" + ); + + assert_eq!(keystore.get_curve().unwrap(), CurveType::SECP256k1); + assert_eq!(keystore.id(), "4b07b86f-cc3f-4bdd-b156-a69d5cbd4bca"); + fs::remove_dir_all("../test-data/walletsV2").unwrap(); + } + + #[test] + #[serial] + pub fn test_migrate_keystores_identified_chain_types() { + let _ = fs::remove_dir_all("../test-data/walletsV2"); + let param = InitTokenCoreXParam { + file_dir: "../test-data".to_string(), + xpub_common_key: "B888D25EC8C12BD5043777B1AC49F872".to_string(), + xpub_common_iv: "9C0C30889CBCC5E01AB5B2BB88715799".to_string(), + is_debug: true, + }; + + handler::init_token_core_x(&encode_message(param).unwrap()).expect("should init tcx"); + + // original = wif, identified_chain_types = BITCOIN + { + let param: MigrateKeystoreParam = MigrateKeystoreParam { + id: "d9e3bb9c-87fd-4836-b146-10a3e249eb75".to_string(), + key: Some(migrate_keystore_param::Key::DerivedKey( + "01073f22079380d2180300c518f6b510d4761fd83ce738271460c9e745b9055dabb28f93ff3a8fd54e0c71c005b5e799f8d52bcce1a81e08b5f15f9604531574".to_string(), + )), + }; + call_api("migrate_keystore", param).unwrap(); + let json = fs::read_to_string(format!( + "../test-data/walletsV2/d9e3bb9c-87fd-4836-b146-10a3e249eb75.json" + )) + .unwrap(); + let keystore = Keystore::from_json(&json).unwrap(); + assert_eq!( + keystore.meta().identified_chain_types, + Some(vec!["BITCOIN".to_string()]) + ); + let unlocker = keystore + .store() + .crypto + .use_key(&tcx_crypto::Key::DerivedKey("01073f22079380d2180300c518f6b510d4761fd83ce738271460c9e745b9055dabb28f93ff3a8fd54e0c71c005b5e799f8d52bcce1a81e08b5f15f9604531574".to_string())) + .unwrap(); + let wif_bytes = unlocker + .decrypt_enc_pair(keystore.store().enc_original.as_ref().unwrap()) + .unwrap(); + let wif = String::from_utf8_lossy(&wif_bytes); + assert_eq!("L1xDTJYPqhofU8DQCiwjStEBr1X6dhiNfweUhxhoRSgYyMJPcZ6B", wif); + } + + // original = hex, identified_chain_types = ETEHREUM + { + let param: MigrateKeystoreParam = MigrateKeystoreParam { + id: "60573d8d-8e83-45c3-85a5-34fbb2aad5e1".to_string(), + key: Some(migrate_keystore_param::Key::DerivedKey( + "8f2316895af6d58b5b75d424977cdaeae2a619c6b941ca5f77dcfed592cd3b23b698040caf397df6153db6f2d5b2815bf8f8cd32f99998ca46534242df82d1ca".to_string(), + )), + }; + call_api("migrate_keystore", param).unwrap(); + let json = fs::read_to_string(format!( + "../test-data/walletsV2/60573d8d-8e83-45c3-85a5-34fbb2aad5e1.json" + )) + .unwrap(); + let keystore = Keystore::from_json(&json).unwrap(); + assert_eq!( + keystore.meta().identified_chain_types, + Some(vec!["ETHEREUM".to_string()]) + ); + + let unlocker = keystore + .store() + .crypto + .use_key(&tcx_crypto::Key::DerivedKey("8f2316895af6d58b5b75d424977cdaeae2a619c6b941ca5f77dcfed592cd3b23b698040caf397df6153db6f2d5b2815bf8f8cd32f99998ca46534242df82d1ca".to_string())) + .unwrap(); + let decrypted = unlocker + .decrypt_enc_pair(keystore.store().enc_original.as_ref().unwrap()) + .unwrap(); + let hex = String::from_utf8_lossy(&decrypted); + assert_eq!( + "7e480e9ef0faccdf1a3aa773682742e099620f6177e95a878c2a612a0785fc7c", + hex + ); + } + + let param: MigrateKeystoreParam = MigrateKeystoreParam { + id: "792a0051-16d7-44a7-921a-9b4a0c893b8f".to_string(), + key: Some(migrate_keystore_param::Key::DerivedKey( + "0xebe2739dd04525823b967b914a74a5dedd0086622d0da3449c1354199518673dd33fca8f6bd64870d6e6dc28b0f6e9de169243679b1668750f23cfe9523c03b3".to_string(), + )), + }; + call_api("migrate_keystore", param).unwrap(); + let json = fs::read_to_string(format!( + "../test-data/walletsV2/792a0051-16d7-44a7-921a-9b4a0c893b8f.json" + )) + .unwrap(); + let keystore = Keystore::from_json(&json).unwrap(); + assert!(keystore.meta().identified_chain_types.is_none()); + + assert!(keystore.store().enc_original.is_none()); + + let param: MigrateKeystoreParam = MigrateKeystoreParam { + id: "f3615a56-cb03-4aa4-a893-89944e49920d".to_string(), + key: Some(migrate_keystore_param::Key::DerivedKey( + "0x79c74b67fc73a255bc66afc1e7c25867a19e6d2afa5b8e3107a472de13201f1924fed05e811e7f5a4c3e72a8a6e047a80393c215412bde239ec7ded520896630".to_string(), + )), + }; + call_api("migrate_keystore", param).unwrap(); + let json = fs::read_to_string(format!( + "../test-data/walletsV2/f3615a56-cb03-4aa4-a893-89944e49920d.json" + )) + .unwrap(); + let keystore = Keystore::from_json(&json).unwrap(); + assert_eq!( + keystore.meta().identified_chain_types, + Some(vec!["ETHEREUM".to_string()]) + ); + + let unlocker = keystore + .store() + .crypto + .use_key(&tcx_crypto::Key::DerivedKey("0x79c74b67fc73a255bc66afc1e7c25867a19e6d2afa5b8e3107a472de13201f1924fed05e811e7f5a4c3e72a8a6e047a80393c215412bde239ec7ded520896630".to_string())) + .unwrap(); + let decrypted = unlocker + .decrypt_enc_pair(keystore.store().enc_original.as_ref().unwrap()) + .unwrap(); + let hex = String::from_utf8_lossy(&decrypted); + assert_eq!( + "4b8e7a47497d810cd11f209b8ce9d3b0eec34e85dc8bad5d12cb602425dd3d6b", + hex + ); + + let param: MigrateKeystoreParam = MigrateKeystoreParam { + id: "fbdc2a0b-58d5-4e43-b368-a0cb1a2d17cb".to_string(), + key: Some(migrate_keystore_param::Key::Password( + TEST_PASSWORD.to_string(), + )), + }; + call_api("migrate_keystore", param).unwrap(); + let json = fs::read_to_string(format!( + "../test-data/walletsV2/fbdc2a0b-58d5-4e43-b368-a0cb1a2d17cb.json" + )) + .unwrap(); + let keystore = Keystore::from_json(&json).unwrap(); + assert_eq!( + keystore.meta().identified_chain_types, + Some(vec!["FILECOIN".to_string()]) + ); + assert!(keystore.store().enc_original.is_none()); + + // fs::remove_dir_all("../test-data/walletsV2").unwrap(); + } } diff --git a/token-core/tcx/src/migration.rs b/token-core/tcx/src/migration.rs new file mode 100644 index 00000000..abed11c5 --- /dev/null +++ b/token-core/tcx/src/migration.rs @@ -0,0 +1,544 @@ +use bytes::BytesMut; +use prost::Message; +use serde::{Deserialize, Serialize}; +use serde_json::Value; +use std::fs; +use std::io::Read; +use std::path::Path; +use std::str::FromStr; +use tcx_eos::address::{EosAddress, EosPublicKeyEncoder}; +use tcx_eos::encode_eos_wif; +use tcx_eth2::transaction::{SignBlsToExecutionChangeParam, SignBlsToExecutionChangeResult}; +use tcx_keystore::keystore::IdentityNetwork; + +use tcx_common::{sha256d, FromHex, ToHex}; +use tcx_primitive::{ + private_key_without_version, PrivateKey, PublicKey, Secp256k1PrivateKey, Secp256k1PublicKey, + Sr25519PrivateKey, TypedPrivateKey, TypedPublicKey, +}; + +use tcx_btc_kin::WIFDisplay; +use tcx_keystore::{ + fingerprint_from_mnemonic, fingerprint_from_private_key, Address, Keystore, KeystoreGuard, + SignatureParameters, Signer, +}; +use tcx_keystore::{Account, HdKeystore, Metadata, PrivateKeystore, Source}; + +use tcx_crypto::{XPUB_COMMON_IV, XPUB_COMMON_KEY_128}; +use tcx_filecoin::KeyInfo; + +use crate::api::{ + migrate_keystore_param, AccountResponse, KeystoreResult, LegacyKeystoreResult, + MigrateKeystoreParam, MigrateKeystoreResult, ScanLegacyKeystoresResult, +}; +use crate::error_handling::Result; +use crate::filemanager::{cache_keystore, delete_keystore_file, KEYSTORE_MAP}; +use crate::filemanager::{flush_keystore, LEGACY_WALLET_FILE_DIR}; + +use crate::handler::{encode_message, encrypt_xpub}; +use crate::IS_DEBUG; + +use base58::FromBase58; +use tcx_keystore::tcx_ensure; + +use tcx_constants::coin_info::{coin_info_from_param, get_xpub_prefix}; +use tcx_constants::{CoinInfo, CurveType}; +use tcx_migration::keystore_upgrade::{mapping_curve_name, KeystoreUpgrade}; + +use tcx_primitive::{Bip32DeterministicPublicKey, Ss58Codec}; +use tcx_substrate::{decode_substrate_keystore, encode_substrate_keystore, SubstrateKeystore}; + +use tcx_migration::migration::{LegacyKeystore, NumberOrNumberStr}; +use tcx_primitive::TypedDeterministicPublicKey; +use tcx_tezos::{build_tezos_base58_private_key, parse_tezos_private_key}; + +pub(crate) fn migrate_keystore(data: &[u8]) -> Result> { + let param: MigrateKeystoreParam = + MigrateKeystoreParam::decode(data).expect("param: MigrateKeystoreParam"); + let legacy_file_dir = { + let dir = LEGACY_WALLET_FILE_DIR.read(); + dir.to_string() + }; + let mut file_path = format!("{}/{}.json", legacy_file_dir, param.id); + let path = Path::new(&file_path); + if !path.exists() { + file_path = format!("{}/{}", legacy_file_dir, param.id); + } + let json_str = fs::read_to_string(file_path)?; + let json = serde_json::from_str::(&json_str)?; + + let key = match param.key.clone().unwrap() { + migrate_keystore_param::Key::Password(password) => tcx_crypto::Key::Password(password), + migrate_keystore_param::Key::DerivedKey(derived_key) => { + tcx_crypto::Key::DerivedKey(derived_key) + } + }; + + let keystore; + + if let Some(version) = json["version"].as_i64() { + match version { + 11000 | 11001 => { + let keystore_upgrade = KeystoreUpgrade::new(json); + keystore = keystore_upgrade.upgrade(&key, &IdentityNetwork::Testnet)?; + } + _ => { + let legacy_keystore = LegacyKeystore::from_json_str(&json_str)?; + keystore = legacy_keystore.migrate(&key, &IdentityNetwork::Testnet)?; + } + } + + let mut is_existed = false; + let mut existed_id = "".to_string(); + let fingerprint = keystore.fingerprint(); + { + let keystore_map = KEYSTORE_MAP.read(); + let existed_ks: Vec<&Keystore> = keystore_map + .values() + .filter(|ks| ks.fingerprint() == fingerprint) + .collect(); + if existed_ks.len() > 0 { + is_existed = true; + existed_id = existed_ks[0].id().to_string(); + } + } + if is_existed { + return encode_message(MigrateKeystoreResult { + is_existed: true, + existed_id, + keystore: None, + }); + } else { + let identity = keystore.identity(); + + let keystore_result = KeystoreResult { + id: keystore.id(), + name: keystore.meta().name, + source: keystore.meta().source.to_string(), + created_at: keystore.meta().timestamp, + identifier: identity.identifier.to_string(), + ipfs_id: identity.ipfs_id.to_string(), + source_fingerprint: keystore.fingerprint().to_string(), + }; + + let ret = encode_message(MigrateKeystoreResult { + is_existed: false, + existed_id: "".to_string(), + keystore: Some(keystore_result), + }); + flush_keystore(&keystore)?; + cache_keystore(keystore); + return ret; + } + } else { + Err(format_err!("invalid version in keystore")) + } +} + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct LegacyAccount { + pub address: String, + pub derivation_path: String, + pub curve: String, + pub coin: String, + pub network: String, + pub seg_wit: String, + pub ext_pub_key: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub public_key: Option, +} + +pub(crate) fn scan_legacy_keystores() -> Result { + let file_dir = LEGACY_WALLET_FILE_DIR.read(); + let p = Path::new(file_dir.as_str()); + let walk_dir = std::fs::read_dir(p).expect("read dir"); + + let mut keystores: Vec = Vec::new(); + let mut result = ScanLegacyKeystoresResult::default(); + + for entry in walk_dir { + let entry = entry.expect("DirEntry"); + let fp = entry.path(); + + let mut f = fs::File::open(fp).expect("open file"); + + let mut contents = String::new(); + let read_ret = f.read_to_string(&mut contents); + if read_ret.is_err() { + // imkey directory + continue; + } + + let v_result = serde_json::from_str::(&contents); + let Ok(v) = v_result else { + continue; + }; + + let version = v["version"].as_i64().expect("version"); + + if version == 11000 || version == 11001 { + // let keystore = Keystore::from_json(&contents)?; + let keystore_result = parse_tcx_keystore(&v)?; + keystores.push(keystore_result); + } else if version == 44 || version == 3 || version == 10001 { + let keystore_result = parse_legacy_kesytore(contents)?; + keystores.push(keystore_result); + } else if version == 10000 { + parse_identity_keystore(&mut result, v); + } + } + result.keystores = keystores; + Ok(result) +} + +fn parse_identity_keystore(result: &mut ScanLegacyKeystoresResult, v: Value) { + result.identifier = v["identifier"].as_str().unwrap_or("").to_string(); + + result.ipfs_id = v["ipfsId"].as_str().unwrap_or("").to_string(); + result.network = v["imTokenMeta"]["network"] + .as_str() + .expect("identity.json network") + .to_string(); + result.source = v["imTokenMeta"]["source"] + .as_str() + .expect("identity.json source") + .to_string(); +} + +fn parse_legacy_kesytore(contents: String) -> Result { + let legacy_keystore = LegacyKeystore::from_json_str(&contents)?; + let meta = legacy_keystore + .im_token_meta + .expect("imToken keystore need meta"); + let chain_type = if let Some(chain_type) = meta.chain_type { + chain_type + } else { + meta.chain.unwrap() + }; + let seg_wit = if let Some(seg_wit) = meta.seg_wit { + seg_wit + } else { + "NONE".to_string() + }; + let path = if let Some(path) = legacy_keystore.mnemonic_path { + if !path.is_empty() && chain_type.eq("BITCOIN") { + format!("{}/0/0", path) + } else { + path + } + } else { + "".to_string() + }; + let (extended_public_key, encrypted_extended_public_key) = + if let Some(xpub) = legacy_keystore.xpub { + if let Ok(ext_pub_key) = Bip32DeterministicPublicKey::from_ss58check(&xpub) { + let ext_pub_key_hex = ext_pub_key.to_hex(); + let enc_ext_pub_key = encrypt_xpub(&ext_pub_key_hex).unwrap_or("".to_string()); + (xpub, enc_ext_pub_key) + } else { + ("".to_string(), "".to_string()) + } + } else { + ("".to_string(), "".to_string()) + }; + let public_key = if let Some(key_paths) = legacy_keystore.key_path_privates { + key_paths + .iter() + .find(|x| x.derived_mode == "PATH_DIRECTLY") + .map(|x| x.public_key.clone()) + .unwrap_or_default() + } else { + "".to_string() + }; + let account = AccountResponse { + chain_type, + address: legacy_keystore.address.expect("legacy address"), + path, + curve: "secp256k1".to_string(), + public_key, + extended_public_key, + encrypted_extended_public_key, + seg_wit, + }; + let created_at = match meta.timestamp.clone() { + NumberOrNumberStr::Number(t) => t, + NumberOrNumberStr::NumberStr(t) => f64::from_str(&t).expect("f64 from timestamp") as i64, + }; + let keystore_result = LegacyKeystoreResult { + id: legacy_keystore.id.to_string(), + name: meta.name.to_string(), + source: meta.source.as_ref().unwrap_or(&"".to_string()).to_string(), + created_at, + accounts: vec![account], + }; + Ok(keystore_result) +} + +fn parse_tcx_keystore(v: &Value) -> Result { + let legacy_accounts: Vec = serde_json::from_value(v["activeAccounts"].clone()) + .ok() + .unwrap_or(vec![]); + let mut account_responses: Vec = vec![]; + for legacy_account in legacy_accounts.iter() { + let public_key = if let Some(public_key) = &legacy_account.public_key { + public_key.to_string() + } else { + "".to_string() + }; + + let (extended_public_key, encrypted_extended_public_key) = if !legacy_account + .ext_pub_key + .is_empty() + { + let Ok(hd_key) = Bip32DeterministicPublicKey::from_hex_auto(&legacy_account.ext_pub_key) else { + continue; + }; + let xpub_prefix = + get_xpub_prefix(&legacy_account.network, &legacy_account.derivation_path); + let extended_public_key = hd_key.to_ss58check_with_version(&xpub_prefix); + + ( + extended_public_key, + encrypt_xpub(&legacy_account.ext_pub_key).unwrap_or("".to_string()), + ) + } else { + ("".to_string(), "".to_string()) + }; + + account_responses.push(AccountResponse { + chain_type: legacy_account.coin.to_string(), + address: legacy_account.address.to_string(), + path: legacy_account.derivation_path.to_string(), + curve: mapping_curve_name(legacy_account.curve.as_str()).to_string(), + public_key, + extended_public_key, + encrypted_extended_public_key, + seg_wit: legacy_account.seg_wit.to_string(), + }) + } + let id = v["id"].as_str().expect("keystore id").to_string(); + let meta: Metadata = serde_json::from_value(v["imTokenMeta"].clone())?; + let keystore_result = LegacyKeystoreResult { + id, + name: meta.name.to_string(), + source: meta.source.to_string(), + created_at: meta.timestamp, + accounts: account_responses, + }; + Ok(keystore_result) +} + +fn read_identity_network() -> Result { + let dir = LEGACY_WALLET_FILE_DIR.read(); + let identify_path = format!("{}/identity.json", dir); + let mut identify_file = fs::File::open(&identify_path)?; + + let mut json_str = String::new(); + identify_file.read_to_string(&mut json_str)?; + let json: Value = serde_json::from_str(&json_str)?; + let network = json["imTokenMeta"]["network"] + .as_str() + .expect("network") + .to_string(); + IdentityNetwork::from_str(&network) +} + +#[cfg(test)] +mod tests { + use crate::{filemanager::LEGACY_WALLET_FILE_DIR, migration::scan_legacy_keystores}; + use serial_test::serial; + use tcx_keystore::keystore::IdentityNetwork; + + use super::read_identity_network; + + #[test] + #[serial] + + fn test_scan_tcx_legacy_keystores() { + *LEGACY_WALLET_FILE_DIR.write() = "../test-data/wallets-ios-2_14_1/".to_string(); + let result = scan_legacy_keystores().unwrap(); + + assert_eq!(result.identifier, "im18MDKM8hcTykvMmhLnov9m2BaFqsdjoA7cwNg"); + + assert_eq!( + result.ipfs_id, + "QmSTTidyfa4np9ak9BZP38atuzkCHy4K59oif23f4dNAGU" + ); + + let keystore = result + .keystores + .iter() + .find(|x| x.id.eq("0a2756cd-ff70-437b-9bdb-ad46b8bb0819")) + .clone() + .unwrap(); + assert_eq!(keystore.id, "0a2756cd-ff70-437b-9bdb-ad46b8bb0819"); + let account = keystore.accounts.first().unwrap(); + assert_eq!(account.address, "TY2uroBeZ5trA9QT96aEWj32XLkAAhQ9R2"); + assert_eq!(account.chain_type, "TRON"); + assert_eq!(account.path, "m/44'/195'/0'/0/0"); + assert_eq!(account.curve, "secp256k1"); + assert_eq!( + account.public_key, + "037b5253c24ce2a293566f9e066051366cda5073e4a43b25f07c990d7c9ac0aab5" + ); + assert_eq!(account.extended_public_key, "tpubDCxD6k9PreNhSacpfSZ3iErESZnncY1n7qU7e3stZXLPh84xVVt5ERMAqKeefUU8jswx2GpCkQpeYow4xH3PGx2iim6ftPa32GNvTKAtknz"); + assert_eq!(account.encrypted_extended_public_key, "b78BOM632Fph4a2xIzWH7Y2fUbHbkYVr2OgJ4WuNxubppAue5npoXgG1kjB7ATxYxpjxYqu/0TgRM1Dz8QO3cT1GPVASzzt4U+f2qeiQcUSj3pnYneGRDcTnY9JsXZmshVbmX7s1He9a0j8x7UeUCS61JM3S9nATdx6YVU/+ViD2tDdRHk6v8IwGnh1uoKb2a/CCsYQbPs5taZoLfwS3BA=="); + + let account = keystore + .accounts + .iter() + .find(|x| x.chain_type.eq("FILECOIN")) + .unwrap(); + assert_eq!(account.address, "t12i3bop43tprlnymx2c75u6uvlq7iur2rcd7qsey"); + assert_eq!(account.chain_type, "FILECOIN"); + assert_eq!(account.path, "m/44'/461'/0'/0/0"); + assert_eq!(account.curve, "secp256k1"); + assert_eq!( + account.public_key, + "03bd460186d29fd9ac68ee88b110c3acc4a4443648a1ec7607af9ce306ad76f785" + ); + assert_eq!(account.extended_public_key, "tpubDDaEZaaDDmwnZTP6u7m3yTKFgnbSx2uTaxp1hKM5oiVZo6iBB46rWnWpdkpbPxtfdYiyLbyhqgbXRXYff3LfW4rCpYyfpb5pC67CPZdKkZB"); + assert_eq!(account.encrypted_extended_public_key, "PyK/ofjxHbRbZlOE7N4Au7LZIzM5DoV6SgHbfrQvaOsWv5ZwXL2s3nQk6eCj1SIRL6A3s9STpPz7Y3KdggApnOgUpIw7v6ZB3kTKbH4Y8RPH8e1Nlkg2J1CDaf5US1nBmWCxKD4gDh9GEI8H41/MaRWIBnyyw+vwCf5hvVpSvpL9b/sUe8boZmw/VfNpbF5MkGMtFyaZnxd80qFqxgc8fA=="); + + let keystore = result + .keystores + .iter() + .find(|x| x.id.eq("4d5cbfcf-aee1-4908-9991-9d060eb68a0e")) + .clone() + .unwrap(); + assert_eq!(keystore.id, "4d5cbfcf-aee1-4908-9991-9d060eb68a0e"); + let account = keystore.accounts.first().unwrap(); + assert_eq!(account.address, "tz1d2TfcvWBwtPqo7f21DVv7HSSCoNAVp8gz"); + assert_eq!(account.chain_type, "TEZOS"); + assert_eq!(account.curve, "ed25519"); + assert_eq!( + account.public_key, + "bdb7b056d28a8610de329fb4c367886256cc15a5e438a42fff485cd4fc73e574" + ); + assert_eq!(account.path, ""); + assert_eq!(account.extended_public_key, ""); + assert_eq!(account.encrypted_extended_public_key, ""); + + let keystore = result + .keystores + .iter() + .find(|x| x.id.eq("fbdc2a0b-58d5-4e43-b368-a0cb1a2d17cb")) + .clone() + .unwrap(); + assert_eq!(keystore.id, "fbdc2a0b-58d5-4e43-b368-a0cb1a2d17cb"); + let account = keystore.accounts.first().unwrap(); + assert_eq!(account.address, "t3qdyntx5snnwgmjkp2ztd6tf6hhcmurxfj53zylrqyympwvzvbznx6vnvdqloate5eviphnzrkupno4wheesa"); + assert_eq!(account.chain_type, "FILECOIN"); + assert_eq!(account.curve, "bls12-381"); + assert_eq!( + account.public_key, + "80f0d9dfb26b6c66254fd6663f4cbe39c4ca46e54f779c2e30c618fb57350e5b7f55b51c16e04c9d2550f3b731551ed7" + ); + assert_eq!(account.path, ""); + assert_eq!(account.extended_public_key, ""); + assert_eq!(account.encrypted_extended_public_key, ""); + } + + #[test] + #[serial] + fn test_scan_legacy_keystores() { + *LEGACY_WALLET_FILE_DIR.write() = "../test-data/wallets-ios-2_14_1/".to_string(); + let result = scan_legacy_keystores().unwrap(); + + let keystore = result + .keystores + .iter() + .find(|x| x.id.eq("00fc0804-7cea-46d8-9e95-ed1efac65358")) + .clone() + .unwrap(); + assert_eq!(keystore.id, "00fc0804-7cea-46d8-9e95-ed1efac65358"); + let account = keystore.accounts.first().unwrap(); + assert_eq!(account.address, "2MwN441dq8qudMvtM5eLVwC3u4zfKuGSQAB"); + assert_eq!(account.chain_type, "BITCOIN"); + assert_eq!(account.curve, "secp256k1"); + assert_eq!(account.public_key, ""); + assert_eq!(account.path, "m/49'/1'/0'/0/0"); + assert_eq!(account.extended_public_key, "tpubDCwNET9ErXmBracx3ZBfi6rXQZRjYkpitFe23FAW9M3RcCw4aveNC4SAV5yYrFDjtP3b46eFfv4VtiYP3EXoTZsbnJia2yNznExS8EEcACv"); + assert_eq!(account.encrypted_extended_public_key, "re1xUqBS63Dybo7YZi60QdPm9MC7VdCoHABo7T1qf+btzCUROCND0HxmrShSZEy08QzLoiXxvZBO20/wS7OGjNXmI9wH2i7554S1ol5kwTKoid3Qhk12U6s1zHKCuInAdcNW+/jh4ttp6cO3hosjQGJRCQu8Ts43/TFsN+I0A/8DtyJlSbg1YYz5Xn9R83IRc4R8EvdHj0M2Mrfnae4T0g=="); + assert_eq!(keystore.source, "RECOVERED_IDENTITY"); + + let keystore = result + .keystores + .iter() + .find(|x| x.id.eq("6c3eae60-ad03-48db-a5e5-61a6f72aef8d")) + .clone() + .unwrap(); + assert_eq!(keystore.id, "6c3eae60-ad03-48db-a5e5-61a6f72aef8d"); + let account = keystore.accounts.first().unwrap(); + assert_eq!(account.address, ""); + assert_eq!(account.chain_type, "EOS"); + assert_eq!(account.curve, "secp256k1"); + assert_eq!(account.path, "m/44'/194'/0'/0/0"); + assert_eq!( + account.public_key, + "EOS88XhiiP7Cu5TmAUJqHbyuhyYgd6sei68AU266PyetDDAtjmYWF" + ); + assert_eq!(account.extended_public_key, ""); + assert_eq!(account.encrypted_extended_public_key, ""); + assert_eq!(keystore.source, "RECOVERED_IDENTITY"); + + let keystore = result + .keystores + .iter() + .find(|x| x.id.eq("9b696367-69c1-4cfe-8325-e5530399fc3f")) + .clone() + .unwrap(); + assert_eq!(keystore.id, "9b696367-69c1-4cfe-8325-e5530399fc3f"); + let account = keystore.accounts.first().unwrap(); + assert_eq!( + account.address, + "cosmos1m566v5rcklnac8vc0dftfu4lnvznhlu7d3f404" + ); + assert_eq!(account.chain_type, "COSMOS"); + assert_eq!(account.curve, "secp256k1"); + assert_eq!(account.path, "m/44'/118'/0'/0/0"); + + let keystore = result + .keystores + .iter() + .find(|x| x.id.eq("60573d8d-8e83-45c3-85a5-34fbb2aad5e1")) + .clone() + .unwrap(); + assert_eq!(keystore.id, "60573d8d-8e83-45c3-85a5-34fbb2aad5e1"); + let account = keystore.accounts.first().unwrap(); + assert_eq!(account.address, "02c98f4ed8c8aab1aaba46539e45070a02e416c0"); + assert_eq!(account.chain_type, "ETHEREUM"); + assert_eq!(account.curve, "secp256k1"); + assert_eq!(account.path, ""); + assert_eq!(keystore.source, "KEYSTORE"); + + let keystore = result + .keystores + .iter() + .find(|x| x.id.eq("792a0051-16d7-44a7-921a-9b4a0c893b8f")) + .clone() + .unwrap(); + assert_eq!(keystore.id, "792a0051-16d7-44a7-921a-9b4a0c893b8f"); + let account = keystore.accounts.first().unwrap(); + assert_eq!(account.address, "7152bcad819b084d57179e293d2765ffa0109e04"); + assert_eq!(account.chain_type, "ETHEREUM"); + assert_eq!(account.curve, "secp256k1"); + assert_eq!(account.path, "m/44'/60'/0'/0/1"); + assert_eq!(keystore.source, "MNEMONIC"); + } + + #[test] + #[serial] + fn test_read_mainnet_identity() { + *LEGACY_WALLET_FILE_DIR.write() = "../test-data/mainnet-identity/".to_string(); + assert_eq!(read_identity_network().unwrap(), IdentityNetwork::Mainnet); + } + + #[test] + #[serial] + fn test_read_testnet_identity() { + *LEGACY_WALLET_FILE_DIR.write() = "../test-data/testnet-identity/".to_string(); + assert_eq!(read_identity_network().unwrap(), IdentityNetwork::Testnet); + } +} diff --git a/token-core/test-data/.gitignore b/token-core/test-data/.gitignore index ea453a05..27135d66 100644 --- a/token-core/test-data/.gitignore +++ b/token-core/test-data/.gitignore @@ -9,4 +9,8 @@ !wallets-ios-2_14_1 !wallets-ios-2_14_1/* !scan-keystores-fixtures -!scan-keystores-fixtures/* \ No newline at end of file +!scan-keystores-fixtures/* +!mainnet-identity +!mainnet-identity/* +!testnet-identity +!testnet-identity/* \ No newline at end of file diff --git a/token-core/test-data/mainnet-identity/identity.json b/token-core/test-data/mainnet-identity/identity.json new file mode 100644 index 00000000..8435d794 --- /dev/null +++ b/token-core/test-data/mainnet-identity/identity.json @@ -0,0 +1,43 @@ +{ + "crypto": { + "cipher": "aes-128-ctr", + "cipherparams": { + "iv": "76e228e45cc66228e1890dec1d211de6" + }, + "ciphertext": "18ce44f304402417eb96f3468a4f49450cb4b6916c32522ae5173960db929f486ff373781f1fca4742ed853b6c2d7e6951533a68805ae7586befc7d830718a3d8e9074ea6cd5bb361a1b06cf3c2cf8b4d82755c6639816b3ec1727cdd0b5e45a144f5cf943177a30ee9ecee2a65314", + "kdf": "pbkdf2", + "kdfparams": { + "c": 65535, + "dklen": 32, + "prf": "hmac-sha256", + "salt": "519463106b45fcb3ffcb39b1b8805cdb3d789a908502ce5f3b976dbca780a2d0" + }, + "mac": "359ea5fe92c0dce5ab03a31e354de1f33a9f7cb822f012d00da68bddc7b02c5d" + }, + "id": "a1b92c23-5fcb-43a7-a8fe-07bb56acd033", + "version": 10000, + "encAuthKey": { + "encStr": "ba3afce29774e6225f6bd24df91fb911dc4583f8d999669912388ce461d49ac0", + "nonce": "b1ab60c19a5fc6568bef1eea30d38a98" + }, + "encKey": "9513617c9b398edebfb46080a8f0cf6c", + "encMnemonic": { + "encStr": "01dfbda9bd529fda10233082e8bed9ea7b7acda60c7f676f9cc5a4cf7c6f65bd3426e5e3cfec27652d9e36f0f855dbd2f8f0693b79f82bece69cd291ec2979862a45b40e758f48336c41", + "nonce": "869c45b926817db6a842eec33acf846e" + }, + "identifier": "im18MDKM8hcTykvMmhLnov9m2BaFqsdjoA7cwNg", + "ipfsId": "QmVoPZQnQjppqcD4gUh1KrfmjbWQCtXQpm2YrFG6Voquh2", + "walletIDs": [ + "175169f7-5a35-4df7-93c1-1ff612168e71", + "02a55ab6-554a-4e78-bc26-6a7acced7e5e", + "045861fe-0e9b-4069-92aa-0ac03cad55e0" + ], + "imTokenMeta": { + "backup": [], + "network": "MAINNET", + "name": "identity_name", + "passwordHint": "", + "source": "RECOVERED_IDENTITY", + "timestamp": 1519611220 + } +} diff --git a/token-core/test-data/scan-keystores-fixtures/1055741c-2904-4973-b7ee-4b69bfd8bcc6.json b/token-core/test-data/scan-keystores-fixtures/1055741c-2904-4973-b7ee-4b69bfd8bcc6.json deleted file mode 100644 index 131b3b44..00000000 --- a/token-core/test-data/scan-keystores-fixtures/1055741c-2904-4973-b7ee-4b69bfd8bcc6.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "id": "1055741c-2904-4973-b7ee-4b69bfd8bcc6", - "version": 12000, - "sourceFingerprint": "0x1468dba9c246fe22183c056540ab4d8b04553217", - "crypto": { - "cipher": "aes-128-ctr", - "cipherparams": { "iv": "d150f6fc944584be8b3addd0bb1190ca" }, - "ciphertext": "c31543b923b3ec367b479d4fafa7d94d1505b9e7223445e50ef6fd84f04c62da43aaf3dddb92b5a4c8357ae155919021a45ef856b18e9fd73e9495f44131cf7fafc81be8d5efa397ca4c", - "kdf": "pbkdf2", - "kdfparams": { - "c": 1, - "prf": "hmac-sha256", - "dklen": 32, - "salt": "1319ae254669eccdccdd1e158183e957cb33c222e3a38768dc03c83953b877fd" - }, - "mac": "928c51acc7a26ec0412fbd3c240066fb6f17083d756846be589a325055c5a851" - }, - "identity": { - "encAuthKey": { - "encStr": "b1f4d88ef74db3e372a6593c05f28e6d76d8e2bb3b8d09e2feab3fa1bbf7febd", - "nonce": "83e733357cf50bfa8017088562c524bf" - }, - "encKey": "ef806a542bcc30da7ce60fc37bd6cc91619b482f6f070af3a9d7b042087886f3", - "identifier": "im14x5GXsdME4JsrHYe2wvznqRz4cUhx2pA4HPf", - "ipfsId": "QmWqwovhrZBMmo32BzY83ZMEBQaP7YRMqXNmMc8mgrpzs6" - }, - "imTokenMeta": { - "name": "test-wallet", - "passwordHint": "imtoken", - "timestamp": 1704252563, - "source": "MNEMONIC", - "network": "MAINNET" - } -} diff --git a/token-core/test-data/scan-keystores-fixtures/1233134b-8377-4fb0-b06f-56062e858708.json b/token-core/test-data/scan-keystores-fixtures/1233134b-8377-4fb0-b06f-56062e858708.json deleted file mode 100644 index 5fe244a7..00000000 --- a/token-core/test-data/scan-keystores-fixtures/1233134b-8377-4fb0-b06f-56062e858708.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "id": "1233134b-8377-4fb0-b06f-56062e858708", - "version": 12001, - "sourceFingerprint": "0x60c8e832975b284511e4be509e1486020b3d058a", - "crypto": { - "cipher": "aes-128-ctr", - "cipherparams": { "iv": "b6d29edba218965c57838b36c7a24a98" }, - "ciphertext": "6841ac080d14bad7fdb48c70a91ff507bfe4445d48a3f471165076994a9457cfae443d76c33c818d0c9377837dbffd669fef65f7b50140d7fd047b2878252d39", - "kdf": "pbkdf2", - "kdfparams": { - "c": 1, - "prf": "hmac-sha256", - "dklen": 32, - "salt": "b55340fe319c29ec991ba56fe7906f10431e92e98e12c64a92202eee5605557e" - }, - "mac": "5bb394c5f24a7aae0f7be3bd5bbd2b371ba7794198071f8926e185e0ef6e1f73" - }, - "identity": { - "encAuthKey": { - "encStr": "13edb6200fbb88a579bfebc1ba26c46d14e19acc85637deb590c5fb20e315045", - "nonce": "2fc95b4e8a240258ddf09bac7f5ed741" - }, - "encKey": "339f2bf78c2dc3eded309e9de71c197479a193d73da20bdf6dc342d026e340bb", - "identifier": "im14x5JEvG1gEwF9ukFv5EsVyQ47V3BegEA3hVa", - "ipfsId": "QmZ86PZ1ipPUz8BzoJCSqRpkmmkph59xDzavvfkvzHbfqv" - }, - "curve": "sr25519", - "imTokenMeta": { - "name": "test_64bytes_import_private_key", - "passwordHint": "", - "timestamp": 1704252030, - "source": "SUBSTRATE_KEYSTORE", - "network": "MAINNET" - } -} diff --git a/token-core/test-data/scan-keystores-fixtures/7e1c2c55-5b7f-4a5a-8061-c42b594ceb2f.json b/token-core/test-data/scan-keystores-fixtures/7e1c2c55-5b7f-4a5a-8061-c42b594ceb2f.json deleted file mode 100644 index bc1e49dc..00000000 --- a/token-core/test-data/scan-keystores-fixtures/7e1c2c55-5b7f-4a5a-8061-c42b594ceb2f.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "id": "7e1c2c55-5b7f-4a5a-8061-c42b594ceb2f", - "version": 12001, - "sourceFingerprint": "0xc7b60806a2af1e89f107b9410da3ab8a825fe5a2", - "crypto": { - "cipher": "aes-128-ctr", - "cipherparams": { "iv": "4008c233ac1b562191d928a43d80f939" }, - "ciphertext": "91260281bf955f3ee3690d8c7136da86de9a04b3ce923392dbb6bf2ccb3183f6", - "kdf": "pbkdf2", - "kdfparams": { - "c": 1, - "prf": "hmac-sha256", - "dklen": 32, - "salt": "ba860b7214535013ccfa9dcd5a11473b893bfbf7b591d97b6d13812dbe535444" - }, - "mac": "ebf0150fa09977ae70f3cbafbb36f4a8747160cad66f853977a8c40b13cd1205" - }, - "identity": { - "encAuthKey": { - "encStr": "a657ed08e0d55fd7381c731eb5dc8e5075d907a3356198b60001aa95ec615ed4", - "nonce": "037fd63d79adab7daa433a4df4a51276" - }, - "encKey": "d2ada8f248a6593fe9a7094650b7349554e7b310acd4ac0344dd654c18e27724", - "identifier": "im14x5LYRt5YsM5iTr2xd6dQ75euijaoDs3nRB2", - "ipfsId": "QmSpWyzy5gkYyJiagFHzfkJwqzDdcjCA3qeu8T3JFd54vZ" - }, - "curve": "bls12-381", - "imTokenMeta": { - "name": "test_filecoin_import_private_key", - "passwordHint": "", - "timestamp": 1704251911, - "source": "PRIVATE", - "network": "MAINNET" - } -} diff --git a/token-core/test-data/scan-keystores-fixtures/beb68589-0f0f-41e2-94d9-d78f10a72dec.json b/token-core/test-data/scan-keystores-fixtures/beb68589-0f0f-41e2-94d9-d78f10a72dec.json deleted file mode 100644 index 9e394de6..00000000 --- a/token-core/test-data/scan-keystores-fixtures/beb68589-0f0f-41e2-94d9-d78f10a72dec.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "id": "beb68589-0f0f-41e2-94d9-d78f10a72dec", - "version": 12001, - "sourceFingerprint": "0xe6cfaab9a59ba187f0a45db0b169c21bb48f09b3", - "crypto": { - "cipher": "aes-128-ctr", - "cipherparams": { "iv": "4fc650b9fc9eec7d6a97ca34b318c835" }, - "ciphertext": "2cf5dbcc942205ce3bd70147a849f204a7ead5776358c9b6b5e54c6733e9e8fb", - "kdf": "pbkdf2", - "kdfparams": { - "c": 1, - "prf": "hmac-sha256", - "dklen": 32, - "salt": "99074bbf1d752ebb2681b016a249a994556b5af8ef7d6e7d1616b2b1a988f4ca" - }, - "mac": "843a6396c953023795348743f3dc4746359745c7712effbd0a90fbbf0e1c7fee" - }, - "identity": { - "encAuthKey": { - "encStr": "39b2596588eb80ff75cb6d2bd1ecbcac39eb0a98403237961ad4df57506e0744", - "nonce": "a8c2559ce54f0c413c253f8173969f8d" - }, - "encKey": "f42b7d20e2800724fc6e3e4b19e14c998fda72a479facf2ba9a1a7321de4770d", - "identifier": "im14x5UPbCXmU2HMQ8jfeKcCDrQYhDppRYaa5C6", - "ipfsId": "QmczBPUeohPPaE8UnPiESyynPwffBqrn4RqrU6nPJw95VT" - }, - "curve": "secp256k1", - "imTokenMeta": { - "name": "test_filecoin_import_private_key", - "passwordHint": "", - "timestamp": 1704252158, - "source": "PRIVATE", - "network": "MAINNET" - } -} diff --git a/token-core/test-data/scan-keystores-fixtures/efcfffb2-9b63-418b-a9d0-ec3600012284.json b/token-core/test-data/scan-keystores-fixtures/efcfffb2-9b63-418b-a9d0-ec3600012284.json deleted file mode 100644 index efbb8509..00000000 --- a/token-core/test-data/scan-keystores-fixtures/efcfffb2-9b63-418b-a9d0-ec3600012284.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "id": "efcfffb2-9b63-418b-a9d0-ec3600012284", - "version": 12001, - "sourceFingerprint": "0x6bd7cc4e20a7de71296b81758d29447dfde9a388", - "crypto": { - "cipher": "aes-128-ctr", - "cipherparams": { "iv": "6a30f3da70d77d19e9acaedc9d2a71d8" }, - "ciphertext": "bd8cb775958759351fbd8cca236bb00489a51ea43fd841868e076577930c815c", - "kdf": "pbkdf2", - "kdfparams": { - "c": 1, - "prf": "hmac-sha256", - "dklen": 32, - "salt": "dc9b5d1dd3814509610e22a297873859fafa0a6e8b383b074846892e337dc2b7" - }, - "mac": "9d458ee6d44b358799ee489a9042d1d035ad9e801d3f23e6171a6febe35e2d17" - }, - "identity": { - "encAuthKey": { - "encStr": "cddde94a515239246bc62639e9908748099e07051b42833e9b22733382c1325a", - "nonce": "72315c7bdfa23e493c216cd4f5ab0731" - }, - "encKey": "823dc41660514b6c6cc0dea8e58d572893f81c8870d73becfd20e648c6c3d525", - "identifier": "im14x5AU2zU5oRyNdGgNbemdP39ATmu16eVgPFQ", - "ipfsId": "Qmb8K5w1fzdTbjTiATvSecNgZYvbMJ6gJB9JPG254aEY8F" - }, - "curve": "ed25519", - "imTokenMeta": { - "name": "test_tezos_import_private_key_export", - "passwordHint": "", - "timestamp": 1704252267, - "source": "PRIVATE", - "network": "MAINNET" - } -} diff --git a/token-core/test-data/testnet-identity/identity.json b/token-core/test-data/testnet-identity/identity.json new file mode 100644 index 00000000..ecab78d3 --- /dev/null +++ b/token-core/test-data/testnet-identity/identity.json @@ -0,0 +1,54 @@ +{ + "imTokenMeta": { + "network": "TESTNET", + "name": "identity_name", + "version": "iOS-2.14.1.1742", + "source": "RECOVERED_IDENTITY", + "passwordHint": "", + "timestamp": "1703213073.8432422", + "backup": [], + "segWit": "P2WPKH", + "mode": "normal" + }, + "version": 10000, + "walletIds": [ + "0597526e-105f-425b-bb44-086fc9dc9568", + "00fc0804-7cea-46d8-9e95-ed1efac65358", + "ac59ccc1-285b-47a7-92f5-a6c432cee21a", + "6c3eae60-ad03-48db-a5e5-61a6f72aef8d", + "f3615a56-cb03-4aa4-a893-89944e49920d", + "60573d8d-8e83-45c3-85a5-34fbb2aad5e1", + "9e3e1a17-ccad-4d93-98ab-cfe1e3f82ed3", + "9b696367-69c1-4cfe-8325-e5530399fc3f", + "6c20aab6-1596-456d-9749-212e6139c5ed", + "792a0051-16d7-44a7-921a-9b4a0c893b8f", + "9f4acb4a-7431-4c7d-bd25-a19656a86ea0", + "d9e3bb9c-87fd-4836-b146-10a3e249eb75" + ], + "crypto": { + "cipherparams": { "iv": "b947b2ece06b648a75187dcddee67cc1" }, + "ciphertext": "5f1cbaef883def74b4b27a64586188da00f020f9611d1efc84dffba5ae31a173962e2196589aa66a065e5e0f929b26eac27ca04c8eecc0727f5f91e0038cf6aaba9d827c76c6f225ceaa9d937c3a3f1a958e5fe74585565175d301ead8a67f401250abcd0e832f76a4ed37628170b6", + "kdf": "scrypt", + "cipher": "aes-128-ctr", + "mac": "deeabc0d49e9b2ec0eb97d55cff25e2c14ff74e257c96145fb8e47bc31d8df26", + "kdfparams": { + "n": 262144, + "r": 8, + "dklen": 32, + "p": 1, + "salt": "b0fdfcb67bdfc6461d907f6b55c364ce98e21057b74df586953e2bc6e293c248" + } + }, + "encMnemonic": { + "encStr": "74c4b82c2af67c35b8ff31c3547a705d94f9a66397f1aa4118eb6450ec140f055117ec2a87a4402069a3837db7d1b20d16e4e1b9b93c6b615c739b833bd9cb20148c6c37efd777fb6b30", + "nonce": "a293778a4eec6fdd6f7c7ac0edcd869f" + }, + "encAuthKey": { + "encStr": "628e9f0926f12abed0c201bc605af2a378a72cbc397c182c270888ca7dad4d1f", + "nonce": "93e5aff1a87c6f3be9b5bef506478dae" + }, + "id": "d5017486-f9b2-4aeb-8f7b-c57da9b77668", + "identifier": "im18MDKM8hcTykvMmhLnov9m2BaFqsdjoA7cwNg", + "ipfsId": "QmSTTidyfa4np9ak9BZP38atuzkCHy4K59oif23f4dNAGU", + "encKey": "9513617c9b398edebfb46080a8f0cf6cab6763866bb06daa63503722bea78907" +}