diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..95760337 --- /dev/null +++ b/Makefile @@ -0,0 +1,11 @@ +build-tcx-proto: + cargo build -p tcx-proto + +check-tcx: + cd token-core; cargo check + +build-tcx: + cd token-core; cargo build + +test-tcx: + cd token-core; cargo test diff --git a/token-core/tcx-keystore/src/keystore/hd.rs b/token-core/tcx-keystore/src/keystore/hd.rs index d8c790ec..6a4b0a2a 100644 --- a/token-core/tcx-keystore/src/keystore/hd.rs +++ b/token-core/tcx-keystore/src/keystore/hd.rs @@ -104,7 +104,6 @@ impl HdKeystore { fn cache_mnemonic(&mut self, mnemonic_bytes: Vec) -> Result<()> { let mnemonic_str = String::from_utf8(mnemonic_bytes)?; - println!("mnemonic_str: {}", mnemonic_str); let _mnemonic = Mnemonic::from_phrase(&mnemonic_str, Language::English) .map_err(transform_mnemonic_error)?; @@ -199,7 +198,6 @@ impl HdKeystore { let public_key = private_key.public_key(); let address = A::from_public_key(&public_key, coin_info)?; - // todo: ext_pub_key let ext_pub_key = match coin_info.curve { CurveType::SubSr25519 | CurveType::BLS | CurveType::ED25519 => "".to_owned(), _ => root diff --git a/token-core/tcx-migration/src/migration.rs b/token-core/tcx-migration/src/migration.rs index eb887174..c40c8401 100644 --- a/token-core/tcx-migration/src/migration.rs +++ b/token-core/tcx-migration/src/migration.rs @@ -339,11 +339,10 @@ impl LegacyKeystore { #[cfg(test)] mod tests { use serde_json::Value; - use tcx_common::hex_to_bytes; + use tcx_common::FromHex; use tcx_constants::{CoinInfo, TEST_MNEMONIC}; use tcx_constants::{TEST_PASSWORD, TEST_PRIVATE_KEY}; use tcx_crypto::crypto::SCryptParams; - use tcx_crypto::hex; use tcx_crypto::Crypto; use tcx_crypto::Pbkdf2Params; use tcx_crypto::{EncPair, Key}; @@ -544,7 +543,7 @@ mod tests { #[test] fn test_export_v3_keystore() { - let private_key_bytes = hex_to_bytes(TEST_PRIVATE_KEY).unwrap(); + let private_key_bytes = &Vec::::from_hex(TEST_PRIVATE_KEY).unwrap(); let v3_keystore = LegacyKeystore::new_v3(&private_key_bytes, TEST_PASSWORD).expect("v3 keystore"); let keystore_json = serde_json::to_string(&v3_keystore).expect("serde v3"); diff --git a/token-core/tcx-proto/src/params.proto b/token-core/tcx-proto/src/params.proto index 8103f441..2cd78b18 100644 --- a/token-core/tcx-proto/src/params.proto +++ b/token-core/tcx-proto/src/params.proto @@ -63,9 +63,10 @@ message AccountResponse { string chainType = 1; string address = 2; string path = 3; - string extendedPublicKey = 4; + string curve = 4; string publicKey = 5; - string curve = 6; + string extendedPublicKey = 6; + string encryptedExtendedPublicKey = 7; } message DeriveAccountsResult { @@ -163,7 +164,7 @@ message SignParam { string path = 5; string curve = 6; string network = 7; - string seg_wit = 8; + string segWit = 8; google.protobuf.Any input = 9; } @@ -173,7 +174,7 @@ message DeriveSubAccountsParam { string curve = 3; string network = 4; string segWit = 5; - repeated string paths = 6; + repeated string relativePaths = 6; string extendedPublicKey = 7; } diff --git a/token-core/tcx/src/api.rs b/token-core/tcx/src/api.rs index c9a8c153..c7958ea4 100644 --- a/token-core/tcx/src/api.rs +++ b/token-core/tcx/src/api.rs @@ -294,11 +294,13 @@ pub struct AccountResponse { #[prost(string, tag = "3")] pub path: ::prost::alloc::string::String, #[prost(string, tag = "4")] - pub extended_public_key: ::prost::alloc::string::String, + pub curve: ::prost::alloc::string::String, #[prost(string, tag = "5")] pub public_key: ::prost::alloc::string::String, #[prost(string, tag = "6")] - pub curve: ::prost::alloc::string::String, + pub extended_public_key: ::prost::alloc::string::String, + #[prost(string, tag = "7")] + pub encrypted_extended_public_key: ::prost::alloc::string::String, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -446,7 +448,7 @@ pub struct DeriveSubAccountsParam { #[prost(string, tag = "5")] pub seg_wit: ::prost::alloc::string::String, #[prost(string, repeated, tag = "6")] - pub paths: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + pub relative_paths: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, #[prost(string, tag = "7")] pub extended_public_key: ::prost::alloc::string::String, } diff --git a/token-core/tcx/src/handler.rs b/token-core/tcx/src/handler.rs index 082a1e00..337b2aef 100644 --- a/token-core/tcx/src/handler.rs +++ b/token-core/tcx/src/handler.rs @@ -307,7 +307,8 @@ pub(crate) fn derive_accounts(data: &[u8]) -> Result> { chain_type: derivation.chain_type.to_owned(), address: account.address.to_owned(), path: account.derivation_path.to_owned(), - extended_public_key: enc_xpub, + extended_public_key: account.ext_pub_key.to_string(), + encrypted_extended_public_key: enc_xpub, public_key: account.public_key, curve: account.curve.as_str().to_string(), }; @@ -989,22 +990,26 @@ pub(crate) fn derive_sub_accounts(data: &[u8]) -> Result> { let xpub = TypedDeterministicPublicKey::from_hex(curve, ¶m.extended_public_key)?; let account_ret: Vec> = param - .paths + .relative_paths .iter() - .map(|path| { + .map(|relative_path| { let coin_info = CoinInfo { coin: param.chain_type.to_string(), - derivation_path: path.to_string(), + derivation_path: relative_path.to_string(), curve, network: param.network.to_string(), seg_wit: param.seg_wit.to_string(), }; - let acc = derive_sub_account(&xpub, &coin_info)?; + let acc: Account = derive_sub_account(&xpub, &coin_info)?; + + let enc_xpub = encrypt_xpub(¶m.extended_public_key.to_string(), &acc.network)?; + let acc_rsp = AccountResponse { chain_type: param.chain_type.to_string(), address: acc.address.to_string(), - path: path.to_string(), + path: relative_path.to_string(), extended_public_key: param.extended_public_key.to_string(), + encrypted_extended_public_key: enc_xpub, public_key: acc.public_key, curve: param.curve.to_string(), }; diff --git a/token-core/tcx/src/lib.rs b/token-core/tcx/src/lib.rs index 1d7b8946..1f3236dd 100644 --- a/token-core/tcx/src/lib.rs +++ b/token-core/tcx/src/lib.rs @@ -176,12 +176,13 @@ mod tests { use crate::api::{ sign_param, CreateKeystoreParam, DeriveAccountsParam, DeriveAccountsResult, - DerivedKeyResult, ExistsKeystoreResult, ExportPrivateKeyParam, ExportResult, GeneralResult, - GenerateMnemonicResult, GetPublicKeysParam, GetPublicKeysResult, ImportMnemonicParam, - ImportPrivateKeyParam, InitTokenCoreXParam, KeyType, KeystoreCommonAccountsParam, - KeystoreCommonExistsParam, KeystoreResult, PrivateKeyStoreExportParam, PublicKeyDerivation, - PublicKeyParam, PublicKeyResult, SignHashesParam, SignHashesResult, SignParam, - WalletKeyParam, ZksyncPrivateKeyFromSeedParam, ZksyncPrivateKeyFromSeedResult, + DeriveSubAccountsParam, DeriveSubAccountsResult, DerivedKeyResult, ExistsKeystoreResult, + ExportPrivateKeyParam, ExportResult, GeneralResult, GenerateMnemonicResult, + GetPublicKeysParam, GetPublicKeysResult, ImportMnemonicParam, ImportPrivateKeyParam, + InitTokenCoreXParam, KeyType, KeystoreCommonAccountsParam, KeystoreCommonExistsParam, + KeystoreResult, PrivateKeyStoreExportParam, PublicKeyDerivation, PublicKeyParam, + PublicKeyResult, SignHashesParam, SignHashesResult, SignParam, WalletKeyParam, + ZksyncPrivateKeyFromSeedParam, ZksyncPrivateKeyFromSeedResult, ZksyncPrivateKeyToPubkeyHashParam, ZksyncPrivateKeyToPubkeyHashResult, ZksyncSignMusigParam, ZksyncSignMusigResult, }; @@ -3475,6 +3476,125 @@ mod tests { }) } + #[test] + pub fn test_derive_btc_legacy_sub_accounts() { + run_test(|| { + let derivation = Derivation { + chain_type: "BITCOIN".to_string(), + path: "m/44'/0'/0'/0/0".to_string(), + network: "MAINNET".to_string(), + seg_wit: "NONE".to_string(), + chain_id: "".to_string(), + curve: "SECP256k1".to_string(), + bech32_prefix: "".to_string(), + }; + + let (wallet, accounts) = import_and_derive(derivation); + let params = DeriveSubAccountsParam { + id: wallet.id.to_string(), + chain_type: "BITCOIN".to_string(), + curve: "SECP256k1".to_string(), + network: "MAINNET".to_string(), + seg_wit: "NONE".to_string(), + relative_paths: vec!["0/0".to_string(), "0/1".to_string(), "1/0".to_string()], + extended_public_key: accounts.accounts[0].extended_public_key.to_string(), + }; + + let result_bytes = derive_sub_accounts(&encode_message(params).unwrap()).unwrap(); + let result = DeriveSubAccountsResult::decode(result_bytes.as_slice()).unwrap(); + assert_eq!( + "12z6UzsA3tjpaeuvA2Zr9jwx19Azz74D6g", + result.accounts[0].address + ); + assert_eq!( + "1962gsZ8PoPUYHneFakkCTrukdFMVQ4i4T", + result.accounts[1].address + ); + assert_eq!( + "19vddWhyq637bqDfuKadsoy5mTNRgfb3hr", + result.accounts[2].address + ); + }) + } + + #[test] + pub fn test_derive_btc_p2wpkh_sub_accounts() { + run_test(|| { + let derivation = Derivation { + chain_type: "BITCOIN".to_string(), + path: "m/49'/0'/0'/0/0".to_string(), + network: "MAINNET".to_string(), + seg_wit: "P2WPKH".to_string(), + chain_id: "".to_string(), + curve: "SECP256k1".to_string(), + bech32_prefix: "".to_string(), + }; + + let (wallet, accounts) = import_and_derive(derivation); + let params = DeriveSubAccountsParam { + id: wallet.id.to_string(), + chain_type: "BITCOIN".to_string(), + curve: "SECP256k1".to_string(), + network: "MAINNET".to_string(), + seg_wit: "P2WPKH".to_string(), + relative_paths: vec!["0/0".to_string(), "0/1".to_string(), "1/0".to_string()], + extended_public_key: accounts.accounts[0].extended_public_key.to_string(), + }; + + let result_bytes = derive_sub_accounts(&encode_message(params).unwrap()).unwrap(); + let result = DeriveSubAccountsResult::decode(result_bytes.as_slice()).unwrap(); + assert_eq!( + "3JmreiUEKn8P3SyLYmZ7C1YCd4r2nFy3Dp", + result.accounts[0].address + ); + assert_eq!( + "33xJxujVGf4qBmPTnGW9P8wrKCmT7Nwt3t", + result.accounts[1].address + ); + assert_eq!( + "33K4nJ6HuM4fuJct11xPPHH65dnGrN5Ggt", + result.accounts[2].address + ); + }) + } + + #[test] + pub fn test_derive_eth_sub_accounts() { + run_test(|| { + let derivation = Derivation { + chain_type: "ETHEREUM".to_string(), + path: "m/44'/60'/0'/0/0".to_string(), + network: "MAINNET".to_string(), + seg_wit: "".to_string(), + chain_id: "".to_string(), + curve: "SECP256k1".to_string(), + bech32_prefix: "".to_string(), + }; + + let (wallet, accounts) = import_and_derive(derivation); + let params = DeriveSubAccountsParam { + id: wallet.id.to_string(), + chain_type: "ETHEREUM".to_string(), + curve: "SECP256k1".to_string(), + network: "MAINNET".to_string(), + seg_wit: "".to_string(), + relative_paths: vec!["0/0".to_string(), "0/1".to_string()], + extended_public_key: accounts.accounts[0].extended_public_key.to_string(), + }; + + let result_bytes = derive_sub_accounts(&encode_message(params).unwrap()).unwrap(); + let result = DeriveSubAccountsResult::decode(result_bytes.as_slice()).unwrap(); + assert_eq!( + "0x6031564e7b2F5cc33737807b2E58DaFF870B590b", + result.accounts[0].address + ); + assert_eq!( + "0x80427Ae1f55bCf60ee4CD2db7549b8BC69a74303", + result.accounts[1].address + ); + }) + } + // #[test] // pub fn test_eth_ec_sign() { // run_test(|| {