From b92ca547b39a941c61ed2bd4d7879f74bd56a35c Mon Sep 17 00:00:00 2001 From: XuNeal Date: Tue, 20 Aug 2024 10:33:40 +0800 Subject: [PATCH] fix: cannot derive sub account with hrp --- token-core/tcx-btc-kin/src/address.rs | 22 ++++++---- token-core/tcx-proto/src/params.proto | 1 + token-core/tcx/src/api.rs | 2 + token-core/tcx/src/handler.rs | 3 ++ token-core/tcx/tests/derive_test.rs | 62 +++++++++++++++++++++++++++ token-core/tcx/tests/export_test.rs | 3 +- 6 files changed, 83 insertions(+), 10 deletions(-) diff --git a/token-core/tcx-btc-kin/src/address.rs b/token-core/tcx-btc-kin/src/address.rs index e8cd6058..c7883224 100644 --- a/token-core/tcx-btc-kin/src/address.rs +++ b/token-core/tcx-btc-kin/src/address.rs @@ -41,6 +41,7 @@ impl WIFDisplay for TypedPrivateKey { Ok(key.to_ss58check_with_version(&version)) } } + #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct BtcKinAddress { pub network: BtcKinNetwork, @@ -139,10 +140,13 @@ impl BtcKinAddress { fn bech32_network(bech32: &str) -> Option<&BtcKinNetwork> { let bech32_prefix = bech32.rfind('1').map(|sep| bech32.split_at(sep).0); - match bech32_prefix { - Some(prefix) => BtcKinNetwork::find_by_hrp(prefix), - None => None, + if bech32_prefix.is_some() { + let prefix = bech32_prefix.unwrap(); + if (!prefix.is_empty()) { + return BtcKinNetwork::find_by_hrp(prefix); + } } + return None; } fn decode_base58(addr: &str) -> result::Result, LibAddressError> { @@ -402,36 +406,36 @@ mod tests { let coin = coin_info_from_param("BITCOIN", "MAINNET", "P2WPKH", "").unwrap(); assert!(BtcKinAddress::is_valid( "3Js9bGaZSQCNLudeGRHL4NExVinc25RbuG", - &coin + &coin, )); let coin = coin_info_from_param("BITCOIN", "MAINNET", "NONE", "").unwrap(); assert!(BtcKinAddress::is_valid( "1Gx9QwpQBFnAjF27Uiz3ea2zYBDrLx31bw", - &coin + &coin, )); let coin = coin_info_from_param("BITCOIN", "MAINNET", "VERSION_0", "").unwrap(); assert!(BtcKinAddress::is_valid( "bc1qnfv46v0wtarc6n82dnehtvzj2gtnqzjhj5wxqj", - &coin + &coin, )); let coin = coin_info_from_param("LITECOIN", "MAINNET", "NONE", "").unwrap(); assert!(BtcKinAddress::is_valid( "Ldfdegx3hJygDuFDUA7Rkzjjx8gfFhP9DP", - &coin + &coin, )); let coin = coin_info_from_param("LITECOIN", "MAINNET", "P2WPKH", "").unwrap(); assert!(BtcKinAddress::is_valid( "MR5Hu9zXPX3o9QuYNJGft1VMpRP418QDfW", - &coin + &coin, )); let coin = coin_info_from_param("LITECOIN", "MAINNET", "P2WPKH", "").unwrap(); assert!(!BtcKinAddress::is_valid( "MR5Hu9zXPX3o9QuYNJGft1VMpRP418QDf", - &coin + &coin, )); let coin = coin_info_from_param("LITECOIN", "MAINNET", "P2WPKH", "").unwrap(); diff --git a/token-core/tcx-proto/src/params.proto b/token-core/tcx-proto/src/params.proto index 560ce6d6..a49cb903 100644 --- a/token-core/tcx-proto/src/params.proto +++ b/token-core/tcx-proto/src/params.proto @@ -191,6 +191,7 @@ message DeriveSubAccountsParam { string segWit = 4; repeated string relativePaths = 5; string extendedPublicKey = 6; + string hrp = 7; } message DeriveSubAccountsResult { diff --git a/token-core/tcx/src/api.rs b/token-core/tcx/src/api.rs index a5113553..a34e48d6 100644 --- a/token-core/tcx/src/api.rs +++ b/token-core/tcx/src/api.rs @@ -550,6 +550,8 @@ pub struct DeriveSubAccountsParam { pub relative_paths: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, #[prost(string, tag = "6")] pub extended_public_key: ::prost::alloc::string::String, + #[prost(string, tag = "7")] + pub hrp: ::prost::alloc::string::String, } #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] diff --git a/token-core/tcx/src/handler.rs b/token-core/tcx/src/handler.rs index 3987eb38..55e16c05 100644 --- a/token-core/tcx/src/handler.rs +++ b/token-core/tcx/src/handler.rs @@ -1289,6 +1289,8 @@ pub fn derive_sub_accounts(data: &[u8]) -> Result> { ¶m.curve, )?; coin_info.derivation_path = relative_path.to_string(); + coin_info.hrp = param.hrp.to_string(); + let acc: Account = derive_sub_account(&xpub, &coin_info)?; let enc_xpub = encrypt_xpub(¶m.extended_public_key.to_string())?; @@ -1387,6 +1389,7 @@ mod tests { "BITCOIN".to_string(), "BITCOINCASH".to_string(), "LITECOIN".to_string(), + "DOGECOIN".to_string(), ] ); assert_eq!(decoded.curve, CurveType::SECP256k1); diff --git a/token-core/tcx/tests/derive_test.rs b/token-core/tcx/tests/derive_test.rs index e29cd1e7..dc998aed 100644 --- a/token-core/tcx/tests/derive_test.rs +++ b/token-core/tcx/tests/derive_test.rs @@ -455,6 +455,7 @@ pub fn test_derive_btc_legacy_sub_accounts() { 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(), + hrp: "".to_string(), }; let result_bytes = derive_sub_accounts(&encode_message(params).unwrap()).unwrap(); @@ -496,6 +497,7 @@ pub fn test_derive_btc_p2wpkh_sub_accounts() { 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(), + hrp: "".to_string(), }; let result_bytes = derive_sub_accounts(&encode_message(params).unwrap()).unwrap(); @@ -537,6 +539,7 @@ pub fn test_derive_eth_sub_accounts() { 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(), + hrp: "".to_string(), }; let result_bytes = derive_sub_accounts(&encode_message(params).unwrap()).unwrap(); @@ -552,6 +555,65 @@ pub fn test_derive_eth_sub_accounts() { }) } +#[test] +#[serial] +pub fn test_derive_cosmos_sub_accounts() { + run_test(|| { + let derivation = Derivation { + chain_type: "COSMOS".to_string(), + path: "m/44'/118'/0'/0/0".to_string(), + network: "MAINNET".to_string(), + seg_wit: "".to_string(), + chain_id: "".to_string(), + curve: "secp256k1".to_string(), + hrp: "cosmos".to_string(), + }; + + let (_, accounts) = import_and_derive(derivation); + let params = DeriveSubAccountsParam { + chain_type: "COSMOS".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(), + hrp: "cosmos".to_string(), + }; + + let result_bytes = derive_sub_accounts(&encode_message(params).unwrap()).unwrap(); + let result = DeriveSubAccountsResult::decode(result_bytes.as_slice()).unwrap(); + assert_eq!( + "cosmos1ajz9y0x3wekez7tz2td2j6l2dftn28v26dd992", + result.accounts[0].address + ); + assert_eq!( + "cosmos1nkujjlktqdue52xc0k09yzc7h3xswsfpl568zc", + result.accounts[1].address + ); + + let params = DeriveSubAccountsParam { + chain_type: "COSMOS".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(), + hrp: "osmo".to_string(), + }; + + let result_bytes = derive_sub_accounts(&encode_message(params).unwrap()).unwrap(); + let result = DeriveSubAccountsResult::decode(result_bytes.as_slice()).unwrap(); + assert_eq!( + "osmo1ajz9y0x3wekez7tz2td2j6l2dftn28v2jk74nc", + result.accounts[0].address + ); + assert_eq!( + "osmo1nkujjlktqdue52xc0k09yzc7h3xswsfph0fh52", + result.accounts[1].address + ) + }) +} + #[test] #[serial] pub fn test_mnemonic_to_public() { diff --git a/token-core/tcx/tests/export_test.rs b/token-core/tcx/tests/export_test.rs index 02947c93..d57833ae 100644 --- a/token-core/tcx/tests/export_test.rs +++ b/token-core/tcx/tests/export_test.rs @@ -680,7 +680,8 @@ pub fn test_backup_private_key() { vec![ "BITCOIN".to_string(), "BITCOINCASH".to_string(), - "LITECOIN".to_string() + "LITECOIN".to_string(), + "DOGECOIN".to_string() ], import_result.identified_chain_types );