diff --git a/imkey-core/ikc-wallet/coin-bitcoin/src/transaction.rs b/imkey-core/ikc-wallet/coin-bitcoin/src/transaction.rs index b4e93cee..9d5088d3 100644 --- a/imkey-core/ikc-wallet/coin-bitcoin/src/transaction.rs +++ b/imkey-core/ikc-wallet/coin-bitcoin/src/transaction.rs @@ -22,9 +22,9 @@ use ikc_common::utility::{bigint_to_byte_vec, hex_to_bytes, secp256k1_sign}; use ikc_device::device_binding::KEY_MANAGER; use ikc_transport::message::{send_apdu, send_apdu_timeout}; use secp256k1::ecdsa::Signature; +use secp256k1::PublicKey; use std::borrow::Borrow; use std::str::FromStr; -use secp256k1::PublicKey; #[derive(Clone)] pub struct Utxo { diff --git a/token-core/tcx-btc-kin/src/address.rs b/token-core/tcx-btc-kin/src/address.rs index eec26826..b17658dd 100644 --- a/token-core/tcx-btc-kin/src/address.rs +++ b/token-core/tcx-btc-kin/src/address.rs @@ -262,6 +262,7 @@ impl Display for BtcKinAddress { #[cfg(test)] mod tests { use bitcoin::SchnorrSighashType::Default; + use secp256k1::rand::seq::index::sample; use std::str::FromStr; use tcx_common::{FromHex, ToHex}; @@ -511,6 +512,36 @@ mod tests { assert_eq!(account.ext_pub_key, "xpub6CDSaXHQokkKmHHG2kNCFZeirJkcZgRZE97ZZUtViif3SFHSNVAvRpWC3CxeRt2VZetEGCcPTmWEFpKF4NDeeZrMNPQgfUaX5Hkw89kW8qE"); } + #[test] + fn test_dogecoin_address_from_str() { + let addr = BtcKinAddress::from_str("DQ4tVEqdPWHc1aVBm4Sfwft8XyNRPMEchR").unwrap(); + assert_eq!(addr.network.coin, "DOGECOIN"); + assert_eq!(addr.network.network, "MAINNET"); + } + + #[test] + fn test_xpub() { + let mut hd = sample_hd_keystore(); + + let test_cases = [ + ("BITCOIN", "MAINNET", "m/44'/0'/0/0", "xpub6CqzLtyBHdq6tZD7Bdxo9bpCEWfFBg7dim6UMxs83nqNYzFatwkr9yGkLtG5ktiKcgaUqP5BpuTMJLyaLQ167gANU8ZsfLRN86VXyx3atJX"), + ("LITECOIN", "MAINNET", "m/44'/2'/0'/0/0", "xpub6D3MqTwuLWB5veAfhDjPu1oHfS6L1imVbf22zQFWJW9EtnSmYYqiGMGkW1MCsT2HmkW872tefMY9deewW6DGd8zE7RcXVv8wKhZnbJeidjT"), + ("DOGECOIN", "MAINNET", "m/44'/3'/0'/0/0", "xpub6CDSaXHQokkKmHHG2kNCFZeirJkcZgRZE97ZZUtViif3SFHSNVAvRpWC3CxeRt2VZetEGCcPTmWEFpKF4NDeeZrMNPQgfUaX5Hkw89kW8qE"), + ]; + + for (coin, network, path, xpub) in test_cases { + let coin_info = CoinInfo { + coin: coin.to_string(), + derivation_path: path.to_string(), + curve: CurveType::SECP256k1, + network: network.to_string(), + seg_wit: "NONE".to_string(), + hrp: "".to_string(), + }; + let account = hd.derive_coin::(&coin_info).unwrap(); + assert_eq!(account.ext_pub_key, xpub); + } + } #[test] fn test_bip84_spec_vector() { diff --git a/token-core/tcx-constants/src/coin_info.rs b/token-core/tcx-constants/src/coin_info.rs index 7100b637..9e9f3bcd 100644 --- a/token-core/tcx-constants/src/coin_info.rs +++ b/token-core/tcx-constants/src/coin_info.rs @@ -302,56 +302,64 @@ lazy_static! { curve: CurveType::SECP256k1, network: "MAINNET".to_string(), seg_wit: "NONE".to_string(), - hrp: "".to_string(),}, + hrp: "".to_string(), + }, CoinInfo { coin: "DOGECOIN".to_string(), derivation_path: "m/44'/1'/0'/0/0".to_string(), curve: CurveType::SECP256k1, network: "TESTNET".to_string(), seg_wit: "NONE".to_string(), - hrp: "".to_string(),}, + hrp: "".to_string(), + }, CoinInfo { coin: "DOGECOIN".to_string(), derivation_path: "m/49'/3'/0'/0/0".to_string(), curve: CurveType::SECP256k1, network: "MAINNET".to_string(), seg_wit: "P2WPKH".to_string(), - hrp: "".to_string(),}, + hrp: "".to_string(), + }, CoinInfo { coin: "DOGECOIN".to_string(), derivation_path: "m/49'/1'/0'/0/0".to_string(), curve: CurveType::SECP256k1, network: "TESTNET".to_string(), seg_wit: "P2WPKH".to_string(), - hrp: "".to_string(),}, + hrp: "".to_string(), + }, CoinInfo { coin: "DOGECOIN".to_string(), derivation_path: "m/84'/3'/0'/0/0".to_string(), curve: CurveType::SECP256k1, network: "MAINNET".to_string(), seg_wit: "VERSION_0".to_string(), - hrp: "".to_string(),}, + hrp: "".to_string(), + }, CoinInfo { coin: "DOGECOIN".to_string(), derivation_path: "m/84'/1'/0'/0/0".to_string(), curve: CurveType::SECP256k1, network: "TESTNET".to_string(), seg_wit: "VERSION_0".to_string(), - hrp: "".to_string(),}, + hrp: "".to_string(), + }, CoinInfo { coin: "DOGECOIN".to_string(), derivation_path: "m/86'/3'/0'/0/0".to_string(), curve: CurveType::SECP256k1, network: "MAINNET".to_string(), seg_wit: "VERSION_1".to_string(), - hrp: "".to_string(),}, + hrp: "".to_string(), + }, CoinInfo { coin: "DOGECOIN".to_string(), derivation_path: "m/86'/1'/0'/0/0".to_string(), curve: CurveType::SECP256k1, network: "TESTNET".to_string(), seg_wit: "VERSION_1".to_string(), - hrp: "".to_string(),}, + hrp: "".to_string(), + }, ]; RwLock::new(coin_infos) diff --git a/token-core/tcx/src/handler.rs b/token-core/tcx/src/handler.rs index 1c37813d..e47cb19d 100644 --- a/token-core/tcx/src/handler.rs +++ b/token-core/tcx/src/handler.rs @@ -292,6 +292,7 @@ pub(crate) fn decode_private_key(private_key: &str) -> Result chain_types.push("BITCOIN".to_string()); chain_types.push("BITCOINCASH".to_string()); chain_types.push("LITECOIN".to_string()); + chain_types.push("DOGECOIN".to_string()); } 0x80 => { if data_len == 37 { @@ -310,6 +311,10 @@ pub(crate) fn decode_private_key(private_key: &str) -> Result network = "MAINNET".to_string(); chain_types.push("LITECOIN".to_string()); } + 0x9e => { + network = "MAINNET".to_string(); + chain_types.push("DOGECOIN".to_string()); + } _ => return Err(anyhow!("unknow ver header when parse wif, ver: {}", ver[0])), } } diff --git a/token-core/tcx/tests/import_test.rs b/token-core/tcx/tests/import_test.rs index 46dec444..d4fcc82c 100644 --- a/token-core/tcx/tests/import_test.rs +++ b/token-core/tcx/tests/import_test.rs @@ -311,6 +311,15 @@ pub fn test_import_private_key() { curve: "secp256k1".to_string(), hrp: "".to_string(), }, + Derivation { + chain_type: "DOGECOIN".to_string(), + path: "pk_not_need_path".to_string(), + network: "MAINNET".to_string(), + seg_wit: "NONE".to_string(), + chain_id: "".to_string(), + curve: "secp256k1".to_string(), + hrp: "".to_string(), + }, ]; let param = DeriveAccountsParam { id: import_result.id.to_string(), @@ -322,7 +331,7 @@ pub fn test_import_private_key() { let derived_accounts_bytes = call_api("derive_accounts", param).unwrap(); let derived_accounts: DeriveAccountsResult = DeriveAccountsResult::decode(derived_accounts_bytes.as_slice()).unwrap(); - assert_eq!(13, derived_accounts.accounts.len()); + assert_eq!(14, derived_accounts.accounts.len()); assert_eq!( "LgGNTHMkgETS7oQcoekvACJQcH355xECog", derived_accounts.accounts[0].address @@ -392,6 +401,11 @@ pub fn test_import_private_key() { derived_accounts.accounts[12].address ); + assert_eq!( + "DSBWjKzZtz7fPzu4N6mBRwQFHCQ6KQSjue", + derived_accounts.accounts[13].address + ); + // pk rederive let derivations = vec![Derivation { chain_type: "LITECOIN".to_string(), @@ -1079,6 +1093,73 @@ pub fn test_import_multi_curve() { }) } +#[test] +#[serial] +pub fn test_import_wif() { + run_test(|| { + let import_result: KeystoreResult = import_default_wallet(); + + let export_param = ExportPrivateKeyParam { + id: import_result.id.to_string(), + key: Some(crate::api::export_private_key_param::Key::Password( + TEST_PASSWORD.to_owned(), + )), + chain_type: "DOGECOIN".to_string(), + network: "MAINNET".to_string(), + curve: "secp256k1".to_string(), + path: "m/44'/3'/0'/0'".to_string(), + }; + + let export_pk_bytes = call_api("export_private_key", export_param).unwrap(); + let export_pk: ExportPrivateKeyResult = + ExportPrivateKeyResult::decode(export_pk_bytes.as_slice()).unwrap(); + assert_eq!( + export_pk.private_key, + "QNfA3BhQaV73MY3QMYAoNrZXSPRyVqUFxb4akc6hBpN6TwZC8GDQ" + ); + + let param: ImportPrivateKeyParam = ImportPrivateKeyParam { + private_key: export_pk.private_key.to_string(), + password: TEST_PASSWORD.to_string(), + name: "import_private_key_wallet".to_string(), + password_hint: "".to_string(), + network: "MAINNET".to_string(), + overwrite_id: "".to_string(), + }; + let ret = call_api("import_private_key", param).unwrap(); + let import_result: ImportPrivateKeyResult = + ImportPrivateKeyResult::decode(ret.as_slice()).unwrap(); + + let derivation = Derivation { + chain_type: "DOGECOIN".to_string(), + path: "m/44'/145'/0'/0/0".to_string(), + network: "MAINNET".to_string(), + seg_wit: "NONE".to_string(), + chain_id: "".to_string(), + curve: "secp256k1".to_string(), + hrp: "".to_string(), + }; + let param = DeriveAccountsParam { + id: import_result.id.to_string(), + key: Some(crate::api::derive_accounts_param::Key::Password( + TEST_PASSWORD.to_owned(), + )), + derivations: vec![derivation], + }; + + let ret = call_api("derive_accounts", param).unwrap(); + let result: DeriveAccountsResult = DeriveAccountsResult::decode(ret.as_slice()).unwrap(); + let account = result.accounts.first().unwrap(); + assert_eq!(account.chain_type, "DOGECOIN"); + assert_eq!(account.address, "DGge46gf1ipekESG6eDFYxA15PxWf2UfrS"); + + assert_eq!( + import_result.identifier, + "im14x5EL1LDHgsXPpvzsF5RQCKE982c9XmM1VZx" + ); + }) +} + #[test] #[serial] pub fn test_import_hex_private_key() {