From 2f545612d10dca03d71c9f84be9c5890c27bafdf Mon Sep 17 00:00:00 2001 From: Neal Xu Date: Thu, 12 Oct 2023 14:02:39 +0800 Subject: [PATCH] dk keystore migrate feature (#47) * feat: add calc external address * feat: add keystore dk migration * fix: ios and android keystore inconsistency * feat: integrate ios dk keystore migration * feat: implement the functions about identity * chore: rename tcx_chain to tcx_keystore --- Cargo.lock | 67 +- Cargo.toml | 3 +- token-core/tcx-atom/Cargo.toml | 2 +- token-core/tcx-atom/src/address.rs | 4 +- token-core/tcx-atom/src/lib.rs | 2 +- token-core/tcx-atom/src/signer.rs | 5 +- token-core/tcx-btc-kin/Cargo.toml | 2 +- token-core/tcx-btc-kin/src/address.rs | 4 +- token-core/tcx-btc-kin/src/bch_address.rs | 4 +- token-core/tcx-btc-kin/src/lib.rs | 39 +- token-core/tcx-btc-kin/src/signer.rs | 12 +- token-core/tcx-ckb/Cargo.toml | 2 +- token-core/tcx-ckb/src/address.rs | 4 +- token-core/tcx-ckb/src/lib.rs | 4 +- token-core/tcx-ckb/src/signer.rs | 6 +- token-core/tcx-ckb/src/transaction_helper.rs | 2 +- token-core/tcx-common/src/lib.rs | 2 +- token-core/tcx-crypto/src/crypto.rs | 7 +- token-core/tcx-docs/TECH.zh.md | 8 +- token-core/tcx-eos/Cargo.toml | 2 +- token-core/tcx-eos/src/address.rs | 4 +- token-core/tcx-eos/src/chain_factory.rs | 4 +- token-core/tcx-eos/src/lib.rs | 2 +- token-core/tcx-eos/src/signer.rs | 2 +- token-core/tcx-eth/Cargo.toml | 2 +- token-core/tcx-eth/src/address.rs | 25 +- token-core/tcx-eth/src/lib.rs | 2 +- token-core/tcx-eth/src/signer.rs | 8 +- token-core/tcx-eth2/Cargo.toml | 2 +- token-core/tcx-eth2/src/address.rs | 6 +- token-core/tcx-eth2/src/lib.rs | 4 +- token-core/tcx-eth2/src/signer.rs | 2 +- token-core/tcx-filecoin/Cargo.toml | 2 +- token-core/tcx-filecoin/src/address.rs | 4 +- token-core/tcx-filecoin/src/key_info.rs | 2 +- token-core/tcx-filecoin/src/lib.rs | 2 +- token-core/tcx-filecoin/src/signer.rs | 4 +- token-core/tcx-identity/Cargo.toml | 2 +- token-core/tcx-identity/src/identity.rs | 663 +++++++++--------- token-core/tcx-identity/src/imt_keystore.rs | 432 ++++++------ token-core/tcx-identity/src/model.rs | 212 +++--- token-core/tcx-identity/src/v3_keystore.rs | 332 ++++----- token-core/tcx-identity/src/wallet_api.rs | 126 +--- .../{tcx-chain => tcx-keystore}/Cargo.toml | 13 +- .../{tcx-chain => tcx-keystore}/README.md | 0 token-core/tcx-keystore/src/identity.rs | 531 ++++++++++++++ .../src/keystore/guard.rs | 0 .../src/keystore/hd.rs | 10 + .../src/keystore/mod.rs | 46 +- .../src/keystore/private.rs | 2 + .../{tcx-chain => tcx-keystore}/src/lib.rs | 23 + .../{tcx-chain => tcx-keystore}/src/signer.rs | 0 token-core/tcx-migration/Cargo.toml | 21 +- token-core/tcx-migration/src/migration.rs | 133 +++- .../5991857a-2488-4546-b730-463a5f84ea6a | 37 + .../b05a0ff9-885a-4a31-9d82-6477d34d1e37.json | 47 ++ token-core/tcx-primitive/src/bip32.rs | 2 + token-core/tcx-primitive/src/ecc.rs | 1 + token-core/tcx-primitive/src/ed25519_bip32.rs | 2 + token-core/tcx-proto/build.rs | 4 - .../tcx-proto/src/keystore_migration.proto | 9 - token-core/tcx-proto/src/params.proto | 149 +++- token-core/tcx-proto/src/wallet_api.proto | 76 +- token-core/tcx-substrate/Cargo.toml | 2 +- token-core/tcx-substrate/src/address.rs | 2 +- token-core/tcx-substrate/src/keystore.rs | 2 +- token-core/tcx-substrate/src/lib.rs | 2 +- token-core/tcx-substrate/src/signer.rs | 2 +- token-core/tcx-tezos/Cargo.toml | 2 +- token-core/tcx-tezos/src/address.rs | 6 +- token-core/tcx-tezos/src/lib.rs | 4 +- token-core/tcx-tezos/src/signer.rs | 2 +- token-core/tcx-tron/Cargo.toml | 2 +- token-core/tcx-tron/src/address.rs | 6 +- token-core/tcx-tron/src/lib.rs | 2 +- token-core/tcx-tron/src/signer.rs | 4 +- token-core/tcx/Cargo.toml | 3 +- token-core/tcx/src/api.rs | 170 +++-- token-core/tcx/src/filemanager.rs | 43 +- token-core/tcx/src/handler.rs | 479 +++++++++++-- token-core/tcx/src/lib.rs | 594 ++++++++-------- token-core/tcx/src/macros.rs | 9 +- .../5991857a-2488-4546-b730-463a5f84ea6a | 37 + 83 files changed, 2846 insertions(+), 1658 deletions(-) rename token-core/{tcx-chain => tcx-keystore}/Cargo.toml (70%) rename token-core/{tcx-chain => tcx-keystore}/README.md (100%) create mode 100644 token-core/tcx-keystore/src/identity.rs rename token-core/{tcx-chain => tcx-keystore}/src/keystore/guard.rs (100%) rename token-core/{tcx-chain => tcx-keystore}/src/keystore/hd.rs (97%) rename token-core/{tcx-chain => tcx-keystore}/src/keystore/mod.rs (95%) rename token-core/{tcx-chain => tcx-keystore}/src/keystore/private.rs (99%) rename token-core/{tcx-chain => tcx-keystore}/src/lib.rs (69%) rename token-core/{tcx-chain => tcx-keystore}/src/signer.rs (100%) create mode 100644 token-core/tcx-migration/test/fixtures/5991857a-2488-4546-b730-463a5f84ea6a create mode 100644 token-core/tcx-migration/test/fixtures/b05a0ff9-885a-4a31-9d82-6477d34d1e37.json create mode 100644 token-core/test-data/wallets/5991857a-2488-4546-b730-463a5f84ea6a diff --git a/Cargo.lock b/Cargo.lock index d8b23755..25b6a550 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3590,12 +3590,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.17" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if 1.0.0", -] +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "lru" @@ -6439,7 +6436,6 @@ dependencies = [ "strum 0.25.0", "tcx-atom", "tcx-btc-kin", - "tcx-chain", "tcx-ckb", "tcx-common", "tcx-constants", @@ -6448,7 +6444,7 @@ dependencies = [ "tcx-eth", "tcx-eth2", "tcx-filecoin", - "tcx-identity", + "tcx-keystore", "tcx-migration", "tcx-primitive", "tcx-proto", @@ -6467,9 +6463,9 @@ dependencies = [ "bytes 1.4.0", "failure", "prost 0.11.8", - "tcx-chain", "tcx-constants", "tcx-crypto", + "tcx-keystore", "tcx-primitive", ] @@ -6495,33 +6491,10 @@ dependencies = [ "secp256k1 0.24.3", "serde", "serde_json", - "tcx-chain", "tcx-common", "tcx-constants", "tcx-crypto", - "tcx-primitive", - "tiny-bip39 0.7.3", - "uuid 1.3.0", -] - -[[package]] -name = "tcx-chain" -version = "0.1.0" -dependencies = [ - "bitcoin", - "bitcoin_hashes", - "byteorder 1.4.3", - "bytes 1.4.0", - "cargo-husky", - "failure", - "hex 0.4.3", - "prost 0.11.8", - "regex 1.9.3", - "secp256k1 0.24.3", - "serde", - "serde_json", - "tcx-constants", - "tcx-crypto", + "tcx-keystore", "tcx-primitive", "tiny-bip39 0.7.3", "uuid 1.3.0", @@ -6539,9 +6512,9 @@ dependencies = [ "hex 0.4.3", "lazy_static", "prost 0.11.8", - "tcx-chain", "tcx-constants", "tcx-crypto", + "tcx-keystore", "tcx-primitive", ] @@ -6611,10 +6584,10 @@ dependencies = [ "failure", "prost 0.11.8", "regex 1.9.3", - "tcx-chain", "tcx-common", "tcx-constants", "tcx-crypto", + "tcx-keystore", "tcx-primitive", ] @@ -6631,9 +6604,9 @@ dependencies = [ "prost-types 0.11.8", "regex 1.9.3", "rlp", - "tcx-chain", "tcx-common", "tcx-constants", + "tcx-keystore", "tcx-primitive", "tiny-keccak 2.0.2", ] @@ -6652,9 +6625,9 @@ dependencies = [ "regex 1.9.3", "ssz_rs", "ssz_rs_derive", - "tcx-chain", "tcx-constants", "tcx-crypto", + "tcx-keystore", "tcx-primitive", ] @@ -6681,19 +6654,21 @@ dependencies = [ "serde", "serde_cbor", "serde_json", - "tcx-chain", "tcx-constants", "tcx-crypto", + "tcx-keystore", "tcx-primitive", ] [[package]] -name = "tcx-identity" +name = "tcx-keystore" version = "0.1.0" dependencies = [ "bitcoin", + "bitcoin_hashes", "byteorder 1.4.3", - "eth-keystore", + "bytes 1.4.0", + "cargo-husky", "failure", "hex 0.4.3", "hmac-sha256", @@ -6701,17 +6676,16 @@ dependencies = [ "multihash 0.18.1", "parking_lot", "prost 0.11.8", - "prost-types 0.11.8", + "regex 1.9.3", "secp256k1 0.24.3", "serde", "serde_json", "sha2 0.10.6", - "tcx-chain", "tcx-common", "tcx-constants", "tcx-crypto", "tcx-primitive", - "tiny-bip39 1.0.0", + "tiny-bip39 0.7.3", "uuid 1.3.0", ] @@ -6735,16 +6709,17 @@ dependencies = [ "serde_json", "tcx-atom", "tcx-btc-kin", - "tcx-chain", "tcx-common", "tcx-constants", "tcx-crypto", "tcx-eos", "tcx-eth", "tcx-eth2", + "tcx-keystore", "tcx-primitive", "tcx-proto", "tcx-tezos", + "tcx-tron", ] [[package]] @@ -6809,9 +6784,9 @@ dependencies = [ "sp-core", "sp-keyring", "sp-runtime", - "tcx-chain", "tcx-constants", "tcx-crypto", + "tcx-keystore", "tcx-primitive", "xsalsa20poly1305", ] @@ -6838,9 +6813,9 @@ dependencies = [ "prost 0.11.8", "prost-types 0.11.8", "ring", - "tcx-chain", "tcx-constants", "tcx-crypto", + "tcx-keystore", "tcx-primitive", ] @@ -6864,10 +6839,10 @@ dependencies = [ "secp256k1 0.24.3", "serde", "serde_json", - "tcx-chain", "tcx-common", "tcx-constants", "tcx-crypto", + "tcx-keystore", "tcx-primitive", "tiny-bip39 0.7.3", "uuid 1.3.0", diff --git a/Cargo.toml b/Cargo.toml index e90410b6..eba1621b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ members=[ "token-core/tcx-constants", "token-core/tcx-primitive", "token-core/tcx-crypto", - "token-core/tcx-chain", + "token-core/tcx-keystore", "token-core/tcx-tron", "token-core/tcx-btc-kin", "token-core/tcx-ckb", @@ -17,7 +17,6 @@ members=[ "token-core/tcx-tezos", "token-core/tcx-tester", "token-core/tcx-eth2", - "token-core/tcx-identity", "token-core/tcx-eth", "token-core/zksync-crypto", "token-core/tcx-common", diff --git a/token-core/tcx-atom/Cargo.toml b/token-core/tcx-atom/Cargo.toml index 0f5de23c..ba74aec3 100644 --- a/token-core/tcx-atom/Cargo.toml +++ b/token-core/tcx-atom/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [dependencies] tcx-crypto = { path = "../tcx-crypto" } -tcx-chain = { path = "../tcx-chain" } +tcx-keystore = { path = "../tcx-keystore" } tcx-primitive = { path = "../tcx-primitive" } tcx-constants = { path = "../tcx-constants" } diff --git a/token-core/tcx-atom/src/address.rs b/token-core/tcx-atom/src/address.rs index 02d9410b..7467d4b7 100644 --- a/token-core/tcx-atom/src/address.rs +++ b/token-core/tcx-atom/src/address.rs @@ -1,9 +1,9 @@ use core::str::FromStr; use bech32::{FromBase32, ToBase32, Variant}; -use tcx_chain::{Address, ChainFactory, PublicKeyEncoder, Result}; use tcx_constants::CoinInfo; use tcx_crypto::hash; +use tcx_keystore::{Address, ChainFactory, PublicKeyEncoder, Result}; use tcx_primitive::TypedPublicKey; // size of address @@ -77,7 +77,7 @@ impl ChainFactory for AtomChainFactory { mod tests { use crate::address::AtomAddress; - use tcx_chain::Address; + use tcx_keystore::Address; use tcx_constants::{CoinInfo, CurveType}; use tcx_crypto::hex; diff --git a/token-core/tcx-atom/src/lib.rs b/token-core/tcx-atom/src/lib.rs index 992a03fd..7eb6ab5a 100644 --- a/token-core/tcx-atom/src/lib.rs +++ b/token-core/tcx-atom/src/lib.rs @@ -3,8 +3,8 @@ pub mod signer; pub mod transaction; pub mod cosmos { - use tcx_chain::{Account, Keystore}; use tcx_constants::{CoinInfo, CurveType}; + use tcx_keystore::{Account, Keystore}; pub const CHAINS: [&'static str; 1] = ["COSMOS"]; diff --git a/token-core/tcx-atom/src/signer.rs b/token-core/tcx-atom/src/signer.rs index 5baf93e7..a65d8c54 100644 --- a/token-core/tcx-atom/src/signer.rs +++ b/token-core/tcx-atom/src/signer.rs @@ -1,5 +1,5 @@ use crate::transaction::{AtomTxInput, AtomTxOutput}; -use tcx_chain::{ChainSigner, Keystore, Result, TransactionSigner as TraitTransactionSigner}; +use tcx_keystore::{ChainSigner, Keystore, Result, TransactionSigner as TraitTransactionSigner}; use tcx_crypto::{hash, hex}; @@ -31,9 +31,9 @@ mod tests { use super::*; use crate::address::AtomAddress; - use tcx_chain::{HdKeystore, Keystore, KeystoreGuard, Metadata}; use tcx_constants::{CoinInfo, TEST_PASSWORD}; use tcx_constants::{CurveType, TEST_MNEMONIC}; + use tcx_keystore::{HdKeystore, Keystore, KeystoreGuard, Metadata}; #[test] fn sign_transaction() -> core::result::Result<(), failure::Error> { @@ -57,7 +57,6 @@ mod tests { let ks = guard.keystore_mut(); let account = ks.derive_coin::(&coin_info).unwrap().clone(); - println!("account {:?}", account); let signed_tx: AtomTxOutput = ks.sign_transaction("COSMOS", &account.address, &tx)?; diff --git a/token-core/tcx-btc-kin/Cargo.toml b/token-core/tcx-btc-kin/Cargo.toml index d02eca34..779deb9c 100644 --- a/token-core/tcx-btc-kin/Cargo.toml +++ b/token-core/tcx-btc-kin/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [dependencies] tcx-crypto = { path = "../tcx-crypto" } -tcx-chain = { path = "../tcx-chain" } +tcx-keystore = { path = "../tcx-keystore" } tcx-primitive = { path = "../tcx-primitive" } tcx-constants = { path = "../tcx-constants" } tcx-common = { path = "../tcx-common" } diff --git a/token-core/tcx-btc-kin/src/address.rs b/token-core/tcx-btc-kin/src/address.rs index bc1c7358..6ad9ac44 100644 --- a/token-core/tcx-btc-kin/src/address.rs +++ b/token-core/tcx-btc-kin/src/address.rs @@ -17,8 +17,8 @@ use bitcoin::{Address as LibAddress, Script}; use bitcoin_hashes::Hash; use secp256k1::Secp256k1; -use tcx_chain::Address; use tcx_constants::CoinInfo; +use tcx_keystore::Address; use tcx_primitive::{Ss58Codec, TypedPrivateKey, TypedPublicKey}; use crate::network::BtcKinNetwork; @@ -294,7 +294,7 @@ mod tests { use tcx_primitive::{Bip32DeterministicPrivateKey, Derive, DeterministicPrivateKey, Ss58Codec}; use crate::address::BtcKinAddress; - use crate::tcx_chain::Address; + use crate::tcx_keystore::Address; use crate::BtcKinNetwork; #[test] diff --git a/token-core/tcx-btc-kin/src/bch_address.rs b/token-core/tcx-btc-kin/src/bch_address.rs index 51903b73..67fe5b64 100644 --- a/token-core/tcx-btc-kin/src/bch_address.rs +++ b/token-core/tcx-btc-kin/src/bch_address.rs @@ -8,8 +8,8 @@ use core::result; use std::fmt::{Display, Formatter}; use std::str::FromStr; -use tcx_chain::Address; use tcx_constants::CoinInfo; +use tcx_keystore::Address; use tcx_primitive::TypedPublicKey; fn remove_bch_prefix(addr: &str) -> String { @@ -108,9 +108,9 @@ mod tests { use super::{remove_bch_prefix, BchAddress}; use crate::address::WIFDisplay; - use tcx_chain::Address; use tcx_constants::coin_info::coin_info_from_param; use tcx_constants::{CoinInfo, CurveType}; + use tcx_keystore::Address; use tcx_primitive::{PrivateKey, Secp256k1PrivateKey, TypedPublicKey}; use tcx_primitive::{PublicKey, TypedPrivateKey}; diff --git a/token-core/tcx-btc-kin/src/lib.rs b/token-core/tcx-btc-kin/src/lib.rs index f9b2179e..d2d158ce 100644 --- a/token-core/tcx-btc-kin/src/lib.rs +++ b/token-core/tcx-btc-kin/src/lib.rs @@ -19,7 +19,7 @@ extern crate num_integer; extern crate num_traits; #[macro_use] -extern crate tcx_chain; +extern crate tcx_keystore; extern crate core; pub type Result = result::Result; @@ -27,6 +27,10 @@ pub type Result = result::Result; pub use address::{BtcKinAddress, WIFDisplay}; pub use bch_address::BchAddress; pub use network::BtcKinNetwork; +use tcx_keystore::{Address, Keystore}; +use tcx_primitive::{ + Bip32DeterministicPublicKey, Derive, DeterministicPublicKey, FromHex, TypedPublicKey, +}; pub use transaction::{BtcKinTxInput, BtcKinTxOutput, OmniTxInput, Utxo}; pub const BITCOIN: &'static str = "BITCOIN"; @@ -60,8 +64,8 @@ pub enum Error { pub mod bitcoin { use crate::{BtcKinAddress, BITCOIN, LITECOIN}; - use tcx_chain::{Account, Keystore}; use tcx_constants::CoinInfo; + use tcx_keystore::{Account, Keystore}; pub const CHAINS: [&'static str; 2] = [BITCOIN, LITECOIN]; @@ -141,10 +145,35 @@ pub mod bitcoin { } } +pub fn calc_btc_change_address( + ks: &Keystore, + seg_wit: &str, + network: &str, + external_idx: u32, +) -> Result<(String, String)> { + let account = ks + .accounts() + .iter() + .find(|x| x.coin == "BITCOIN" && x.seg_wit == seg_wit && x.network == network); + let Some(acc) = account else { + return Err(format_err!("wallet_not_found")); + }; + + let xpub = acc.ext_pub_key.to_string(); + let acc_path = acc.derivation_path.to_string(); + let external_path = format!("0/{}", external_idx); + let change_path = format!("{}/{}", acc_path, external_path); + let account_xpub = Bip32DeterministicPublicKey::from_hex(&xpub)?; + let public_key = account_xpub.derive(&change_path)?.public_key(); + let typed_pk = TypedPublicKey::Secp256k1(public_key); + let address = BtcKinAddress::from_public_key(&typed_pk, &acc.coin_info())?; + Ok((address.to_string(), external_path)) +} + pub mod bitcoincash { use crate::BITCOINCASH; - use tcx_chain::{Account, Keystore}; use tcx_constants::{CoinInfo, CurveType}; + use tcx_keystore::{Account, Keystore}; pub static CHAINS: [&'static str; 1] = [BITCOINCASH]; @@ -196,10 +225,10 @@ mod tests { use serde_json::Value; - use tcx_chain::KeystoreGuard; - use tcx_chain::{HdKeystore, Keystore, Metadata}; use tcx_constants::CurveType; use tcx_constants::{CoinInfo, TEST_MNEMONIC, TEST_PASSWORD}; + use tcx_keystore::KeystoreGuard; + use tcx_keystore::{HdKeystore, Keystore, Metadata}; const BIP_PATH: &str = "m/44'/145'/0'"; diff --git a/token-core/tcx-btc-kin/src/signer.rs b/token-core/tcx-btc-kin/src/signer.rs index b3303e15..e8094058 100644 --- a/token-core/tcx-btc-kin/src/signer.rs +++ b/token-core/tcx-btc-kin/src/signer.rs @@ -19,9 +19,9 @@ use bitcoin_hashes::Hash; use byteorder::{BigEndian, WriteBytesExt}; use secp256k1::{Message, Secp256k1}; -use tcx_chain::keystore::Error as KeystoreError; -use tcx_chain::Address; -use tcx_chain::{Keystore, TransactionSigner}; +use tcx_keystore::keystore::Error as KeystoreError; +use tcx_keystore::Address; +use tcx_keystore::{Keystore, TransactionSigner}; use tcx_primitive::{Derive, PrivateKey, PublicKey, Secp256k1PrivateKey, TypedPrivateKey}; use crate::address::{BtcKinAddress, ScriptPubkey}; @@ -475,10 +475,10 @@ impl TransactionSigner for Keystore { #[cfg(test)] mod tests { - use tcx_chain::Metadata; - use tcx_chain::{Keystore, TransactionSigner}; use tcx_constants::coin_info::coin_info_from_param; use tcx_constants::{TEST_MNEMONIC, TEST_PASSWORD, TEST_WIF}; + use tcx_keystore::Metadata; + use tcx_keystore::{Keystore, TransactionSigner}; use tcx_primitive::Secp256k1PrivateKey; use crate::address::BtcKinAddress; @@ -615,8 +615,8 @@ mod tests { mod bitcoincash { use crate::{bitcoincash, BtcKinTxInput, Utxo, BITCOINCASH}; - use tcx_chain::{Keystore, Metadata, TransactionSigner}; use tcx_constants::TEST_PASSWORD; + use tcx_keystore::{Keystore, Metadata, TransactionSigner}; #[test] pub fn test_bch_signer() { diff --git a/token-core/tcx-ckb/Cargo.toml b/token-core/tcx-ckb/Cargo.toml index e171fd8a..68fd4964 100644 --- a/token-core/tcx-ckb/Cargo.toml +++ b/token-core/tcx-ckb/Cargo.toml @@ -8,7 +8,7 @@ edition = "2021" [dependencies] tcx-crypto = { path = "../tcx-crypto" } -tcx-chain = { path = "../tcx-chain" } +tcx-keystore = { path = "../tcx-keystore" } tcx-primitive = { path = "../tcx-primitive" } tcx-constants = { path = "../tcx-constants" } diff --git a/token-core/tcx-ckb/src/address.rs b/token-core/tcx-ckb/src/address.rs index d1f96012..d22cdf21 100644 --- a/token-core/tcx-ckb/src/address.rs +++ b/token-core/tcx-ckb/src/address.rs @@ -1,8 +1,8 @@ use crate::hash::blake2b_160; use bech32::{FromBase32, ToBase32, Variant}; use std::str::FromStr; -use tcx_chain::{Address, Result}; use tcx_constants::CoinInfo; +use tcx_keystore::{Address, Result}; use tcx_primitive::TypedPublicKey; // TYPE should be u5 @@ -82,7 +82,7 @@ impl ToString for CkbAddress { #[cfg(test)] mod tests { use crate::address::CkbAddress; - use tcx_chain::Address; + use tcx_keystore::Address; use tcx_constants::{CoinInfo, CurveType}; use tcx_primitive::TypedPublicKey; diff --git a/token-core/tcx-ckb/src/lib.rs b/token-core/tcx-ckb/src/lib.rs index a97e3e1a..33054a89 100644 --- a/token-core/tcx-ckb/src/lib.rs +++ b/token-core/tcx-ckb/src/lib.rs @@ -9,7 +9,7 @@ use failure::Fail; pub use address::CkbAddress; pub use serializer::Serializer; -use tcx_chain::Result; +use tcx_keystore::Result; pub use transaction::{CachedCell, CellInput, CkbTxInput, CkbTxOutput, OutPoint, Script, Witness}; #[derive(Fail, Debug, PartialEq)] @@ -59,8 +59,8 @@ pub fn hex_to_bytes(value: &str) -> Result> { } pub mod nervos { - use tcx_chain::{Account, Keystore}; use tcx_constants::{CoinInfo, CurveType}; + use tcx_keystore::{Account, Keystore}; pub const CHAINS: [&'static str; 1] = ["NERVOS"]; diff --git a/token-core/tcx-ckb/src/signer.rs b/token-core/tcx-ckb/src/signer.rs index aea47bc4..97dfe3ca 100644 --- a/token-core/tcx-ckb/src/signer.rs +++ b/token-core/tcx-ckb/src/signer.rs @@ -1,11 +1,11 @@ -use tcx_chain::{Keystore, Result, TransactionSigner}; +use tcx_keystore::{Keystore, Result, TransactionSigner}; use crate::hash::new_blake2b; use crate::serializer::Serializer; use crate::transaction::{CachedCell, CkbTxInput, CkbTxOutput, OutPoint, Witness}; use crate::{hex_to_bytes, Error}; use std::collections::HashMap; -use tcx_chain::ChainSigner; +use tcx_keystore::ChainSigner; use lazy_static::lazy_static; @@ -194,8 +194,8 @@ impl TransactionSigner for Keystore { mod tests { use crate::address::CkbAddress; use crate::transaction::{CachedCell, CellInput, CkbTxInput, OutPoint, Script, Witness}; - use tcx_chain::{Keystore, Metadata, TransactionSigner}; use tcx_constants::{CoinInfo, CurveType}; + use tcx_keystore::{Keystore, Metadata, TransactionSigner}; #[test] fn test_sign_transaction() { diff --git a/token-core/tcx-ckb/src/transaction_helper.rs b/token-core/tcx-ckb/src/transaction_helper.rs index afbbe7b5..6cc87bb8 100644 --- a/token-core/tcx-ckb/src/transaction_helper.rs +++ b/token-core/tcx-ckb/src/transaction_helper.rs @@ -4,7 +4,7 @@ use crate::transaction::{Script, Witness}; use super::Error; use crate::hash::blake2b_256; use crate::hex_to_bytes; -use tcx_chain::Result; +use tcx_keystore::Result; impl Script { pub fn serialize_hash_type(&self) -> Result> { diff --git a/token-core/tcx-common/src/lib.rs b/token-core/tcx-common/src/lib.rs index 2bf76d97..ac1d5bc9 100644 --- a/token-core/tcx-common/src/lib.rs +++ b/token-core/tcx-common/src/lib.rs @@ -1,6 +1,7 @@ mod hash; mod rand; +mod errors; mod time; pub use crate::errors::*; @@ -9,7 +10,6 @@ pub use crate::rand::*; pub use crate::time::*; pub use crate::util::*; -mod errors; pub mod util; use std::result; diff --git a/token-core/tcx-crypto/src/crypto.rs b/token-core/tcx-crypto/src/crypto.rs index d8bed6a9..9a8d585b 100644 --- a/token-core/tcx-crypto/src/crypto.rs +++ b/token-core/tcx-crypto/src/crypto.rs @@ -230,6 +230,7 @@ impl Crypto { match key { Key::Password(password) => { let derived_key = self.derive_key(password)?; + if self.mac != "" && !self.verify_derived_key(&derived_key) { return Err(Error::PasswordIncorrect.into()); } @@ -239,10 +240,10 @@ impl Crypto { derived_key, }) } - Key::DerivedKey(derived_key) => { - let derived_key = hex::decode(derived_key)?; + Key::DerivedKey(derived_key_hex) => { + let derived_key = hex::decode(derived_key_hex)?; - if !self.verify_derived_key(&hex::decode(&derived_key)?) { + if !self.verify_derived_key(&derived_key) { return Err(Error::PasswordIncorrect.into()); } diff --git a/token-core/tcx-docs/TECH.zh.md b/token-core/tcx-docs/TECH.zh.md index 9c2b0a64..a113b81a 100644 --- a/token-core/tcx-docs/TECH.zh.md +++ b/token-core/tcx-docs/TECH.zh.md @@ -291,8 +291,8 @@ fn _sign_btc_fork_transaction(json: &str, keystore: &HdKeystore, password: &str) ## 新链接入(简略版) 1. 添加一个新的package,命名为`tcx-blockchain`其中的blockchain用链的名称替换,将如下代码写入该package里面 2. 定义CoinInfo, CoinInfo可以指定你要添加的链的名称,所在的曲线以及助记词的派生路径。 -3. 可选)如果你所添加的链包含有特殊的需要保存的信息,你需要实现你自己的`tcx_chain::Extra`。该接口用于根据`CoinInfo`和`Seed`生成特定信息,最终该信息会被序列化成JSON数据存储在account中的extra字段内。 -4. 实现tcx_chain::Address,添加你所在的链的地址推导方法,分别添加判断地址是否有效,根据pub_key生成地址 -5. 实现tcx_chain::Transaction,添加你签名是需要输入的字段, 实现tcx_chain::SignedTransaction,添加你签名结束后的输出内容。如果的的签名结果比较简单,你可以使用tcx默认提供的TxSignResult结构。 -6. 实现tcx_chain::TransactionSigner,添加你所在的链特定的签名算法 +3. 可选)如果你所添加的链包含有特殊的需要保存的信息,你需要实现你自己的`tcx_keystore::Extra`。该接口用于根据`CoinInfo`和`Seed`生成特定信息,最终该信息会被序列化成JSON数据存储在account中的extra字段内。 +4. 实现tcx_keystore::Address,添加你所在的链的地址推导方法,分别添加判断地址是否有效,根据pub_key生成地址 +5. 实现tcx_keystore::Transaction,添加你签名是需要输入的字段, 实现tcx_keystore::SignedTransaction,添加你签名结束后的输出内容。如果的的签名结果比较简单,你可以使用tcx默认提供的TxSignResult结构。 +6. 实现tcx_keystore::TransactionSigner,添加你所在的链特定的签名算法 7. 在tcx层添加match pattern 指向正确的处理逻辑 diff --git a/token-core/tcx-eos/Cargo.toml b/token-core/tcx-eos/Cargo.toml index 9e582e1a..33767e25 100644 --- a/token-core/tcx-eos/Cargo.toml +++ b/token-core/tcx-eos/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [dependencies] tcx-crypto = { path = "../tcx-crypto" } -tcx-chain = { path = "../tcx-chain" } +tcx-keystore = { path = "../tcx-keystore" } tcx-primitive = { path = "../tcx-primitive" } tcx-constants = { path = "../tcx-constants" } tcx-common = {path = "../tcx-common"} diff --git a/token-core/tcx-eos/src/address.rs b/token-core/tcx-eos/src/address.rs index f146ecc3..ccbf3486 100644 --- a/token-core/tcx-eos/src/address.rs +++ b/token-core/tcx-eos/src/address.rs @@ -1,10 +1,10 @@ use base58::{FromBase58, ToBase58}; use regex::Regex; use std::str::FromStr; -use tcx_chain::{Address, ChainFactory, PublicKeyEncoder, Result}; use tcx_common::CommonError; use tcx_constants::CoinInfo; use tcx_crypto::hash; +use tcx_keystore::{Address, ChainFactory, PublicKeyEncoder, Result}; use tcx_primitive::{PublicKey, TypedPublicKey}; #[derive(PartialEq, Eq, Clone)] @@ -69,7 +69,7 @@ impl ToString for EosAddress { mod tests { use crate::address::EosAddress; use std::str::FromStr; - use tcx_chain::{Address, ChainFactory, PublicKeyEncoder}; + use tcx_keystore::{Address, ChainFactory, PublicKeyEncoder}; use tcx_constants::{CoinInfo, CurveType}; use tcx_primitive::{ diff --git a/token-core/tcx-eos/src/chain_factory.rs b/token-core/tcx-eos/src/chain_factory.rs index c8e7df8a..2fc275d3 100644 --- a/token-core/tcx-eos/src/chain_factory.rs +++ b/token-core/tcx-eos/src/chain_factory.rs @@ -1,7 +1,7 @@ use base58::{FromBase58, ToBase58}; use failure::format_err; -use tcx_chain::{tcx_ensure, ChainFactory, PrivateKeyEncoder, PublicKeyEncoder, Result}; use tcx_crypto::hash; +use tcx_keystore::{tcx_ensure, ChainFactory, PrivateKeyEncoder, PublicKeyEncoder, Result}; use tcx_primitive::{PrivateKey, PublicKey, Secp256k1PrivateKey, Secp256k1PublicKey, Ss58Codec}; pub struct EosPublicKeyEncoder(); @@ -51,7 +51,7 @@ impl ChainFactory for EosChainFactory { mod tests { use crate::{address::EosAddress, EosChainFactory}; - use tcx_chain::{ + use tcx_keystore::{ ChainFactory, HdKeystore, Keystore, Metadata, PrivateKeyEncoder, PublicKeyEncoder, }; diff --git a/token-core/tcx-eos/src/lib.rs b/token-core/tcx-eos/src/lib.rs index 4e64bdf5..fd5b104c 100644 --- a/token-core/tcx-eos/src/lib.rs +++ b/token-core/tcx-eos/src/lib.rs @@ -5,8 +5,8 @@ pub mod transaction; pub use chain_factory::EosChainFactory; pub mod eos { - use tcx_chain::{Account, Keystore}; use tcx_constants::{CoinInfo, CurveType}; + use tcx_keystore::{Account, Keystore}; pub const CHAINS: [&'static str; 1] = ["EOS"]; diff --git a/token-core/tcx-eos/src/signer.rs b/token-core/tcx-eos/src/signer.rs index 596d07db..a8453914 100644 --- a/token-core/tcx-eos/src/signer.rs +++ b/token-core/tcx-eos/src/signer.rs @@ -1,6 +1,6 @@ use crate::transaction::{EosMessageInput, EosMessageOutput, EosTxInput, EosTxOutput, SigData}; use base58::ToBase58; -use tcx_chain::{ +use tcx_keystore::{ ChainSigner, Keystore, MessageSigner, Result, TransactionSigner as TraitTransactionSigner, }; diff --git a/token-core/tcx-eth/Cargo.toml b/token-core/tcx-eth/Cargo.toml index 3af08d4f..bd7c6754 100644 --- a/token-core/tcx-eth/Cargo.toml +++ b/token-core/tcx-eth/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -tcx-chain = {path = "../tcx-chain"} +tcx-keystore = {path = "../tcx-keystore"} tcx-primitive = {path = "../tcx-primitive"} tcx-constants = {path = "../tcx-constants"} tcx-common = {path = "../tcx-common"} diff --git a/token-core/tcx-eth/src/address.rs b/token-core/tcx-eth/src/address.rs index dadb3335..5a4a9d42 100644 --- a/token-core/tcx-eth/src/address.rs +++ b/token-core/tcx-eth/src/address.rs @@ -1,22 +1,23 @@ use crate::Result; -use ethers::utils::ConversionError::InvalidAddressChecksum; +use ethereum_types::H160; use failure::format_err; use regex::Regex; use std::str::FromStr; -use tcx_chain::Address; use tcx_common::keccak256; use tcx_constants::CoinInfo; +use tcx_keystore::Address; use tcx_primitive::TypedPublicKey; #[derive(PartialEq, Eq, Clone)] -pub struct EthAddress(Vec); +pub struct EthAddress(H160); impl Address for EthAddress { fn from_public_key(public_key: &TypedPublicKey, _coin: &CoinInfo) -> Result { let bytes = public_key.as_secp256k1()?.to_uncompressed(); let pubkey_hash = keccak256(bytes[1..].as_ref()); let addr_bytes = pubkey_hash[12..].to_vec(); - Ok(EthAddress(addr_bytes)) + let addr = H160::from_slice(&addr_bytes); + Ok(EthAddress(addr)) } fn is_valid(address: &str, _coin: &CoinInfo) -> bool { @@ -26,7 +27,7 @@ impl Address for EthAddress { impl ToString for EthAddress { fn to_string(&self) -> String { - format!("0x{}", hex::encode(&self.0)) + ethers::utils::to_checksum(&self.0, None) } } @@ -38,7 +39,8 @@ impl FromStr for EthAddress { } let bytes = hex::decode(&s[2..])?; - Ok(EthAddress(bytes)) + let addr = H160::from_slice(&bytes); + Ok(EthAddress(addr)) } } @@ -58,7 +60,6 @@ pub fn is_valid_address(address: &str) -> bool { let address = &address[2..]; let lower_address_bytes = address.to_lowercase(); - let mut hash = [0u8; 32]; let hash = keccak256(lower_address_bytes.as_bytes()); let hash_str = hex::encode(hash); @@ -76,8 +77,8 @@ pub fn is_valid_address(address: &str) -> bool { #[cfg(test)] mod test { use crate::address::EthAddress; - use tcx_chain::Address; use tcx_constants::{CoinInfo, CurveType}; + use tcx_keystore::Address; use tcx_primitive::{PrivateKey, Secp256k1PrivateKey, TypedPrivateKey}; #[test] @@ -85,10 +86,10 @@ mod test { let private_key_bytes = hex::decode("a392604efc2fad9c0b3da43b5f698a2e3f270f170d859912be0d54742275c5f6") .unwrap(); - let mut secp256k1_privateKey = + let mut secp256k1_private_key = Secp256k1PrivateKey::from_slice(private_key_bytes.as_slice()).unwrap(); - secp256k1_privateKey.0.compressed = false; - let typed_public_key = TypedPrivateKey::Secp256k1(secp256k1_privateKey).public_key(); + secp256k1_private_key.0.compressed = false; + let typed_public_key = TypedPrivateKey::Secp256k1(secp256k1_private_key).public_key(); let coin_info = CoinInfo { coin: "ETHEREUM".to_string(), derivation_path: "m/44'/60'/0'/0/0".to_string(), @@ -99,7 +100,7 @@ mod test { let address = EthAddress::from_public_key(&typed_public_key, &coin_info).unwrap(); assert_eq!( address.to_string(), - "0xef678007d18427e6022059dbc264f27507cd1ffc" + "0xef678007D18427E6022059Dbc264f27507CD1ffC" ); let is_valid = diff --git a/token-core/tcx-eth/src/lib.rs b/token-core/tcx-eth/src/lib.rs index 26e88438..3932b1d2 100644 --- a/token-core/tcx-eth/src/lib.rs +++ b/token-core/tcx-eth/src/lib.rs @@ -8,8 +8,8 @@ pub type Result = result::Result; pub mod ethereum { use crate::address::EthAddress; - use tcx_chain::{Account, Keystore}; use tcx_constants::{CoinInfo, CurveType}; + use tcx_keystore::{Account, Keystore}; pub const CHAINS: [&'static str; 1] = ["ETHEREUM"]; diff --git a/token-core/tcx-eth/src/signer.rs b/token-core/tcx-eth/src/signer.rs index 78cf74f2..95244f16 100644 --- a/token-core/tcx-eth/src/signer.rs +++ b/token-core/tcx-eth/src/signer.rs @@ -11,8 +11,8 @@ use ethers::types::{Bytes, Eip1559TransactionRequest, Signature, TransactionRequ use ethers::utils::{hash_message, keccak256}; use keccak_hash::keccak; use std::str::FromStr; -use tcx_chain::{Keystore, MessageSigner, TransactionSigner}; use tcx_common::{hex_to_bytes, utf8_or_hex_to_bytes}; +use tcx_keystore::{Keystore, MessageSigner, TransactionSigner}; impl TransactionSigner for Keystore { fn sign_transaction( @@ -20,7 +20,7 @@ impl TransactionSigner for Keystore { symbol: &str, address: &str, tx: &EthTxInput, - ) -> tcx_chain::Result { + ) -> tcx_keystore::Result { let private_key = self.find_private_key(symbol, address)?; tx.sign_transaction(&private_key.to_bytes()) } @@ -32,7 +32,7 @@ impl MessageSigner for Keystore { symbol: &str, address: &str, message: &EthMessageInput, - ) -> tcx_chain::Result { + ) -> tcx_keystore::Result { let private_key = self.find_private_key(symbol, address)?; if message.signature_type == SignatureType::PersonalSign as i32 { message.sign_message(&private_key.to_bytes()) @@ -174,7 +174,7 @@ mod test { use crate::transaction::{ AccessList, EthMessageInput, EthMessageOutput, EthTxInput, EthTxOutput, SignatureType, }; - use tcx_chain::{Keystore, MessageSigner, Metadata, TransactionSigner}; + use tcx_keystore::{Keystore, MessageSigner, Metadata, TransactionSigner}; fn private_key_store(key: &str) -> Keystore { let mut ks = Keystore::from_private_key(key, "imToken1", Metadata::default()); diff --git a/token-core/tcx-eth2/Cargo.toml b/token-core/tcx-eth2/Cargo.toml index bd8a51c9..cd0788c2 100644 --- a/token-core/tcx-eth2/Cargo.toml +++ b/token-core/tcx-eth2/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -tcx-chain = { path = "../tcx-chain" } +tcx-keystore = { path = "../tcx-keystore" } tcx-constants = { path = "../tcx-constants" } tcx-primitive = { path = "../tcx-primitive" } tcx-crypto = { path = "../tcx-crypto" } diff --git a/token-core/tcx-eth2/src/address.rs b/token-core/tcx-eth2/src/address.rs index deb8cec2..3299ba4b 100644 --- a/token-core/tcx-eth2/src/address.rs +++ b/token-core/tcx-eth2/src/address.rs @@ -1,7 +1,7 @@ use std::str::FromStr; -use tcx_chain::Address; -use tcx_chain::Result; use tcx_constants::CoinInfo; +use tcx_keystore::Address; +use tcx_keystore::Result; use tcx_primitive::TypedPublicKey; #[derive(PartialEq, Eq, Clone)] @@ -47,8 +47,8 @@ impl FromStr for Eth2Address { #[cfg(test)] mod test { use crate::address::Eth2Address; - use tcx_chain::Address; use tcx_constants::{CoinInfo, CurveType}; + use tcx_keystore::Address; #[test] fn is_valid_test() { diff --git a/token-core/tcx-eth2/src/lib.rs b/token-core/tcx-eth2/src/lib.rs index f3f4543e..535fe1e6 100644 --- a/token-core/tcx-eth2/src/lib.rs +++ b/token-core/tcx-eth2/src/lib.rs @@ -5,7 +5,7 @@ mod bls_to_execution_change; pub mod signer; pub mod transaction; use failure::Fail; -use tcx_chain::Result; +use tcx_keystore::Result; #[derive(Fail, Debug, PartialEq)] pub enum Error { @@ -26,8 +26,8 @@ pub fn hex_to_bytes(value: &str) -> Result> { pub mod ethereum2 { use crate::address::Eth2Address; - use tcx_chain::{Account, Keystore}; use tcx_constants::{CoinInfo, CurveType}; + use tcx_keystore::{Account, Keystore}; pub const CHAINS: [&'static str; 1] = ["ETHEREUM2"]; diff --git a/token-core/tcx-eth2/src/signer.rs b/token-core/tcx-eth2/src/signer.rs index f7782567..5f53bc1c 100644 --- a/token-core/tcx-eth2/src/signer.rs +++ b/token-core/tcx-eth2/src/signer.rs @@ -6,7 +6,7 @@ use crate::transaction::{ use crate::{Error, Result}; use keccak_hash; use regex::Regex; -use tcx_chain::{ChainSigner, Keystore}; +use tcx_keystore::{ChainSigner, Keystore}; impl SignBlsToExecutionChangeParam { pub fn sign_bls_to_execution_change( diff --git a/token-core/tcx-filecoin/Cargo.toml b/token-core/tcx-filecoin/Cargo.toml index 644dcefa..ac0d2fc3 100644 --- a/token-core/tcx-filecoin/Cargo.toml +++ b/token-core/tcx-filecoin/Cargo.toml @@ -8,7 +8,7 @@ edition = "2021" [dependencies] tcx-crypto = { path = "../tcx-crypto" } -tcx-chain = { path = "../tcx-chain" } +tcx-keystore = { path = "../tcx-keystore" } tcx-primitive = { path = "../tcx-primitive" } tcx-constants = { path = "../tcx-constants" } diff --git a/token-core/tcx-filecoin/src/address.rs b/token-core/tcx-filecoin/src/address.rs index de198722..b4c57858 100644 --- a/token-core/tcx-filecoin/src/address.rs +++ b/token-core/tcx-filecoin/src/address.rs @@ -1,5 +1,5 @@ -use tcx_chain::{Address, Result}; use tcx_constants::CoinInfo; +use tcx_keystore::{Address, Result}; use tcx_primitive::{PublicKey, TypedPublicKey}; use forest_address::Address as ForestAddress; @@ -104,8 +104,8 @@ impl ToString for FilecoinAddress { #[cfg(test)] mod tests { use crate::address::FilecoinAddress; - use tcx_chain::{Address, Keystore, Metadata}; use tcx_constants::{coin_info_from_param, CoinInfo, CurveType}; + use tcx_keystore::{Address, Keystore, Metadata}; use tcx_primitive::TypedPublicKey; #[test] diff --git a/token-core/tcx-filecoin/src/key_info.rs b/token-core/tcx-filecoin/src/key_info.rs index a258a5bc..404723ba 100644 --- a/token-core/tcx-filecoin/src/key_info.rs +++ b/token-core/tcx-filecoin/src/key_info.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -use tcx_chain::Result; use tcx_constants::CurveType; +use tcx_keystore::Result; use super::Error; diff --git a/token-core/tcx-filecoin/src/lib.rs b/token-core/tcx-filecoin/src/lib.rs index 5a202c40..e506bb24 100644 --- a/token-core/tcx-filecoin/src/lib.rs +++ b/token-core/tcx-filecoin/src/lib.rs @@ -34,8 +34,8 @@ pub enum Error { } pub mod filecoin { - use tcx_chain::{Account, Keystore}; use tcx_constants::{CoinInfo, CurveType}; + use tcx_keystore::{Account, Keystore}; pub static CHAINS: [&'static str; 1] = ["FILECOIN"]; diff --git a/token-core/tcx-filecoin/src/signer.rs b/token-core/tcx-filecoin/src/signer.rs index 4fa2bb32..acc7714c 100644 --- a/token-core/tcx-filecoin/src/signer.rs +++ b/token-core/tcx-filecoin/src/signer.rs @@ -9,8 +9,8 @@ use forest_vm::Serialized; use num_bigint_chainsafe::BigInt; use std::convert::TryFrom; use std::str::FromStr; -use tcx_chain::{ChainSigner, Keystore, Result, TransactionSigner}; use tcx_constants::CurveType; +use tcx_keystore::{ChainSigner, Keystore, Result, TransactionSigner}; impl TryFrom<&UnsignedMessage> for ForestUnsignedMessage { type Error = crate::Error; @@ -107,8 +107,8 @@ impl TransactionSigner for Keystore { #[cfg(test)] mod tests { use crate::{FilecoinAddress, KeyInfo, UnsignedMessage}; - use tcx_chain::{Keystore, Metadata, TransactionSigner}; use tcx_constants::{CoinInfo, CurveType}; + use tcx_keystore::{Keystore, Metadata, TransactionSigner}; #[test] fn test_sign_spec256k1() { diff --git a/token-core/tcx-identity/Cargo.toml b/token-core/tcx-identity/Cargo.toml index 0ffbce61..274e99b1 100644 --- a/token-core/tcx-identity/Cargo.toml +++ b/token-core/tcx-identity/Cargo.toml @@ -10,7 +10,7 @@ tcx-primitive = { path = "../tcx-primitive" } tcx-crypto = { path = "../tcx-crypto"} tcx-constants = { path = "../tcx-constants"} #tcx-eth = {path = "../tcx-eth"} -tcx-chain = {path = "../tcx-chain"} +# tcx-keystore = {path = "../tcx-keystore"} tcx-common = {path = "../tcx-common"} failure = "0.1.8" diff --git a/token-core/tcx-identity/src/identity.rs b/token-core/tcx-identity/src/identity.rs index cac3766e..4ba07e26 100644 --- a/token-core/tcx-identity/src/identity.rs +++ b/token-core/tcx-identity/src/identity.rs @@ -1,7 +1,7 @@ -use crate::constants::{CHAIN_TYPE_ETHEREUM, ETHEREUM_PATH}; -use crate::imt_keystore::{IMTKeystore, WALLETS, WALLET_KEYSTORE_DIR}; -use crate::model::{Metadata, FROM_NEW_IDENTITY, FROM_RECOVERED_IDENTITY}; -use crate::wallet_api::{CreateIdentityParam, RecoverIdentityParam}; +// use crate::constants::{CHAIN_TYPE_ETHEREUM, ETHEREUM_PATH}; +// use crate::imt_keystore::{IMTKeystore, WALLETS, WALLET_KEYSTORE_DIR}; +// use crate::model::{Metadata, FROM_NEW_IDENTITY, FROM_RECOVERED_IDENTITY}; +// use crate::wallet_api::{CreateIdentityParam, RecoverIdentityParam}; use crate::Error; use crate::Result; use bip39::{Language, Mnemonic, Seed}; @@ -27,44 +27,47 @@ use std::path::Path; use std::time::{SystemTime, UNIX_EPOCH}; use tcx_common::{keccak256, merkle_hash, random_u8_16, sha256, unix_timestamp}; use tcx_crypto::aes::cbc::{decrypt_pkcs7, encrypt_pkcs7}; +use tcx_crypto::crypto::Unlocker; use tcx_crypto::{Crypto, EncPair, Key}; use tcx_primitive::{PrivateKey as TraitPrivateKey, Secp256k1PrivateKey}; use uuid::Uuid; -lazy_static! { - pub static ref IDENTITY_KEYSTORE: RwLock = - RwLock::new(IdentityKeystore::default()); -} +// lazy_static! { +// pub static ref IDENTITY_KEYSTORE: RwLock = +// RwLock::new(IdentityKeystore::default()); +// } pub const IDENTITY_KEYSTORE_FILE_NAME: &'static str = "identity.json"; pub const VERSION: u32 = 1000; +// #[derive(Debug, Deserialize, Serialize, Clone, Default)] +// #[serde(rename_all = "camelCase")] +// pub struct IdentityKeystore { +// pub crypto: Crypto, +// pub id: String, +// pub version: u32, +// pub enc_auth_key: EncPair, +// pub enc_key: String, +// pub enc_mnemonic: EncPair, +// pub identifier: String, +// pub ipfs_id: String, +// #[serde(rename = "walletIDs")] +// pub wallet_ids: Vec, +// pub im_token_meta: Metadata, +// } + #[derive(Debug, Deserialize, Serialize, Clone, Default)] #[serde(rename_all = "camelCase")] -pub struct IdentityKeystore { - pub crypto: Crypto, - pub id: String, - pub version: u32, +pub struct Identity { pub enc_auth_key: EncPair, pub enc_key: String, - pub enc_mnemonic: EncPair, pub identifier: String, pub ipfs_id: String, - #[serde(rename = "walletIDs")] - pub wallet_ids: Vec, - pub im_token_meta: Metadata, } -impl IdentityKeystore { - pub fn new( - metadata: Metadata, - password: &str, - mnemonic_phrase: &str, - ) -> Result { - let network_type = match metadata.is_main_net() { - true => Network::Bitcoin, - _ => Network::Testnet, - }; +impl Identity { + pub fn new(mnemonic_phrase: &str, unlocker: &Unlocker) -> Result { + let network_type = Network::Bitcoin; let validate_result = Mnemonic::validate(mnemonic_phrase, Language::English); if validate_result.is_err() { @@ -74,10 +77,8 @@ impl IdentityKeystore { let seed = Seed::new(&mnemonic, ""); let master_key = ExtendedPrivKey::new_master(network_type, seed.as_ref())?; - let salt = match metadata.is_main_net() { - true => "Automatic Backup Key Mainnet", - _ => "Automatic Backup Key Testnet", - }; + let salt = "Automatic Backup Key Mainnet"; + let backup_key = HMAC::mac(salt.as_bytes(), master_key.private_key.secret_bytes()); let authentication_key = HMAC::mac("Authentication Key".as_bytes(), backup_key); @@ -86,10 +87,7 @@ impl IdentityKeystore { let secp = Secp256k1::new(); let auth_pubkey_hash = auth_private_key.public_key(&secp).pubkey_hash(); - let network_header = match metadata.is_main_net() { - true => 0, - _ => 111, - }; + let network_header = 0; let version = 2; let magic_hex = "0fdc0c"; @@ -111,28 +109,33 @@ impl IdentityKeystore { let ipfs_id = base58::encode_slice(&multihash.to_bytes()); let master_prikey_bytes = master_key.encode(); - let master_prikey_bytes = base58::check_encode_slice(master_prikey_bytes.as_slice()); + // let master_prikey_bytes = base58::check_encode_slice(master_prikey_bytes.as_slice()); - let mut crypto: Crypto = Crypto::new(password, master_prikey_bytes.as_bytes()); - let unlocker = crypto.use_key(&Key::Password(password.to_string()))?; + // let mut crypto: Crypto = Crypto::new(password, master_prikey_bytes.as_bytes()); + // let unlocker = crypto.use_key(&Key::Password(password.to_string()))?; let enc_auth_key = unlocker.encrypt_with_random_iv(authentication_key.as_slice())?; - let enc_mnemonic = unlocker.encrypt_with_random_iv(mnemonic.phrase().as_bytes())?; - - let identity_keystore = IdentityKeystore { - crypto, - id: Uuid::new_v4().as_hyphenated().to_string(), - version: VERSION, + // let enc_mnemonic = unlocker.encrypt_with_random_iv(mnemonic.phrase().as_bytes())?; + + // let identity_keystore = IdentityKeystore { + // crypto, + // id: Uuid::new_v4().as_hyphenated().to_string(), + // version: VERSION, + // enc_auth_key, + // enc_key: hex::encode(enc_key_bytes), + // enc_mnemonic, + // identifier, + // ipfs_id, + // wallet_ids: vec![], + // im_token_meta: metadata, + // }; + + Ok(Identity { enc_auth_key, enc_key: hex::encode(enc_key_bytes), - enc_mnemonic, identifier, ipfs_id, - wallet_ids: vec![], - im_token_meta: metadata, - }; - - Ok(identity_keystore) + }) } pub fn calculate_ipfs_id(pub_key: &PublicKey) -> String { @@ -140,47 +143,47 @@ impl IdentityKeystore { base58::encode_slice(&multihash.to_bytes()) } - pub fn to_json(&self) -> Result { - Ok(serde_json::to_string(&self)?) - } - - pub fn cache(&self) { - let mut identity_keystore_obj = IDENTITY_KEYSTORE.write(); - *identity_keystore_obj = self.to_owned(); - } - - pub fn flush(&self) -> Result<()> { - let json = self.to_json()?; - let file_dir = WALLET_KEYSTORE_DIR.read(); - let ks_path = format!("{}/{}", file_dir, IDENTITY_KEYSTORE_FILE_NAME); - let path = Path::new(&ks_path); - let mut file = fs::File::create(path)?; - let _ = file.write_all(&json.as_bytes()); - Ok(()) - } - - pub fn export_identity(&self, password: &str) -> Result { - let decrypt_data = self - .crypto - .use_key(&Key::Password(password.to_string()))? - .plaintext()?; - Ok(String::from_utf8(decrypt_data)?) - } - - pub fn delete_identity(&self, password: &str) -> Result<()> { - if !self.crypto.verify_password(password) { - return Err(Error::WalletInvalidPassword.into()); - } - - let clean_wallet_keystore_result = IMTKeystore::clean_keystore_dir(); - if clean_wallet_keystore_result.is_ok() { - IMTKeystore::clear_keystore_map(); - let mut identity_keystore = IDENTITY_KEYSTORE.write(); - *identity_keystore = IdentityKeystore::default(); - } - - Ok(()) - } + // pub fn to_json(&self) -> Result { + // Ok(serde_json::to_string(&self)?) + // } + + // pub fn cache(&self) { + // let mut identity_keystore_obj = IDENTITY_KEYSTORE.write(); + // *identity_keystore_obj = self.to_owned(); + // } + + // pub fn flush(&self) -> Result<()> { + // let json = self.to_json()?; + // let file_dir = WALLET_KEYSTORE_DIR.read(); + // let ks_path = format!("{}/{}", file_dir, IDENTITY_KEYSTORE_FILE_NAME); + // let path = Path::new(&ks_path); + // let mut file = fs::File::create(path)?; + // let _ = file.write_all(&json.as_bytes()); + // Ok(()) + // } + + // pub fn export_identity(&self, password: &str) -> Result { + // let decrypt_data = self + // .crypto + // .use_key(&Key::Password(password.to_string()))? + // .plaintext()?; + // Ok(String::from_utf8(decrypt_data)?) + // } + + // pub fn delete_identity(&self, password: &str) -> Result<()> { + // if !self.crypto.verify_password(password) { + // return Err(Error::WalletInvalidPassword.into()); + // } + + // let clean_wallet_keystore_result = IMTKeystore::clean_keystore_dir(); + // if clean_wallet_keystore_result.is_ok() { + // IMTKeystore::clear_keystore_map(); + // let mut identity_keystore = IDENTITY_KEYSTORE.write(); + // *identity_keystore = IdentityKeystore::default(); + // } + + // Ok(()) + // } pub fn encrypt_ipfs(&self, plaintext: &str) -> Result { let iv: [u8; 16] = random_u8_16(); @@ -275,9 +278,9 @@ impl IdentityKeystore { &self, access_time: u64, device_token: &str, - password: &str, + unlocker: &Unlocker, ) -> Result { - let unlocker = self.crypto.use_key(&Key::Password(password.to_string()))?; + // let unlocker = self.crypto.use_key(&Key::Password(password.to_string()))?; let enc_auth_key = unlocker.decrypt_enc_pair(&self.enc_auth_key)?; let mut signature = Secp256k1PrivateKey::from_slice(&enc_auth_key)?.sign_recoverable( &keccak256(format!("{}.{}.{}", access_time, self.identifier, device_token).as_bytes()), @@ -286,243 +289,243 @@ impl IdentityKeystore { Ok(format!("0x{}", signature.to_hex())) } - pub fn get_wallets(&self) -> Result> { - let ids = &self.wallet_ids; - let dir = WALLET_KEYSTORE_DIR.read(); - - let mut wallets = WALLETS.write(); - let mut ret_wallets = Vec::new(); - let mut keystore_context = String::new(); - for id in ids { - if wallets.get(id.as_str()).is_some() { - ret_wallets.push(wallets.get(id.as_str()).unwrap().clone()); - continue; - } - - let path_str = format!("{}/{}.json", dir.as_str(), id); - let path = Path::new(path_str.as_str()); - if !path.exists() { - return Err(Error::KeystoreFileNotExist.into()); - } - let mut file = File::open(&path)?; - file.read_to_string(&mut keystore_context)?; - let imt_keystore: IMTKeystore = serde_json::from_str(keystore_context.as_str())?; - ret_wallets.push(imt_keystore.to_owned()); - wallets.insert(id.to_string(), imt_keystore); - } - Ok(ret_wallets) - } + // pub fn get_wallets(&self) -> Result> { + // let ids = &self.wallet_ids; + // let dir = WALLET_KEYSTORE_DIR.read(); + + // let mut wallets = WALLETS.write(); + // let mut ret_wallets = Vec::new(); + // let mut keystore_context = String::new(); + // for id in ids { + // if wallets.get(id.as_str()).is_some() { + // ret_wallets.push(wallets.get(id.as_str()).unwrap().clone()); + // continue; + // } + + // let path_str = format!("{}/{}.json", dir.as_str(), id); + // let path = Path::new(path_str.as_str()); + // if !path.exists() { + // return Err(Error::KeystoreFileNotExist.into()); + // } + // let mut file = File::open(&path)?; + // file.read_to_string(&mut keystore_context)?; + // let imt_keystore: IMTKeystore = serde_json::from_str(keystore_context.as_str())?; + // ret_wallets.push(imt_keystore.to_owned()); + // wallets.insert(id.to_string(), imt_keystore); + // } + // Ok(ret_wallets) + // } } -pub struct Identity(); - -impl Identity { - pub fn get_current_identity() -> Result { - let mut identity_keystore_obj = IDENTITY_KEYSTORE.write(); - if !identity_keystore_obj.id.is_empty() { - return Ok(identity_keystore_obj.to_owned()); - } - let dir = WALLET_KEYSTORE_DIR.read(); - let path_str = format!("{}/{}", dir.as_str(), IDENTITY_KEYSTORE_FILE_NAME); - let path = Path::new(path_str.as_str()); - if !path.exists() { - return Err(Error::KeystoreFileNotExist.into()); - } - let mut file = File::open(&path)?; - let mut keystore_context = String::new(); - file.read_to_string(&mut keystore_context)?; - let identity_keystore: IdentityKeystore = serde_json::from_str(keystore_context.as_str())?; - *identity_keystore_obj = identity_keystore.to_owned(); - - Ok(identity_keystore) - } - - pub fn create_identity(param: CreateIdentityParam) -> Result { - let name = param.name.as_str(); - let password = param.password.as_str(); - let password_hint = param.password_hint; - let network = param.network.as_str(); - let seg_wit = param.seg_wit.as_deref(); - let mnemonic_phrase = tcx_primitive::generate_mnemonic(); - - let metadata = Metadata::new( - name, - password_hint.clone(), - FROM_NEW_IDENTITY, - network, - None, - seg_wit, - )?; - let mut identity_keystore = - IdentityKeystore::new(metadata.clone(), password, mnemonic_phrase.as_str())?; - - let eth_keystore = Self::derive_ethereum_wallet( - password_hint, - FROM_NEW_IDENTITY, - mnemonic_phrase.as_str(), - password, - )?; - - identity_keystore.wallet_ids.push(eth_keystore.id); - identity_keystore.flush()?; - identity_keystore.cache(); - - Ok(identity_keystore) - } - - pub fn recover_identity(param: RecoverIdentityParam) -> Result { - let name = param.name.as_str(); - let password = param.password.as_str(); - let password_hint = param.password_hint; - let network = param.network.as_str(); - let seg_wit = param.seg_wit.as_deref(); - let mnemonic_phrase = param.mnemonic.as_str(); - let metadata = Metadata::new( - name, - password_hint.clone(), - FROM_RECOVERED_IDENTITY, - network, - None, - seg_wit, - )?; - let mut identity_keystore = IdentityKeystore::new(metadata, password, mnemonic_phrase)?; - let eth_keystore = Self::derive_ethereum_wallet( - password_hint, - FROM_RECOVERED_IDENTITY, - mnemonic_phrase, - password, - )?; - - identity_keystore.wallet_ids.push(eth_keystore.id); - identity_keystore.flush()?; - identity_keystore.cache(); - - Ok(identity_keystore) - } - - fn derive_ethereum_wallet( - password_hint: Option, - source: &str, - mnemonic_phrase: &str, - password: &str, - ) -> Result { - let mut metadata = Metadata::default(); - metadata.chain_type = Some(CHAIN_TYPE_ETHEREUM.to_string()); - metadata.password_hint = password_hint; - metadata.source = source.to_string(); - metadata.name = "ETH".to_string(); - let imt_keystore = IMTKeystore::create_v3_mnemonic_keystore( - &mut metadata, - password, - mnemonic_phrase, - ETHEREUM_PATH, - )?; - imt_keystore.create_wallet()?; - Ok(imt_keystore) - } -} - -#[cfg(test)] -mod test { - use crate::identity::Identity; - use crate::model::FROM_NEW_IDENTITY; - use crate::wallet_api::{CreateIdentityParam, RecoverIdentityParam}; - use tcx_constants::sample_key::{MNEMONIC, NAME, PASSWORD, PASSWORD_HINT}; - use tcx_constants::{sample_key, TEST_MNEMONIC}; - #[test] - fn test_ipfs() { - let param = RecoverIdentityParam { - name: NAME.to_string(), - mnemonic: MNEMONIC.to_string(), - password: PASSWORD.to_string(), - password_hint: Some(PASSWORD_HINT.to_string()), - network: "TESTNET".to_string(), - seg_wit: None, - }; - let keystore = Identity::recover_identity(param).unwrap(); - - // header: data, iv, encrypted data - let test_cases = [ - ("imToken", "11111111111111111111111111111111", "0340b2495a1111111111111111111111111111111110b6602c68084bdd08dae796657aa6854ad13312fedc88f5b6f16c56b3e755dde125a1c4775db536ac0442ac942f9634c777f3ae5ca39f6abcae4bd6c87e54ab29ae0062b04d917b32e8d7c88eeb6261301b"), - ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "11111111111111111111111111111111", "0340b2495a11111111111111111111111111111111708b7e9486a339f6c482ec9d3786dd9f99222fa64753bc2e7d246b0fed9c2153b8a5dcc59ea3e320aa153ceefdd909e8484d215121a9b8416d395de38313ef65b9e27d2ba0cc17bf29c5b26fa5aa5be1a2500b017f06cdd001e8cd908c5a48f10962880a61b4704754fd6bbe3b5a1a8332376651c28205a02574ed95a70363e0d1031d133c8d2376808b74ffd78b831ec659b44e9f3d3734d26abd44dda88fac86d1a5f0128f77d0558fb1ef6d2cc8f9541c"), - ("a", "11111111111111111111111111111111", "0340b2495a111111111111111111111111111111111084e741e2b83ec644e844985088fd58d8449cb690cd7389d74e3be1ccdca755b0235c90431b7635a441944d880bd52c860b109b7a05a960192719eb3f294ec1b72f5dfd1b8f4c6e992b9c3add7c7c1b871b"), - ("A", "11111111111111111111111111111111", "0340b2495a1111111111111111111111111111111110de32f176b67269ddfe24b2162eae14968d2eafcb53ec5741a07a1d65dc10189e0f6b4c199e98b02fcb9ec744b134cecc4ae8bfbf79e7703781c259eab9ee2fa31f887b24d04b37b7c5aa49a3ff2a8d5e1b"), - ("a", "11111111111111111111111111111111", "0340b2495a111111111111111111111111111111111084e741e2b83ec644e844985088fd58d8449cb690cd7389d74e3be1ccdca755b0235c90431b7635a441944d880bd52c860b109b7a05a960192719eb3f294ec1b72f5dfd1b8f4c6e992b9c3add7c7c1b871b"), - ("a", "22222222222222222222222222222222", "0340b2495a22222222222222222222222222222222102906146aa78fadd4abac01d9aa34dbd66463220fa0a98b9212594e7624a34bb20ba50df75cb04362f8dcfe7a8c44b2b5740a2d66de015d867e609463482686959ebba6047600562fa82e94ee905f1d291c"), - ]; - - let unix_timestamp = 1514779200u64; - for t in test_cases { - let iv: [u8; 16] = hex::decode(t.1).unwrap().try_into().unwrap(); - assert_eq!( - keystore - .encrypt_ipfs_wth_timestamp_iv(t.0, unix_timestamp, &iv) - .unwrap(), - t.2 - ); - assert_eq!(keystore.decrypt_ipfs(t.2).unwrap(), t.0); - } - } - - #[test] - fn test_authentication() { - let test_cases = [ - ("MAINNET", "0x120cc977f9023c90635144bd0f4c8b85ff8aa23c003edcced9449f0465d05e954bccf9c114484e472c1837b0394f1933ad78ec8050673099e8bf5e9329737fe01c"), - ("TESTNET", "0x663ace6d60225f6d1a71d25735c66646f71977a9f25f709fca162db3c664a1e161881a51a8034c240dd8f0093285fd6245f65246708546e8eadd592f995daeb11c"), - ]; - - for item in test_cases { - let param = RecoverIdentityParam { - name: NAME.to_string(), - mnemonic: MNEMONIC.to_string(), - password: PASSWORD.to_string(), - password_hint: Some(PASSWORD_HINT.to_string()), - network: item.0.to_string(), - seg_wit: None, - }; - let keystore = Identity::recover_identity(param).unwrap(); - - let actual = keystore - .sign_authentication_message(1514736000, "12345ABCDE", PASSWORD) - .unwrap(); - assert_eq!(actual, item.1); - } - } - - #[test] - fn test_create_identity() { - let param = CreateIdentityParam { - name: sample_key::NAME.to_string(), - password: PASSWORD.to_string(), - password_hint: Some(PASSWORD_HINT.to_string()), - network: "TESTNET".to_string(), - seg_wit: None, - }; - let identity_keystore = Identity::create_identity(param).unwrap(); - let imt_keystore_id = identity_keystore.wallet_ids.get(0).unwrap(); - let ret = Identity::get_current_identity(); - assert_eq!(ret.is_ok(), true); - assert_eq!(ret.unwrap().wallet_ids.get(0).unwrap(), imt_keystore_id); - } - - #[test] - fn test_recover_identity() { - let param = RecoverIdentityParam { - name: NAME.to_string(), - mnemonic: MNEMONIC.to_string(), - password: PASSWORD.to_string(), - password_hint: Some(PASSWORD_HINT.to_string()), - network: "TESTNET".to_string(), - seg_wit: None, - }; - let recover_result = Identity::recover_identity(param); - assert_eq!(recover_result.is_ok(), true); - let identity_keystore = Identity::get_current_identity().unwrap(); - let wallets = identity_keystore.get_wallets().unwrap(); - assert_eq!(wallets.len(), 1); - assert_eq!( - "6031564e7b2f5cc33737807b2e58daff870b590b", - wallets.get(0).unwrap().address - ); - } -} +// pub struct Identity(); + +// impl Identity { +// pub fn get_current_identity() -> Result { +// let mut identity_keystore_obj = IDENTITY_KEYSTORE.write(); +// if !identity_keystore_obj.id.is_empty() { +// return Ok(identity_keystore_obj.to_owned()); +// } +// let dir = WALLET_KEYSTORE_DIR.read(); +// let path_str = format!("{}/{}", dir.as_str(), IDENTITY_KEYSTORE_FILE_NAME); +// let path = Path::new(path_str.as_str()); +// if !path.exists() { +// return Err(Error::KeystoreFileNotExist.into()); +// } +// let mut file = File::open(&path)?; +// let mut keystore_context = String::new(); +// file.read_to_string(&mut keystore_context)?; +// let identity_keystore: IdentityKeystore = serde_json::from_str(keystore_context.as_str())?; +// *identity_keystore_obj = identity_keystore.to_owned(); + +// Ok(identity_keystore) +// } + +// pub fn create_identity(param: CreateIdentityParam) -> Result { +// let name = param.name.as_str(); +// let password = param.password.as_str(); +// let password_hint = param.password_hint; +// let network = param.network.as_str(); +// let seg_wit = param.seg_wit.as_deref(); +// let mnemonic_phrase = tcx_primitive::generate_mnemonic(); + +// let metadata = Metadata::new( +// name, +// password_hint.clone(), +// FROM_NEW_IDENTITY, +// network, +// None, +// seg_wit, +// )?; +// let mut identity_keystore = +// IdentityKeystore::new(metadata.clone(), password, mnemonic_phrase.as_str())?; + +// let eth_keystore = Self::derive_ethereum_wallet( +// password_hint, +// FROM_NEW_IDENTITY, +// mnemonic_phrase.as_str(), +// password, +// )?; + +// identity_keystore.wallet_ids.push(eth_keystore.id); +// identity_keystore.flush()?; +// identity_keystore.cache(); + +// Ok(identity_keystore) +// } + +// pub fn recover_identity(param: RecoverIdentityParam) -> Result { +// let name = param.name.as_str(); +// let password = param.password.as_str(); +// let password_hint = param.password_hint; +// let network = param.network.as_str(); +// let seg_wit = param.seg_wit.as_deref(); +// let mnemonic_phrase = param.mnemonic.as_str(); +// let metadata = Metadata::new( +// name, +// password_hint.clone(), +// FROM_RECOVERED_IDENTITY, +// network, +// None, +// seg_wit, +// )?; +// let mut identity_keystore = IdentityKeystore::new(metadata, password, mnemonic_phrase)?; +// let eth_keystore = Self::derive_ethereum_wallet( +// password_hint, +// FROM_RECOVERED_IDENTITY, +// mnemonic_phrase, +// password, +// )?; + +// identity_keystore.wallet_ids.push(eth_keystore.id); +// identity_keystore.flush()?; +// identity_keystore.cache(); + +// Ok(identity_keystore) +// } + +// fn derive_ethereum_wallet( +// password_hint: Option, +// source: &str, +// mnemonic_phrase: &str, +// password: &str, +// ) -> Result { +// let mut metadata = Metadata::default(); +// metadata.chain_type = Some(CHAIN_TYPE_ETHEREUM.to_string()); +// metadata.password_hint = password_hint; +// metadata.source = source.to_string(); +// metadata.name = "ETH".to_string(); +// let imt_keystore = IMTKeystore::create_v3_mnemonic_keystore( +// &mut metadata, +// password, +// mnemonic_phrase, +// ETHEREUM_PATH, +// )?; +// imt_keystore.create_wallet()?; +// Ok(imt_keystore) +// } +// } + +// #[cfg(test)] +// mod test { +// use crate::identity::Identity; +// use crate::model::FROM_NEW_IDENTITY; +// use crate::wallet_api::{CreateIdentityParam, RecoverIdentityParam}; +// use tcx_constants::sample_key::{MNEMONIC, NAME, PASSWORD, PASSWORD_HINT}; +// use tcx_constants::{sample_key, TEST_MNEMONIC}; +// #[test] +// fn test_ipfs() { +// let param = RecoverIdentityParam { +// name: NAME.to_string(), +// mnemonic: MNEMONIC.to_string(), +// password: PASSWORD.to_string(), +// password_hint: Some(PASSWORD_HINT.to_string()), +// network: "TESTNET".to_string(), +// seg_wit: None, +// }; +// let keystore = Identity::recover_identity(param).unwrap(); + +// // header: data, iv, encrypted data +// let test_cases = [ +// ("imToken", "11111111111111111111111111111111", "0340b2495a1111111111111111111111111111111110b6602c68084bdd08dae796657aa6854ad13312fedc88f5b6f16c56b3e755dde125a1c4775db536ac0442ac942f9634c777f3ae5ca39f6abcae4bd6c87e54ab29ae0062b04d917b32e8d7c88eeb6261301b"), +// ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "11111111111111111111111111111111", "0340b2495a11111111111111111111111111111111708b7e9486a339f6c482ec9d3786dd9f99222fa64753bc2e7d246b0fed9c2153b8a5dcc59ea3e320aa153ceefdd909e8484d215121a9b8416d395de38313ef65b9e27d2ba0cc17bf29c5b26fa5aa5be1a2500b017f06cdd001e8cd908c5a48f10962880a61b4704754fd6bbe3b5a1a8332376651c28205a02574ed95a70363e0d1031d133c8d2376808b74ffd78b831ec659b44e9f3d3734d26abd44dda88fac86d1a5f0128f77d0558fb1ef6d2cc8f9541c"), +// ("a", "11111111111111111111111111111111", "0340b2495a111111111111111111111111111111111084e741e2b83ec644e844985088fd58d8449cb690cd7389d74e3be1ccdca755b0235c90431b7635a441944d880bd52c860b109b7a05a960192719eb3f294ec1b72f5dfd1b8f4c6e992b9c3add7c7c1b871b"), +// ("A", "11111111111111111111111111111111", "0340b2495a1111111111111111111111111111111110de32f176b67269ddfe24b2162eae14968d2eafcb53ec5741a07a1d65dc10189e0f6b4c199e98b02fcb9ec744b134cecc4ae8bfbf79e7703781c259eab9ee2fa31f887b24d04b37b7c5aa49a3ff2a8d5e1b"), +// ("a", "11111111111111111111111111111111", "0340b2495a111111111111111111111111111111111084e741e2b83ec644e844985088fd58d8449cb690cd7389d74e3be1ccdca755b0235c90431b7635a441944d880bd52c860b109b7a05a960192719eb3f294ec1b72f5dfd1b8f4c6e992b9c3add7c7c1b871b"), +// ("a", "22222222222222222222222222222222", "0340b2495a22222222222222222222222222222222102906146aa78fadd4abac01d9aa34dbd66463220fa0a98b9212594e7624a34bb20ba50df75cb04362f8dcfe7a8c44b2b5740a2d66de015d867e609463482686959ebba6047600562fa82e94ee905f1d291c"), +// ]; + +// let unix_timestamp = 1514779200u64; +// for t in test_cases { +// let iv: [u8; 16] = hex::decode(t.1).unwrap().try_into().unwrap(); +// assert_eq!( +// keystore +// .encrypt_ipfs_wth_timestamp_iv(t.0, unix_timestamp, &iv) +// .unwrap(), +// t.2 +// ); +// assert_eq!(keystore.decrypt_ipfs(t.2).unwrap(), t.0); +// } +// } + +// #[test] +// fn test_authentication() { +// let test_cases = [ +// ("MAINNET", "0x120cc977f9023c90635144bd0f4c8b85ff8aa23c003edcced9449f0465d05e954bccf9c114484e472c1837b0394f1933ad78ec8050673099e8bf5e9329737fe01c"), +// ("TESTNET", "0x663ace6d60225f6d1a71d25735c66646f71977a9f25f709fca162db3c664a1e161881a51a8034c240dd8f0093285fd6245f65246708546e8eadd592f995daeb11c"), +// ]; + +// for item in test_cases { +// let param = RecoverIdentityParam { +// name: NAME.to_string(), +// mnemonic: MNEMONIC.to_string(), +// password: PASSWORD.to_string(), +// password_hint: Some(PASSWORD_HINT.to_string()), +// network: item.0.to_string(), +// seg_wit: None, +// }; +// let keystore = Identity::recover_identity(param).unwrap(); + +// let actual = keystore +// .sign_authentication_message(1514736000, "12345ABCDE", PASSWORD) +// .unwrap(); +// assert_eq!(actual, item.1); +// } +// } + +// #[test] +// fn test_create_identity() { +// let param = CreateIdentityParam { +// name: sample_key::NAME.to_string(), +// password: PASSWORD.to_string(), +// password_hint: Some(PASSWORD_HINT.to_string()), +// network: "TESTNET".to_string(), +// seg_wit: None, +// }; +// let identity_keystore = Identity::create_identity(param).unwrap(); +// let imt_keystore_id = identity_keystore.wallet_ids.get(0).unwrap(); +// let ret = Identity::get_current_identity(); +// assert_eq!(ret.is_ok(), true); +// assert_eq!(ret.unwrap().wallet_ids.get(0).unwrap(), imt_keystore_id); +// } + +// #[test] +// fn test_recover_identity() { +// let param = RecoverIdentityParam { +// name: NAME.to_string(), +// mnemonic: MNEMONIC.to_string(), +// password: PASSWORD.to_string(), +// password_hint: Some(PASSWORD_HINT.to_string()), +// network: "TESTNET".to_string(), +// seg_wit: None, +// }; +// let recover_result = Identity::recover_identity(param); +// assert_eq!(recover_result.is_ok(), true); +// let identity_keystore = Identity::get_current_identity().unwrap(); +// let wallets = identity_keystore.get_wallets().unwrap(); +// assert_eq!(wallets.len(), 1); +// assert_eq!( +// "6031564e7b2f5cc33737807b2e58daff870b590b", +// wallets.get(0).unwrap().address +// ); +// } +// } diff --git a/token-core/tcx-identity/src/imt_keystore.rs b/token-core/tcx-identity/src/imt_keystore.rs index bf0c895e..7b2b0e9c 100644 --- a/token-core/tcx-identity/src/imt_keystore.rs +++ b/token-core/tcx-identity/src/imt_keystore.rs @@ -1,216 +1,216 @@ -use crate::constants::CHAIN_TYPE_ETHEREUM; -use crate::model::Metadata; -use crate::model::V3; -use crate::Error; -use crate::Result; -use bip39::{Language, Mnemonic}; -use lazy_static::lazy_static; -use serde::{Deserialize, Serialize}; -use std::fs; -use std::io::Write; -use std::path::Path; -use std::time::{SystemTime, UNIX_EPOCH}; -use tcx_crypto::crypto::KdfType; -use tcx_crypto::crypto::SCryptParams; -use tcx_crypto::{Crypto, EncPair, Key, Pbkdf2Params}; -use tcx_primitive::Secp256k1PrivateKey; -// use tcx_eth::address::EthAddress; -use parking_lot::RwLock; -use std::collections::HashMap; -use tcx_common::util::get_address_from_pubkey; -use tcx_primitive::{Bip32DeterministicPrivateKey, Derive, DeterministicPrivateKey, PrivateKey}; -use uuid::Uuid; - -pub const VERSION: u32 = 3; - -lazy_static! { - pub static ref WALLETS: RwLock> = RwLock::new(HashMap::new()); - pub static ref WALLET_KEYSTORE_DIR: RwLock = RwLock::new("../test-data".to_string()); -} - -#[derive(Debug, Deserialize, Serialize, Clone, Default)] -#[serde(rename_all = "camelCase")] -pub struct IMTKeystore { - pub crypto: Crypto, - pub id: String, - pub version: u32, - pub address: String, - #[serde(skip_serializing)] - pub mnemonic_path: Option, - #[serde(skip_serializing)] - pub enc_mnemonic: Option, - #[serde(skip_serializing)] - pub im_token_meta: Option, -} - -impl IMTKeystore { - pub fn create_v3_mnemonic_keystore( - metadata: &mut Metadata, - password: &str, - mnemonic_phrase: &str, - path: &str, - ) -> Result { - Mnemonic::validate(mnemonic_phrase, Language::English).unwrap(); - - let bip32_deterministic_private_key = - Bip32DeterministicPrivateKey::from_mnemonic(mnemonic_phrase)?; - let bip32_deterministic_private_key = bip32_deterministic_private_key.derive(path)?; - - let mut crypto: Crypto = Crypto::new( - password, - bip32_deterministic_private_key - .private_key() - .0 - .to_bytes() - .as_slice(), - ); - let enc_mnemonic = crypto - .use_key(&Key::Password(password.to_owned()))? - .encrypt_with_random_iv(mnemonic_phrase.as_bytes())?; - metadata.timestamp = SystemTime::now().duration_since(UNIX_EPOCH)?.as_micros(); - - let publick_key = bip32_deterministic_private_key - .private_key() - .public_key() - .to_uncompressed(); - - let address = get_address( - metadata.chain_type.as_ref().unwrap(), - metadata.is_main_net(), - publick_key.as_slice(), - )?; - - Ok(IMTKeystore { - crypto, - id: Uuid::new_v4().as_hyphenated().to_string(), - version: VERSION, - address, - mnemonic_path: Some(path.to_string()), - enc_mnemonic: Some(enc_mnemonic), - im_token_meta: Some(metadata.clone()), - }) - } - - #[warn(dead_code)] - pub fn create_hd_mnemonic_keystore( - _metadata: &mut Metadata, - _password: &str, - _mnemonic_phrase: &str, - _path: &str, - _id: Option<&str>, - ) -> Result { - unimplemented!(); - } - - pub fn to_json(&self) -> Result { - Ok(serde_json::to_string(&self)?) - } - - pub fn decrypt_main_key(&self, password: &str) -> Result> { - self.crypto - .use_key(&Key::Password(password.to_owned()))? - .plaintext() - } -} - -impl IMTKeystore { - pub fn create_wallet(&self) -> Result<()> { - let file_dir = WALLET_KEYSTORE_DIR.read(); - let ks_path = format!("{}/{}.json", file_dir, self.id); - let path = Path::new(&ks_path); - let mut file = fs::File::create(path)?; - let json = self.to_json()?; - let _ = file.write_all(&json.as_bytes()); - - WALLETS.write().insert(self.id.to_string(), self.to_owned()); - Ok(()) - } - - pub fn clear_keystore_map() { - WALLETS.write().clear(); - } - - pub fn must_find_wallet_by_id(id: &str) -> Result { - let map = WALLETS.write(); - match map.get(id) { - Some(keystore) => Ok(keystore.to_owned()), - _ => Err(Error::WalletNotFound.into()), - } - } - - pub fn clean_keystore_dir() -> Result<()> { - let dir = WALLET_KEYSTORE_DIR.read(); - let paths = fs::read_dir(dir.as_str()).unwrap(); - for path in paths { - let path = path?.path(); - if path.is_file() { - fs::remove_file(path)?; - } - } - Ok(()) - } - - pub fn export_keystore(&self, password: &str) -> Result { - // let fixtures = IMTKeystore::must_find_wallet_by_id(id)?; - if !self.verify_password(password) { - return Err(Error::WalletInvalidPassword.into()); - } - let json = self.to_json()?; - Ok(json) - } - - pub fn verify_password(&self, password: &str) -> bool { - self.crypto.verify_password(password) - } -} - -fn get_address(chain_type: &str, _is_mainnet: bool, public_key: &[u8]) -> Result { - let address = match chain_type { - CHAIN_TYPE_ETHEREUM => get_address_from_pubkey(public_key)?, - _ => return Err(Error::WalletInvalidType.into()), - }; - Ok(address) -} - -#[cfg(test)] -mod test { - use crate::constants; - use crate::constants::{CHAIN_TYPE_ETHEREUM, ETHEREUM_PATH}; - use crate::imt_keystore::{get_address, IMTKeystore}; - use crate::model::{Metadata, FROM_NEW_IDENTITY}; - use tcx_constants::sample_key::{MNEMONIC, PASSWORD, PASSWORD_HINT}; - #[test] - fn test_get_address() { - let public_key = hex::decode("0480c98b8ea7cab630defb0c09a4295c2193cdee016c1d5b9b0cb18572b9c370fefbc790fc3291d3cb6441ac94c3952035c409f4374d1780f400c1ed92972ce83c").unwrap(); - let address = get_address(constants::CHAIN_TYPE_ETHEREUM, false, public_key.as_slice()); - assert!(address.is_ok()); - assert_eq!(address.unwrap(), "6031564e7b2f5cc33737807b2e58daff870b590b"); - - let address = get_address("WRONG_CHAIN_TYPE", false, public_key.as_slice()); - assert!(address.is_err()); - } - - #[test] - fn test_create_v3_mnemonic_keystore() { - let mut metadata = Metadata::default(); - metadata.chain_type = Some(CHAIN_TYPE_ETHEREUM.to_string()); - metadata.password_hint = Some(PASSWORD_HINT.to_string()); - metadata.source = FROM_NEW_IDENTITY.to_string(); - metadata.name = "ETH".to_string(); - let imt_keystore = IMTKeystore::create_v3_mnemonic_keystore( - &mut metadata, - PASSWORD, - MNEMONIC, - ETHEREUM_PATH, - ) - .unwrap(); - assert_eq!( - imt_keystore.address, - "6031564e7b2f5cc33737807b2e58daff870b590b" - ); - assert_eq!( - hex::encode(imt_keystore.decrypt_main_key(PASSWORD).unwrap()), - "cce64585e3b15a0e4ee601a467e050c9504a0db69a559d7ec416fa25ad3410c2" - ); - } -} +// use crate::constants::CHAIN_TYPE_ETHEREUM; +// use crate::model::Metadata; +// use crate::model::V3; +// use crate::Error; +// use crate::Result; +// use bip39::{Language, Mnemonic}; +// use lazy_static::lazy_static; +// use serde::{Deserialize, Serialize}; +// use std::fs; +// use std::io::Write; +// use std::path::Path; +// use std::time::{SystemTime, UNIX_EPOCH}; +// use tcx_crypto::crypto::KdfType; +// use tcx_crypto::crypto::SCryptParams; +// use tcx_crypto::{Crypto, EncPair, Key, Pbkdf2Params}; +// use tcx_primitive::Secp256k1PrivateKey; +// // use tcx_eth::address::EthAddress; +// use parking_lot::RwLock; +// use std::collections::HashMap; +// use tcx_common::util::get_address_from_pubkey; +// use tcx_primitive::{Bip32DeterministicPrivateKey, Derive, DeterministicPrivateKey, PrivateKey}; +// use uuid::Uuid; + +// pub const VERSION: u32 = 3; + +// lazy_static! { +// pub static ref WALLETS: RwLock> = RwLock::new(HashMap::new()); +// pub static ref WALLET_KEYSTORE_DIR: RwLock = RwLock::new("../test-data".to_string()); +// } + +// #[derive(Debug, Deserialize, Serialize, Clone, Default)] +// #[serde(rename_all = "camelCase")] +// pub struct IMTKeystore { +// pub crypto: Crypto, +// pub id: String, +// pub version: u32, +// pub address: String, +// #[serde(skip_serializing)] +// pub mnemonic_path: Option, +// #[serde(skip_serializing)] +// pub enc_mnemonic: Option, +// #[serde(skip_serializing)] +// pub im_token_meta: Option, +// } + +// impl IMTKeystore { +// pub fn create_v3_mnemonic_keystore( +// metadata: &mut Metadata, +// password: &str, +// mnemonic_phrase: &str, +// path: &str, +// ) -> Result { +// Mnemonic::validate(mnemonic_phrase, Language::English).unwrap(); + +// let bip32_deterministic_private_key = +// Bip32DeterministicPrivateKey::from_mnemonic(mnemonic_phrase)?; +// let bip32_deterministic_private_key = bip32_deterministic_private_key.derive(path)?; + +// let mut crypto: Crypto = Crypto::new( +// password, +// bip32_deterministic_private_key +// .private_key() +// .0 +// .to_bytes() +// .as_slice(), +// ); +// let enc_mnemonic = crypto +// .use_key(&Key::Password(password.to_owned()))? +// .encrypt_with_random_iv(mnemonic_phrase.as_bytes())?; +// metadata.timestamp = SystemTime::now().duration_since(UNIX_EPOCH)?.as_micros(); + +// let publick_key = bip32_deterministic_private_key +// .private_key() +// .public_key() +// .to_uncompressed(); + +// let address = get_address( +// metadata.chain_type.as_ref().unwrap(), +// metadata.is_main_net(), +// publick_key.as_slice(), +// )?; + +// Ok(IMTKeystore { +// crypto, +// id: Uuid::new_v4().as_hyphenated().to_string(), +// version: VERSION, +// address, +// mnemonic_path: Some(path.to_string()), +// enc_mnemonic: Some(enc_mnemonic), +// im_token_meta: Some(metadata.clone()), +// }) +// } + +// #[warn(dead_code)] +// pub fn create_hd_mnemonic_keystore( +// _metadata: &mut Metadata, +// _password: &str, +// _mnemonic_phrase: &str, +// _path: &str, +// _id: Option<&str>, +// ) -> Result { +// unimplemented!(); +// } + +// pub fn to_json(&self) -> Result { +// Ok(serde_json::to_string(&self)?) +// } + +// pub fn decrypt_main_key(&self, password: &str) -> Result> { +// self.crypto +// .use_key(&Key::Password(password.to_owned()))? +// .plaintext() +// } +// } + +// impl IMTKeystore { +// pub fn create_wallet(&self) -> Result<()> { +// let file_dir = WALLET_KEYSTORE_DIR.read(); +// let ks_path = format!("{}/{}.json", file_dir, self.id); +// let path = Path::new(&ks_path); +// let mut file = fs::File::create(path)?; +// let json = self.to_json()?; +// let _ = file.write_all(&json.as_bytes()); + +// WALLETS.write().insert(self.id.to_string(), self.to_owned()); +// Ok(()) +// } + +// pub fn clear_keystore_map() { +// WALLETS.write().clear(); +// } + +// pub fn must_find_wallet_by_id(id: &str) -> Result { +// let map = WALLETS.write(); +// match map.get(id) { +// Some(keystore) => Ok(keystore.to_owned()), +// _ => Err(Error::WalletNotFound.into()), +// } +// } + +// pub fn clean_keystore_dir() -> Result<()> { +// let dir = WALLET_KEYSTORE_DIR.read(); +// let paths = fs::read_dir(dir.as_str()).unwrap(); +// for path in paths { +// let path = path?.path(); +// if path.is_file() { +// fs::remove_file(path)?; +// } +// } +// Ok(()) +// } + +// pub fn export_keystore(&self, password: &str) -> Result { +// // let fixtures = IMTKeystore::must_find_wallet_by_id(id)?; +// if !self.verify_password(password) { +// return Err(Error::WalletInvalidPassword.into()); +// } +// let json = self.to_json()?; +// Ok(json) +// } + +// pub fn verify_password(&self, password: &str) -> bool { +// self.crypto.verify_password(password) +// } +// } + +// fn get_address(chain_type: &str, _is_mainnet: bool, public_key: &[u8]) -> Result { +// let address = match chain_type { +// CHAIN_TYPE_ETHEREUM => get_address_from_pubkey(public_key)?, +// _ => return Err(Error::WalletInvalidType.into()), +// }; +// Ok(address) +// } + +// #[cfg(test)] +// mod test { +// use crate::constants; +// use crate::constants::{CHAIN_TYPE_ETHEREUM, ETHEREUM_PATH}; +// use crate::imt_keystore::{get_address, IMTKeystore}; +// use crate::model::{Metadata, FROM_NEW_IDENTITY}; +// use tcx_constants::sample_key::{MNEMONIC, PASSWORD, PASSWORD_HINT}; +// #[test] +// fn test_get_address() { +// let public_key = hex::decode("0480c98b8ea7cab630defb0c09a4295c2193cdee016c1d5b9b0cb18572b9c370fefbc790fc3291d3cb6441ac94c3952035c409f4374d1780f400c1ed92972ce83c").unwrap(); +// let address = get_address(constants::CHAIN_TYPE_ETHEREUM, false, public_key.as_slice()); +// assert!(address.is_ok()); +// assert_eq!(address.unwrap(), "6031564e7b2f5cc33737807b2e58daff870b590b"); + +// let address = get_address("WRONG_CHAIN_TYPE", false, public_key.as_slice()); +// assert!(address.is_err()); +// } + +// #[test] +// fn test_create_v3_mnemonic_keystore() { +// let mut metadata = Metadata::default(); +// metadata.chain_type = Some(CHAIN_TYPE_ETHEREUM.to_string()); +// metadata.password_hint = Some(PASSWORD_HINT.to_string()); +// metadata.source = FROM_NEW_IDENTITY.to_string(); +// metadata.name = "ETH".to_string(); +// let imt_keystore = IMTKeystore::create_v3_mnemonic_keystore( +// &mut metadata, +// PASSWORD, +// MNEMONIC, +// ETHEREUM_PATH, +// ) +// .unwrap(); +// assert_eq!( +// imt_keystore.address, +// "6031564e7b2f5cc33737807b2e58daff870b590b" +// ); +// assert_eq!( +// hex::encode(imt_keystore.decrypt_main_key(PASSWORD).unwrap()), +// "cce64585e3b15a0e4ee601a467e050c9504a0db69a559d7ec416fa25ad3410c2" +// ); +// } +// } diff --git a/token-core/tcx-identity/src/model.rs b/token-core/tcx-identity/src/model.rs index c88fcd58..22c4f579 100644 --- a/token-core/tcx-identity/src/model.rs +++ b/token-core/tcx-identity/src/model.rs @@ -1,115 +1,115 @@ -use crate::Result; -use serde::{Deserialize, Serialize}; -use std::time::{SystemTime, UNIX_EPOCH}; +// use crate::Result; +// use serde::{Deserialize, Serialize}; +// use std::time::{SystemTime, UNIX_EPOCH}; -pub const FROM_MNEMONIC: &str = "MNEMONIC"; -pub const FROM_KEYSTORE: &str = "KEYSTORE"; -pub const FROM_PRIVATE: &str = "PRIVATE"; -pub const FROM_WIF: &str = "WIF"; -pub const FROM_NEW_IDENTITY: &str = "NEW_IDENTITY"; -pub const FROM_RECOVERED_IDENTITY: &str = "RECOVERED_IDENTITY"; -pub const P2WPKH: &str = "P2WPKH"; -pub const NONE: &str = "NONE"; -pub const NORMAL: &str = "NORMAL"; -pub const HD: &str = "HD"; -pub const RANDOM: &str = "RANDOM"; -pub const HD_SHA256: &str = "HD_SHA256"; -pub const V3: &str = "V3"; +// pub const FROM_MNEMONIC: &str = "MNEMONIC"; +// pub const FROM_KEYSTORE: &str = "KEYSTORE"; +// pub const FROM_PRIVATE: &str = "PRIVATE"; +// pub const FROM_WIF: &str = "WIF"; +// pub const FROM_NEW_IDENTITY: &str = "NEW_IDENTITY"; +// pub const FROM_RECOVERED_IDENTITY: &str = "RECOVERED_IDENTITY"; +// pub const P2WPKH: &str = "P2WPKH"; +// pub const NONE: &str = "NONE"; +// pub const NORMAL: &str = "NORMAL"; +// pub const HD: &str = "HD"; +// pub const RANDOM: &str = "RANDOM"; +// pub const HD_SHA256: &str = "HD_SHA256"; +// pub const V3: &str = "V3"; -pub const NETWORK_MAINNET: &str = "MAINNET"; -pub const NETWORK_TESTNET: &str = "TESTNET"; +// pub const NETWORK_MAINNET: &str = "MAINNET"; +// pub const NETWORK_TESTNET: &str = "TESTNET"; -#[derive(Debug, Deserialize, Serialize, Clone, Default)] -#[serde(rename_all = "camelCase")] -pub struct Metadata { - pub name: String, - pub password_hint: Option, - pub chain_type: Option, - pub timestamp: u128, - pub network: String, - pub backup: Option>, - pub source: String, - pub mode: Option, - pub wallet_type: Option, - pub seg_wit: Option, -} +// #[derive(Debug, Deserialize, Serialize, Clone, Default)] +// #[serde(rename_all = "camelCase")] +// pub struct Metadata { +// pub name: String, +// pub password_hint: Option, +// pub chain_type: Option, +// pub timestamp: u128, +// pub network: String, +// pub backup: Option>, +// pub source: String, +// pub mode: Option, +// pub wallet_type: Option, +// pub seg_wit: Option, +// } -impl Metadata { - pub fn new( - name: &str, - password_hint: Option, - source: &str, - network: &str, - chain_type: Option<&str>, - seg_wit: Option<&str>, - ) -> Result { - let timestamp = SystemTime::now().duration_since(UNIX_EPOCH)?.as_micros(); - Ok(Metadata { - name: name.to_string(), - password_hint, - chain_type: chain_type.and_then(|val| Some(val.to_string())), - timestamp, - network: network.to_string(), - backup: None, - source: source.to_string(), - mode: None, - wallet_type: None, - seg_wit: seg_wit.and_then(|val| Some(val.to_string())), - }) - } +// impl Metadata { +// pub fn new( +// name: &str, +// password_hint: Option, +// source: &str, +// network: &str, +// chain_type: Option<&str>, +// seg_wit: Option<&str>, +// ) -> Result { +// let timestamp = SystemTime::now().duration_since(UNIX_EPOCH)?.as_micros(); +// Ok(Metadata { +// name: name.to_string(), +// password_hint, +// chain_type: chain_type.and_then(|val| Some(val.to_string())), +// timestamp, +// network: network.to_string(), +// backup: None, +// source: source.to_string(), +// mode: None, +// wallet_type: None, +// seg_wit: seg_wit.and_then(|val| Some(val.to_string())), +// }) +// } - pub fn is_main_net(&self) -> bool { - self.network.eq_ignore_ascii_case(NETWORK_MAINNET) - } +// pub fn is_main_net(&self) -> bool { +// self.network.eq_ignore_ascii_case(NETWORK_MAINNET) +// } - pub fn to_json(&self) -> Result { - Ok(serde_json::to_string(&self)?) - } -} +// pub fn to_json(&self) -> Result { +// Ok(serde_json::to_string(&self)?) +// } +// } -#[cfg(test)] -mod test { - use crate::model::Metadata; - use crate::model::FROM_NEW_IDENTITY; +// #[cfg(test)] +// mod test { +// use crate::model::Metadata; +// use crate::model::FROM_NEW_IDENTITY; - #[test] - fn test_metadata_new() { - let mut metadata = Metadata::new( - "name", - Some("password_hint".to_string()), - FROM_NEW_IDENTITY, - "MAINNET", - None, - None, - ) - .unwrap(); - metadata.timestamp = 1686105373257408; - let metadata_json = metadata.to_json().unwrap(); - let expected_json = "{\"name\":\"name\",\"passwordHint\":\"password_hint\",\"chainType\":\"\",\"timestamp\":1686105373257408,\"network\":\"MAINNET\",\"backup\":null,\"source\":\"NEW_IDENTITY\",\"mode\":null,\"walletType\":null,\"segWit\":null}"; - assert_eq!(metadata_json, expected_json); - metadata.chain_type = Some("ETHEREUM".to_string()); - let metadata_json = metadata.to_json().unwrap(); - let expected_json = "{\"name\":\"name\",\"passwordHint\":\"password_hint\",\"chainType\":\"ETHEREUM\",\"timestamp\":1686105373257408,\"network\":\"MAINNET\",\"backup\":null,\"source\":\"NEW_IDENTITY\",\"mode\":null,\"walletType\":null,\"segWit\":null}"; - assert_eq!(metadata_json, expected_json); - } +// #[test] +// fn test_metadata_new() { +// let mut metadata = Metadata::new( +// "name", +// Some("password_hint".to_string()), +// FROM_NEW_IDENTITY, +// "MAINNET", +// None, +// None, +// ) +// .unwrap(); +// metadata.timestamp = 1686105373257408; +// let metadata_json = metadata.to_json().unwrap(); +// let expected_json = "{\"name\":\"name\",\"passwordHint\":\"password_hint\",\"chainType\":\"\",\"timestamp\":1686105373257408,\"network\":\"MAINNET\",\"backup\":null,\"source\":\"NEW_IDENTITY\",\"mode\":null,\"walletType\":null,\"segWit\":null}"; +// assert_eq!(metadata_json, expected_json); +// metadata.chain_type = Some("ETHEREUM".to_string()); +// let metadata_json = metadata.to_json().unwrap(); +// let expected_json = "{\"name\":\"name\",\"passwordHint\":\"password_hint\",\"chainType\":\"ETHEREUM\",\"timestamp\":1686105373257408,\"network\":\"MAINNET\",\"backup\":null,\"source\":\"NEW_IDENTITY\",\"mode\":null,\"walletType\":null,\"segWit\":null}"; +// assert_eq!(metadata_json, expected_json); +// } - #[test] - fn test_is_main_net() { - let mut metadata = Metadata::new( - "name", - Some("password_hint".to_string()), - FROM_NEW_IDENTITY, - "MAINNET", - None, - None, - ) - .unwrap(); - assert_eq!(metadata.is_main_net(), true); - metadata.network = "TESTNET".to_string(); - assert_eq!(metadata.is_main_net(), false); - metadata.network = "WRONG_NET".to_string(); - assert_eq!(metadata.is_main_net(), false); - metadata.network = "".to_string(); - assert_eq!(metadata.is_main_net(), false); - } -} +// #[test] +// fn test_is_main_net() { +// let mut metadata = Metadata::new( +// "name", +// Some("password_hint".to_string()), +// FROM_NEW_IDENTITY, +// "MAINNET", +// None, +// None, +// ) +// .unwrap(); +// assert_eq!(metadata.is_main_net(), true); +// metadata.network = "TESTNET".to_string(); +// assert_eq!(metadata.is_main_net(), false); +// metadata.network = "WRONG_NET".to_string(); +// assert_eq!(metadata.is_main_net(), false); +// metadata.network = "".to_string(); +// assert_eq!(metadata.is_main_net(), false); +// } +// } diff --git a/token-core/tcx-identity/src/v3_keystore.rs b/token-core/tcx-identity/src/v3_keystore.rs index a5dd41b9..05ed4ca1 100644 --- a/token-core/tcx-identity/src/v3_keystore.rs +++ b/token-core/tcx-identity/src/v3_keystore.rs @@ -1,166 +1,166 @@ -use std::time::SystemTime; -use std::time::UNIX_EPOCH; - -use crate::identity::Identity; -use crate::imt_keystore::IMTKeystore; -use crate::model::{Metadata, FROM_KEYSTORE, FROM_WIF, V3}; -use crate::wallet_api::V3KeystoreImportInput; -use crate::Result; -use failure::format_err; -use tcx_common::util::get_address_from_pubkey; -use tcx_crypto::crypto::{KdfParams, KdfType}; -use tcx_crypto::Crypto; -use tcx_crypto::Key; -use tcx_crypto::Pbkdf2Params; -use tcx_primitive::{PrivateKey, Secp256k1PrivateKey}; -use uuid::Uuid; - -const VERSION: u32 = 3; - -impl IMTKeystore { - pub fn create_v3_privkey_keystore( - metadata: &mut Metadata, - password: &str, - private_key: &[u8], - id: Option<&str>, - ) -> Result { - let secp256k1_private_key = Secp256k1PrivateKey::from_slice(private_key)?; - let address = get_address_from_pubkey( - secp256k1_private_key - .public_key() - .to_uncompressed() - .as_slice(), - )?; - - let crypto: Crypto = Crypto::new(password, private_key); - - let id = if id.is_some() { - id.unwrap().to_string() - } else { - Uuid::new_v4().as_hyphenated().to_string() - }; - metadata.wallet_type = Some(V3.to_string()); - Ok(IMTKeystore { - id, - version: VERSION, - address, - crypto, - mnemonic_path: None, - enc_mnemonic: None, - im_token_meta: Some(metadata.clone()), - }) - } - - pub fn change_password( - &mut self, - old_password: &str, - new_password: &str, - ) -> Result { - let decrypted = self - .crypto - .use_key(&Key::Password(old_password.to_string()))? - .plaintext()?; - Ok(IMTKeystore::create_v3_privkey_keystore( - &mut self.im_token_meta.as_mut().unwrap(), - new_password, - &decrypted, - Some(self.id.as_str()), - )?) - } - - pub fn validate(&self, password: &str) -> Result<()> { - if self.address.is_empty() { - return Err(format_err!("{}", "wallet_not_found")); - } - // imported_keystore.crypto.kdfparams.validate()?; - - if !self.crypto.verify_password(password) { - return Err(format_err!("password error")); - } - - let private_key = self - .crypto - .use_key(&Key::Password(password.to_string()))? - .plaintext()?; - - let private_key = Secp256k1PrivateKey::from_slice(&private_key)?; - let address = - get_address_from_pubkey(private_key.public_key().to_uncompressed().as_slice())?; - if address.is_empty() || !address.eq_ignore_ascii_case(&self.address) { - return Err(format_err!("password error")); - } - Ok(()) - } -} - -pub fn import_wallet_from_keystore(input: V3KeystoreImportInput) -> Result { - let json = if input.keystore.contains("scrypt") { - input.keystore.replace("kdfparams", "scryptparams") - } else { - input.keystore - }; - let keystore: IMTKeystore = serde_json::from_str(&json)?; - keystore.validate(&input.password)?; - let private_key = keystore - .crypto - .use_key(&Key::Password(input.password.to_owned()))? - .plaintext()?; - //私钥校验 - - let source = if input.source.is_empty() { - FROM_KEYSTORE.to_string() - } else { - input.source - }; - - let mut metadata = Metadata::new( - &input.name, - None, - &source, - "", - Some(&input.chain_type), - None, - )?; - let v3_keystore = IMTKeystore::create_v3_privkey_keystore( - &mut metadata, - &input.password, - &private_key, - Some(&keystore.id), - )?; - v3_keystore.create_wallet()?; - - let mut identity = Identity::get_current_identity()?; - identity.wallet_ids.push(v3_keystore.id.clone()); - identity.flush()?; - identity.cache(); - Ok(v3_keystore) -} - -#[cfg(test)] -mod test { - use crate::model::{Metadata, NETWORK_MAINNET}; - - #[test] - fn test_create_v3_privkey_keystore() { - // let metadata = Metadata{ - // name: "V3Keystore Test".to_string(), - // password_hint: None, - // chain_type: "ETHEREUM".to_string(), - // timestamp: 11, - // network: NETWORK_MAINNET.to_string(), - // backup: todo!(), - // source: todo!(), - // mode: todo!(), - // wallet_type: todo!(), - // seg_wit: todo!(), - // }; - } - - #[test] - fn test_change_password() { - println!("test change password"); - let a = "hello world".as_bytes().to_vec(); - let b = String::from_utf8(a).unwrap(); - println!("{}", b); - } -} +// use std::time::SystemTime; +// use std::time::UNIX_EPOCH; + +// use crate::identity::Identity; +// use crate::imt_keystore::IMTKeystore; +// use crate::model::{Metadata, FROM_KEYSTORE, FROM_WIF, V3}; +// use crate::wallet_api::V3KeystoreImportInput; +// use crate::Result; +// use failure::format_err; +// use tcx_common::util::get_address_from_pubkey; +// use tcx_crypto::crypto::{KdfParams, KdfType}; +// use tcx_crypto::Crypto; +// use tcx_crypto::Key; +// use tcx_crypto::Pbkdf2Params; +// use tcx_primitive::{PrivateKey, Secp256k1PrivateKey}; +// use uuid::Uuid; + +// const VERSION: u32 = 3; + +// impl IMTKeystore { +// pub fn create_v3_privkey_keystore( +// metadata: &mut Metadata, +// password: &str, +// private_key: &[u8], +// id: Option<&str>, +// ) -> Result { +// let secp256k1_private_key = Secp256k1PrivateKey::from_slice(private_key)?; +// let address = get_address_from_pubkey( +// secp256k1_private_key +// .public_key() +// .to_uncompressed() +// .as_slice(), +// )?; + +// let crypto: Crypto = Crypto::new(password, private_key); + +// let id = if id.is_some() { +// id.unwrap().to_string() +// } else { +// Uuid::new_v4().as_hyphenated().to_string() +// }; +// metadata.wallet_type = Some(V3.to_string()); +// Ok(IMTKeystore { +// id, +// version: VERSION, +// address, +// crypto, +// mnemonic_path: None, +// enc_mnemonic: None, +// im_token_meta: Some(metadata.clone()), +// }) +// } + +// pub fn change_password( +// &mut self, +// old_password: &str, +// new_password: &str, +// ) -> Result { +// let decrypted = self +// .crypto +// .use_key(&Key::Password(old_password.to_string()))? +// .plaintext()?; +// Ok(IMTKeystore::create_v3_privkey_keystore( +// &mut self.im_token_meta.as_mut().unwrap(), +// new_password, +// &decrypted, +// Some(self.id.as_str()), +// )?) +// } + +// pub fn validate(&self, password: &str) -> Result<()> { +// if self.address.is_empty() { +// return Err(format_err!("{}", "wallet_not_found")); +// } +// // imported_keystore.crypto.kdfparams.validate()?; + +// if !self.crypto.verify_password(password) { +// return Err(format_err!("password error")); +// } + +// let private_key = self +// .crypto +// .use_key(&Key::Password(password.to_string()))? +// .plaintext()?; + +// let private_key = Secp256k1PrivateKey::from_slice(&private_key)?; +// let address = +// get_address_from_pubkey(private_key.public_key().to_uncompressed().as_slice())?; +// if address.is_empty() || !address.eq_ignore_ascii_case(&self.address) { +// return Err(format_err!("password error")); +// } +// Ok(()) +// } +// } + +// pub fn import_wallet_from_keystore(input: V3KeystoreImportInput) -> Result { +// let json = if input.keystore.contains("scrypt") { +// input.keystore.replace("kdfparams", "scryptparams") +// } else { +// input.keystore +// }; +// let keystore: IMTKeystore = serde_json::from_str(&json)?; +// keystore.validate(&input.password)?; +// let private_key = keystore +// .crypto +// .use_key(&Key::Password(input.password.to_owned()))? +// .plaintext()?; +// //私钥校验 + +// let source = if input.source.is_empty() { +// FROM_KEYSTORE.to_string() +// } else { +// input.source +// }; + +// let mut metadata = Metadata::new( +// &input.name, +// None, +// &source, +// "", +// Some(&input.chain_type), +// None, +// )?; +// let v3_keystore = IMTKeystore::create_v3_privkey_keystore( +// &mut metadata, +// &input.password, +// &private_key, +// Some(&keystore.id), +// )?; +// v3_keystore.create_wallet()?; + +// let mut identity = Identity::get_current_identity()?; +// identity.wallet_ids.push(v3_keystore.id.clone()); +// identity.flush()?; +// identity.cache(); +// Ok(v3_keystore) +// } + +// #[cfg(test)] +// mod test { +// use crate::model::{Metadata, NETWORK_MAINNET}; + +// #[test] +// fn test_create_v3_privkey_keystore() { +// // let metadata = Metadata{ +// // name: "V3Keystore Test".to_string(), +// // password_hint: None, +// // chain_type: "ETHEREUM".to_string(), +// // timestamp: 11, +// // network: NETWORK_MAINNET.to_string(), +// // backup: todo!(), +// // source: todo!(), +// // mode: todo!(), +// // wallet_type: todo!(), +// // seg_wit: todo!(), +// // }; +// } + +// #[test] +// fn test_change_password() { +// println!("test change password"); +// let a = "hello world".as_bytes().to_vec(); +// let b = String::from_utf8(a).unwrap(); +// println!("{}", b); +// } +// } diff --git a/token-core/tcx-identity/src/wallet_api.rs b/token-core/tcx-identity/src/wallet_api.rs index 66ce406c..07653e1d 100644 --- a/token-core/tcx-identity/src/wallet_api.rs +++ b/token-core/tcx-identity/src/wallet_api.rs @@ -1,11 +1,5 @@ #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] -pub struct GenerateMnemonicResult { - #[prost(string, tag = "1")] - pub mnemonic: ::prost::alloc::string::String, -} -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] pub struct CreateIdentityParam { #[prost(string, tag = "1")] pub name: ::prost::alloc::string::String, @@ -15,8 +9,8 @@ pub struct CreateIdentityParam { pub password_hint: ::core::option::Option<::prost::alloc::string::String>, #[prost(string, tag = "4")] pub network: ::prost::alloc::string::String, - #[prost(string, optional, tag = "5")] - pub seg_wit: ::core::option::Option<::prost::alloc::string::String>, + #[prost(string, tag = "5")] + pub seg_wit: ::prost::alloc::string::String, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -41,6 +35,22 @@ pub struct Wallet { pub source: ::prost::alloc::string::String, #[prost(string, tag = "5")] pub chain_type: ::prost::alloc::string::String, + #[prost(message, optional, tag = "6")] + pub external_address: ::core::option::Option, + #[prost(string, optional, tag = "7")] + pub enc_x_pub: ::core::option::Option<::prost::alloc::string::String>, + #[prost(string, optional, tag = "8")] + pub seg_wit: ::core::option::Option<::prost::alloc::string::String>, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ExternalAddress { + #[prost(string, tag = "1")] + pub address: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub r#type: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + pub derived_path: ::prost::alloc::string::String, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -121,8 +131,8 @@ pub struct RecoverIdentityParam { pub password_hint: ::core::option::Option<::prost::alloc::string::String>, #[prost(string, tag = "5")] pub network: ::prost::alloc::string::String, - #[prost(string, optional, tag = "6")] - pub seg_wit: ::core::option::Option<::prost::alloc::string::String>, + #[prost(string, tag = "6")] + pub seg_wit: ::prost::alloc::string::String, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -136,99 +146,3 @@ pub struct RecoverIdentityResult { #[prost(message, repeated, tag = "4")] pub wallets: ::prost::alloc::vec::Vec, } -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct RemoveIdentityParam { - #[prost(string, tag = "1")] - pub identifier: ::prost::alloc::string::String, - #[prost(string, tag = "2")] - pub password: ::prost::alloc::string::String, -} -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct RemoveIdentityResult { - #[prost(string, tag = "1")] - pub identifier: ::prost::alloc::string::String, -} -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct V3KeystoreImportInput { - #[prost(string, tag = "1")] - pub keystore: ::prost::alloc::string::String, - #[prost(string, tag = "2")] - pub password: ::prost::alloc::string::String, - #[prost(bool, tag = "3")] - pub overwrite: bool, - #[prost(string, tag = "4")] - pub name: ::prost::alloc::string::String, - #[prost(string, tag = "5")] - pub chain_type: ::prost::alloc::string::String, - #[prost(string, tag = "6")] - pub source: ::prost::alloc::string::String, -} -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct V3KeystoreExportInput { - #[prost(string, tag = "1")] - pub id: ::prost::alloc::string::String, - #[prost(string, tag = "2")] - pub password: ::prost::alloc::string::String, -} -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct V3KeystoreExportOutput { - #[prost(string, tag = "1")] - pub json: ::prost::alloc::string::String, -} -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct EncryptDataToIpfsParam { - #[prost(string, tag = "1")] - pub identifier: ::prost::alloc::string::String, - #[prost(string, tag = "2")] - pub content: ::prost::alloc::string::String, -} -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct EncryptDataToIpfsResult { - #[prost(string, tag = "1")] - pub identifier: ::prost::alloc::string::String, - #[prost(string, tag = "2")] - pub encrypted: ::prost::alloc::string::String, -} -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct DecryptDataFromIpfsParam { - #[prost(string, tag = "1")] - pub identifier: ::prost::alloc::string::String, - #[prost(string, tag = "2")] - pub encrypted: ::prost::alloc::string::String, -} -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct DecryptDataFromIpfsResult { - #[prost(string, tag = "1")] - pub identifier: ::prost::alloc::string::String, - #[prost(string, tag = "2")] - pub content: ::prost::alloc::string::String, -} -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct SignAuthenticationMessageParam { - #[prost(uint64, tag = "1")] - pub access_time: u64, - #[prost(string, tag = "2")] - pub identifier: ::prost::alloc::string::String, - #[prost(string, tag = "3")] - pub device_token: ::prost::alloc::string::String, - #[prost(string, tag = "4")] - pub password: ::prost::alloc::string::String, -} -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct SignAuthenticationMessageResult { - #[prost(uint64, tag = "1")] - pub access_time: u64, - #[prost(string, tag = "2")] - pub signature: ::prost::alloc::string::String, -} diff --git a/token-core/tcx-chain/Cargo.toml b/token-core/tcx-keystore/Cargo.toml similarity index 70% rename from token-core/tcx-chain/Cargo.toml rename to token-core/tcx-keystore/Cargo.toml index d78d6977..21ecf387 100644 --- a/token-core/tcx-chain/Cargo.toml +++ b/token-core/tcx-keystore/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "tcx-chain" +name = "tcx-keystore" version = "0.1.0" authors = ["Neal Xu "] edition = "2021" @@ -8,10 +8,12 @@ edition = "2021" tcx-crypto = { path = "../tcx-crypto" } tcx-primitive = { path = "../tcx-primitive" } tcx-constants = { path = "../tcx-constants" } +tcx-common = {path = "../tcx-common"} +# tcx-identity = { path = "../tcx-identity" } bitcoin = "0.29.2" byteorder = "1.4.3" -secp256k1 = {version ="0.24.1", features = ["rand", "recovery"] } +secp256k1 = { version = "0.24.1", features = ["rand", "recovery"] } tiny-bip39 = "0.7.3" bitcoin_hashes = "0.11.0" uuid = { version = "1.2.2", features = ["serde", "v4"] } @@ -21,6 +23,11 @@ failure = "0.1.8" regex = "1.7.0" hex = "0.4.3" +sha2 = "0.10.6" +hmac-sha256 = "1.1.6" +multihash = "0.18.1" +lazy_static = "1.4.0" +parking_lot = "0.12.1" prost = "0.11.2" bytes = "1.3.0" @@ -32,4 +39,4 @@ features = ["precommit-hook", "run-cargo-fmt"] [features] default = [] -cache_dk = [] \ No newline at end of file +cache_dk = [] diff --git a/token-core/tcx-chain/README.md b/token-core/tcx-keystore/README.md similarity index 100% rename from token-core/tcx-chain/README.md rename to token-core/tcx-keystore/README.md diff --git a/token-core/tcx-keystore/src/identity.rs b/token-core/tcx-keystore/src/identity.rs new file mode 100644 index 00000000..4ba07e26 --- /dev/null +++ b/token-core/tcx-keystore/src/identity.rs @@ -0,0 +1,531 @@ +// use crate::constants::{CHAIN_TYPE_ETHEREUM, ETHEREUM_PATH}; +// use crate::imt_keystore::{IMTKeystore, WALLETS, WALLET_KEYSTORE_DIR}; +// use crate::model::{Metadata, FROM_NEW_IDENTITY, FROM_RECOVERED_IDENTITY}; +// use crate::wallet_api::{CreateIdentityParam, RecoverIdentityParam}; +use crate::Error; +use crate::Result; +use bip39::{Language, Mnemonic, Seed}; +use bitcoin::consensus::{Decodable, Encodable}; +use bitcoin::hashes::hex::{FromHex, ToHex}; +use bitcoin::network::constants::Network; +use bitcoin::util::base58; +use bitcoin::util::bip32::ExtendedPrivKey; +use bitcoin::util::key::PrivateKey; +use bitcoin::VarInt; +use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; +use hmac_sha256::HMAC; +use lazy_static::lazy_static; +use multihash::{Code, MultihashDigest}; +use parking_lot::RwLock; +use secp256k1::ecdsa::{RecoverableSignature, RecoveryId}; +use secp256k1::{Message, PublicKey, Secp256k1, SecretKey}; +use serde::{Deserialize, Serialize}; +use std::fs; +use std::fs::File; +use std::io::{Cursor, Read, Write}; +use std::path::Path; +use std::time::{SystemTime, UNIX_EPOCH}; +use tcx_common::{keccak256, merkle_hash, random_u8_16, sha256, unix_timestamp}; +use tcx_crypto::aes::cbc::{decrypt_pkcs7, encrypt_pkcs7}; +use tcx_crypto::crypto::Unlocker; +use tcx_crypto::{Crypto, EncPair, Key}; +use tcx_primitive::{PrivateKey as TraitPrivateKey, Secp256k1PrivateKey}; +use uuid::Uuid; + +// lazy_static! { +// pub static ref IDENTITY_KEYSTORE: RwLock = +// RwLock::new(IdentityKeystore::default()); +// } + +pub const IDENTITY_KEYSTORE_FILE_NAME: &'static str = "identity.json"; +pub const VERSION: u32 = 1000; + +// #[derive(Debug, Deserialize, Serialize, Clone, Default)] +// #[serde(rename_all = "camelCase")] +// pub struct IdentityKeystore { +// pub crypto: Crypto, +// pub id: String, +// pub version: u32, +// pub enc_auth_key: EncPair, +// pub enc_key: String, +// pub enc_mnemonic: EncPair, +// pub identifier: String, +// pub ipfs_id: String, +// #[serde(rename = "walletIDs")] +// pub wallet_ids: Vec, +// pub im_token_meta: Metadata, +// } + +#[derive(Debug, Deserialize, Serialize, Clone, Default)] +#[serde(rename_all = "camelCase")] +pub struct Identity { + pub enc_auth_key: EncPair, + pub enc_key: String, + pub identifier: String, + pub ipfs_id: String, +} + +impl Identity { + pub fn new(mnemonic_phrase: &str, unlocker: &Unlocker) -> Result { + let network_type = Network::Bitcoin; + + let validate_result = Mnemonic::validate(mnemonic_phrase, Language::English); + if validate_result.is_err() { + return Err(Error::InvalidMnemonic.into()); + } + let mnemonic = Mnemonic::from_phrase(mnemonic_phrase, Language::English).unwrap(); + let seed = Seed::new(&mnemonic, ""); + let master_key = ExtendedPrivKey::new_master(network_type, seed.as_ref())?; + + let salt = "Automatic Backup Key Mainnet"; + + let backup_key = HMAC::mac(salt.as_bytes(), master_key.private_key.secret_bytes()); + + let authentication_key = HMAC::mac("Authentication Key".as_bytes(), backup_key); + + let auth_private_key = PrivateKey::from_slice(authentication_key.as_ref(), network_type)?; + let secp = Secp256k1::new(); + let auth_pubkey_hash = auth_private_key.public_key(&secp).pubkey_hash(); + + let network_header = 0; + + let version = 2; + let magic_hex = "0fdc0c"; + let full_identifier = format!( + "{}{:02x}{:02x}{:02x}", + magic_hex, network_header, version, auth_pubkey_hash + ); + let identifier = base58::check_encode_slice(hex::decode(full_identifier)?.as_slice()); + + //gen enckey + let enc_key_bytes = HMAC::mac("Encryption Key".as_bytes(), backup_key); + + let enc_private_key = PrivateKey::new_uncompressed( + secp256k1::SecretKey::from_slice(enc_key_bytes.as_ref())?, + network_type, + ); + let multihash = + Code::Sha2_256.digest(enc_private_key.public_key(&secp).to_bytes().as_slice()); + let ipfs_id = base58::encode_slice(&multihash.to_bytes()); + + let master_prikey_bytes = master_key.encode(); + // let master_prikey_bytes = base58::check_encode_slice(master_prikey_bytes.as_slice()); + + // let mut crypto: Crypto = Crypto::new(password, master_prikey_bytes.as_bytes()); + // let unlocker = crypto.use_key(&Key::Password(password.to_string()))?; + + let enc_auth_key = unlocker.encrypt_with_random_iv(authentication_key.as_slice())?; + // let enc_mnemonic = unlocker.encrypt_with_random_iv(mnemonic.phrase().as_bytes())?; + + // let identity_keystore = IdentityKeystore { + // crypto, + // id: Uuid::new_v4().as_hyphenated().to_string(), + // version: VERSION, + // enc_auth_key, + // enc_key: hex::encode(enc_key_bytes), + // enc_mnemonic, + // identifier, + // ipfs_id, + // wallet_ids: vec![], + // im_token_meta: metadata, + // }; + + Ok(Identity { + enc_auth_key, + enc_key: hex::encode(enc_key_bytes), + identifier, + ipfs_id, + }) + } + + pub fn calculate_ipfs_id(pub_key: &PublicKey) -> String { + let multihash = Code::Sha2_256.digest(&pub_key.serialize_uncompressed()); + base58::encode_slice(&multihash.to_bytes()) + } + + // pub fn to_json(&self) -> Result { + // Ok(serde_json::to_string(&self)?) + // } + + // pub fn cache(&self) { + // let mut identity_keystore_obj = IDENTITY_KEYSTORE.write(); + // *identity_keystore_obj = self.to_owned(); + // } + + // pub fn flush(&self) -> Result<()> { + // let json = self.to_json()?; + // let file_dir = WALLET_KEYSTORE_DIR.read(); + // let ks_path = format!("{}/{}", file_dir, IDENTITY_KEYSTORE_FILE_NAME); + // let path = Path::new(&ks_path); + // let mut file = fs::File::create(path)?; + // let _ = file.write_all(&json.as_bytes()); + // Ok(()) + // } + + // pub fn export_identity(&self, password: &str) -> Result { + // let decrypt_data = self + // .crypto + // .use_key(&Key::Password(password.to_string()))? + // .plaintext()?; + // Ok(String::from_utf8(decrypt_data)?) + // } + + // pub fn delete_identity(&self, password: &str) -> Result<()> { + // if !self.crypto.verify_password(password) { + // return Err(Error::WalletInvalidPassword.into()); + // } + + // let clean_wallet_keystore_result = IMTKeystore::clean_keystore_dir(); + // if clean_wallet_keystore_result.is_ok() { + // IMTKeystore::clear_keystore_map(); + // let mut identity_keystore = IDENTITY_KEYSTORE.write(); + // *identity_keystore = IdentityKeystore::default(); + // } + + // Ok(()) + // } + + pub fn encrypt_ipfs(&self, plaintext: &str) -> Result { + let iv: [u8; 16] = random_u8_16(); + self.encrypt_ipfs_wth_timestamp_iv(plaintext, unix_timestamp(), &iv) + } + fn encrypt_ipfs_wth_timestamp_iv( + &self, + plaintext: &str, + timestamp: u64, + iv: &[u8; 16], + ) -> Result { + let mut header = Vec::new(); + + header.write_u8(0x03)?; + header.write_all(×tamp.to_le_bytes()[..4]); + header.write_all(iv)?; + + let enc_key = Vec::from_hex(&self.enc_key)?; + + let ciphertext = encrypt_pkcs7(plaintext.as_bytes(), &enc_key[0..16], iv)?; + let hash = keccak256(&[header.clone(), merkle_hash(&ciphertext).to_vec()].concat()); + let mut signature = Secp256k1PrivateKey::from_slice(&enc_key)?.sign_recoverable(&hash)?; + //ETH-compatible ec_recover, in chain_id = 1 case, v = 27 + rec_id + signature[64] += 27; + + let var_len = VarInt(ciphertext.len() as u64); + + let mut payload = vec![]; + payload.write_all(&header)?; + var_len.consensus_encode(&mut payload)?; + payload.write_all(&ciphertext)?; + payload.write_all(&signature)?; + + Ok(payload.to_hex()) + } + + pub fn decrypt_ipfs(&self, ciphertext: &str) -> Result { + let ciphertext = Vec::::from_hex(ciphertext)?; + if ciphertext.len() <= 21 { + return Err(Error::InvalidEncryptionData.into()); + } + + let mut rdr = Cursor::new(&ciphertext); + let mut header = vec![]; + + let version = rdr.read_u8()?; + if version != 0x03 { + return Err(Error::UnsupportEncryptionDataVersion.into()); + } + header.write_u8(version)?; + header.write_u32::(rdr.read_u32::()?)?; + + let mut iv = [0u8; 16]; + rdr.read(&mut iv)?; + header.write(&iv)?; + + let var_len = VarInt::consensus_decode(&mut rdr)?; + if var_len.0 as usize != ciphertext.len() - 21 - 65 - var_len.len() { + return Err(Error::InvalidEncryptionData.into()); + } + + let mut enc_data = vec![0u8; var_len.0 as usize]; + rdr.read(&mut enc_data)?; + + let mut signature = [0u8; 64]; + rdr.read(&mut signature)?; + + let recover_id = RecoveryId::from_i32(rdr.read_u8()? as i32 - 27)?; + + let hash = keccak256( + [header, merkle_hash(&enc_data).to_vec()] + .concat() + .as_slice(), + ); + + let message = Message::from_slice(&hash)?; + let sig = RecoverableSignature::from_compact(&signature, recover_id)?; + let pub_key = Secp256k1::new().recover_ecdsa(&message, &sig)?; + let ipfs_id = Self::calculate_ipfs_id(&pub_key); + + if self.ipfs_id != ipfs_id { + return Err(Error::InvalidEncryptionDataSignature.into()); + } + + let enc_key = Vec::from_hex(&self.enc_key)?; + let plaintext = decrypt_pkcs7(&enc_data, &enc_key[..16], &iv)?; + + Ok(String::from_utf8(plaintext)?) + } + + pub fn sign_authentication_message( + &self, + access_time: u64, + device_token: &str, + unlocker: &Unlocker, + ) -> Result { + // let unlocker = self.crypto.use_key(&Key::Password(password.to_string()))?; + let enc_auth_key = unlocker.decrypt_enc_pair(&self.enc_auth_key)?; + let mut signature = Secp256k1PrivateKey::from_slice(&enc_auth_key)?.sign_recoverable( + &keccak256(format!("{}.{}.{}", access_time, self.identifier, device_token).as_bytes()), + )?; + signature[64] += 27; + Ok(format!("0x{}", signature.to_hex())) + } + + // pub fn get_wallets(&self) -> Result> { + // let ids = &self.wallet_ids; + // let dir = WALLET_KEYSTORE_DIR.read(); + + // let mut wallets = WALLETS.write(); + // let mut ret_wallets = Vec::new(); + // let mut keystore_context = String::new(); + // for id in ids { + // if wallets.get(id.as_str()).is_some() { + // ret_wallets.push(wallets.get(id.as_str()).unwrap().clone()); + // continue; + // } + + // let path_str = format!("{}/{}.json", dir.as_str(), id); + // let path = Path::new(path_str.as_str()); + // if !path.exists() { + // return Err(Error::KeystoreFileNotExist.into()); + // } + // let mut file = File::open(&path)?; + // file.read_to_string(&mut keystore_context)?; + // let imt_keystore: IMTKeystore = serde_json::from_str(keystore_context.as_str())?; + // ret_wallets.push(imt_keystore.to_owned()); + // wallets.insert(id.to_string(), imt_keystore); + // } + // Ok(ret_wallets) + // } +} + +// pub struct Identity(); + +// impl Identity { +// pub fn get_current_identity() -> Result { +// let mut identity_keystore_obj = IDENTITY_KEYSTORE.write(); +// if !identity_keystore_obj.id.is_empty() { +// return Ok(identity_keystore_obj.to_owned()); +// } +// let dir = WALLET_KEYSTORE_DIR.read(); +// let path_str = format!("{}/{}", dir.as_str(), IDENTITY_KEYSTORE_FILE_NAME); +// let path = Path::new(path_str.as_str()); +// if !path.exists() { +// return Err(Error::KeystoreFileNotExist.into()); +// } +// let mut file = File::open(&path)?; +// let mut keystore_context = String::new(); +// file.read_to_string(&mut keystore_context)?; +// let identity_keystore: IdentityKeystore = serde_json::from_str(keystore_context.as_str())?; +// *identity_keystore_obj = identity_keystore.to_owned(); + +// Ok(identity_keystore) +// } + +// pub fn create_identity(param: CreateIdentityParam) -> Result { +// let name = param.name.as_str(); +// let password = param.password.as_str(); +// let password_hint = param.password_hint; +// let network = param.network.as_str(); +// let seg_wit = param.seg_wit.as_deref(); +// let mnemonic_phrase = tcx_primitive::generate_mnemonic(); + +// let metadata = Metadata::new( +// name, +// password_hint.clone(), +// FROM_NEW_IDENTITY, +// network, +// None, +// seg_wit, +// )?; +// let mut identity_keystore = +// IdentityKeystore::new(metadata.clone(), password, mnemonic_phrase.as_str())?; + +// let eth_keystore = Self::derive_ethereum_wallet( +// password_hint, +// FROM_NEW_IDENTITY, +// mnemonic_phrase.as_str(), +// password, +// )?; + +// identity_keystore.wallet_ids.push(eth_keystore.id); +// identity_keystore.flush()?; +// identity_keystore.cache(); + +// Ok(identity_keystore) +// } + +// pub fn recover_identity(param: RecoverIdentityParam) -> Result { +// let name = param.name.as_str(); +// let password = param.password.as_str(); +// let password_hint = param.password_hint; +// let network = param.network.as_str(); +// let seg_wit = param.seg_wit.as_deref(); +// let mnemonic_phrase = param.mnemonic.as_str(); +// let metadata = Metadata::new( +// name, +// password_hint.clone(), +// FROM_RECOVERED_IDENTITY, +// network, +// None, +// seg_wit, +// )?; +// let mut identity_keystore = IdentityKeystore::new(metadata, password, mnemonic_phrase)?; +// let eth_keystore = Self::derive_ethereum_wallet( +// password_hint, +// FROM_RECOVERED_IDENTITY, +// mnemonic_phrase, +// password, +// )?; + +// identity_keystore.wallet_ids.push(eth_keystore.id); +// identity_keystore.flush()?; +// identity_keystore.cache(); + +// Ok(identity_keystore) +// } + +// fn derive_ethereum_wallet( +// password_hint: Option, +// source: &str, +// mnemonic_phrase: &str, +// password: &str, +// ) -> Result { +// let mut metadata = Metadata::default(); +// metadata.chain_type = Some(CHAIN_TYPE_ETHEREUM.to_string()); +// metadata.password_hint = password_hint; +// metadata.source = source.to_string(); +// metadata.name = "ETH".to_string(); +// let imt_keystore = IMTKeystore::create_v3_mnemonic_keystore( +// &mut metadata, +// password, +// mnemonic_phrase, +// ETHEREUM_PATH, +// )?; +// imt_keystore.create_wallet()?; +// Ok(imt_keystore) +// } +// } + +// #[cfg(test)] +// mod test { +// use crate::identity::Identity; +// use crate::model::FROM_NEW_IDENTITY; +// use crate::wallet_api::{CreateIdentityParam, RecoverIdentityParam}; +// use tcx_constants::sample_key::{MNEMONIC, NAME, PASSWORD, PASSWORD_HINT}; +// use tcx_constants::{sample_key, TEST_MNEMONIC}; +// #[test] +// fn test_ipfs() { +// let param = RecoverIdentityParam { +// name: NAME.to_string(), +// mnemonic: MNEMONIC.to_string(), +// password: PASSWORD.to_string(), +// password_hint: Some(PASSWORD_HINT.to_string()), +// network: "TESTNET".to_string(), +// seg_wit: None, +// }; +// let keystore = Identity::recover_identity(param).unwrap(); + +// // header: data, iv, encrypted data +// let test_cases = [ +// ("imToken", "11111111111111111111111111111111", "0340b2495a1111111111111111111111111111111110b6602c68084bdd08dae796657aa6854ad13312fedc88f5b6f16c56b3e755dde125a1c4775db536ac0442ac942f9634c777f3ae5ca39f6abcae4bd6c87e54ab29ae0062b04d917b32e8d7c88eeb6261301b"), +// ("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "11111111111111111111111111111111", "0340b2495a11111111111111111111111111111111708b7e9486a339f6c482ec9d3786dd9f99222fa64753bc2e7d246b0fed9c2153b8a5dcc59ea3e320aa153ceefdd909e8484d215121a9b8416d395de38313ef65b9e27d2ba0cc17bf29c5b26fa5aa5be1a2500b017f06cdd001e8cd908c5a48f10962880a61b4704754fd6bbe3b5a1a8332376651c28205a02574ed95a70363e0d1031d133c8d2376808b74ffd78b831ec659b44e9f3d3734d26abd44dda88fac86d1a5f0128f77d0558fb1ef6d2cc8f9541c"), +// ("a", "11111111111111111111111111111111", "0340b2495a111111111111111111111111111111111084e741e2b83ec644e844985088fd58d8449cb690cd7389d74e3be1ccdca755b0235c90431b7635a441944d880bd52c860b109b7a05a960192719eb3f294ec1b72f5dfd1b8f4c6e992b9c3add7c7c1b871b"), +// ("A", "11111111111111111111111111111111", "0340b2495a1111111111111111111111111111111110de32f176b67269ddfe24b2162eae14968d2eafcb53ec5741a07a1d65dc10189e0f6b4c199e98b02fcb9ec744b134cecc4ae8bfbf79e7703781c259eab9ee2fa31f887b24d04b37b7c5aa49a3ff2a8d5e1b"), +// ("a", "11111111111111111111111111111111", "0340b2495a111111111111111111111111111111111084e741e2b83ec644e844985088fd58d8449cb690cd7389d74e3be1ccdca755b0235c90431b7635a441944d880bd52c860b109b7a05a960192719eb3f294ec1b72f5dfd1b8f4c6e992b9c3add7c7c1b871b"), +// ("a", "22222222222222222222222222222222", "0340b2495a22222222222222222222222222222222102906146aa78fadd4abac01d9aa34dbd66463220fa0a98b9212594e7624a34bb20ba50df75cb04362f8dcfe7a8c44b2b5740a2d66de015d867e609463482686959ebba6047600562fa82e94ee905f1d291c"), +// ]; + +// let unix_timestamp = 1514779200u64; +// for t in test_cases { +// let iv: [u8; 16] = hex::decode(t.1).unwrap().try_into().unwrap(); +// assert_eq!( +// keystore +// .encrypt_ipfs_wth_timestamp_iv(t.0, unix_timestamp, &iv) +// .unwrap(), +// t.2 +// ); +// assert_eq!(keystore.decrypt_ipfs(t.2).unwrap(), t.0); +// } +// } + +// #[test] +// fn test_authentication() { +// let test_cases = [ +// ("MAINNET", "0x120cc977f9023c90635144bd0f4c8b85ff8aa23c003edcced9449f0465d05e954bccf9c114484e472c1837b0394f1933ad78ec8050673099e8bf5e9329737fe01c"), +// ("TESTNET", "0x663ace6d60225f6d1a71d25735c66646f71977a9f25f709fca162db3c664a1e161881a51a8034c240dd8f0093285fd6245f65246708546e8eadd592f995daeb11c"), +// ]; + +// for item in test_cases { +// let param = RecoverIdentityParam { +// name: NAME.to_string(), +// mnemonic: MNEMONIC.to_string(), +// password: PASSWORD.to_string(), +// password_hint: Some(PASSWORD_HINT.to_string()), +// network: item.0.to_string(), +// seg_wit: None, +// }; +// let keystore = Identity::recover_identity(param).unwrap(); + +// let actual = keystore +// .sign_authentication_message(1514736000, "12345ABCDE", PASSWORD) +// .unwrap(); +// assert_eq!(actual, item.1); +// } +// } + +// #[test] +// fn test_create_identity() { +// let param = CreateIdentityParam { +// name: sample_key::NAME.to_string(), +// password: PASSWORD.to_string(), +// password_hint: Some(PASSWORD_HINT.to_string()), +// network: "TESTNET".to_string(), +// seg_wit: None, +// }; +// let identity_keystore = Identity::create_identity(param).unwrap(); +// let imt_keystore_id = identity_keystore.wallet_ids.get(0).unwrap(); +// let ret = Identity::get_current_identity(); +// assert_eq!(ret.is_ok(), true); +// assert_eq!(ret.unwrap().wallet_ids.get(0).unwrap(), imt_keystore_id); +// } + +// #[test] +// fn test_recover_identity() { +// let param = RecoverIdentityParam { +// name: NAME.to_string(), +// mnemonic: MNEMONIC.to_string(), +// password: PASSWORD.to_string(), +// password_hint: Some(PASSWORD_HINT.to_string()), +// network: "TESTNET".to_string(), +// seg_wit: None, +// }; +// let recover_result = Identity::recover_identity(param); +// assert_eq!(recover_result.is_ok(), true); +// let identity_keystore = Identity::get_current_identity().unwrap(); +// let wallets = identity_keystore.get_wallets().unwrap(); +// assert_eq!(wallets.len(), 1); +// assert_eq!( +// "6031564e7b2f5cc33737807b2e58daff870b590b", +// wallets.get(0).unwrap().address +// ); +// } +// } diff --git a/token-core/tcx-chain/src/keystore/guard.rs b/token-core/tcx-keystore/src/keystore/guard.rs similarity index 100% rename from token-core/tcx-chain/src/keystore/guard.rs rename to token-core/tcx-keystore/src/keystore/guard.rs diff --git a/token-core/tcx-chain/src/keystore/hd.rs b/token-core/tcx-keystore/src/keystore/hd.rs similarity index 97% rename from token-core/tcx-chain/src/keystore/hd.rs rename to token-core/tcx-keystore/src/keystore/hd.rs index fba3f9ff..174e3545 100644 --- a/token-core/tcx-chain/src/keystore/hd.rs +++ b/token-core/tcx-keystore/src/keystore/hd.rs @@ -1,5 +1,6 @@ use bip39::{Language, Mnemonic, Seed}; +use crate::identity::Identity; use uuid::Uuid; use super::Account; @@ -19,11 +20,13 @@ use tcx_primitive::{ TypedDeterministicPublicKey, TypedPrivateKey, }; +#[derive(Clone)] struct Cache { mnemonic: String, keys: HashMap, } +#[derive(Clone)] pub struct HdKeystore { store: Store, cache: Option, @@ -167,6 +170,8 @@ impl HdKeystore { let key_hash = key_hash_from_mnemonic(mnemonic)?; let crypto: Crypto = Crypto::new(password, mnemonic.as_bytes()); + let unlocker = crypto.use_key(&Key::Password(password.to_string()))?; + let identity = Identity::new(mnemonic, &unlocker)?; Ok(HdKeystore { store: Store { key_hash, @@ -175,6 +180,7 @@ impl HdKeystore { version: Self::VERSION, active_accounts: vec![], meta, + identity: Some(identity), }, cache: None, @@ -222,6 +228,10 @@ impl HdKeystore { self.store.account(symbol, address) } + pub fn identity(&self) -> Option<&Identity> { + self.store().identity.as_ref() + } + pub(crate) fn verify_password(&self, password: &str) -> bool { self.store.crypto.verify_password(password) } diff --git a/token-core/tcx-chain/src/keystore/mod.rs b/token-core/tcx-keystore/src/keystore/mod.rs similarity index 95% rename from token-core/tcx-chain/src/keystore/mod.rs rename to token-core/tcx-keystore/src/keystore/mod.rs index d3c8c96f..561209a5 100644 --- a/token-core/tcx-chain/src/keystore/mod.rs +++ b/token-core/tcx-keystore/src/keystore/mod.rs @@ -1,4 +1,5 @@ use super::Result; +use std::fmt; use std::str::FromStr; use std::time::{SystemTime, UNIX_EPOCH}; @@ -9,16 +10,17 @@ mod private; use serde::{Deserialize, Serialize}; use tcx_constants::{CoinInfo, CurveType}; +use tcx_crypto::crypto::Unlocker; pub use self::{ guard::KeystoreGuard, hd::key_hash_from_mnemonic, hd::HdKeystore, private::key_hash_from_private_key, private::PrivateKeystore, }; +use crate::identity::Identity; use crate::signer::ChainSigner; use tcx_crypto::{Crypto, Key}; use tcx_primitive::{TypedDeterministicPublicKey, TypedPrivateKey, TypedPublicKey}; - #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct Store { @@ -26,6 +28,7 @@ pub struct Store { pub version: i64, pub key_hash: String, pub crypto: Crypto, + pub identity: Option, pub active_accounts: Vec, #[serde(rename = "imTokenMeta")] @@ -130,6 +133,35 @@ pub enum Source { RecoveredIdentity, } +impl FromStr for Source { + type Err = failure::Error; + + fn from_str(input: &str) -> std::result::Result { + match input { + "WIF" => Ok(Source::Wif), + "PRIVATE" => Ok(Source::Private), + "KEYSTORE" => Ok(Source::Keystore), + "MNEMONIC" => Ok(Source::Mnemonic), + "NEW_IDENTITY" => Ok(Source::NewIdentity), + "RECOVER_IDENTITY" => Ok(Source::RecoveredIdentity), + _ => Err(format_err!("unknown_source")), + } + } +} + +impl fmt::Display for Source { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Source::Wif => write!(f, "WIF"), + Source::Private => write!(f, "PRIVATE"), + Source::Keystore => write!(f, "KEYSTORE"), + Source::Mnemonic => write!(f, "MNEMONIC"), + Source::NewIdentity => write!(f, "NEW_IDENTITY"), + Source::RecoveredIdentity => write!(f, "RECOVER_IDENTITY"), + } + } +} + /// Metadata of fixtures, for presenting wallet data #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] @@ -163,6 +195,7 @@ impl Default for Metadata { } } +#[derive(Clone)] pub enum Keystore { PrivateKey(PrivateKeystore), Hd(HdKeystore), @@ -250,6 +283,10 @@ impl Keystore { } } + pub fn use_key(&self, key: &Key) -> Result { + self.store().crypto.use_key(key) + } + #[cfg(feature = "cache_dk")] pub fn get_derived_key(&mut self, password: &str) -> Result { Ok(hex::encode( @@ -369,6 +406,13 @@ impl Keystore { } } + pub fn identity(&self) -> Option<&Identity> { + match self { + Keystore::Hd(ks) => ks.store().identity.as_ref(), + Keystore::PrivateKey(_) => None, + } + } + pub fn account(&self, symbol: &str, address: &str) -> Option<&Account> { match self { Keystore::PrivateKey(ks) => ks.account(symbol, address), diff --git a/token-core/tcx-chain/src/keystore/private.rs b/token-core/tcx-keystore/src/keystore/private.rs similarity index 99% rename from token-core/tcx-chain/src/keystore/private.rs rename to token-core/tcx-keystore/src/keystore/private.rs index 4341c2f8..4f6449d5 100644 --- a/token-core/tcx-chain/src/keystore/private.rs +++ b/token-core/tcx-keystore/src/keystore/private.rs @@ -15,6 +15,7 @@ pub fn key_hash_from_private_key(data: &[u8]) -> String { hex::encode(dsha256(data)[..20].to_vec()) } +#[derive(Clone)] pub struct PrivateKeystore { store: Store, @@ -117,6 +118,7 @@ impl PrivateKeystore { id: Uuid::new_v4().as_hyphenated().to_string(), version: PrivateKeystore::VERSION, active_accounts: vec![], + identity: None, }; PrivateKeystore { diff --git a/token-core/tcx-chain/src/lib.rs b/token-core/tcx-keystore/src/lib.rs similarity index 69% rename from token-core/tcx-chain/src/lib.rs rename to token-core/tcx-keystore/src/lib.rs index 4296fc31..2a7bb7bf 100644 --- a/token-core/tcx-chain/src/lib.rs +++ b/token-core/tcx-keystore/src/lib.rs @@ -19,6 +19,7 @@ use core::result; extern crate failure; extern crate regex; +pub mod identity; pub mod keystore; mod signer; @@ -31,6 +32,28 @@ pub use signer::{ChainSigner, HashSigner, MessageSigner, TransactionSigner}; pub type Result = result::Result; +#[derive(Fail, Debug, PartialOrd, PartialEq)] +pub enum Error { + #[fail(display = "network_params_invalid")] + NetworkParamsInvalid, + #[fail(display = "unsupported_chain")] + WalletInvalidType, + #[fail(display = "wallet_not_found")] + WalletNotFound, + #[fail(display = "keystore_file_not_exist")] + KeystoreFileNotExist, + #[fail(display = "password_incorrect")] + WalletInvalidPassword, + #[fail(display = "invalid_mnemonic")] + InvalidMnemonic, + #[fail(display = "unsupport_encryption_data_version")] + UnsupportEncryptionDataVersion, + #[fail(display = "invalid_encryption_data_signature")] + InvalidEncryptionDataSignature, + #[fail(display = "invalid_encryption_data")] + InvalidEncryptionData, +} + pub trait PublicKeyEncoder { fn encode(&self, public_key: &[u8]) -> Result; } diff --git a/token-core/tcx-chain/src/signer.rs b/token-core/tcx-keystore/src/signer.rs similarity index 100% rename from token-core/tcx-chain/src/signer.rs rename to token-core/tcx-keystore/src/signer.rs diff --git a/token-core/tcx-migration/Cargo.toml b/token-core/tcx-migration/Cargo.toml index 08a60df2..8e2d11a5 100644 --- a/token-core/tcx-migration/Cargo.toml +++ b/token-core/tcx-migration/Cargo.toml @@ -8,16 +8,17 @@ edition = "2021" [dependencies] tcx-proto = { path = "../tcx-proto" } tcx-crypto = { path = "../tcx-crypto", features = ["cache_dk"] } -tcx-chain = { path = "../tcx-chain", features = ["cache_dk"] } -tcx-atom = {path = "../tcx-atom"} -tcx-eos = {path = "../tcx-eos"} -tcx-btc-kin = {path = "../tcx-btc-kin"} +tcx-keystore = { path = "../tcx-keystore", features = ["cache_dk"] } +tcx-atom = { path = "../tcx-atom" } +tcx-eos = { path = "../tcx-eos" } +tcx-btc-kin = { path = "../tcx-btc-kin" } tcx-primitive = { path = "../tcx-primitive" } -tcx-constants = { path = "../tcx-constants"} -tcx-tezos = {path = "../tcx-tezos"} -tcx-eth2 = {path = "../tcx-eth2"} -tcx-eth = {path = "../tcx-eth" } -tcx-common = {path = "../tcx-common" } +tcx-constants = { path = "../tcx-constants" } +tcx-tezos = { path = "../tcx-tezos" } +tcx-tron = { path = "../tcx-tron" } +tcx-eth2 = { path = "../tcx-eth2" } +tcx-eth = { path = "../tcx-eth" } +tcx-common = { path = "../tcx-common" } #zksync-crypto = { git="https://github.com/consenlabs/zksync", branch="hotfix/compatible_with_tcx" } prost = "0.11.2" @@ -34,4 +35,4 @@ hex = "0.4.3" base64 = "0.13.1" base58 = "0.2.0" parking_lot = "0.12.1" -ethers = "2.0.3" \ No newline at end of file +ethers = "2.0.3" diff --git a/token-core/tcx-migration/src/migration.rs b/token-core/tcx-migration/src/migration.rs index d9deb57b..4bae8121 100644 --- a/token-core/tcx-migration/src/migration.rs +++ b/token-core/tcx-migration/src/migration.rs @@ -1,41 +1,56 @@ use core::fmt; -use std::str::FromStr; +use std::{fs, str::FromStr}; use failure::format_err; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Deserializer, Serialize}; use tcx_atom::address::AtomAddress; -use tcx_chain::{ +use tcx_constants::CoinInfo; +use tcx_crypto::{Crypto, EncPair, KdfParams, Key}; +use tcx_keystore::{ key_hash_from_mnemonic, key_hash_from_private_key, Account, HdKeystore, PrivateKeystore, Result, Source, }; -use tcx_constants::CoinInfo; -use tcx_crypto::{Crypto, EncPair, KdfParams, Key}; use tcx_btc_kin::address::BtcKinAddress; use tcx_btc_kin::Error; -use tcx_chain::keystore::{Keystore, Metadata, Store}; use tcx_eos::address::EosAddress; use tcx_eth::address::EthAddress; +use tcx_keystore::identity::Identity; +use tcx_keystore::keystore::{Keystore, Metadata, Store}; use tcx_primitive::{PrivateKey, Secp256k1PrivateKey}; +#[derive(Debug, Serialize, Deserialize, Clone)] +#[serde(untagged)] +pub enum NumberOrNumberStr { + Number(i64), + NumberStr(String), +} + #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct OldMetadata { pub name: String, - pub chain_type: String, + pub chain_type: Option, + pub chain: Option, pub network: Option, pub password_hint: String, - pub timestamp: i64, + pub timestamp: NumberOrNumberStr, pub source: Source, pub seg_wit: Option, } impl OldMetadata { pub fn to_metadata(&self) -> Metadata { + let timestamp = match &self.timestamp { + NumberOrNumberStr::Number(num) => num.clone(), + NumberOrNumberStr::NumberStr(str) => { + f64::from_str(&str).expect("f64 from timestamp") as i64 + } + }; Metadata { name: self.name.clone(), password_hint: self.password_hint.clone(), - timestamp: self.timestamp, + timestamp: timestamp, source: self.source, } } @@ -83,7 +98,17 @@ impl LegacyKeystore { derivation_path = "".to_string(); } - match self.im_token_meta.chain_type.as_str() { + let chain_type = if self.im_token_meta.chain_type.is_some() { + self.im_token_meta.chain_type.as_ref().unwrap().to_string() + } else { + self.im_token_meta + .chain + .as_ref() + .expect("meta chain") + .to_string() + }; + + match chain_type.as_str() { "ETHEREUM" => { let coin_info = CoinInfo { coin: "ETHEREUM".to_string(), @@ -158,12 +183,14 @@ impl LegacyKeystore { let mnemonic = String::from_utf8(mnemonic_data.to_owned())?; let key_hash = key_hash_from_mnemonic(&mnemonic)?; + let identity = Identity::new(&mnemonic, &unlocker)?; let mut store = Store { id: self.id.to_string(), version: HdKeystore::VERSION, key_hash: key_hash.to_string(), crypto: self.crypto.clone(), active_accounts: vec![], + identity: Some(identity), meta: self.im_token_meta.to_metadata(), }; @@ -196,6 +223,7 @@ impl LegacyKeystore { crypto: self.crypto.clone(), active_accounts: vec![], meta: self.im_token_meta.to_metadata(), + identity: None, }; let unlocker = self.crypto.use_key(key)?; @@ -214,7 +242,7 @@ impl LegacyKeystore { pub fn migrate_identity_wallets( &self, key: &Key, - new_keystore: &mut Keystore, + new_keystore: Option, ) -> Result { let mut keystore = self.migrate_to_hd(key)?; keystore.unlock(key)?; @@ -226,11 +254,14 @@ impl LegacyKeystore { tcx_eos::eos::enable_account("EOS", 0, &mut keystore)?; // TODO: Create identity wallets + if let Some(exists_keystore) = new_keystore { + // TODO Backup old file + keystore.merge(&exists_keystore)?; - new_keystore.merge(&keystore)?; - - new_keystore.store_mut().crypto = keystore.store().crypto.clone(); - new_keystore.store_mut().id = keystore.id().to_string(); + // 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) } @@ -239,14 +270,16 @@ impl LegacyKeystore { #[cfg(test)] mod tests { use serde_json::Value; - use tcx_chain::{Keystore, KeystoreGuard, Metadata, PrivateKeystore, Source}; - use tcx_constants::TEST_MNEMONIC; use tcx_constants::TEST_PASSWORD; + use tcx_constants::{CoinInfo, TEST_MNEMONIC}; use tcx_crypto::crypto::SCryptParams; use tcx_crypto::hex; use tcx_crypto::Crypto; use tcx_crypto::Pbkdf2Params; use tcx_crypto::{EncPair, Key}; + use tcx_keystore::keystore::Store; + use tcx_keystore::HdKeystore; + use tcx_keystore::{Keystore, KeystoreGuard, Metadata, PrivateKeystore, Source}; use super::LegacyKeystore; @@ -274,10 +307,18 @@ mod tests { include_str!("../test/fixtures/175169f7-5a35-4df7-93c1-1ff612168e71.json") } + fn tcx_ks() -> &'static str { + include_str!("../test/fixtures/b05a0ff9-885a-4a31-9d82-6477d34d1e37.json") + } + fn identity() -> &'static str { include_str!("../test/fixtures/identity.json") } + fn ios_metadata() -> &'static str { + include_str!("../test/fixtures/5991857a-2488-4546-b730-463a5f84ea6a") + } + #[test] fn test_eos_private_key() { let keystore_str = v3_eos_private_key(); @@ -361,4 +402,62 @@ mod tests { .account("ETHEREUM", "0x6031564e7b2f5cc33737807b2e58daff870b590b") .is_some()); } + + #[test] + fn test_migrate_dk_keystore() { + let keystore_str = v3_eth_mnemonic(); + let ks = LegacyKeystore::from_json_str(keystore_str).unwrap(); + + let derived_key_hex = "3223cd3abf2422d0ad3503f73aaa6e7e36a555385c6825b383908c1e8acf5e9d9a4c751809473c75599a632fe5b1437f51a3a848e054d9c170f8c3b5c5701b8b"; + let keystore = ks + .migrate_identity_wallets(&Key::DerivedKey(derived_key_hex.to_string()), None) + .unwrap(); + + assert_eq!(keystore.accounts().len(), 11); + assert_eq!(keystore.derivable(), true); + assert_eq!(keystore.id(), "175169f7-5a35-4df7-93c1-1ff612168e71"); + assert!(keystore + .account("ETHEREUM", "0x6031564e7b2f5cc33737807b2e58daff870b590b") + .is_some()); + } + + #[test] + fn test_migrate_dk_exists_keystore() { + let keystore_str = v3_eth_mnemonic(); + let ks = LegacyKeystore::from_json_str(keystore_str).unwrap(); + + let existed_ks = Keystore::from_json(tcx_ks()).unwrap(); + + let derived_key_hex = "3223cd3abf2422d0ad3503f73aaa6e7e36a555385c6825b383908c1e8acf5e9d9a4c751809473c75599a632fe5b1437f51a3a848e054d9c170f8c3b5c5701b8b"; + let keystore = ks + .migrate_identity_wallets( + &Key::DerivedKey(derived_key_hex.to_string()), + Some(existed_ks), + ) + .unwrap(); + + assert_eq!(keystore.accounts().len(), 12); + assert_eq!(keystore.derivable(), true); + assert_eq!(keystore.id(), "175169f7-5a35-4df7-93c1-1ff612168e71"); + assert!(keystore + .account("ETHEREUM", "0x6031564e7b2f5cc33737807b2e58daff870b590b") + .is_some()); + assert!(keystore + .account("TRON", "TY2uroBeZ5trA9QT96aEWj32XLkAAhQ9R2") + .is_some()); + } + + #[test] + fn test_ios_metadata() { + let keystore_str = ios_metadata(); + let ks = LegacyKeystore::from_json_str(keystore_str).unwrap(); + let keystore = ks.migrate(&Key::Password("imtoken1".to_string())).unwrap(); + + assert_eq!(keystore.accounts().len(), 1); + assert_eq!(keystore.derivable(), true); + assert_eq!(keystore.id(), "5991857a-2488-4546-b730-463a5f84ea6a"); + assert!(keystore + .account("ETHEREUM", "0x6031564e7b2f5cc33737807b2e58daff870b590b") + .is_some()); + } } diff --git a/token-core/tcx-migration/test/fixtures/5991857a-2488-4546-b730-463a5f84ea6a b/token-core/tcx-migration/test/fixtures/5991857a-2488-4546-b730-463a5f84ea6a new file mode 100644 index 00000000..c2d312f3 --- /dev/null +++ b/token-core/tcx-migration/test/fixtures/5991857a-2488-4546-b730-463a5f84ea6a @@ -0,0 +1,37 @@ +{ + "id": "5991857a-2488-4546-b730-463a5f84ea6a", + "crypto": { + "cipher": "aes-128-ctr", + "cipherparams": { + "iv": "3a442e8b02843edf71b8d3a9c9da2c3b" + }, + "ciphertext": "fcbcceae1d239f9575c55f4c4f81eeba44a6ad9d948f544af2ffee9efef2c038", + "kdf": "pbkdf2", + "kdfparams": { + "c": 65535, + "dklen": 32, + "prf": "hmac-sha256", + "salt": "fa141145a343d9b6c7e2f12e0e56d564bc4d1b46cd48e8f7d898779e06357f1f" + }, + "mac": "50ee3b40129c5f18f9ff6982db0eb18504ea2e8f3d96e4ac062b4eb5849cf011" + }, + "version": 3, + "address": "6031564e7b2f5cc33737807b2e58daff870b590b", + "encMnemonic": { + "encStr": "267bda938e4edbf7c420e89c59c6862f9127c7275d012b1b607f9e91ddb94574e81e94f6d8155e3c85ede03f584e09916122f03c72b67a1f96ddbf291beb46894d9a02d30170a9444692", + "nonce": "3cfe9f0b32b5d592e5fab54bd28863cd" + }, + "mnemonicPath": "m/44'/60'/0'/0/0", + "imTokenMeta": { + "mode": "normal", + "chain": "ETHEREUM", + "segWit": "NONE", + "passwordHint": "", + "backup": [], + "source": "RECOVERED_IDENTITY", + "version": "iOS-2.14.0.1742", + "name": "ETH", + "network": "MAINNET", + "timestamp": "1695800371.215641" + } +} \ No newline at end of file diff --git a/token-core/tcx-migration/test/fixtures/b05a0ff9-885a-4a31-9d82-6477d34d1e37.json b/token-core/tcx-migration/test/fixtures/b05a0ff9-885a-4a31-9d82-6477d34d1e37.json new file mode 100644 index 00000000..6da6572b --- /dev/null +++ b/token-core/tcx-migration/test/fixtures/b05a0ff9-885a-4a31-9d82-6477d34d1e37.json @@ -0,0 +1,47 @@ +{ + "id": "b05a0ff9-885a-4a31-9d82-6477d34d1e37", + "version": 11000, + "keyHash": "512115eca3ae86646aeb06861d551e403b543509", + "crypto": { + "cipher": "aes-128-ctr", + "cipherparams": { + "iv": "080d1850e994e9a8efab603c3b8e6258" + }, + "ciphertext": "518f80dfd42d8cd90842d0849d3198d44d4e49cc1f7543a6e73a23c576ba4e3833b946676d97e574bf5a0201f15742740b287db4a7b0a9afb6678d966797249ace94c2c0d46187ae9657", + "kdf": "pbkdf2", + "kdfparams": { + "c": 262144, + "prf": "hmac-sha256", + "dklen": 32, + "salt": "08c8c9ab072b92b2081db806d3f6309dc336a4a632b8a80a7e993ceca5aeaf34" + }, + "mac": "200a54358c0eb0032396906b9280c5331280cafce4e826d9d66b0dd86e5dcb66" + }, + "identity": { + "encAuthKey": { + "encStr": "00a4c73e5196d143842a0a26916e3c1fc1190e44580bfe62fcd85ee1eaf9a4e9", + "nonce": "afecf1dde1f383d81b03f149f67e0057" + }, + "encKey": "ef806a542bcc30da7ce60fc37bd6cc91619b482f6f070af3a9d7b042087886f3", + "identifier": "im14x5GXsdME4JsrHYe2wvznqRz4cUhx2pA4HPf", + "ipfsId": "QmWqwovhrZBMmo32BzY83ZMEBQaP7YRMqXNmMc8mgrpzs6" + }, + "activeAccounts": [ + { + "address": "TY2uroBeZ5trA9QT96aEWj32XLkAAhQ9R2", + "derivationPath": "m/44'/195'/0'/0/0", + "curve": "SECP256k1", + "coin": "TRON", + "network": "MAINNET", + "segWit": "NONE", + "extPubKey": "037e39800880000000988ebb67dc6f173948ccaed62dd3c4b60fea66a4280da93eed971b55d4e6e9c203349ff19e96c1aa7f568e493f85fa506320410245b4e69146bb0d3d8b5df3b901", + "publicKey": "037b5253c24ce2a293566f9e066051366cda5073e4a43b25f07c990d7c9ac0aab5" + } + ], + "imTokenMeta": { + "name": "Unknown", + "passwordHint": "", + "timestamp": 1695733663, + "source": "MNEMONIC" + } +} \ No newline at end of file diff --git a/token-core/tcx-primitive/src/bip32.rs b/token-core/tcx-primitive/src/bip32.rs index 44b8f444..906871c9 100644 --- a/token-core/tcx-primitive/src/bip32.rs +++ b/token-core/tcx-primitive/src/bip32.rs @@ -16,8 +16,10 @@ use byteorder::ByteOrder; use bip39::{Language, Mnemonic}; +#[derive(Clone)] pub struct Bip32DeterministicPrivateKey(ExtendedPrivKey); +#[derive(Clone)] pub struct Bip32DeterministicPublicKey(ExtendedPubKey); impl From for KeyError { diff --git a/token-core/tcx-primitive/src/ecc.rs b/token-core/tcx-primitive/src/ecc.rs index 568c843f..10e56656 100644 --- a/token-core/tcx-primitive/src/ecc.rs +++ b/token-core/tcx-primitive/src/ecc.rs @@ -306,6 +306,7 @@ impl Derive for TypedDeterministicPublicKey { } } +#[derive(Clone)] pub enum TypedDeterministicPrivateKey { Bip32Sepc256k1(Bip32DeterministicPrivateKey), SubSr25519(Sr25519PrivateKey), diff --git a/token-core/tcx-primitive/src/ed25519_bip32.rs b/token-core/tcx-primitive/src/ed25519_bip32.rs index 9ac318dc..c8d2e183 100644 --- a/token-core/tcx-primitive/src/ed25519_bip32.rs +++ b/token-core/tcx-primitive/src/ed25519_bip32.rs @@ -12,8 +12,10 @@ use crate::{ }; use bip39::{Language, Mnemonic}; +#[derive(Clone)] pub struct Ed25519DeterministicPrivateKey(EdExtPrivateKey); +#[derive(Clone)] pub struct Ed25519DeterministicPublicKey(EdPublicKey); impl Ed25519DeterministicPrivateKey { diff --git a/token-core/tcx-proto/build.rs b/token-core/tcx-proto/build.rs index b1b3b36f..c7ba83be 100644 --- a/token-core/tcx-proto/build.rs +++ b/token-core/tcx-proto/build.rs @@ -59,10 +59,6 @@ fn main() { env::set_var("OUT_DIR", "../tcx-eth2/src"); prost_build::compile_protos(&["src/eth2.proto"], &["src/"]).unwrap(); - //tcx-identity - env::set_var("OUT_DIR", "../tcx-identity/src"); - prost_build::compile_protos(&["src/wallet_api.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/keystore_migration.proto b/token-core/tcx-proto/src/keystore_migration.proto index 9f0fcbfe..2de2f36d 100644 --- a/token-core/tcx-proto/src/keystore_migration.proto +++ b/token-core/tcx-proto/src/keystore_migration.proto @@ -1,18 +1,9 @@ syntax = "proto3"; package api; -message IdentityMigrationParam { - string id = 1; - string tcxId = 2; - string password = 3; - string derivedKey = 4; - string json_str = 5; -} - message KeystoreMigrationParam { string id = 1; string tcxId = 2; string password = 3; string derivedKey = 4; - string json_str = 5; } diff --git a/token-core/tcx-proto/src/params.proto b/token-core/tcx-proto/src/params.proto index 910c8cc2..2bd3505b 100644 --- a/token-core/tcx-proto/src/params.proto +++ b/token-core/tcx-proto/src/params.proto @@ -12,6 +12,12 @@ message HdStoreCreateParam { string password = 1; string passwordHint = 2; string name = 3; + string source = 4; +} + +message IdentityResult { + string identifier = 1; + string ipfsId = 2; } message WalletResult { @@ -20,6 +26,7 @@ message WalletResult { string source = 3; repeated AccountResponse accounts = 4; int64 createdAt = 5; + optional IdentityResult identity = 6; } // FUNCTION: hd_store_import(HdStoreImportParam): WalletResult @@ -148,44 +155,58 @@ message SignParam { /// Other // TODO: annotate following message usage -// btc-fork -message ExternalAddressParam { + +message CalcExternalAddressParam { string id = 1; - string chainType = 2; - uint32 externalIdx = 3; + string network = 2; + string segWit = 3; + uint32 externalIdx = 4; } -message ExternalAddressResult { +message CalcExternalAddressResult { string address = 1; - string derivedPath = 2; - string type = 3; + string type = 2; + string derivedPath = 3; } -message ExternalAddressExtra { - string encXpub = 1; - message ExternalAddress { - string address = 1; - string derivedPath = 2; - string type = 3; - } - ExternalAddress externalAddress = 2; -} +// btc-fork +// message ExternalAddressParam { +// string id = 1; +// string chainType = 2; +// uint32 externalIdx = 3; +// } -message BtcForkDeriveExtraParam { - string network = 1; - string segWit = 2; -} +// message ExternalAddressResult { +// string address = 1; +// string derivedPath = 2; +// string type = 3; +// } -message HdStoreExtendedPublicKeyParam { - string id = 1; - string password = 2; - string chainType = 3; - string address = 4; -} +// message ExternalAddressExtra { +// string encXpub = 1; +// message ExternalAddress { +// string address = 1; +// string derivedPath = 2; +// string type = 3; +// } +// ExternalAddress externalAddress = 2; +// } -message HdStoreExtendedPublicKeyResponse { - string extendedPublicKey = 1; -} +// message BtcForkDeriveExtraParam { +// string network = 1; +// string segWit = 2; +// } + +// message HdStoreExtendedPublicKeyParam { +// string id = 1; +// string password = 2; +// string chainType = 3; +// string address = 4; +// } + +// message HdStoreExtendedPublicKeyResponse { +// string extendedPublicKey = 1; +// } message PublicKeyParam { string id = 1; @@ -201,8 +222,74 @@ message PublicKeyResult { } -message KeystoreUpdateAccount { +// message KeystoreUpdateAccount { +// string id = 1; +// string password = 2; +// string accountName = 3; +// } + + + + +message RemoveWalletsParam { + string identifier = 1; + string password = 2; +} + +message RemoveWalletsResult { + string identifier = 1; +} + +message V3KeystoreImportInput { + string keystore = 1; + string password = 2; + bool overwrite = 3; + string name = 4; + string chainType = 5; + string source = 6; +} + +message V3KeystoreExportInput { string id = 1; string password = 2; - string accountName = 3; +} + +message V3KeystoreExportOutput { + string json = 1; +} + +message EncryptDataToIpfsParam { + string identifier = 1; + string content = 2; +} + +message EncryptDataToIpfsResult { + string identifier = 1; + string encrypted = 2; +} + +message DecryptDataFromIpfsParam { + string identifier = 1; + string encrypted = 2; +} + +message DecryptDataFromIpfsResult { + string identifier = 1; + string content = 2; +} + +message SignAuthenticationMessageParam { + uint64 accessTime = 1; + string identifier = 2 ; + string deviceToken = 3; + string password = 4; +} + +message SignAuthenticationMessageResult { + uint64 accessTime = 1; + string signature = 2; +} + +message GenerateMnemonicResult { + string mnemonic = 1; } \ No newline at end of file diff --git a/token-core/tcx-proto/src/wallet_api.proto b/token-core/tcx-proto/src/wallet_api.proto index 0e4da99f..97d6e892 100644 --- a/token-core/tcx-proto/src/wallet_api.proto +++ b/token-core/tcx-proto/src/wallet_api.proto @@ -1,18 +1,13 @@ syntax = "proto3"; package wallet_api; -import "google/protobuf/any.proto"; - -message GenerateMnemonicResult { - string mnemonic = 1; -} message CreateIdentityParam { string name = 1; string password = 2; optional string passwordHint = 3; string network = 4; - optional string segWit = 5; + string segWit = 5; } message CreateIdentityResult { @@ -27,6 +22,15 @@ message Wallet{ 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 { @@ -73,7 +77,7 @@ message RecoverIdentityParam { string password = 3; optional string passwordHint = 4; string network = 5; - optional string segWit = 6; + string segWit = 6; } message RecoverIdentityResult { @@ -83,64 +87,6 @@ message RecoverIdentityResult { repeated Wallet wallets = 4; } -message RemoveIdentityParam { - string identifier = 1; - string password = 2; -} - -message RemoveIdentityResult { - string identifier = 1; -} - -message V3KeystoreImportInput { - string keystore = 1; - string password = 2; - bool overwrite = 3; - string name = 4; - string chainType = 5; - string source = 6; -} - -message V3KeystoreExportInput { - string id = 1; - string password = 2; -} - -message V3KeystoreExportOutput { - string json = 1; -} - -message EncryptDataToIpfsParam { - string identifier = 1; - string content = 2; -} - -message EncryptDataToIpfsResult { - string identifier = 1; - string encrypted = 2; -} - -message DecryptDataFromIpfsParam { - string identifier = 1; - string encrypted = 2; -} - -message DecryptDataFromIpfsResult { - string identifier = 1; - string content = 2; -} - -message SignAuthenticationMessageParam { - uint64 accessTime = 1; - string identifier = 2 ; - string deviceToken = 3; - string password = 4; -} - -message SignAuthenticationMessageResult { - uint64 accessTime = 1; - string signature = 2; -} diff --git a/token-core/tcx-substrate/Cargo.toml b/token-core/tcx-substrate/Cargo.toml index 6780640e..90da6c48 100644 --- a/token-core/tcx-substrate/Cargo.toml +++ b/token-core/tcx-substrate/Cargo.toml @@ -8,7 +8,7 @@ edition = "2021" [dependencies] tcx-crypto = { path = "../tcx-crypto" } -tcx-chain = { path = "../tcx-chain" } +tcx-keystore = { path = "../tcx-keystore" } tcx-primitive = { path = "../tcx-primitive" } tcx-constants = { path = "../tcx-constants" } diff --git a/token-core/tcx-substrate/src/address.rs b/token-core/tcx-substrate/src/address.rs index ea608984..0f12c3d4 100644 --- a/token-core/tcx-substrate/src/address.rs +++ b/token-core/tcx-substrate/src/address.rs @@ -2,8 +2,8 @@ use sp_core::crypto::Ss58AddressFormat; use sp_core::crypto::Ss58Codec; use sp_core::sr25519::Public; use std::str::FromStr; -use tcx_chain::Address; use tcx_constants::{CoinInfo, Result}; +use tcx_keystore::Address; use tcx_primitive::{PublicKey, Sr25519PublicKey, TypedPublicKey}; #[derive(PartialEq, Eq, Clone)] diff --git a/token-core/tcx-substrate/src/keystore.rs b/token-core/tcx-substrate/src/keystore.rs index 92001f5f..a891a44f 100644 --- a/token-core/tcx-substrate/src/keystore.rs +++ b/token-core/tcx-substrate/src/keystore.rs @@ -2,7 +2,7 @@ use crate::SubstrateAddress; use rand::Rng; use serde::{de, Deserialize, Deserializer, Serialize}; use std::convert::TryInto; -use tcx_chain::Address; +use tcx_keystore::Address; use byteorder::LittleEndian; use byteorder::{ReadBytesExt, WriteBytesExt}; diff --git a/token-core/tcx-substrate/src/lib.rs b/token-core/tcx-substrate/src/lib.rs index 9d4db028..1ca81d46 100644 --- a/token-core/tcx-substrate/src/lib.rs +++ b/token-core/tcx-substrate/src/lib.rs @@ -17,8 +17,8 @@ extern crate failure; extern crate serde_json; pub mod polkadot { - use tcx_chain::{Account, Keystore}; use tcx_constants::{CoinInfo, CurveType}; + use tcx_keystore::{Account, Keystore}; pub const CHAINS: [&'static str; 2] = ["POLKADOT", "KUSAMA"]; diff --git a/token-core/tcx-substrate/src/signer.rs b/token-core/tcx-substrate/src/signer.rs index 20350689..38e57bab 100644 --- a/token-core/tcx-substrate/src/signer.rs +++ b/token-core/tcx-substrate/src/signer.rs @@ -2,8 +2,8 @@ use crate::transaction::{SubstrateRawTxIn, SubstrateTxOut}; use crate::{PAYLOAD_HASH_THRESHOLD, SIGNATURE_TYPE_SR25519}; use sp_core::blake2_256; -use tcx_chain::{ChainSigner, Keystore, TransactionSigner as TraitTransactionSigner}; use tcx_constants::Result; +use tcx_keystore::{ChainSigner, Keystore, TransactionSigner as TraitTransactionSigner}; pub(crate) fn hash_unsigned_payload(payload: &[u8]) -> Result> { if payload.len() > PAYLOAD_HASH_THRESHOLD { diff --git a/token-core/tcx-tezos/Cargo.toml b/token-core/tcx-tezos/Cargo.toml index 23f395a3..1bc8fbe4 100644 --- a/token-core/tcx-tezos/Cargo.toml +++ b/token-core/tcx-tezos/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -tcx-chain = { path = "../tcx-chain" } +tcx-keystore = { path = "../tcx-keystore" } tcx-constants = { path = "../tcx-constants" } tcx-primitive = { path = "../tcx-primitive" } tcx-crypto = { path = "../tcx-crypto" } diff --git a/token-core/tcx-tezos/src/address.rs b/token-core/tcx-tezos/src/address.rs index 504e80e8..8698c885 100644 --- a/token-core/tcx-tezos/src/address.rs +++ b/token-core/tcx-tezos/src/address.rs @@ -1,9 +1,9 @@ use bitcoin::util::base58; use blake2b_simd::Params; use std::str::FromStr; -use tcx_chain::Address; -use tcx_chain::Result; use tcx_constants::CoinInfo; +use tcx_keystore::Address; +use tcx_keystore::Result; use tcx_primitive::TypedPublicKey; #[derive(PartialEq, Eq, Clone)] @@ -70,8 +70,8 @@ pub fn sha256_hash(data: &[u8]) -> Vec { #[cfg(test)] mod test { use crate::address::TezosAddress; - use tcx_chain::Address; use tcx_constants::{CoinInfo, CurveType}; + use tcx_keystore::Address; use tcx_primitive::TypedPublicKey; #[test] diff --git a/token-core/tcx-tezos/src/lib.rs b/token-core/tcx-tezos/src/lib.rs index 39405cf0..15e87a91 100644 --- a/token-core/tcx-tezos/src/lib.rs +++ b/token-core/tcx-tezos/src/lib.rs @@ -2,7 +2,7 @@ pub mod address; pub mod signer; pub mod transaction; use bitcoin::util::base58; -use tcx_chain::Result; +use tcx_keystore::Result; use tcx_primitive::{Ed25519PrivateKey, PrivateKey, PublicKey}; pub fn build_tezos_base58_private_key(sk: &str) -> Result { @@ -27,8 +27,8 @@ pub fn pars_tezos_private_key(private_key: &str) -> Result> { } pub mod tezos { - use tcx_chain::{Account, Keystore}; use tcx_constants::{CoinInfo, CurveType}; + use tcx_keystore::{Account, Keystore}; pub const CHAINS: [&'static str; 1] = ["TEZOS"]; diff --git a/token-core/tcx-tezos/src/signer.rs b/token-core/tcx-tezos/src/signer.rs index d442b117..75441d73 100644 --- a/token-core/tcx-tezos/src/signer.rs +++ b/token-core/tcx-tezos/src/signer.rs @@ -1,8 +1,8 @@ use crate::transaction::{TezosRawTxIn, TezosTxOut}; use bitcoin::util::base58; use blake2b_simd::Params; -use tcx_chain::{ChainSigner, Keystore, TransactionSigner as TraitTransactionSigner}; use tcx_constants::Result; +use tcx_keystore::{ChainSigner, Keystore, TransactionSigner as TraitTransactionSigner}; impl TraitTransactionSigner for Keystore { fn sign_transaction( diff --git a/token-core/tcx-tron/Cargo.toml b/token-core/tcx-tron/Cargo.toml index badb352a..9b871e48 100644 --- a/token-core/tcx-tron/Cargo.toml +++ b/token-core/tcx-tron/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [dependencies] tcx-crypto = { path = "../tcx-crypto" } -tcx-chain = { path = "../tcx-chain" } +tcx-keystore = { path = "../tcx-keystore" } tcx-primitive = { path = "../tcx-primitive" } tcx-constants = { path = "../tcx-constants" } tcx-common = { path = "../tcx-common" } diff --git a/token-core/tcx-tron/src/address.rs b/token-core/tcx-tron/src/address.rs index 9470a37c..aba97d3a 100644 --- a/token-core/tcx-tron/src/address.rs +++ b/token-core/tcx-tron/src/address.rs @@ -3,9 +3,9 @@ use core::str::FromStr; use tcx_common::keccak256; -use tcx_chain::Address; -use tcx_chain::Result; use tcx_constants::CoinInfo; +use tcx_keystore::Address; +use tcx_keystore::Result; use tcx_primitive::TypedPublicKey; #[derive(PartialEq, Eq, Clone)] @@ -49,9 +49,9 @@ impl ToString for TronAddress { mod tests { use super::Address; use crate::TronAddress; - use tcx_chain::Address as TraitAddress; use tcx_constants::coin_info::coin_info_from_param; use tcx_constants::{CoinInfo, CurveType}; + use tcx_keystore::Address as TraitAddress; use tcx_primitive::TypedPublicKey; #[test] diff --git a/token-core/tcx-tron/src/lib.rs b/token-core/tcx-tron/src/lib.rs index 6fe715c5..ae32766c 100644 --- a/token-core/tcx-tron/src/lib.rs +++ b/token-core/tcx-tron/src/lib.rs @@ -5,8 +5,8 @@ pub mod transaction; pub use crate::address::TronAddress; pub mod tron { - use tcx_chain::{Account, Keystore}; use tcx_constants::{CoinInfo, CurveType}; + use tcx_keystore::{Account, Keystore}; pub const CHAINS: [&'static str; 1] = ["TRON"]; diff --git a/token-core/tcx-tron/src/signer.rs b/token-core/tcx-tron/src/signer.rs index 7a467f13..285ab832 100644 --- a/token-core/tcx-tron/src/signer.rs +++ b/token-core/tcx-tron/src/signer.rs @@ -1,5 +1,5 @@ use crate::transaction::{TronMessageInput, TronMessageOutput, TronTxInput, TronTxOutput}; -use tcx_chain::{ +use tcx_keystore::{ ChainSigner, Keystore, MessageSigner as TraitMessageSigner, Result, TransactionSigner as TraitTransactionSigner, }; @@ -90,9 +90,9 @@ mod tests { use super::*; use crate::address::TronAddress; - use tcx_chain::{HdKeystore, Keystore, KeystoreGuard, Metadata}; use tcx_constants::{CoinInfo, TEST_PASSWORD}; use tcx_constants::{CurveType, TEST_MNEMONIC}; + use tcx_keystore::{HdKeystore, Keystore, KeystoreGuard, Metadata}; use tcx_primitive::{PrivateKey, Secp256k1PrivateKey}; #[test] diff --git a/token-core/tcx/Cargo.toml b/token-core/tcx/Cargo.toml index 22ace82f..b3d1c8f1 100644 --- a/token-core/tcx/Cargo.toml +++ b/token-core/tcx/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [dependencies] tcx-proto = { path = "../tcx-proto" } tcx-crypto = { path = "../tcx-crypto", features = ["cache_dk"] } -tcx-chain = { path = "../tcx-chain", features = ["cache_dk"] } +tcx-keystore = { path = "../tcx-keystore", features = ["cache_dk"] } tcx-btc-kin = { path = "../tcx-btc-kin" } tcx-tron = { path = "../tcx-tron" } tcx-ckb = { path = "../tcx-ckb" } @@ -20,7 +20,6 @@ tcx-constants = { path = "../tcx-constants" } tcx-tezos = { path = "../tcx-tezos" } tcx-eth2 = { path = "../tcx-eth2" } zksync-crypto = { path = "../zksync-crypto" } -tcx-identity = { path = "../tcx-identity" } tcx-eth = { path = "../tcx-eth" } tcx-common = { path = "../tcx-common" } tcx-migration = { path = "../tcx-migration" } diff --git a/token-core/tcx/src/api.rs b/token-core/tcx/src/api.rs index c497b096..bd384ce9 100644 --- a/token-core/tcx/src/api.rs +++ b/token-core/tcx/src/api.rs @@ -119,6 +119,16 @@ pub struct HdStoreCreateParam { pub password_hint: ::prost::alloc::string::String, #[prost(string, tag = "3")] pub name: ::prost::alloc::string::String, + #[prost(string, tag = "4")] + pub source: ::prost::alloc::string::String, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct IdentityResult { + #[prost(string, tag = "1")] + pub identifier: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub ipfs_id: ::prost::alloc::string::String, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -133,6 +143,8 @@ pub struct WalletResult { pub accounts: ::prost::alloc::vec::Vec, #[prost(int64, tag = "5")] pub created_at: i64, + #[prost(message, optional, tag = "6")] + pub identity: ::core::option::Option, } /// FUNCTION: hd_store_import(HdStoreImportParam): WalletResult /// @@ -309,105 +321,151 @@ pub mod sign_param { DerivedKey(::prost::alloc::string::String), } } -/// btc-fork #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] -pub struct ExternalAddressParam { +pub struct CalcExternalAddressParam { #[prost(string, tag = "1")] pub id: ::prost::alloc::string::String, #[prost(string, tag = "2")] - pub chain_type: ::prost::alloc::string::String, - #[prost(uint32, tag = "3")] + pub network: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + pub seg_wit: ::prost::alloc::string::String, + #[prost(uint32, tag = "4")] pub external_idx: u32, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] -pub struct ExternalAddressResult { +pub struct CalcExternalAddressResult { #[prost(string, tag = "1")] pub address: ::prost::alloc::string::String, #[prost(string, tag = "2")] + pub r#type: ::prost::alloc::string::String, + #[prost(string, tag = "3")] pub derived_path: ::prost::alloc::string::String, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PublicKeyParam { + #[prost(string, tag = "1")] + pub id: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub chain_type: ::prost::alloc::string::String, #[prost(string, tag = "3")] - pub r#type: ::prost::alloc::string::String, + pub address: ::prost::alloc::string::String, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] -pub struct ExternalAddressExtra { +pub struct PublicKeyResult { #[prost(string, tag = "1")] - pub enc_xpub: ::prost::alloc::string::String, - #[prost(message, optional, tag = "2")] - pub external_address: ::core::option::Option, + pub id: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub chain_type: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + pub address: ::prost::alloc::string::String, + #[prost(string, tag = "4")] + pub public_key: ::prost::alloc::string::String, } -/// Nested message and enum types in `ExternalAddressExtra`. -pub mod external_address_extra { - #[allow(clippy::derive_partial_eq_without_eq)] - #[derive(Clone, PartialEq, ::prost::Message)] - pub struct ExternalAddress { - #[prost(string, tag = "1")] - pub address: ::prost::alloc::string::String, - #[prost(string, tag = "2")] - pub derived_path: ::prost::alloc::string::String, - #[prost(string, tag = "3")] - pub r#type: ::prost::alloc::string::String, - } +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RemoveWalletsParam { + #[prost(string, tag = "1")] + pub identifier: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub password: ::prost::alloc::string::String, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] -pub struct BtcForkDeriveExtraParam { +pub struct RemoveWalletsResult { #[prost(string, tag = "1")] - pub network: ::prost::alloc::string::String, + pub identifier: ::prost::alloc::string::String, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct V3KeystoreImportInput { + #[prost(string, tag = "1")] + pub keystore: ::prost::alloc::string::String, #[prost(string, tag = "2")] - pub seg_wit: ::prost::alloc::string::String, + pub password: ::prost::alloc::string::String, + #[prost(bool, tag = "3")] + pub overwrite: bool, + #[prost(string, tag = "4")] + pub name: ::prost::alloc::string::String, + #[prost(string, tag = "5")] + pub chain_type: ::prost::alloc::string::String, + #[prost(string, tag = "6")] + pub source: ::prost::alloc::string::String, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] -pub struct HdStoreExtendedPublicKeyParam { +pub struct V3KeystoreExportInput { #[prost(string, tag = "1")] pub id: ::prost::alloc::string::String, #[prost(string, tag = "2")] pub password: ::prost::alloc::string::String, - #[prost(string, tag = "3")] - pub chain_type: ::prost::alloc::string::String, - #[prost(string, tag = "4")] - pub address: ::prost::alloc::string::String, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] -pub struct HdStoreExtendedPublicKeyResponse { +pub struct V3KeystoreExportOutput { #[prost(string, tag = "1")] - pub extended_public_key: ::prost::alloc::string::String, + pub json: ::prost::alloc::string::String, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] -pub struct PublicKeyParam { +pub struct EncryptDataToIpfsParam { #[prost(string, tag = "1")] - pub id: ::prost::alloc::string::String, + pub identifier: ::prost::alloc::string::String, #[prost(string, tag = "2")] - pub chain_type: ::prost::alloc::string::String, - #[prost(string, tag = "3")] - pub address: ::prost::alloc::string::String, + pub content: ::prost::alloc::string::String, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] -pub struct PublicKeyResult { +pub struct EncryptDataToIpfsResult { #[prost(string, tag = "1")] - pub id: ::prost::alloc::string::String, + pub identifier: ::prost::alloc::string::String, #[prost(string, tag = "2")] - pub chain_type: ::prost::alloc::string::String, - #[prost(string, tag = "3")] - pub address: ::prost::alloc::string::String, - #[prost(string, tag = "4")] - pub public_key: ::prost::alloc::string::String, + pub encrypted: ::prost::alloc::string::String, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] -pub struct KeystoreUpdateAccount { +pub struct DecryptDataFromIpfsParam { #[prost(string, tag = "1")] - pub id: ::prost::alloc::string::String, + pub identifier: ::prost::alloc::string::String, #[prost(string, tag = "2")] - pub password: ::prost::alloc::string::String, + pub encrypted: ::prost::alloc::string::String, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DecryptDataFromIpfsResult { + #[prost(string, tag = "1")] + pub identifier: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub content: ::prost::alloc::string::String, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SignAuthenticationMessageParam { + #[prost(uint64, tag = "1")] + pub access_time: u64, + #[prost(string, tag = "2")] + pub identifier: ::prost::alloc::string::String, #[prost(string, tag = "3")] - pub account_name: ::prost::alloc::string::String, + pub device_token: ::prost::alloc::string::String, + #[prost(string, tag = "4")] + pub password: ::prost::alloc::string::String, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SignAuthenticationMessageResult { + #[prost(uint64, tag = "1")] + pub access_time: u64, + #[prost(string, tag = "2")] + pub signature: ::prost::alloc::string::String, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GenerateMnemonicResult { + #[prost(string, tag = "1")] + pub mnemonic: ::prost::alloc::string::String, } /// only support two types #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] @@ -476,20 +534,6 @@ pub struct BiometricModeResult { } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] -pub struct IdentityMigrationParam { - #[prost(string, tag = "1")] - pub id: ::prost::alloc::string::String, - #[prost(string, tag = "2")] - pub tcx_id: ::prost::alloc::string::String, - #[prost(string, tag = "3")] - pub password: ::prost::alloc::string::String, - #[prost(string, tag = "4")] - pub derived_key: ::prost::alloc::string::String, - #[prost(string, tag = "5")] - pub json_str: ::prost::alloc::string::String, -} -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] pub struct KeystoreMigrationParam { #[prost(string, tag = "1")] pub id: ::prost::alloc::string::String, @@ -499,6 +543,4 @@ pub struct KeystoreMigrationParam { pub password: ::prost::alloc::string::String, #[prost(string, tag = "4")] pub derived_key: ::prost::alloc::string::String, - #[prost(string, tag = "5")] - pub json_str: ::prost::alloc::string::String, } diff --git a/token-core/tcx/src/filemanager.rs b/token-core/tcx/src/filemanager.rs index 335e40ba..8dfa7615 100644 --- a/token-core/tcx/src/filemanager.rs +++ b/token-core/tcx/src/filemanager.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use std::fs; use std::io::Write; use std::path::Path; -use tcx_chain::Keystore; +use tcx_keystore::Keystore; use crate::error_handling::Result; @@ -14,8 +14,8 @@ lazy_static! { pub static ref IS_DEBUG: RwLock = RwLock::new(false); } -const WALLET_V1_DIR: &str = "wallets"; -const WALLET_V2_DIR: &str = "walletsV2"; +pub const WALLET_V1_DIR: &str = "wallets"; +pub const WALLET_V2_DIR: &str = "walletsV2"; pub fn copy_to_v2_if_need() -> Result<()> { let base = KEYSTORE_BASE_DIR.read(); @@ -30,19 +30,27 @@ pub fn copy_to_v2_if_need() -> Result<()> { return Err(format_err!("keystore_dir_v2_missing")); }; + if let Ok(meta) = path.metadata() { + if meta.is_dir() { + continue; + } + } + let file_name_oss = path.file_name(); let file_name_opt = file_name_oss.to_str(); let Some(file_name) = file_name_opt else { return Err(format_err!("keystore_dir_v2_missing")); }; - if file_name.ends_with(".json") { - let v1_file = path.path(); - let v2_file_str = format!("{}/{}", v2_path, file_name); - let v2_file = Path::new(&v2_file_str); - if !v2_file.exists() { - fs::copy(v1_file, v2_file)?; - } + let v1_file = path.path(); + let v2_file_str = if file_name.ends_with(".json") { + format!("{}/{}", v2_path, file_name) + } else { + format!("{}/{}.json", v2_path, file_name) + }; + let v2_file = Path::new(&v2_file_str); + if !v2_file.exists() { + fs::copy(v1_file, v2_file)?; } } @@ -54,9 +62,9 @@ pub fn clean_keystore() { } pub fn cache_keystore(keystore: Keystore) { - KEYSTORE_MAP - .write() - .insert(keystore.id().to_owned(), keystore); + let mut map = KEYSTORE_MAP.write(); + map.remove(&keystore.id()); + map.insert(keystore.id().to_owned(), keystore); } pub fn flush_keystore(ks: &Keystore) -> Result<()> { @@ -78,6 +86,14 @@ pub fn delete_keystore_file(wid: &str) -> Result<()> { Ok(()) } +pub fn delete_keystore_files() -> Result<()> { + let file_dir = WALLET_FILE_DIR.read(); + let dir_str = file_dir.as_str(); + let path = Path::new(&dir_str); + fs::remove_dir(path)?; + Ok(()) +} + #[cfg(test)] mod tests { use std::{fs, path::Path}; @@ -99,6 +115,7 @@ mod tests { "045861fe-0e9b-4069-92aa-0ac03cad55e0.json", "175169f7-5a35-4df7-93c1-1ff612168e71.json", "3831346d-0b81-405b-89cf-cdb1d010430e.json", + "5991857a-2488-4546-b730-463a5f84ea6a.json", "identity.json", ]; diff --git a/token-core/tcx/src/handler.rs b/token-core/tcx/src/handler.rs index 529765fb..850da801 100644 --- a/token-core/tcx/src/handler.rs +++ b/token-core/tcx/src/handler.rs @@ -10,10 +10,10 @@ use tcx_eos::EosChainFactory; use tcx_primitive::{get_account_path, private_key_without_version, FromHex, TypedPrivateKey}; use tcx_btc_kin::WIFDisplay; -use tcx_chain::{ +use tcx_keystore::{ key_hash_from_mnemonic, key_hash_from_private_key, ChainFactory, Keystore, KeystoreGuard, }; -use tcx_chain::{Account, HdKeystore, Metadata, PrivateKeystore, Source}; +use tcx_keystore::{Account, HdKeystore, Metadata, PrivateKeystore, Source}; use tcx_crypto::{XPUB_COMMON_IV, XPUB_COMMON_KEY_128}; use tcx_filecoin::KeyInfo; @@ -21,26 +21,32 @@ use tcx_filecoin::KeyInfo; use crate::api::keystore_common_derive_param::Derivation; use crate::api::sign_param::Key; use crate::api::{ - AccountResponse, AccountsResponse, DerivedKeyResult, ExportPrivateKeyParam, HdStoreCreateParam, - HdStoreImportParam, IdentityMigrationParam, KeyType, KeystoreCommonAccountsParam, + AccountResponse, AccountsResponse, CalcExternalAddressParam, CalcExternalAddressResult, + DecryptDataFromIpfsParam, DecryptDataFromIpfsResult, DerivedKeyResult, EncryptDataToIpfsParam, + EncryptDataToIpfsResult, ExportPrivateKeyParam, GenerateMnemonicResult, HdStoreCreateParam, + HdStoreImportParam, IdentityResult, KeyType, KeystoreCommonAccountsParam, KeystoreCommonDeriveParam, KeystoreCommonExistsParam, KeystoreCommonExistsResult, - KeystoreCommonExportResult, KeystoreMigrationParam, KeystoreUpdateAccount, - PrivateKeyStoreExportParam, PrivateKeyStoreImportParam, PublicKeyParam, PublicKeyResult, - Response, WalletKeyParam, WalletResult, ZksyncPrivateKeyFromSeedParam, - ZksyncPrivateKeyFromSeedResult, ZksyncPrivateKeyToPubkeyHashParam, - ZksyncPrivateKeyToPubkeyHashResult, ZksyncSignMusigParam, ZksyncSignMusigResult, + KeystoreCommonExportResult, KeystoreMigrationParam, PrivateKeyStoreExportParam, + PrivateKeyStoreImportParam, PublicKeyParam, PublicKeyResult, RemoveWalletsParam, + RemoveWalletsResult, Response, SignAuthenticationMessageParam, SignAuthenticationMessageResult, + V3KeystoreExportInput, V3KeystoreExportOutput, V3KeystoreImportInput, WalletKeyParam, + WalletResult, ZksyncPrivateKeyFromSeedParam, ZksyncPrivateKeyFromSeedResult, + ZksyncPrivateKeyToPubkeyHashParam, ZksyncPrivateKeyToPubkeyHashResult, ZksyncSignMusigParam, + ZksyncSignMusigResult, }; use crate::api::{InitTokenCoreXParam, SignParam}; use crate::error_handling::Result; -use crate::filemanager::{cache_keystore, clean_keystore, flush_keystore, WALLET_FILE_DIR}; +use crate::filemanager::{ + self, cache_keystore, clean_keystore, copy_to_v2_if_need, flush_keystore, KEYSTORE_BASE_DIR, + WALLET_FILE_DIR, WALLET_V2_DIR, +}; use crate::filemanager::{delete_keystore_file, KEYSTORE_MAP}; use crate::IS_DEBUG; use base58::ToBase58; -use tcx_chain::tcx_ensure; +use tcx_keystore::tcx_ensure; -use tcx_chain::{MessageSigner, TransactionSigner}; use tcx_constants::coin_info::coin_info_from_param; use tcx_constants::{CoinInfo, CurveType}; use tcx_crypto::aes::cbc::encrypt_pkcs7; @@ -48,6 +54,7 @@ use tcx_crypto::hash::dsha256; use tcx_crypto::KDF_ROUNDS; use tcx_eos::transaction::EosMessageInput; use tcx_eth::transaction::{EthRecoverAddressInput, EthRecoverAddressOutput}; +use tcx_keystore::{MessageSigner, TransactionSigner}; use tcx_eth2::transaction::{SignBlsToExecutionChangeParam, SignBlsToExecutionChangeResult}; use tcx_primitive::{Bip32DeterministicPublicKey, Ss58Codec}; @@ -56,17 +63,17 @@ use tcx_substrate::{ SubstrateKeystore, SubstrateKeystoreParam, SubstrateRawTxIn, }; -use tcx_identity::identity::Identity; -use tcx_identity::imt_keystore::{IMTKeystore, WALLET_KEYSTORE_DIR}; -use tcx_identity::v3_keystore::import_wallet_from_keystore; -use tcx_identity::wallet_api::{ - CreateIdentityParam, CreateIdentityResult, DecryptDataFromIpfsParam, EncryptDataToIpfsParam, - EncryptDataToIpfsResult, ExportIdentityParam, ExportIdentityResult, GenerateMnemonicResult, - GetCurrentIdentityResult, ImtKeystore, Metadata as MetadataRes, RecoverIdentityParam, - RecoverIdentityResult, RemoveIdentityParam, RemoveIdentityResult, - SignAuthenticationMessageParam, SignAuthenticationMessageResult, V3KeystoreExportInput, - V3KeystoreExportOutput, V3KeystoreImportInput, Wallet, -}; +use tcx_keystore::identity::Identity; +// use tcx_identity::imt_keystore::{IMTKeystore, WALLET_KEYSTORE_DIR}; +// use tcx_identity::v3_keystore::import_wallet_from_keystore; +// use tcx_identity::wallet_api::{ +// CreateIdentityParam, CreateIdentityResult, DecryptDataFromIpfsParam, DecryptDataFromIpfsResult, +// EncryptDataToIpfsParam, EncryptDataToIpfsResult, ExportIdentityParam, ExportIdentityResult, +// GenerateMnemonicResult, GetCurrentIdentityResult, ImtKeystore, Metadata as MetadataRes, +// RecoverIdentityParam, RecoverIdentityResult, RemoveIdentityParam, RemoveIdentityResult, +// SignAuthenticationMessageParam, SignAuthenticationMessageResult, V3KeystoreExportInput, +// V3KeystoreExportOutput, V3KeystoreImportInput, Wallet, +// }; use tcx_migration::migration::LegacyKeystore; use tcx_tezos::transaction::TezosRawTxIn; use tcx_tezos::{build_tezos_base58_private_key, pars_tezos_private_key}; @@ -127,8 +134,13 @@ pub fn init_token_core_x(data: &[u8]) -> Result<()> { xpub_common_iv, is_debug, } = InitTokenCoreXParam::decode(data).unwrap(); - *WALLET_FILE_DIR.write() = file_dir.to_string(); - *WALLET_KEYSTORE_DIR.write() = file_dir.to_string(); + // TODO: pass the file_dir as keystore root + *KEYSTORE_BASE_DIR.write() = file_dir.to_string(); + copy_to_v2_if_need()?; + + *WALLET_FILE_DIR.write() = format!("{}/{}", file_dir, WALLET_V2_DIR); + + // *WALLET_KEYSTORE_DIR.write() = file_dir.to_string(); *XPUB_COMMON_KEY_128.write() = xpub_common_key.to_string(); *XPUB_COMMON_IV.write() = xpub_common_iv.to_string(); @@ -185,21 +197,31 @@ pub(crate) fn hd_store_create(data: &[u8]) -> Result> { let mut meta = Metadata::default(); meta.name = param.name.to_owned(); meta.password_hint = param.password_hint.to_owned(); - meta.source = Source::Mnemonic; + meta.source = param.source.parse()?; let ks = HdKeystore::new(¶m.password, meta); let keystore = Keystore::Hd(ks); flush_keystore(&keystore)?; + let identity_result = match keystore.identity() { + Some(identity) => Some(IdentityResult { + identifier: identity.identifier.to_string(), + ipfs_id: identity.ipfs_id.to_string(), + }), + None => None, + }; + let meta = keystore.meta(); let wallet = WalletResult { id: keystore.id(), name: meta.name.to_owned(), - source: "MNEMONIC".to_owned(), + source: param.source, accounts: vec![], created_at: meta.timestamp.clone(), + identity: identity_result, }; + let ret = encode_message(wallet)?; cache_keystore(keystore); Ok(ret) @@ -228,7 +250,7 @@ pub(crate) fn hd_store_import(data: &[u8]) -> Result> { let mut meta = Metadata::default(); meta.name = param.name.to_owned(); meta.password_hint = param.password_hint.to_owned(); - meta.source = Source::Mnemonic; + meta.source = param.source.parse()?; let ks = HdKeystore::from_mnemonic(¶m.mnemonic, ¶m.password, meta)?; @@ -241,12 +263,22 @@ pub(crate) fn hd_store_import(data: &[u8]) -> Result> { flush_keystore(&keystore)?; let meta = keystore.meta(); + + let identity_result = match keystore.identity() { + Some(identity) => Some(IdentityResult { + identifier: identity.identifier.to_string(), + ipfs_id: identity.ipfs_id.to_string(), + }), + None => None, + }; + let wallet = WalletResult { id: keystore.id(), name: meta.name.to_owned(), - source: "MNEMONIC".to_owned(), + source: param.source, accounts: vec![], created_at: meta.timestamp.clone(), + identity: identity_result, }; let ret = encode_message(wallet)?; cache_keystore(keystore); @@ -435,6 +467,7 @@ pub(crate) fn private_key_store_import(data: &[u8]) -> Result> { source: "PRIVATE".to_owned(), accounts: vec![], created_at: meta.timestamp.clone(), + identity: None, }; let ret = encode_message(wallet)?; cache_keystore(keystore); @@ -945,6 +978,172 @@ pub(crate) fn generate_mnemonic() -> Result> { encode_message(result) } +// pub(crate) fn create_identity(data: &[u8]) -> Result> { +// let param: CreateIdentityParam = CreateIdentityParam::decode(data)?; +// let identity_keystore = Identity::create_identity(param)?; + +// let current_identity: GetCurrentIdentityResult = +// GetCurrentIdentityResult::decode(get_current_identity()?.as_slice()).unwrap(); +// let wallets = create_wallets(current_identity.wallets); +// let result = CreateIdentityResult { +// identifier: identity_keystore.identifier.clone(), +// ipfs_id: identity_keystore.ipfs_id.clone(), +// wallets, +// }; + +// encode_message(result) +// } + +// fn create_wallets(wallets: Vec) -> Vec { +// let mut ret_data = vec![]; +// for imt_keystore in wallets { +// let metadata = imt_keystore.metadata.unwrap().clone(); +// ret_data.push(Wallet { +// id: imt_keystore.id, +// address: imt_keystore.address, +// created_at: metadata.timestamp, +// source: metadata.source, +// chain_type: metadata.chain_type, +// }); +// } +// ret_data +// } + +// pub(crate) fn get_current_identity() -> Result> { +// let current_identity = Identity::get_current_identity()?; +// let wallets = current_identity.get_wallets()?; +// let im_token_meta = current_identity.im_token_meta; +// let identity_metadata = MetadataRes { +// name: im_token_meta.name, +// password_hint: im_token_meta.password_hint, +// chain_type: im_token_meta.chain_type.unwrap_or("".to_string()), +// timestamp: im_token_meta.timestamp as u64, +// network: im_token_meta.network, +// backup: im_token_meta.backup.unwrap_or(vec![]), +// source: im_token_meta.source, +// mode: im_token_meta.mode, +// wallet_type: im_token_meta.wallet_type, +// seg_wit: im_token_meta.seg_wit, +// }; +// let mut ret_wallet = vec![]; +// for wallet in wallets { +// let temp_metadata = wallet.im_token_meta.unwrap(); +// let wallet_metadata = MetadataRes { +// name: temp_metadata.name, +// password_hint: temp_metadata.password_hint, +// chain_type: temp_metadata.chain_type.unwrap_or("".to_string()), +// timestamp: temp_metadata.timestamp as u64, +// network: temp_metadata.network, +// backup: temp_metadata.backup.unwrap_or(vec![]), +// source: temp_metadata.source, +// mode: temp_metadata.mode, +// wallet_type: temp_metadata.wallet_type, +// seg_wit: temp_metadata.seg_wit, +// }; +// let imt_keystore = ImtKeystore { +// id: wallet.id, +// version: wallet.version, +// address: wallet.address, +// mnemonic_path: wallet.mnemonic_path, +// metadata: Some(wallet_metadata), +// }; +// ret_wallet.push(imt_keystore); +// } +// let result = GetCurrentIdentityResult { +// identifier: current_identity.identifier, +// ipfs_id: current_identity.ipfs_id, +// wallets: ret_wallet, +// metadata: Some(identity_metadata), +// }; +// encode_message(result) +// } + +// pub(crate) fn export_identity(data: &[u8]) -> Result> { +// let param: ExportIdentityParam = ExportIdentityParam::decode(data)?; +// let identifier = param.identifier; +// let password = param.password; +// let identity = Identity::get_current_identity()?; +// if identity.identifier != identifier { +// return Err(format_err!("invalid_identity")); +// } + +// let mnemonic = identity.export_identity(password.as_str())?; +// let result = ExportIdentityResult { +// identifier, +// mnemonic, +// }; +// encode_message(result) +// } + +// pub(crate) fn recover_identity(data: &[u8]) -> Result> { +// let param: RecoverIdentityParam = RecoverIdentityParam::decode(data)?; +// let mnemonic = param.mnemonic.to_owned(); + +// let identity_keystore = Identity::recover_identity(param)?; + +// let current_identity: GetCurrentIdentityResult = +// GetCurrentIdentityResult::decode(get_current_identity()?.as_slice()).unwrap(); +// let wallets = create_wallets(current_identity.wallets); +// let result = RecoverIdentityResult { +// identifier: identity_keystore.identifier.clone(), +// mnemonic, +// ipfs_id: identity_keystore.ipfs_id.clone(), +// wallets, +// }; + +// encode_message(result) +// } + +// TODO: may rename to remove wallets? +pub(crate) fn remove_wallets(data: &[u8]) -> Result> { + let param: RemoveWalletsParam = RemoveWalletsParam::decode(data)?; + let map = KEYSTORE_MAP.read(); + let Some(identity_ks) = map.values().find(|ks| ks.identity().is_some() && ks.identity().unwrap().identifier == param.identifier) else { + return Err(failure::format_err!("identity not found")); + }; + + if !identity_ks.verify_password(¶m.password) { + return Err(failure::format_err!("password_incorrect")); + } + filemanager::delete_keystore_files()?; + + let result = RemoveWalletsResult { + identifier: param.identifier, + }; + encode_message(result) +} + +pub(crate) fn eth_ec_sign(_data: &[u8]) -> Result> { + todo!() + /* + let param: SignParam = SignParam::decode(data).expect("EthMessageSignParam"); + + let fixtures = IMTKeystore::must_find_wallet_by_id(¶m.id)?; + let password = match param.key.clone().unwrap() { + Key::Password(password) => password, + _ => { + return Err(format_err!("key_type_error")); + } + }; + + let private_key = fixtures.decrypt_main_key(password.as_str())?; + + let input: EthMessageInput = EthMessageInput::decode( + param + .input + .expect("EthMessageInput") + .value + .clone() + .as_slice(), + )?; + let sign_result: Result = + task::block_on(async { input.ec_sign(private_key.as_slice()).await }); + + encode_message(sign_result?) + + */ +} + pub(crate) fn eth_recover_address(data: &[u8]) -> Result> { let input: EthRecoverAddressInput = EthRecoverAddressInput::decode(data).expect("EthRecoverAddressParam"); @@ -953,53 +1152,189 @@ pub(crate) fn eth_recover_address(data: &[u8]) -> Result> { encode_message(result?) } -pub(crate) fn eth_v3keystore_import(data: &[u8]) -> Result> { - let input: V3KeystoreImportInput = - V3KeystoreImportInput::decode(data).expect("V3KeystoreImportInput"); - let v3_keystore = import_wallet_from_keystore(input)?; - let metadata = v3_keystore.im_token_meta.unwrap(); - let ret_wallet: WalletResult = WalletResult { - id: v3_keystore.id, - created_at: metadata.timestamp as i64, - source: metadata.source, - name: metadata.name, - accounts: vec![], +// pub(crate) fn eos_update_account(data: &[u8]) -> Result> { +// let param: KeystoreUpdateAccount = +// KeystoreUpdateAccount::decode(data).expect("eos_update_account params"); +// let mut map = KEYSTORE_MAP.write(); +// let keystore: &mut Keystore = match map.get_mut(¶m.id) { +// Some(keystore) => Ok(keystore), +// _ => Err(format_err!("{}", "wallet_not_found")), +// }?; + +// // todo: use ErrorKind +// tcx_ensure!( +// keystore.verify_password(¶m.password), +// format_err!("password_incorrect") +// ); + +// tcx_eos::address::eos_update_account(keystore, ¶m.account_name)?; +// flush_keystore(&keystore)?; +// let rsp = Response { +// is_success: true, +// error: "".to_string(), +// }; +// encode_message(rsp) +// } + +// pub(crate) fn eth_v3keystore_import(data: &[u8]) -> Result> { +// let input: V3KeystoreImportInput = +// V3KeystoreImportInput::decode(data).expect("V3KeystoreImportInput"); +// let v3_keystore = import_wallet_from_keystore(input)?; +// let metadata = v3_keystore.im_token_meta.unwrap(); +// let ret_wallet: WalletResult = WalletResult { +// id: v3_keystore.id, +// created_at: metadata.timestamp as i64, +// source: metadata.source, +// name: metadata.name, +// accounts: vec![], +// }; +// encode_message(ret_wallet) +// } + +// pub(crate) fn eth_v3keystore_export(data: &[u8]) -> Result> { +// let input: V3KeystoreExportInput = +// V3KeystoreExportInput::decode(data).expect("V3KeystoreExportInput"); +// let keystore = IMTKeystore::must_find_wallet_by_id(&input.id)?; +// let json = keystore.export_keystore(&input.password)?; +// let output = V3KeystoreExportOutput { json }; +// encode_message(output) +// } + +pub(crate) fn encrypt_data_to_ipfs(data: &[u8]) -> Result> { + let param = EncryptDataToIpfsParam::decode(data).expect("EncryptDataToIpfsParam"); + // let identity = Identity::get_current_identity()?; + // let ciphertext = identity.encrypt_ipfs(&input.content)?; + // Identity::encrypt_ipfs(&self, plaintext) + + let map = KEYSTORE_MAP.read(); + let Some(identity_ks) = map.values().find(|ks| ks.identity().is_some() && ks.identity().unwrap().identifier == param.identifier) else { + return Err(failure::format_err!("identity not found")); }; - encode_message(ret_wallet) + + let cipher_text = identity_ks + .identity() + .unwrap() + .encrypt_ipfs(¶m.content)?; + + let output = EncryptDataToIpfsResult { + identifier: param.identifier.to_string(), + encrypted: cipher_text, + }; + + encode_message(output) } -pub(crate) fn eth_v3keystore_export(data: &[u8]) -> Result> { - let input: V3KeystoreExportInput = - V3KeystoreExportInput::decode(data).expect("V3KeystoreExportInput"); - let keystore = IMTKeystore::must_find_wallet_by_id(&input.id)?; - let json = keystore.export_keystore(&input.password)?; - let output = V3KeystoreExportOutput { json }; +pub(crate) fn decrypt_data_from_ipfs(data: &[u8]) -> Result> { + let param = DecryptDataFromIpfsParam::decode(data).expect("DecryptDataFromIpfsParam"); + // let identity = Identity::get_current_identity()?; + // let ciphertext = identity.decrypt_ipfs(&input.encrypted)?; + // let map = KEYSTORE_MAP.read(); + // let Some(identity_ks) = map.values().find(|ks| ks.identity().is_some() && ks.identity().unwrap().identifier == param.identifier) else { + // return Err(failure::format_err!("identity not found")); + // }; + + let map = KEYSTORE_MAP.read(); + let Some(identity_ks) = map.values().find(|ks| ks.identity().is_some() && ks.identity().unwrap().identifier == param.identifier) else { + return Err(failure::format_err!("identity not found")); + }; + + let content = identity_ks + .identity() + .unwrap() + .decrypt_ipfs(¶m.encrypted)?; + + let output = DecryptDataFromIpfsResult { + identifier: param.identifier.to_string(), + content, + }; + encode_message(output) } -// pub(crate) fn migrate_identity_keystore(data: &[u8]) -> Result> { -// let param = -// IdentityMigrationParam::decode(data).expect("IdentityMigrationParam"); -// // Lega -// // let identity = Identity::get_current_identity()?; -// let legach_keystore = LegacyKeystore::from_json_str(¶m.json_str)?; -// let mut map = KEYSTORE_MAP.write(); -// let tcx_keystore: &mut Keystore = match map.get_mut(¶m.tcx_id) { -// Some(keystore) => Ok(keystore), -// _ => Err(format_err!("{}", "wallet_not_found")), -// }?; +pub(crate) fn sign_authentication_message(data: &[u8]) -> Result> { + let param = + SignAuthenticationMessageParam::decode(data).expect("SignAuthenticationMessageParam"); -// let key = if param.derived_key.is_empty() { Key::Password(param.password)} else {Key::DerivedKey(param.derived_key)}; - -// let keystore = legach_keystore.migrate_identity_wallets(key, &mut tcx_keystore)?; -// flush_keystore(&keystore); -// // let signature = identity.sign_authentication_message( -// // input.access_time, -// // &input.identifier, -// // &input.device_token, -// // )?; -// encode_message(SignAuthenticationMessageResult { -// signature, -// access_time: param.access_time, -// }) -// } + let map = KEYSTORE_MAP.read(); + let Some(identity_ks) = map.values().find(|ks| ks.identity().is_some() && ks.identity().unwrap().identifier == param.identifier) else { + return Err(failure::format_err!("identity not found")); + }; + + let key = tcx_crypto::Key::Password(param.password); + let unlocker = identity_ks.use_key(&key)?; + + let signature = identity_ks + .identity() + .unwrap() + .sign_authentication_message(param.access_time, ¶m.device_token, &unlocker)?; + + encode_message(SignAuthenticationMessageResult { + signature, + access_time: param.access_time, + }) +} + +pub(crate) fn calc_external_address(data: &[u8]) -> Result> { + let param: CalcExternalAddressParam = + CalcExternalAddressParam::decode(data).expect("CalcExternalAddressParam"); + let mut map = KEYSTORE_MAP.write(); + let keystore: &mut Keystore = match map.get_mut(¶m.id) { + Some(keystore) => Ok(keystore), + _ => Err(format_err!("{}", "wallet_not_found")), + }?; + + let (address, external_path) = tcx_btc_kin::calc_btc_change_address( + &keystore, + ¶m.seg_wit, + ¶m.network, + param.external_idx, + )?; + encode_message(CalcExternalAddressResult { + address, + r#type: "EXTERNAL".to_string(), + derived_path: external_path, + }) +} + +pub(crate) fn migrate_keystore(data: &[u8]) -> Result> { + let param = KeystoreMigrationParam::decode(data).expect("KeystoreMigrationParam"); + let json_str = fs::read_to_string(format!("{}/{}.json", WALLET_FILE_DIR.read(), param.id))?; + let legacy_keystore = LegacyKeystore::from_json_str(&json_str)?; + + let mut tcx_ks: Option = None; + { + let map = KEYSTORE_MAP.read(); + if param.tcx_id.len() > 0 { + tcx_ks = map.get(¶m.tcx_id).and_then(|ks| Some(ks.clone())) + } + } + + let key = if param.derived_key.is_empty() { + tcx_crypto::Key::Password(param.password) + } else { + tcx_crypto::Key::DerivedKey(param.derived_key) + }; + + let keystore = legacy_keystore.migrate_identity_wallets(&key, tcx_ks)?; + flush_keystore(&keystore)?; + let accounts = keystore + .accounts() + .iter() + .map(|acc| AccountResponse { + chain_type: acc.coin.to_string(), + address: acc.address.to_string(), + path: acc.derivation_path.to_string(), + extended_xpub_key: acc.ext_pub_key.to_string(), + }) + .collect::>(); + let ret = encode_message(WalletResult { + id: keystore.id().to_string(), + name: keystore.meta().name.to_string(), + source: keystore.meta().source.to_string(), + accounts, + created_at: keystore.meta().timestamp, + identity: None, + }); + cache_keystore(keystore); + ret +} diff --git a/token-core/tcx/src/lib.rs b/token-core/tcx/src/lib.rs index c95b2fc6..2613264c 100644 --- a/token-core/tcx/src/lib.rs +++ b/token-core/tcx/src/lib.rs @@ -2,8 +2,11 @@ use std::ffi::{CStr, CString}; use std::os::raw::c_char; -use handler::sign_message; -use handler::{eth_v3keystore_export, eth_v3keystore_import}; +use handler::{ + calc_external_address, decrypt_data_from_ipfs, encrypt_data_to_ipfs, migrate_keystore, + remove_wallets, sign_authentication_message, sign_message, +}; +// use handler::{eth_v3keystore_export, eth_v3keystore_import}; use prost::Message; pub mod api; @@ -27,13 +30,13 @@ use crate::handler::{ }; mod filemanager; -mod identity; +// mod identity; mod macros; -use crate::identity::{ - create_identity, decrypt_data_from_ipfs, encrypt_data_to_ipfs, export_identity, - get_current_identity, recover_identity, remove_identity, sign_authentication_message, -}; +// use crate::identity::{ +// create_identity, decrypt_data_from_ipfs, encrypt_data_to_ipfs, export_identity, +// get_current_identity, recover_identity, remove_identity, sign_authentication_message, +// }; use crate::handler::{ eth_recover_address, export_substrate_keystore, generate_mnemonic, get_public_key, @@ -108,6 +111,9 @@ pub unsafe extern "C" fn call_tcx_api(hex_str: *const c_char) -> *const c_char { "keystore_common_accounts" => { landingpad(|| keystore_common_accounts(&action.param.unwrap().value)) } + "calc_external_address" => { + landingpad(|| calc_external_address(&action.param.unwrap().value)) + } "sign_tx" => landingpad(|| sign_tx(&action.param.unwrap().value)), "get_public_key" => landingpad(|| get_public_key(&action.param.unwrap().value)), // use the sign_msg instead @@ -141,15 +147,16 @@ pub unsafe extern "C" fn call_tcx_api(hex_str: *const c_char) -> *const c_char { landingpad(|| sign_bls_to_execution_change(&action.param.unwrap().value)) } "generate_mnemonic" => landingpad(|| generate_mnemonic()), - "create_identity" => landingpad(|| create_identity(&action.param.unwrap().value)), - "get_current_identity" => landingpad(|| get_current_identity()), - "recover_identity" => landingpad(|| recover_identity(&action.param.unwrap().value)), - "export_identity" => landingpad(|| export_identity(&action.param.unwrap().value)), - "remove_identity" => landingpad(|| remove_identity(&action.param.unwrap().value)), - //migrate to sign_message "eth_ec_sign" => landingpad(|| eth_ec_sign(&action.param.unwrap().value)), - "eth_recover_address" => landingpad(|| eth_recover_address(&action.param.unwrap().value)), - "eth_keystore_import" => landingpad(|| eth_v3keystore_import(&action.param.unwrap().value)), - "eth_keystore_export" => landingpad(|| eth_v3keystore_export(&action.param.unwrap().value)), + // "create_identity" => landingpad(|| create_identity(&action.param.unwrap().value)), + // "get_current_identity" => landingpad(|| get_current_identity()), + // "recover_identity" => landingpad(|| recover_identity(&action.param.unwrap().value)), + // "export_identity" => landingpad(|| export_identity(&action.param.unwrap().value)), + "remove_wallets" => landingpad(|| remove_wallets(&action.param.unwrap().value)), + // "eth_ec_sign" => landingpad(|| eth_ec_sign(&action.param.unwrap().value)), + // "eth_recover_address" => landingpad(|| eth_recover_address(&action.param.unwrap().value)), + // "eos_update_account" => landingpad(|| eos_update_account(&action.param.unwrap().value)), + // "eth_keystore_import" => landingpad(|| eth_v3keystore_import(&action.param.unwrap().value)), + // "eth_keystore_export" => landingpad(|| eth_v3keystore_export(&action.param.unwrap().value)), "encrypt_data_to_ipfs" => landingpad(|| encrypt_data_to_ipfs(&action.param.unwrap().value)), "decrypt_data_from_ipfs" => { landingpad(|| decrypt_data_from_ipfs(&action.param.unwrap().value)) @@ -157,6 +164,7 @@ pub unsafe extern "C" fn call_tcx_api(hex_str: *const c_char) -> *const c_char { "sign_authentication_message" => { landingpad(|| sign_authentication_message(&action.param.unwrap().value)) } + "migrate_keystore" => landingpad(|| migrate_keystore(&action.param.unwrap().value)), _ => landingpad(|| Err(format_err!("unsupported_method"))), }; match reply { @@ -210,23 +218,27 @@ mod tests { use crate::api::keystore_common_derive_param::Derivation; use crate::api::{ - sign_param, AccountsResponse, DerivedKeyResult, ExportPrivateKeyParam, HdStoreCreateParam, + sign_param, AccountResponse, AccountsResponse, CalcExternalAddressParam, + CalcExternalAddressResult, DecryptDataFromIpfsParam, DecryptDataFromIpfsResult, + DerivedKeyResult, EncryptDataToIpfsParam, EncryptDataToIpfsResult, ExportPrivateKeyParam, + GenerateMnemonicResult, HdStoreCreateParam, HdStoreImportParam, IdentityResult, InitTokenCoreXParam, KeyType, KeystoreCommonAccountsParam, KeystoreCommonDeriveParam, KeystoreCommonExistsParam, KeystoreCommonExistsResult, KeystoreCommonExportResult, - KeystoreUpdateAccount, PrivateKeyStoreExportParam, PrivateKeyStoreImportParam, - PublicKeyParam, PublicKeyResult, Response, SignParam, WalletKeyParam, - ZksyncPrivateKeyFromSeedParam, ZksyncPrivateKeyFromSeedResult, + KeystoreMigrationParam, PrivateKeyStoreExportParam, PrivateKeyStoreImportParam, + PublicKeyParam, PublicKeyResult, RemoveWalletsParam, RemoveWalletsResult, Response, + SignAuthenticationMessageParam, SignAuthenticationMessageResult, SignParam, + V3KeystoreExportInput, V3KeystoreExportOutput, V3KeystoreImportInput, WalletKeyParam, + WalletResult, ZksyncPrivateKeyFromSeedParam, ZksyncPrivateKeyFromSeedResult, ZksyncPrivateKeyToPubkeyHashParam, ZksyncPrivateKeyToPubkeyHashResult, ZksyncSignMusigParam, ZksyncSignMusigResult, }; - use crate::api::{HdStoreImportParam, WalletResult}; use crate::handler::hd_store_import; use crate::handler::{encode_message, private_key_store_import}; use prost::Message; - use tcx_chain::Keystore; use tcx_constants::sample_key; use tcx_constants::{TEST_MNEMONIC, TEST_PASSWORD}; - use tcx_identity::{constants, model}; + use tcx_keystore::{Keystore, Source}; + // use tcx_identity::{constants, model}; use std::fs; use tcx_btc_kin::transaction::BtcKinTxInput; @@ -244,12 +256,12 @@ mod tests { }; use tcx_eth2::transaction::{SignBlsToExecutionChangeParam, SignBlsToExecutionChangeResult}; use tcx_filecoin::{SignedMessage, UnsignedMessage}; - use tcx_identity::wallet_api::{ - CreateIdentityParam, CreateIdentityResult, ExportIdentityParam, ExportIdentityResult, - GenerateMnemonicResult, GetCurrentIdentityResult, RecoverIdentityParam, - RecoverIdentityResult, RemoveIdentityParam, RemoveIdentityResult, V3KeystoreExportInput, - V3KeystoreExportOutput, V3KeystoreImportInput, - }; + // use tcx_identity::wallet_api::{ + // CreateIdentityParam, CreateIdentityResult, ExportIdentityParam, ExportIdentityResult, + // GenerateMnemonicResult, GetCurrentIdentityResult, RecoverIdentityParam, + // RecoverIdentityResult, RemoveIdentityParam, RemoveIdentityResult, V3KeystoreExportInput, + // V3KeystoreExportOutput, V3KeystoreImportInput, + // }; use tcx_substrate::{ ExportSubstrateKeystoreResult, SubstrateKeystore, SubstrateKeystoreParam, SubstrateRawTxIn, SubstrateTxOut, @@ -469,6 +481,7 @@ mod tests { password: TEST_PASSWORD.to_string(), password_hint: "".to_string(), name: "aaa".to_string(), + source: "MNEMONIC".to_string(), }; let ret = call_api("hd_store_create", param).unwrap(); @@ -2619,25 +2632,21 @@ mod tests { (TronMessageInput { value: "645c0b7b58158babbfa6c6cd5a48aa7340a8749176b120e8516216787a13dc76" .to_string(), - is_hex: true, is_tron_header: true, }, "16417c6489da3a88ef980bf0a42551b9e76181d03e7334548ab3cb36e7622a484482722882a29e2fe4587b95c739a68624ebf9ada5f013a9340d883f03fcf9af1b"), (TronMessageInput { value: "0x645c0b7b58158babbfa6c6cd5a48aa7340a8749176b120e8516216787a13dc76" .to_string(), - is_hex: true, is_tron_header: true, }, "16417c6489da3a88ef980bf0a42551b9e76181d03e7334548ab3cb36e7622a484482722882a29e2fe4587b95c739a68624ebf9ada5f013a9340d883f03fcf9af1b"), (TronMessageInput { value: "645c0b7b58158babbfa6c6cd5a48aa7340a8749176b120e8516216787a13dc76" .to_string(), - is_hex: true, is_tron_header: false, }, "06ff3c5f98b8e8e257f47a66ce8e953c7a7d0f96eb6687da6a98b66a36c2a725759cab3df94d014bd17760328adf860649303c68c4fa6644d9f307e2f32cc3311c"), (TronMessageInput { value: "abcdef" .to_string(), - is_hex: false, is_tron_header: true, }, "a87eb6ae7e97621b6ba2e2f70db31fe0c744c6adcfdc005044026506b70ac11a33f415f4478b6cf84af32b3b5d70a13a77e53287613449b345bb16fe012c04081b"), ]; @@ -2683,7 +2692,6 @@ mod tests { let input = TronMessageInput { value: "645c0b7b58158babbfa6c6cd5a48aa7340a8749176b120e8516216787a13dc76" .to_string(), - is_hex: true, is_tron_header: true, }; @@ -3156,147 +3164,147 @@ mod tests { }) } - #[test] - pub fn test_identity_wallet_create() { - run_test(|| { - let param = CreateIdentityParam { - name: sample_key::NAME.to_string(), - password: sample_key::PASSWORD.to_string(), - password_hint: Some(sample_key::PASSWORD_HINT.to_string()), - network: model::NETWORK_TESTNET.to_string(), - seg_wit: None, - }; - let ret = call_api("create_identity", param).unwrap(); - let create_result: CreateIdentityResult = - CreateIdentityResult::decode(ret.as_slice()).unwrap(); - assert!(create_result.ipfs_id.len() > 0); - assert!(create_result.identifier.len() > 0); - - let param = CreateIdentityParam { - name: sample_key::NAME.to_string(), - password: sample_key::PASSWORD.to_string(), - password_hint: None, - network: model::NETWORK_TESTNET.to_string(), - seg_wit: None, - }; - let ret = call_api("create_identity", param).unwrap(); - let create_result: CreateIdentityResult = - CreateIdentityResult::decode(ret.as_slice()).unwrap(); - assert!(create_result.ipfs_id.len() > 0); - assert!(create_result.identifier.len() > 0); - assert_eq!(create_result.wallets.len(), 1); - let wallets = create_result.wallets.get(0).unwrap(); - assert_eq!(wallets.chain_type, "ETHEREUM"); - - let param = CreateIdentityParam { - name: sample_key::NAME.to_string(), - password: sample_key::PASSWORD.to_string(), - password_hint: None, - network: model::NETWORK_MAINNET.to_string(), - seg_wit: None, - }; - let ret = call_api("create_identity", param).unwrap(); - let create_result: CreateIdentityResult = - CreateIdentityResult::decode(ret.as_slice()).unwrap(); - assert!(create_result.ipfs_id.len() > 0); - assert!(create_result.identifier.len() > 0); - - let ret_bytes = call_api("get_current_identity", ()).unwrap(); - let ret: GetCurrentIdentityResult = - GetCurrentIdentityResult::decode(ret_bytes.as_slice()).unwrap(); - assert_eq!(ret.wallets.len(), 1); - let wallet = ret.wallets.get(0).unwrap(); - assert_eq!( - wallet.metadata.clone().unwrap().chain_type, - constants::CHAIN_TYPE_ETHEREUM - ) - }) - } + // #[test] + // pub fn test_identity_wallet_create() { + // run_test(|| { + // let param = CreateIdentityParam { + // name: sample_key::NAME.to_string(), + // password: sample_key::PASSWORD.to_string(), + // password_hint: Some(sample_key::PASSWORD_HINT.to_string()), + // network: model::NETWORK_TESTNET.to_string(), + // seg_wit: None, + // }; + // let ret = call_api("create_identity", param).unwrap(); + // let create_result: CreateIdentityResult = + // CreateIdentityResult::decode(ret.as_slice()).unwrap(); + // assert!(create_result.ipfs_id.len() > 0); + // assert!(create_result.identifier.len() > 0); + + // let param = CreateIdentityParam { + // name: sample_key::NAME.to_string(), + // password: sample_key::PASSWORD.to_string(), + // password_hint: None, + // network: model::NETWORK_TESTNET.to_string(), + // seg_wit: None, + // }; + // let ret = call_api("create_identity", param).unwrap(); + // let create_result: CreateIdentityResult = + // CreateIdentityResult::decode(ret.as_slice()).unwrap(); + // assert!(create_result.ipfs_id.len() > 0); + // assert!(create_result.identifier.len() > 0); + // assert_eq!(create_result.wallets.len(), 1); + // let wallets = create_result.wallets.get(0).unwrap(); + // assert_eq!(wallets.chain_type, "ETHEREUM"); + + // let param = CreateIdentityParam { + // name: sample_key::NAME.to_string(), + // password: sample_key::PASSWORD.to_string(), + // password_hint: None, + // network: model::NETWORK_MAINNET.to_string(), + // seg_wit: None, + // }; + // let ret = call_api("create_identity", param).unwrap(); + // let create_result: CreateIdentityResult = + // CreateIdentityResult::decode(ret.as_slice()).unwrap(); + // assert!(create_result.ipfs_id.len() > 0); + // assert!(create_result.identifier.len() > 0); + + // let ret_bytes = call_api("get_current_identity", ()).unwrap(); + // let ret: GetCurrentIdentityResult = + // GetCurrentIdentityResult::decode(ret_bytes.as_slice()).unwrap(); + // assert_eq!(ret.wallets.len(), 1); + // let wallet = ret.wallets.get(0).unwrap(); + // assert_eq!( + // wallet.metadata.clone().unwrap().chain_type, + // constants::CHAIN_TYPE_ETHEREUM + // ) + // }) + // } - #[test] - pub fn test_recover_identity_on_testnet() { - run_test(|| { - let param = RecoverIdentityParam { - name: sample_key::NAME.to_string(), - mnemonic: sample_key::MNEMONIC.to_string(), - password: sample_key::PASSWORD.to_string(), - password_hint: Some(sample_key::PASSWORD_HINT.to_string()), - network: model::NETWORK_TESTNET.to_string(), - seg_wit: None, - }; - let ret = call_api("recover_identity", param).unwrap(); - let recover_result: RecoverIdentityResult = - RecoverIdentityResult::decode(ret.as_slice()).unwrap(); - assert_eq!( - recover_result.ipfs_id, - "QmSTTidyfa4np9ak9BZP38atuzkCHy4K59oif23f4dNAGU" - ); - assert_eq!( - recover_result.identifier, - "im18MDKM8hcTykvMmhLnov9m2BaFqsdjoA7cwNg" - ); + // #[test] + // pub fn test_recover_identity_on_testnet() { + // run_test(|| { + // let param = RecoverIdentityParam { + // name: sample_key::NAME.to_string(), + // mnemonic: sample_key::MNEMONIC.to_string(), + // password: sample_key::PASSWORD.to_string(), + // password_hint: Some(sample_key::PASSWORD_HINT.to_string()), + // network: model::NETWORK_TESTNET.to_string(), + // seg_wit: None, + // }; + // let ret = call_api("recover_identity", param).unwrap(); + // let recover_result: RecoverIdentityResult = + // RecoverIdentityResult::decode(ret.as_slice()).unwrap(); + // assert_eq!( + // recover_result.ipfs_id, + // "QmSTTidyfa4np9ak9BZP38atuzkCHy4K59oif23f4dNAGU" + // ); + // assert_eq!( + // recover_result.identifier, + // "im18MDKM8hcTykvMmhLnov9m2BaFqsdjoA7cwNg" + // ); - let wallet = recover_result.wallets.get(0).unwrap(); - assert_eq!(wallet.chain_type, constants::CHAIN_TYPE_ETHEREUM); - assert_eq!(wallet.address, "6031564e7b2f5cc33737807b2e58daff870b590b"); - }) - } + // let wallet = recover_result.wallets.get(0).unwrap(); + // assert_eq!(wallet.chain_type, constants::CHAIN_TYPE_ETHEREUM); + // assert_eq!(wallet.address, "6031564e7b2f5cc33737807b2e58daff870b590b"); + // }) + // } - #[test] - pub fn test_export_identity() { - run_test(|| { - let param = RecoverIdentityParam { - name: sample_key::NAME.to_string(), - mnemonic: MNEMONIC.to_string(), - password: sample_key::PASSWORD.to_string(), - password_hint: Some(sample_key::PASSWORD_HINT.to_string()), - network: model::NETWORK_TESTNET.to_string(), - seg_wit: None, - }; - let ret = call_api("recover_identity", param).unwrap(); - let recover_result: RecoverIdentityResult = - RecoverIdentityResult::decode(ret.as_slice()).unwrap(); - assert!(recover_result.ipfs_id.len() > 0); - assert!(recover_result.identifier.len() > 0); - - let param = ExportIdentityParam { - identifier: recover_result.identifier.to_owned(), - password: sample_key::PASSWORD.to_string(), - }; - let ret = call_api("export_identity", param).unwrap(); - let export_result: ExportIdentityResult = - ExportIdentityResult::decode(ret.as_slice()).unwrap(); - assert_eq!(export_result.mnemonic, MNEMONIC); - assert_eq!(recover_result.identifier, export_result.identifier); - }) - } + // #[test] + // pub fn test_export_identity() { + // run_test(|| { + // let param = RecoverIdentityParam { + // name: sample_key::NAME.to_string(), + // mnemonic: MNEMONIC.to_string(), + // password: sample_key::PASSWORD.to_string(), + // password_hint: Some(sample_key::PASSWORD_HINT.to_string()), + // network: model::NETWORK_TESTNET.to_string(), + // seg_wit: None, + // }; + // let ret = call_api("recover_identity", param).unwrap(); + // let recover_result: RecoverIdentityResult = + // RecoverIdentityResult::decode(ret.as_slice()).unwrap(); + // assert!(recover_result.ipfs_id.len() > 0); + // assert!(recover_result.identifier.len() > 0); + + // let param = ExportIdentityParam { + // identifier: recover_result.identifier.to_owned(), + // password: sample_key::PASSWORD.to_string(), + // }; + // let ret = call_api("export_identity", param).unwrap(); + // let export_result: ExportIdentityResult = + // ExportIdentityResult::decode(ret.as_slice()).unwrap(); + // assert_eq!(export_result.mnemonic, MNEMONIC); + // assert_eq!(recover_result.identifier, export_result.identifier); + // }) + // } - #[test] - pub fn test_delete_identity() { - run_test(|| { - let param = CreateIdentityParam { - name: sample_key::NAME.to_string(), - password: sample_key::PASSWORD.to_string(), - password_hint: Some(sample_key::PASSWORD_HINT.to_string()), - network: model::NETWORK_TESTNET.to_string(), - seg_wit: None, - }; - let ret = call_api("create_identity", param).unwrap(); - let create_result: CreateIdentityResult = - CreateIdentityResult::decode(ret.as_slice()).unwrap(); - assert!(create_result.ipfs_id.len() > 0); - assert!(create_result.identifier.len() > 0); - - let remove_identity_param = RemoveIdentityParam { - identifier: create_result.identifier.to_owned(), - password: sample_key::PASSWORD.to_string(), - }; - let ret = call_api("remove_identity", remove_identity_param).unwrap(); - let remove_result: RemoveIdentityResult = - RemoveIdentityResult::decode(ret.as_slice()).unwrap(); - assert_eq!(remove_result.identifier, create_result.identifier); - }) - } + // #[test] + // pub fn test_delete_identity() { + // run_test(|| { + // let param = CreateIdentityParam { + // name: sample_key::NAME.to_string(), + // password: sample_key::PASSWORD.to_string(), + // password_hint: Some(sample_key::PASSWORD_HINT.to_string()), + // network: model::NETWORK_TESTNET.to_string(), + // seg_wit: None, + // }; + // let ret = call_api("create_identity", param).unwrap(); + // let create_result: CreateIdentityResult = + // CreateIdentityResult::decode(ret.as_slice()).unwrap(); + // assert!(create_result.ipfs_id.len() > 0); + // assert!(create_result.identifier.len() > 0); + + // let remove_identity_param = RemoveIdentityParam { + // identifier: create_result.identifier.to_owned(), + // password: sample_key::PASSWORD.to_string(), + // }; + // let ret = call_api("remove_identity", remove_identity_param).unwrap(); + // let remove_result: RemoveIdentityResult = + // RemoveIdentityResult::decode(ret.as_slice()).unwrap(); + // assert_eq!(remove_result.identifier, create_result.identifier); + // }) + // } #[test] pub fn test_sign_ethereum_legacy_tx() { @@ -3467,132 +3475,132 @@ mod tests { }) } - #[test] - pub fn test_eth_ec_sign() { - run_test(|| { - let param = RecoverIdentityParam { - name: sample_key::NAME.to_string(), - mnemonic: MNEMONIC.to_string(), - password: sample_key::PASSWORD.to_string(), - password_hint: Some(sample_key::PASSWORD_HINT.to_string()), - network: model::NETWORK_TESTNET.to_string(), - seg_wit: None, - }; - let ret = call_api("recover_identity", param).unwrap(); - let recover_result: RecoverIdentityResult = - RecoverIdentityResult::decode(ret.as_slice()).unwrap(); - assert!(recover_result.ipfs_id.len() > 0); - assert!(recover_result.identifier.len() > 0); - let eth_message_input = EthMessageInput { - message: "Hello imToken".to_string(), - is_hex: None, - }; - let input_value = encode_message(eth_message_input).unwrap(); - let param = SignParam { - id: recover_result.wallets.get(0).unwrap().id.clone(), - chain_type: "ETHEREUM".to_string(), - address: recover_result.wallets.get(0).unwrap().address.clone(), - input: Some(::prost_types::Any { - type_url: "imtoken".to_string(), - value: input_value, - }), - key: Some(sign_param::Key::Password(sample_key::PASSWORD.to_string())), - }; - let ret = call_api("eth_ec_sign", param).unwrap(); - let output: EthMessageOutput = EthMessageOutput::decode(ret.as_slice()).unwrap(); - assert_eq!(output.signature.to_owned(), "0x509afc633572c8f1885ec217cf1a42fb87a2c341217dbfcc21417e2dce357c0b41116412d1dd51af86ed25920ce6b5648d80d77bbbba8c79d68476bdffd773a31b"); - - let recover_input = EthRecoverAddressInput { - message: "Hello imToken".to_string(), - signature: output.signature.to_owned(), - is_hex: None, - }; - let ret = call_api("eth_recover_address", recover_input).unwrap(); - let recover_output: EthRecoverAddressOutput = - EthRecoverAddressOutput::decode(ret.as_slice()).unwrap(); - assert_eq!( - recover_output.address, - "6031564e7b2f5cc33737807b2e58daff870b590b" - ); + // #[test] + // pub fn test_eth_ec_sign() { + // run_test(|| { + // let param = RecoverIdentityParam { + // name: sample_key::NAME.to_string(), + // mnemonic: MNEMONIC.to_string(), + // password: sample_key::PASSWORD.to_string(), + // password_hint: Some(sample_key::PASSWORD_HINT.to_string()), + // network: model::NETWORK_TESTNET.to_string(), + // seg_wit: None, + // }; + // let ret = call_api("recover_identity", param).unwrap(); + // let recover_result: RecoverIdentityResult = + // RecoverIdentityResult::decode(ret.as_slice()).unwrap(); + // assert!(recover_result.ipfs_id.len() > 0); + // assert!(recover_result.identifier.len() > 0); + // let eth_message_input = EthMessageInput { + // message: "Hello imToken".to_string(), + // is_hex: None, + // }; + // let input_value = encode_message(eth_message_input).unwrap(); + // let param = SignParam { + // id: recover_result.wallets.get(0).unwrap().id.clone(), + // chain_type: "ETHEREUM".to_string(), + // address: recover_result.wallets.get(0).unwrap().address.clone(), + // input: Some(::prost_types::Any { + // type_url: "imtoken".to_string(), + // value: input_value, + // }), + // key: Some(sign_param::Key::Password(sample_key::PASSWORD.to_string())), + // }; + // let ret = call_api("eth_ec_sign", param).unwrap(); + // let output: EthMessageOutput = EthMessageOutput::decode(ret.as_slice()).unwrap(); + // assert_eq!(output.signature.to_owned(), "0x509afc633572c8f1885ec217cf1a42fb87a2c341217dbfcc21417e2dce357c0b41116412d1dd51af86ed25920ce6b5648d80d77bbbba8c79d68476bdffd773a31b"); + + // let recover_input = EthRecoverAddressInput { + // message: "Hello imToken".to_string(), + // signature: output.signature.to_owned(), + // is_hex: None, + // }; + // let ret = call_api("eth_recover_address", recover_input).unwrap(); + // let recover_output: EthRecoverAddressOutput = + // EthRecoverAddressOutput::decode(ret.as_slice()).unwrap(); + // assert_eq!( + // recover_output.address, + // "6031564e7b2f5cc33737807b2e58daff870b590b" + // ); - let recover_input = EthRecoverAddressInput { - message: hex::encode("Hello imToken".as_bytes()), - signature: output.signature, - is_hex: Some(true), - }; - let ret = call_api("eth_recover_address", recover_input).unwrap(); - let recover_output: EthRecoverAddressOutput = - EthRecoverAddressOutput::decode(ret.as_slice()).unwrap(); - assert_eq!( - recover_output.address, - "6031564e7b2f5cc33737807b2e58daff870b590b" - ); - }) - } + // let recover_input = EthRecoverAddressInput { + // message: hex::encode("Hello imToken".as_bytes()), + // signature: output.signature, + // is_hex: Some(true), + // }; + // let ret = call_api("eth_recover_address", recover_input).unwrap(); + // let recover_output: EthRecoverAddressOutput = + // EthRecoverAddressOutput::decode(ret.as_slice()).unwrap(); + // assert_eq!( + // recover_output.address, + // "6031564e7b2f5cc33737807b2e58daff870b590b" + // ); + // }) + // } - #[test] - pub fn test_eth_keystore_import() { - run_test(|| { - let param = CreateIdentityParam { - name: sample_key::NAME.to_string(), - password: sample_key::PASSWORD.to_string(), - password_hint: Some(sample_key::PASSWORD_HINT.to_string()), - network: model::NETWORK_TESTNET.to_string(), - seg_wit: None, - }; - let ret = call_api("create_identity", param).unwrap(); - let create_result: CreateIdentityResult = - CreateIdentityResult::decode(ret.as_slice()).unwrap(); - assert!(create_result.ipfs_id.len() > 0); - assert!(create_result.identifier.len() > 0); - - // let fixtures = r#"{"address":"6344e16b7733e5211a6b8b5ac1c5628b06d20560", - // "id":"7abda3f2-fa83-415f-a000-6b8af5b8f05a", - // "crypto":{ - // "ciphertext":"b28ad9edbeb57f89d32476c2f1415cb328276c573d14a4096faea6588a6931de", - // "cipherparams":{"iv":"647980eb665ea204e97c5c829f2b4aba"}, - // "kdf":"scrypt", - // "kdfparams":{"r":8,"p":1,"n":8192,"dklen":32,"salt":"47b199e0082bf2a8fc7ae724f272e7daf2c62a5dc0d01da160d6c6411d2a49ea"}, - // "mac":"a0cf4479d4e46fbc228bc9f7d10786a4501f36dfef591c316d8503f0e450f242", - // "cipher":"aes-128-ctr" - // }, - // "version":3}"#.to_string(); - let keystore = r#"{"crypto":{"cipher":"aes-128-ctr","cipherparams":{"iv":"eccde5515c1f9c833ee76ae3c354d1a2"},"ciphertext":"dda14dd1946c89ca85425f588eb32c545061bd022a9e873aa021de64b6f3b2d5","kdf":"pbkdf2","kdfparams":{"c":10240,"dklen":32,"prf":"hmac-sha256","salt":"3d327145c38932079d5400443fd612fe3e685ae13355d2897a78ea58ab8abda8"},"mac":"9e65da279df5d067c6fa9c4e7a689634885142d2106e7ef592cdb973dc1a22e3"},"id":"dea8739a-bd34-401b-9aaf-a92c1a7c381c","version":3,"address":"436ee8da3d1185a55f183d6e2363bad5aad36a13"}"#.to_string(); - let param = V3KeystoreImportInput { - keystore, - password: "abcd1234".to_string(), - overwrite: true, - name: "ETH".to_string(), - chain_type: "ETHEREUM".to_string(), - source: "KEYSTORE".to_string(), - }; + // #[test] + // pub fn test_eth_keystore_import() { + // run_test(|| { + // let param = CreateIdentityParam { + // name: sample_key::NAME.to_string(), + // password: sample_key::PASSWORD.to_string(), + // password_hint: Some(sample_key::PASSWORD_HINT.to_string()), + // network: model::NETWORK_TESTNET.to_string(), + // seg_wit: None, + // }; + // let ret = call_api("create_identity", param).unwrap(); + // let create_result: CreateIdentityResult = + // CreateIdentityResult::decode(ret.as_slice()).unwrap(); + // assert!(create_result.ipfs_id.len() > 0); + // assert!(create_result.identifier.len() > 0); + + // // let fixtures = r#"{"address":"6344e16b7733e5211a6b8b5ac1c5628b06d20560", + // // "id":"7abda3f2-fa83-415f-a000-6b8af5b8f05a", + // // "crypto":{ + // // "ciphertext":"b28ad9edbeb57f89d32476c2f1415cb328276c573d14a4096faea6588a6931de", + // // "cipherparams":{"iv":"647980eb665ea204e97c5c829f2b4aba"}, + // // "kdf":"scrypt", + // // "kdfparams":{"r":8,"p":1,"n":8192,"dklen":32,"salt":"47b199e0082bf2a8fc7ae724f272e7daf2c62a5dc0d01da160d6c6411d2a49ea"}, + // // "mac":"a0cf4479d4e46fbc228bc9f7d10786a4501f36dfef591c316d8503f0e450f242", + // // "cipher":"aes-128-ctr" + // // }, + // // "version":3}"#.to_string(); + // let keystore = r#"{"crypto":{"cipher":"aes-128-ctr","cipherparams":{"iv":"eccde5515c1f9c833ee76ae3c354d1a2"},"ciphertext":"dda14dd1946c89ca85425f588eb32c545061bd022a9e873aa021de64b6f3b2d5","kdf":"pbkdf2","kdfparams":{"c":10240,"dklen":32,"prf":"hmac-sha256","salt":"3d327145c38932079d5400443fd612fe3e685ae13355d2897a78ea58ab8abda8"},"mac":"9e65da279df5d067c6fa9c4e7a689634885142d2106e7ef592cdb973dc1a22e3"},"id":"dea8739a-bd34-401b-9aaf-a92c1a7c381c","version":3,"address":"436ee8da3d1185a55f183d6e2363bad5aad36a13"}"#.to_string(); + // let param = V3KeystoreImportInput { + // keystore, + // password: "abcd1234".to_string(), + // overwrite: true, + // name: "ETH".to_string(), + // chain_type: "ETHEREUM".to_string(), + // source: "KEYSTORE".to_string(), + // }; - let ret = call_api("eth_keystore_import", param).unwrap(); - let import_result: WalletResult = WalletResult::decode(ret.as_slice()).unwrap(); - println!("{}", import_result.id); - // assert_eq!(import_result.id, "7abda3f2-fa83-415f-a000-6b8af5b8f05a"); - let param = V3KeystoreExportInput { - password: "abcd1234".to_string(), - id: import_result.id, - }; - let ret = call_api("eth_keystore_export", param).unwrap(); - let export_result = V3KeystoreExportOutput::decode(ret.as_slice()).unwrap(); - println!("{}", export_result.json); - }) - } + // let ret = call_api("eth_keystore_import", param).unwrap(); + // let import_result: WalletResult = WalletResult::decode(ret.as_slice()).unwrap(); + // println!("{}", import_result.id); + // // assert_eq!(import_result.id, "7abda3f2-fa83-415f-a000-6b8af5b8f05a"); + // let param = V3KeystoreExportInput { + // password: "abcd1234".to_string(), + // id: import_result.id, + // }; + // let ret = call_api("eth_keystore_export", param).unwrap(); + // let export_result = V3KeystoreExportOutput::decode(ret.as_slice()).unwrap(); + // println!("{}", export_result.json); + // }) + // } - #[test] - fn test_encrypt_data_to_ipfs() { - todo!() - } + // #[test] + // fn test_encrypt_data_to_ipfs() { + // todo!() + // } - #[test] - fn test_decrypt_data_from_ipfs() { - todo!() - } + // #[test] + // fn test_decrypt_data_from_ipfs() { + // todo!() + // } - #[test] - fn test_sign_authentication_message() { - todo!() - } + // #[test] + // fn test_sign_authentication_message() { + // todo!() + // } } diff --git a/token-core/tcx/src/macros.rs b/token-core/tcx/src/macros.rs index 28e1b9cc..4ddde7ae 100644 --- a/token-core/tcx/src/macros.rs +++ b/token-core/tcx/src/macros.rs @@ -1,6 +1,6 @@ use std::str::FromStr; -use tcx_chain::{Address, Keystore, MessageSigner, TransactionSigner}; use tcx_constants::CoinInfo; +use tcx_keystore::{Address, Keystore, MessageSigner, TransactionSigner}; use tcx_primitive::TypedPublicKey; #[allow(clippy::derive_partial_eq_without_eq)] @@ -36,7 +36,7 @@ impl MessageSigner for Keystore { symbol: &str, address: &str, message: &MockMessageInput, - ) -> tcx_chain::Result { + ) -> tcx_keystore::Result { Err(format_err!("unsupported_chain")) } } @@ -45,7 +45,10 @@ impl MessageSigner for Keystore { pub struct MockAddress(); impl Address for MockAddress { - fn from_public_key(_public_key: &TypedPublicKey, _coin: &CoinInfo) -> tcx_chain::Result { + fn from_public_key( + _public_key: &TypedPublicKey, + _coin: &CoinInfo, + ) -> tcx_keystore::Result { Err(format_err!("unsupported_chain")) } diff --git a/token-core/test-data/wallets/5991857a-2488-4546-b730-463a5f84ea6a b/token-core/test-data/wallets/5991857a-2488-4546-b730-463a5f84ea6a new file mode 100644 index 00000000..c2d312f3 --- /dev/null +++ b/token-core/test-data/wallets/5991857a-2488-4546-b730-463a5f84ea6a @@ -0,0 +1,37 @@ +{ + "id": "5991857a-2488-4546-b730-463a5f84ea6a", + "crypto": { + "cipher": "aes-128-ctr", + "cipherparams": { + "iv": "3a442e8b02843edf71b8d3a9c9da2c3b" + }, + "ciphertext": "fcbcceae1d239f9575c55f4c4f81eeba44a6ad9d948f544af2ffee9efef2c038", + "kdf": "pbkdf2", + "kdfparams": { + "c": 65535, + "dklen": 32, + "prf": "hmac-sha256", + "salt": "fa141145a343d9b6c7e2f12e0e56d564bc4d1b46cd48e8f7d898779e06357f1f" + }, + "mac": "50ee3b40129c5f18f9ff6982db0eb18504ea2e8f3d96e4ac062b4eb5849cf011" + }, + "version": 3, + "address": "6031564e7b2f5cc33737807b2e58daff870b590b", + "encMnemonic": { + "encStr": "267bda938e4edbf7c420e89c59c6862f9127c7275d012b1b607f9e91ddb94574e81e94f6d8155e3c85ede03f584e09916122f03c72b67a1f96ddbf291beb46894d9a02d30170a9444692", + "nonce": "3cfe9f0b32b5d592e5fab54bd28863cd" + }, + "mnemonicPath": "m/44'/60'/0'/0/0", + "imTokenMeta": { + "mode": "normal", + "chain": "ETHEREUM", + "segWit": "NONE", + "passwordHint": "", + "backup": [], + "source": "RECOVERED_IDENTITY", + "version": "iOS-2.14.0.1742", + "name": "ETH", + "network": "MAINNET", + "timestamp": "1695800371.215641" + } +} \ No newline at end of file