From aa45d6ed3a3e17cd5d8e56ab98d424e1fc84d33f Mon Sep 17 00:00:00 2001 From: xiaoguang Date: Thu, 29 Feb 2024 15:47:29 +0800 Subject: [PATCH 1/5] feat: add ikc get_extended_public_key api --- imkey-core/ikc-common/src/path.rs | 4 +- imkey-core/ikc-proto/src/api.proto | 14 ++ .../ikc-wallet/coin-substrate/src/address.rs | 2 +- imkey-core/ikc/src/api.rs | 22 +++ imkey-core/ikc/src/handler.rs | 23 ++- imkey-core/ikc/src/lib.rs | 176 +++++++++++++++++- 6 files changed, 230 insertions(+), 11 deletions(-) diff --git a/imkey-core/ikc-common/src/path.rs b/imkey-core/ikc-common/src/path.rs index d90d044c..01d932d6 100644 --- a/imkey-core/ikc-common/src/path.rs +++ b/imkey-core/ikc-common/src/path.rs @@ -6,7 +6,7 @@ pub fn check_path_validity(path: &str) -> Result<()> { //check depth and length let strings: Vec<&str> = path.split("/").collect(); let depth = strings.len(); - if depth < 2 || depth > 10 { + if depth < 3 || depth > 10 { return Err(CommonError::ImkeyPathIllegal.into()); } Ok(()) @@ -16,7 +16,7 @@ pub fn check_path_max_five_depth(path: &str) -> Result<()> { //check depth and length let strings: Vec<&str> = path.split("/").collect(); let depth = strings.len(); - if depth < 2 || depth > 6 { + if depth < 3 || depth > 6 { return Err(CommonError::ImkeyPathIllegal.into()); } Ok(()) diff --git a/imkey-core/ikc-proto/src/api.proto b/imkey-core/ikc-proto/src/api.proto index 3584e102..8933548d 100644 --- a/imkey-core/ikc-proto/src/api.proto +++ b/imkey-core/ikc-proto/src/api.proto @@ -141,4 +141,18 @@ message DeriveSubAccountsParam { message DeriveSubAccountsResult { repeated AccountResponse accounts = 1; +} + +message GetExtendedPublicKeysParam { + repeated PublicKeyDerivation derivations = 4; +} + +message GetExtendedPublicKeysResult { + repeated string extendedPublicKeys = 1; +} + +message PublicKeyDerivation { + string chainType = 1; + string path = 2; + string curve = 3; } \ No newline at end of file diff --git a/imkey-core/ikc-wallet/coin-substrate/src/address.rs b/imkey-core/ikc-wallet/coin-substrate/src/address.rs index b5a5dd7f..3c2625d4 100644 --- a/imkey-core/ikc-wallet/coin-substrate/src/address.rs +++ b/imkey-core/ikc-wallet/coin-substrate/src/address.rs @@ -67,7 +67,7 @@ impl SubstrateAddress { return Err(CoinError::ImkeySignatureVerifyFail.into()); } - Ok(pubkey.to_string()) + Ok(pubkey.to_lowercase()) } pub fn get_address(path: &str, address_type: &AddressType) -> Result { diff --git a/imkey-core/ikc/src/api.rs b/imkey-core/ikc/src/api.rs index 6732719b..a7b72279 100644 --- a/imkey-core/ikc/src/api.rs +++ b/imkey-core/ikc/src/api.rs @@ -235,3 +235,25 @@ pub struct DeriveSubAccountsResult { #[prost(message, repeated, tag = "1")] pub accounts: ::prost::alloc::vec::Vec, } +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GetExtendedPublicKeysParam { + #[prost(message, repeated, tag = "4")] + pub derivations: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GetExtendedPublicKeysResult { + #[prost(string, repeated, tag = "1")] + pub extended_public_keys: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PublicKeyDerivation { + #[prost(string, tag = "1")] + pub chain_type: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub path: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + pub curve: ::prost::alloc::string::String, +} diff --git a/imkey-core/ikc/src/handler.rs b/imkey-core/ikc/src/handler.rs index d86359b9..454e4b2e 100644 --- a/imkey-core/ikc/src/handler.rs +++ b/imkey-core/ikc/src/handler.rs @@ -1,7 +1,4 @@ -use crate::api::{ - AccountResponse, DeriveAccountsParam, DeriveAccountsResult, DeriveSubAccountsParam, - DeriveSubAccountsResult, -}; +use crate::api::{AccountResponse, DeriveAccountsParam, DeriveAccountsResult, DeriveSubAccountsParam, DeriveSubAccountsResult, GetExtendedPublicKeysParam, GetExtendedPublicKeysResult}; use crate::message_handler::encode_message; use crate::Result; use anyhow::anyhow; @@ -26,6 +23,7 @@ use ikc_common::utility::{ }; use prost::Message; use std::str::FromStr; +use bitcoin::Network; pub(crate) fn derive_accounts(data: &[u8]) -> Result> { let param: DeriveAccountsParam = @@ -195,6 +193,23 @@ pub(crate) fn derive_sub_accounts(data: &[u8]) -> Result> { }) } +pub(crate) fn get_extended_public_key(data: &[u8]) -> Result> { + let param: GetExtendedPublicKeysParam = GetExtendedPublicKeysParam::decode(data)?; + let mut extended_public_keys = vec![]; + for public_key_derivation in param.derivations.iter(){ + // if "".eq(&public_key_derivation.path) || &public_key_derivation.path.split("/") { } + let extended_public_key = match public_key_derivation.curve.as_str() { + "secp256k1" => BtcAddress::get_xpub(Network::Bitcoin, public_key_derivation.path.as_str())?, + "ed25519" => SubstrateAddress::get_public_key(public_key_derivation.path.as_str(), &AddressType::Polkadot)?, + _ => return Err(anyhow!("unsupported_chain_type")), + }; + extended_public_keys.push(extended_public_key); + } + encode_message(GetExtendedPublicKeysResult{ + extended_public_keys + }) +} + #[cfg(test)] mod test { use crate::api::derive_accounts_param::Derivation; diff --git a/imkey-core/ikc/src/lib.rs b/imkey-core/ikc/src/lib.rs index 830a86d5..c1e9841a 100644 --- a/imkey-core/ikc/src/lib.rs +++ b/imkey-core/ikc/src/lib.rs @@ -41,7 +41,7 @@ pub mod tezos_signer; extern crate lazy_static; extern crate anyhow; use crate::error_handling::{landingpad, LAST_ERROR}; -use crate::handler::derive_accounts; +use crate::handler::{derive_accounts, get_extended_public_key}; use crate::message_handler::encode_message; use ikc_transport::message; @@ -272,6 +272,7 @@ pub unsafe extern "C" fn call_imkey_api(hex_str: *const c_char) -> *const c_char "btc_get_xpub" => landingpad(|| btc_address::get_btc_xpub(&action.param.unwrap().value)), + "get_extended_public_key" => landingpad(|| get_extended_public_key(&action.param.unwrap().value)), _ => landingpad(|| Err(anyhow!("unsupported_method"))), }; match reply { @@ -311,9 +312,7 @@ pub unsafe extern "C" fn imkey_get_last_err_message() -> *const c_char { mod tests { use super::*; use crate::api::derive_accounts_param::Derivation; - use crate::api::{ - DeriveAccountsParam, DeriveAccountsResult, DeriveSubAccountsParam, DeriveSubAccountsResult, - }; + use crate::api::{DeriveAccountsParam, DeriveAccountsResult, DeriveSubAccountsParam, DeriveSubAccountsResult, GetExtendedPublicKeysParam, GetExtendedPublicKeysResult, PublicKeyDerivation}; use ikc_device::deviceapi::{BindAcquireReq, BindCheckRes}; use ikc_transport::hid_api::hid_connect; @@ -1401,6 +1400,175 @@ mod tests { ); } + #[test] + fn test_get_extended_public_key() { + connect_and_bind(); + + let derivations = vec![ + PublicKeyDerivation{ + chain_type: "BITCOIN".to_string(), + path: "m/44'/145'/0'/0/0".to_string(), + curve: "secp256k1".to_string(), + }, + PublicKeyDerivation{ + chain_type: "ETHEREUM".to_string(), + path: "m/44'/60'/0'/0/0".to_string(), + curve: "secp256k1".to_string(), + }, + PublicKeyDerivation{ + chain_type: "POLKADOT".to_string(), + path: "m/44'/354'/0'/0'/0'".to_string(), + curve: "ed25519".to_string(), + }, + PublicKeyDerivation{ + chain_type: "KUSAMA".to_string(), + path: "m/44'/434'/0'/0'/0'".to_string(), + curve: "ed25519".to_string(), + }, + PublicKeyDerivation{ + chain_type: "BITCOIN".to_string(), + path: "m/0/1".to_string(), + curve: "secp256k1".to_string(), + }, + PublicKeyDerivation{ + chain_type: "POLKADOT".to_string(), + path: "m/0'/1'".to_string(), + curve: "ed25519".to_string(), + }, + ]; + let param = GetExtendedPublicKeysParam{ + derivations, + }; + let action: ImkeyAction = ImkeyAction { + method: "get_extended_public_key".to_string(), + param: Some(::prost_types::Any { + type_url: "get_extended_public_key".to_string(), + value: encode_message(param).unwrap(), + }), + }; + let action = hex::encode(encode_message(action).unwrap()); + let ret_hex = unsafe { _to_str(call_imkey_api(_to_c_char(action.as_str()))) }; + let ret_bytes = hex::decode(ret_hex).unwrap(); + let extended_public_key: GetExtendedPublicKeysResult = + GetExtendedPublicKeysResult::decode(ret_bytes.as_slice()).unwrap(); + assert_eq!(extended_public_key.extended_public_keys[0], "xpub6GZjFnyumLtEwC4KQkigvc3vXJdZvy71QxHTsFQQv1YtEUWNEwynKWsK2LBFZNLWdTk3w1Y9cRv4NN7V2pnDBoWgH3PkVE9r9Q2kSQL2zkH"); + assert_eq!(extended_public_key.extended_public_keys[1], "xpub6FmdMKZ36pLzf1iF7DLCzKtZms33cZ6mVjvBSy2dCPugFCH23cS3jgHfQ9PKmxs989ZyiyALuADMtLokCzpw7Fi35ap4uybfQAY5WVakan7"); + assert_eq!(extended_public_key.extended_public_keys[2], "edb9955556c8e07287df95ad77fad826168f8a50488cce0d738df3769e24613a"); + assert_eq!(extended_public_key.extended_public_keys[3], "873cf8e52a7b93a55197ef2846e9627a6f105b0a06c86659c813f1a50438b479"); + assert_eq!(extended_public_key.extended_public_keys[4], "xpub6AQmexrYd5utZNmD9Gnf4CjrzJ4kuvaxacLyuSD5sA34g4oKuzBpX5rhAZrCZoxkcqWLVyWSz1rEh5ECs4PDRN16PLfNKFftxm48y6zsWX3"); + assert_eq!(extended_public_key.extended_public_keys[5], "99908c0806ddcda0a8779c4f0c0a87fb679c08c444798cafb21a28cd459388fe"); + } + + #[test] + fn test_get_extended_public_key_error_case() { + connect_and_bind(); + + let test_data = vec![ + vec![ + PublicKeyDerivation{ + chain_type: "POLKADOT".to_string(), + path: "m/44'/354'/0'/0'/0'".to_string(), + curve: "sr25519".to_string(), + }, + ], + vec![ + PublicKeyDerivation{ + chain_type: "BITCOIN".to_string(), + path: "".to_string(), + curve: "secp256k1".to_string(), + }, + ], + vec![ + PublicKeyDerivation{ + chain_type: "BITCOIN".to_string(), + path: "m/0".to_string(), + curve: "secp256k1".to_string(), + }, + ], + vec![ + PublicKeyDerivation{ + chain_type: "POLKADOT".to_string(), + path: "".to_string(), + curve: "ed25519".to_string(), + }, + ], + vec![ + PublicKeyDerivation{ + chain_type: "POLKADOT".to_string(), + path: "m/0'".to_string(), + curve: "ed25519".to_string(), + }, + ], + ]; + for i in 0..test_data.len() { + let param = GetExtendedPublicKeysParam{ + derivations: test_data[i].clone(), + }; + let action: ImkeyAction = ImkeyAction { + method: "get_extended_public_key".to_string(), + param: Some(::prost_types::Any { + type_url: "get_extended_public_key".to_string(), + value: encode_message(param).unwrap(), + }), + }; + let action = hex::encode(encode_message(action).unwrap()); + let ret_hex = unsafe { _to_str(call_imkey_api(_to_c_char(action.as_str()))) }; + let err = unsafe { _to_str(imkey_get_last_err_message()) }; + assert!(!err.is_empty()); + let error_ret: ErrorResponse = ErrorResponse::decode(hex::decode(err).unwrap().as_slice()).unwrap(); + match i { + 0 => { assert_eq!(error_ret.error, "unsupported_chain_type"); }, + 1 => { assert_eq!(error_ret.error, "imkey_path_illegal"); }, + 2 => { assert_eq!(error_ret.error, "imkey_path_illegal"); }, + 3 => { assert_eq!(error_ret.error, "imkey_path_illegal"); }, + 4 => { assert_eq!(error_ret.error, "imkey_path_illegal"); }, + _ => {} + }; + } + } + + #[test] + fn test_get_extended_public_key_wrong_path() { + connect_and_bind(); + + let derivations = vec![ + PublicKeyDerivation{ + chain_type: "POLKADOT".to_string(), + path: "m/44'/354'/0'/0'/0'".to_string(), + curve: "sr25519".to_string(), + }, + // PublicKeyDerivation{ + // chain_type: "BITCOIN".to_string(), + // path: "".to_string(), + // curve: "secp256k1".to_string(), + // }, + // PublicKeyDerivation{ + // chain_type: "BITCOIN".to_string(), + // path: "m/0".to_string(), + // curve: "secp256k1".to_string(), + // }, + ]; + let param = GetExtendedPublicKeysParam{ + derivations, + }; + let action: ImkeyAction = ImkeyAction { + method: "get_extended_public_key".to_string(), + param: Some(::prost_types::Any { + type_url: "get_extended_public_key".to_string(), + value: encode_message(param).unwrap(), + }), + }; + let action = hex::encode(encode_message(action).unwrap()); + let ret_hex = unsafe { _to_str(call_imkey_api(_to_c_char(action.as_str()))) }; + let err = unsafe { _to_str(imkey_get_last_err_message()) }; + assert!(!err.is_empty()); + let error_ret: ErrorResponse = ErrorResponse::decode(hex::decode(err).unwrap().as_slice()).unwrap(); + assert_eq!( + error_ret.error, + "unsupported_chain_type" + ); + } + fn derive_account(derivation: Derivation) -> DeriveAccountsResult { connect_and_bind(); From 70d1c69ecfca4862fed97a65a054fd224988214d Mon Sep 17 00:00:00 2001 From: xiaoguang Date: Thu, 29 Feb 2024 15:56:40 +0800 Subject: [PATCH 2/5] fix: derive_sub_accounts returns data and adds path field --- imkey-core/ikc/src/handler.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/imkey-core/ikc/src/handler.rs b/imkey-core/ikc/src/handler.rs index 454e4b2e..54bd07cd 100644 --- a/imkey-core/ikc/src/handler.rs +++ b/imkey-core/ikc/src/handler.rs @@ -166,6 +166,7 @@ pub(crate) fn derive_sub_accounts(data: &[u8]) -> Result> { let ext_pub_key = extended_pub_key_derive(&xpub.0, &relative_path)?; let pub_key_uncompressed = ext_pub_key.public_key.serialize_uncompressed().to_vec(); account.public_key = format!("0x{}", ext_pub_key.public_key.serialize().to_hex()); + account.path = relative_path; let address = match param.chain_type.as_str() { "ETHEREUM" => EthAddress::from_pub_key(pub_key_uncompressed)?, "BITCOIN" | "LITECOIN" => { From 8f19ca6972ca783f0b62600ac29857bec81e8efd Mon Sep 17 00:00:00 2001 From: xiaoguang Date: Thu, 29 Feb 2024 16:17:59 +0800 Subject: [PATCH 3/5] fix: modify GetExtendedPublicKeysParam proto message tag number --- imkey-core/ikc-proto/src/api.proto | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imkey-core/ikc-proto/src/api.proto b/imkey-core/ikc-proto/src/api.proto index 8933548d..a0120472 100644 --- a/imkey-core/ikc-proto/src/api.proto +++ b/imkey-core/ikc-proto/src/api.proto @@ -144,7 +144,7 @@ message DeriveSubAccountsResult { } message GetExtendedPublicKeysParam { - repeated PublicKeyDerivation derivations = 4; + repeated PublicKeyDerivation derivations = 1; } message GetExtendedPublicKeysResult { From 93ba478d48360e42fd19128dd53e2b00999fd25c Mon Sep 17 00:00:00 2001 From: xiaoguang Date: Thu, 29 Feb 2024 16:35:47 +0800 Subject: [PATCH 4/5] chore: modify pr reivew problem --- imkey-core/ikc/src/api.rs | 2 +- imkey-core/ikc/src/handler.rs | 4 +- imkey-core/ikc/src/lib.rs | 72 ++++++++++------------------------- 3 files changed, 24 insertions(+), 54 deletions(-) diff --git a/imkey-core/ikc/src/api.rs b/imkey-core/ikc/src/api.rs index a7b72279..c6c8cf3e 100644 --- a/imkey-core/ikc/src/api.rs +++ b/imkey-core/ikc/src/api.rs @@ -238,7 +238,7 @@ pub struct DeriveSubAccountsResult { #[allow(clippy::derive_partial_eq_without_eq)] #[derive(Clone, PartialEq, ::prost::Message)] pub struct GetExtendedPublicKeysParam { - #[prost(message, repeated, tag = "4")] + #[prost(message, repeated, tag = "1")] pub derivations: ::prost::alloc::vec::Vec, } #[allow(clippy::derive_partial_eq_without_eq)] diff --git a/imkey-core/ikc/src/handler.rs b/imkey-core/ikc/src/handler.rs index 54bd07cd..374a2b40 100644 --- a/imkey-core/ikc/src/handler.rs +++ b/imkey-core/ikc/src/handler.rs @@ -194,7 +194,7 @@ pub(crate) fn derive_sub_accounts(data: &[u8]) -> Result> { }) } -pub(crate) fn get_extended_public_key(data: &[u8]) -> Result> { +pub(crate) fn get_extended_public_keys(data: &[u8]) -> Result> { let param: GetExtendedPublicKeysParam = GetExtendedPublicKeysParam::decode(data)?; let mut extended_public_keys = vec![]; for public_key_derivation in param.derivations.iter(){ @@ -202,7 +202,7 @@ pub(crate) fn get_extended_public_key(data: &[u8]) -> Result> { let extended_public_key = match public_key_derivation.curve.as_str() { "secp256k1" => BtcAddress::get_xpub(Network::Bitcoin, public_key_derivation.path.as_str())?, "ed25519" => SubstrateAddress::get_public_key(public_key_derivation.path.as_str(), &AddressType::Polkadot)?, - _ => return Err(anyhow!("unsupported_chain_type")), + _ => return Err(anyhow!("unsupported_curve_type")), }; extended_public_keys.push(extended_public_key); } diff --git a/imkey-core/ikc/src/lib.rs b/imkey-core/ikc/src/lib.rs index c1e9841a..e34e2794 100644 --- a/imkey-core/ikc/src/lib.rs +++ b/imkey-core/ikc/src/lib.rs @@ -41,7 +41,7 @@ pub mod tezos_signer; extern crate lazy_static; extern crate anyhow; use crate::error_handling::{landingpad, LAST_ERROR}; -use crate::handler::{derive_accounts, get_extended_public_key}; +use crate::handler::{derive_accounts, get_extended_public_keys}; use crate::message_handler::encode_message; use ikc_transport::message; @@ -272,7 +272,7 @@ pub unsafe extern "C" fn call_imkey_api(hex_str: *const c_char) -> *const c_char "btc_get_xpub" => landingpad(|| btc_address::get_btc_xpub(&action.param.unwrap().value)), - "get_extended_public_key" => landingpad(|| get_extended_public_key(&action.param.unwrap().value)), + "get_extended_public_keys" => landingpad(|| get_extended_public_keys(&action.param.unwrap().value)), _ => landingpad(|| Err(anyhow!("unsupported_method"))), }; match reply { @@ -1401,7 +1401,7 @@ mod tests { } #[test] - fn test_get_extended_public_key() { + fn test_get_extended_public_keys() { connect_and_bind(); let derivations = vec![ @@ -1435,14 +1435,24 @@ mod tests { path: "m/0'/1'".to_string(), curve: "ed25519".to_string(), }, + PublicKeyDerivation{ + chain_type: "BITCOIN".to_string(), + path: "m/44'/145'/0'".to_string(), + curve: "secp256k1".to_string(), + }, + PublicKeyDerivation{ + chain_type: "POLKADOT".to_string(), + path: "m/44'/354'/0'".to_string(), + curve: "ed25519".to_string(), + }, ]; let param = GetExtendedPublicKeysParam{ derivations, }; let action: ImkeyAction = ImkeyAction { - method: "get_extended_public_key".to_string(), + method: "get_extended_public_keys".to_string(), param: Some(::prost_types::Any { - type_url: "get_extended_public_key".to_string(), + type_url: "get_extended_public_keys".to_string(), value: encode_message(param).unwrap(), }), }; @@ -1457,10 +1467,12 @@ mod tests { assert_eq!(extended_public_key.extended_public_keys[3], "873cf8e52a7b93a55197ef2846e9627a6f105b0a06c86659c813f1a50438b479"); assert_eq!(extended_public_key.extended_public_keys[4], "xpub6AQmexrYd5utZNmD9Gnf4CjrzJ4kuvaxacLyuSD5sA34g4oKuzBpX5rhAZrCZoxkcqWLVyWSz1rEh5ECs4PDRN16PLfNKFftxm48y6zsWX3"); assert_eq!(extended_public_key.extended_public_keys[5], "99908c0806ddcda0a8779c4f0c0a87fb679c08c444798cafb21a28cd459388fe"); + assert_eq!(extended_public_key.extended_public_keys[6], "xpub6Bmkv3mmRZZWoFSBdj9vDMqR2PCPSP6DEj8u3bBuv44g3Ncnro6cPVqZAw6wTEcxHQuodkuJG4EmAinqrrRXGsN3HHnRRMtAvzfYTiBATV1"); + assert_eq!(extended_public_key.extended_public_keys[7], "2d9aecea337e9eee9d9a86f2d81aadafa88557fe5fb49efa187ce8ca3bc4e2a2"); } #[test] - fn test_get_extended_public_key_error_case() { + fn test_get_extended_public_keys_error_case() { connect_and_bind(); let test_data = vec![ @@ -1505,9 +1517,9 @@ mod tests { derivations: test_data[i].clone(), }; let action: ImkeyAction = ImkeyAction { - method: "get_extended_public_key".to_string(), + method: "get_extended_public_keys".to_string(), param: Some(::prost_types::Any { - type_url: "get_extended_public_key".to_string(), + type_url: "get_extended_public_keys".to_string(), value: encode_message(param).unwrap(), }), }; @@ -1517,7 +1529,7 @@ mod tests { assert!(!err.is_empty()); let error_ret: ErrorResponse = ErrorResponse::decode(hex::decode(err).unwrap().as_slice()).unwrap(); match i { - 0 => { assert_eq!(error_ret.error, "unsupported_chain_type"); }, + 0 => { assert_eq!(error_ret.error, "unsupported_curve_type"); }, 1 => { assert_eq!(error_ret.error, "imkey_path_illegal"); }, 2 => { assert_eq!(error_ret.error, "imkey_path_illegal"); }, 3 => { assert_eq!(error_ret.error, "imkey_path_illegal"); }, @@ -1527,48 +1539,6 @@ mod tests { } } - #[test] - fn test_get_extended_public_key_wrong_path() { - connect_and_bind(); - - let derivations = vec![ - PublicKeyDerivation{ - chain_type: "POLKADOT".to_string(), - path: "m/44'/354'/0'/0'/0'".to_string(), - curve: "sr25519".to_string(), - }, - // PublicKeyDerivation{ - // chain_type: "BITCOIN".to_string(), - // path: "".to_string(), - // curve: "secp256k1".to_string(), - // }, - // PublicKeyDerivation{ - // chain_type: "BITCOIN".to_string(), - // path: "m/0".to_string(), - // curve: "secp256k1".to_string(), - // }, - ]; - let param = GetExtendedPublicKeysParam{ - derivations, - }; - let action: ImkeyAction = ImkeyAction { - method: "get_extended_public_key".to_string(), - param: Some(::prost_types::Any { - type_url: "get_extended_public_key".to_string(), - value: encode_message(param).unwrap(), - }), - }; - let action = hex::encode(encode_message(action).unwrap()); - let ret_hex = unsafe { _to_str(call_imkey_api(_to_c_char(action.as_str()))) }; - let err = unsafe { _to_str(imkey_get_last_err_message()) }; - assert!(!err.is_empty()); - let error_ret: ErrorResponse = ErrorResponse::decode(hex::decode(err).unwrap().as_slice()).unwrap(); - assert_eq!( - error_ret.error, - "unsupported_chain_type" - ); - } - fn derive_account(derivation: Derivation) -> DeriveAccountsResult { connect_and_bind(); From fc24d5a3a5e4faf6c288240360490e660626dc53 Mon Sep 17 00:00:00 2001 From: xiaoguang Date: Fri, 1 Mar 2024 11:38:16 +0800 Subject: [PATCH 5/5] feat: add get_public_keys api and code optimization 1. add get_public_keys api 2. remove btc_get_xpub api 3. get_extended_public_key interface removes ed25519 curve support --- imkey-core/ikc-proto/src/api.proto | 8 + imkey-core/ikc-proto/src/btc.proto | 11 - .../ikc-wallet/coin-bitcoin/src/btcapi.rs | 14 - imkey-core/ikc/src/api.rs | 12 + imkey-core/ikc/src/btc_address.rs | 10 - imkey-core/ikc/src/handler.rs | 48 ++- imkey-core/ikc/src/lib.rs | 299 ++++++++++++------ 7 files changed, 268 insertions(+), 134 deletions(-) diff --git a/imkey-core/ikc-proto/src/api.proto b/imkey-core/ikc-proto/src/api.proto index a0120472..66a70a6e 100644 --- a/imkey-core/ikc-proto/src/api.proto +++ b/imkey-core/ikc-proto/src/api.proto @@ -155,4 +155,12 @@ message PublicKeyDerivation { string chainType = 1; string path = 2; string curve = 3; +} + +message GetPublicKeysParam { + repeated PublicKeyDerivation derivations = 1; +} + +message GetPublicKeysResult { + repeated string publicKeys = 1; } \ No newline at end of file diff --git a/imkey-core/ikc-proto/src/btc.proto b/imkey-core/ikc-proto/src/btc.proto index 076b4965..cf4b9c1e 100644 --- a/imkey-core/ikc-proto/src/btc.proto +++ b/imkey-core/ikc-proto/src/btc.proto @@ -32,14 +32,3 @@ message BtcTxOutput { string txHash = 2; string wtxHash = 3; } - -message BtcXpubReq { - string network = 1; - string path = 2; -} - -message BtcXpubRes { - string xpub = 1; -} - - diff --git a/imkey-core/ikc-wallet/coin-bitcoin/src/btcapi.rs b/imkey-core/ikc-wallet/coin-bitcoin/src/btcapi.rs index 2bca90ae..f9217c83 100644 --- a/imkey-core/ikc-wallet/coin-bitcoin/src/btcapi.rs +++ b/imkey-core/ikc-wallet/coin-bitcoin/src/btcapi.rs @@ -56,17 +56,3 @@ pub struct BtcTxOutput { #[prost(string, tag = "3")] pub wtx_hash: ::prost::alloc::string::String, } -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct BtcXpubReq { - #[prost(string, tag = "1")] - pub network: ::prost::alloc::string::String, - #[prost(string, tag = "2")] - pub path: ::prost::alloc::string::String, -} -#[allow(clippy::derive_partial_eq_without_eq)] -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct BtcXpubRes { - #[prost(string, tag = "1")] - pub xpub: ::prost::alloc::string::String, -} diff --git a/imkey-core/ikc/src/api.rs b/imkey-core/ikc/src/api.rs index c6c8cf3e..939d8e34 100644 --- a/imkey-core/ikc/src/api.rs +++ b/imkey-core/ikc/src/api.rs @@ -257,3 +257,15 @@ pub struct PublicKeyDerivation { #[prost(string, tag = "3")] pub curve: ::prost::alloc::string::String, } +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GetPublicKeysParam { + #[prost(message, repeated, tag = "1")] + pub derivations: ::prost::alloc::vec::Vec, +} +#[allow(clippy::derive_partial_eq_without_eq)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GetPublicKeysResult { + #[prost(string, repeated, tag = "1")] + pub public_keys: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, +} diff --git a/imkey-core/ikc/src/btc_address.rs b/imkey-core/ikc/src/btc_address.rs index 09d0e353..90af7762 100644 --- a/imkey-core/ikc/src/btc_address.rs +++ b/imkey-core/ikc/src/btc_address.rs @@ -5,19 +5,9 @@ use crate::error_handling::Result; use crate::message_handler::encode_message; use bitcoin::network::constants::Network; use coin_bitcoin::address::BtcAddress; -use coin_bitcoin::btcapi::{BtcXpubReq, BtcXpubRes}; use ikc_common::utility::network_convert; use prost::Message; -pub fn get_btc_xpub(data: &[u8]) -> Result> { - let input: BtcXpubReq = BtcXpubReq::decode(data).expect("imkey_illegal_param"); - let network = network_convert(input.network.as_ref()); - let xpub = BtcAddress::get_xpub(network, input.path.as_ref())?; - - let address_message = BtcXpubRes { xpub }; - encode_message(address_message) -} - pub fn get_address(param: &AddressParam) -> Result> { let network = network_convert(param.network.as_ref()); let account_path = param.path.to_string(); diff --git a/imkey-core/ikc/src/handler.rs b/imkey-core/ikc/src/handler.rs index 374a2b40..fdc84b0c 100644 --- a/imkey-core/ikc/src/handler.rs +++ b/imkey-core/ikc/src/handler.rs @@ -1,9 +1,14 @@ -use crate::api::{AccountResponse, DeriveAccountsParam, DeriveAccountsResult, DeriveSubAccountsParam, DeriveSubAccountsResult, GetExtendedPublicKeysParam, GetExtendedPublicKeysResult}; +use crate::api::{ + AccountResponse, DeriveAccountsParam, DeriveAccountsResult, DeriveSubAccountsParam, + DeriveSubAccountsResult, GetExtendedPublicKeysParam, GetExtendedPublicKeysResult, + GetPublicKeysParam, GetPublicKeysResult, +}; use crate::message_handler::encode_message; use crate::Result; use anyhow::anyhow; use bitcoin::hashes::hex::ToHex; use bitcoin::util::bip32::ExtendedPubKey; +use bitcoin::Network; use coin_bch::address::BchAddress; use coin_bitcoin::address::BtcAddress; use coin_btc_fork::address::BtcForkAddress; @@ -19,11 +24,10 @@ use ikc_common::curve::CurveType; use ikc_common::path::get_account_path; use ikc_common::utility::{ encrypt_xpub, extended_pub_key_derive, from_ss58check_with_version, get_xpub_prefix, - network_convert, to_ss58check_with_version, uncompress_pubkey_2_compress, + hex_to_bytes, network_convert, to_ss58check_with_version, uncompress_pubkey_2_compress, }; use prost::Message; use std::str::FromStr; -use bitcoin::Network; pub(crate) fn derive_accounts(data: &[u8]) -> Result> { let param: DeriveAccountsParam = @@ -197,20 +201,48 @@ pub(crate) fn derive_sub_accounts(data: &[u8]) -> Result> { pub(crate) fn get_extended_public_keys(data: &[u8]) -> Result> { let param: GetExtendedPublicKeysParam = GetExtendedPublicKeysParam::decode(data)?; let mut extended_public_keys = vec![]; - for public_key_derivation in param.derivations.iter(){ + for public_key_derivation in param.derivations.iter() { // if "".eq(&public_key_derivation.path) || &public_key_derivation.path.split("/") { } let extended_public_key = match public_key_derivation.curve.as_str() { - "secp256k1" => BtcAddress::get_xpub(Network::Bitcoin, public_key_derivation.path.as_str())?, - "ed25519" => SubstrateAddress::get_public_key(public_key_derivation.path.as_str(), &AddressType::Polkadot)?, + "secp256k1" => { + BtcAddress::get_xpub(Network::Bitcoin, public_key_derivation.path.as_str())? + } _ => return Err(anyhow!("unsupported_curve_type")), }; extended_public_keys.push(extended_public_key); } - encode_message(GetExtendedPublicKeysResult{ - extended_public_keys + encode_message(GetExtendedPublicKeysResult { + extended_public_keys, }) } +pub(crate) fn get_public_keys(data: &[u8]) -> Result> { + let param: GetPublicKeysParam = GetPublicKeysParam::decode(data)?; + let mut public_keys = vec![]; + for public_key_derivation in param.derivations.iter() { + let mut public_key = match public_key_derivation.curve.as_str() { + "secp256k1" => uncompress_pubkey_2_compress(&BtcAddress::get_pub_key( + public_key_derivation.path.as_str(), + )?), + "ed25519" => SubstrateAddress::get_public_key( + public_key_derivation.path.as_str(), + &AddressType::Polkadot, + )?, + _ => return Err(anyhow!("unsupported_curve_type")), + }; + if !public_key.starts_with("0x") { + public_key = format!("0x{}", public_key); + }; + let public_key = match public_key_derivation.chain_type.as_str() { + "EOS" => EosPubkey::from_pub_key(hex_to_bytes(&public_key)?.as_slice())?, + _ => public_key, + }; + public_keys.push(public_key); + } + + encode_message(GetPublicKeysResult { public_keys }) +} + #[cfg(test)] mod test { use crate::api::derive_accounts_param::Derivation; diff --git a/imkey-core/ikc/src/lib.rs b/imkey-core/ikc/src/lib.rs index e34e2794..a4a6fd3f 100644 --- a/imkey-core/ikc/src/lib.rs +++ b/imkey-core/ikc/src/lib.rs @@ -41,7 +41,7 @@ pub mod tezos_signer; extern crate lazy_static; extern crate anyhow; use crate::error_handling::{landingpad, LAST_ERROR}; -use crate::handler::{derive_accounts, get_extended_public_keys}; +use crate::handler::{derive_accounts, get_extended_public_keys, get_public_keys}; use crate::message_handler::encode_message; use ikc_transport::message; @@ -154,17 +154,7 @@ pub unsafe extern "C" fn call_imkey_api(hex_str: *const c_char) -> *const c_char }), "derive_accounts" => landingpad(|| derive_accounts(&action.param.unwrap().value)), "derive_sub_accounts" => landingpad(|| derive_sub_accounts(&action.param.unwrap().value)), - "get_pub_key" => landingpad(|| { - let param: PubKeyParam = PubKeyParam::decode(action.param.unwrap().value.as_slice()) - .expect("imkey_illegal_param"); - match param.chain_type.as_str() { - "EOS" => eos_pubkey::get_eos_pubkey(¶m), - "TEZOS" => tezos_address::get_pub_key(¶m), - "COSMOS" => cosmos_address::get_cosmos_pub_key(¶m), - _ => Err(anyhow!("get_pub_key unsupported_chain")), - } - }), - + "get_public_keys" => landingpad(|| get_public_keys(&action.param.unwrap().value)), "register_pub_key" => landingpad(|| { let param: PubKeyParam = PubKeyParam::decode(action.param.unwrap().value.as_slice()) .expect("imkey_illegal_param"); @@ -270,9 +260,9 @@ pub unsafe extern "C" fn call_imkey_api(hex_str: *const c_char) -> *const c_char } }), - "btc_get_xpub" => landingpad(|| btc_address::get_btc_xpub(&action.param.unwrap().value)), - - "get_extended_public_keys" => landingpad(|| get_extended_public_keys(&action.param.unwrap().value)), + "get_extended_public_keys" => { + landingpad(|| get_extended_public_keys(&action.param.unwrap().value)) + } _ => landingpad(|| Err(anyhow!("unsupported_method"))), }; match reply { @@ -312,7 +302,11 @@ pub unsafe extern "C" fn imkey_get_last_err_message() -> *const c_char { mod tests { use super::*; use crate::api::derive_accounts_param::Derivation; - use crate::api::{DeriveAccountsParam, DeriveAccountsResult, DeriveSubAccountsParam, DeriveSubAccountsResult, GetExtendedPublicKeysParam, GetExtendedPublicKeysResult, PublicKeyDerivation}; + use crate::api::{ + DeriveAccountsParam, DeriveAccountsResult, DeriveSubAccountsParam, DeriveSubAccountsResult, + GetExtendedPublicKeysParam, GetExtendedPublicKeysResult, GetPublicKeysParam, + GetPublicKeysResult, PublicKeyDerivation, + }; use ikc_device::deviceapi::{BindAcquireReq, BindCheckRes}; use ikc_transport::hid_api::hid_connect; @@ -1405,50 +1399,28 @@ mod tests { connect_and_bind(); let derivations = vec![ - PublicKeyDerivation{ + PublicKeyDerivation { chain_type: "BITCOIN".to_string(), path: "m/44'/145'/0'/0/0".to_string(), curve: "secp256k1".to_string(), }, - PublicKeyDerivation{ + PublicKeyDerivation { chain_type: "ETHEREUM".to_string(), path: "m/44'/60'/0'/0/0".to_string(), curve: "secp256k1".to_string(), }, - PublicKeyDerivation{ - chain_type: "POLKADOT".to_string(), - path: "m/44'/354'/0'/0'/0'".to_string(), - curve: "ed25519".to_string(), - }, - PublicKeyDerivation{ - chain_type: "KUSAMA".to_string(), - path: "m/44'/434'/0'/0'/0'".to_string(), - curve: "ed25519".to_string(), - }, - PublicKeyDerivation{ + PublicKeyDerivation { chain_type: "BITCOIN".to_string(), path: "m/0/1".to_string(), curve: "secp256k1".to_string(), }, - PublicKeyDerivation{ - chain_type: "POLKADOT".to_string(), - path: "m/0'/1'".to_string(), - curve: "ed25519".to_string(), - }, - PublicKeyDerivation{ + PublicKeyDerivation { chain_type: "BITCOIN".to_string(), path: "m/44'/145'/0'".to_string(), curve: "secp256k1".to_string(), }, - PublicKeyDerivation{ - chain_type: "POLKADOT".to_string(), - path: "m/44'/354'/0'".to_string(), - curve: "ed25519".to_string(), - }, ]; - let param = GetExtendedPublicKeysParam{ - derivations, - }; + let param = GetExtendedPublicKeysParam { derivations }; let action: ImkeyAction = ImkeyAction { method: "get_extended_public_keys".to_string(), param: Some(::prost_types::Any { @@ -1463,12 +1435,8 @@ mod tests { GetExtendedPublicKeysResult::decode(ret_bytes.as_slice()).unwrap(); assert_eq!(extended_public_key.extended_public_keys[0], "xpub6GZjFnyumLtEwC4KQkigvc3vXJdZvy71QxHTsFQQv1YtEUWNEwynKWsK2LBFZNLWdTk3w1Y9cRv4NN7V2pnDBoWgH3PkVE9r9Q2kSQL2zkH"); assert_eq!(extended_public_key.extended_public_keys[1], "xpub6FmdMKZ36pLzf1iF7DLCzKtZms33cZ6mVjvBSy2dCPugFCH23cS3jgHfQ9PKmxs989ZyiyALuADMtLokCzpw7Fi35ap4uybfQAY5WVakan7"); - assert_eq!(extended_public_key.extended_public_keys[2], "edb9955556c8e07287df95ad77fad826168f8a50488cce0d738df3769e24613a"); - assert_eq!(extended_public_key.extended_public_keys[3], "873cf8e52a7b93a55197ef2846e9627a6f105b0a06c86659c813f1a50438b479"); - assert_eq!(extended_public_key.extended_public_keys[4], "xpub6AQmexrYd5utZNmD9Gnf4CjrzJ4kuvaxacLyuSD5sA34g4oKuzBpX5rhAZrCZoxkcqWLVyWSz1rEh5ECs4PDRN16PLfNKFftxm48y6zsWX3"); - assert_eq!(extended_public_key.extended_public_keys[5], "99908c0806ddcda0a8779c4f0c0a87fb679c08c444798cafb21a28cd459388fe"); - assert_eq!(extended_public_key.extended_public_keys[6], "xpub6Bmkv3mmRZZWoFSBdj9vDMqR2PCPSP6DEj8u3bBuv44g3Ncnro6cPVqZAw6wTEcxHQuodkuJG4EmAinqrrRXGsN3HHnRRMtAvzfYTiBATV1"); - assert_eq!(extended_public_key.extended_public_keys[7], "2d9aecea337e9eee9d9a86f2d81aadafa88557fe5fb49efa187ce8ca3bc4e2a2"); + assert_eq!(extended_public_key.extended_public_keys[2], "xpub6AQmexrYd5utZNmD9Gnf4CjrzJ4kuvaxacLyuSD5sA34g4oKuzBpX5rhAZrCZoxkcqWLVyWSz1rEh5ECs4PDRN16PLfNKFftxm48y6zsWX3"); + assert_eq!(extended_public_key.extended_public_keys[3], "xpub6Bmkv3mmRZZWoFSBdj9vDMqR2PCPSP6DEj8u3bBuv44g3Ncnro6cPVqZAw6wTEcxHQuodkuJG4EmAinqrrRXGsN3HHnRRMtAvzfYTiBATV1"); } #[test] @@ -1476,44 +1444,24 @@ mod tests { connect_and_bind(); let test_data = vec![ - vec![ - PublicKeyDerivation{ - chain_type: "POLKADOT".to_string(), - path: "m/44'/354'/0'/0'/0'".to_string(), - curve: "sr25519".to_string(), - }, - ], - vec![ - PublicKeyDerivation{ - chain_type: "BITCOIN".to_string(), - path: "".to_string(), - curve: "secp256k1".to_string(), - }, - ], - vec![ - PublicKeyDerivation{ - chain_type: "BITCOIN".to_string(), - path: "m/0".to_string(), - curve: "secp256k1".to_string(), - }, - ], - vec![ - PublicKeyDerivation{ - chain_type: "POLKADOT".to_string(), - path: "".to_string(), - curve: "ed25519".to_string(), - }, - ], - vec![ - PublicKeyDerivation{ - chain_type: "POLKADOT".to_string(), - path: "m/0'".to_string(), - curve: "ed25519".to_string(), - }, - ], + vec![PublicKeyDerivation { + chain_type: "POLKADOT".to_string(), + path: "m/44'/354'/0'/0'/0'".to_string(), + curve: "ed25519".to_string(), + }], + vec![PublicKeyDerivation { + chain_type: "BITCOIN".to_string(), + path: "".to_string(), + curve: "secp256k1".to_string(), + }], + vec![PublicKeyDerivation { + chain_type: "BITCOIN".to_string(), + path: "m/0".to_string(), + curve: "secp256k1".to_string(), + }], ]; for i in 0..test_data.len() { - let param = GetExtendedPublicKeysParam{ + let param = GetExtendedPublicKeysParam { derivations: test_data[i].clone(), }; let action: ImkeyAction = ImkeyAction { @@ -1527,18 +1475,187 @@ mod tests { let ret_hex = unsafe { _to_str(call_imkey_api(_to_c_char(action.as_str()))) }; let err = unsafe { _to_str(imkey_get_last_err_message()) }; assert!(!err.is_empty()); - let error_ret: ErrorResponse = ErrorResponse::decode(hex::decode(err).unwrap().as_slice()).unwrap(); + let error_ret: ErrorResponse = + ErrorResponse::decode(hex::decode(err).unwrap().as_slice()).unwrap(); match i { - 0 => { assert_eq!(error_ret.error, "unsupported_curve_type"); }, - 1 => { assert_eq!(error_ret.error, "imkey_path_illegal"); }, - 2 => { assert_eq!(error_ret.error, "imkey_path_illegal"); }, - 3 => { assert_eq!(error_ret.error, "imkey_path_illegal"); }, - 4 => { assert_eq!(error_ret.error, "imkey_path_illegal"); }, + 0 => { + assert_eq!(error_ret.error, "unsupported_curve_type"); + } + 1 => { + assert_eq!(error_ret.error, "imkey_path_illegal"); + } + 2 => { + assert_eq!(error_ret.error, "imkey_path_illegal"); + } _ => {} }; } } + #[test] + fn test_get_public_keys() { + connect_and_bind(); + + let derivations = vec![ + PublicKeyDerivation { + chain_type: "BITCOIN".to_string(), + path: "m/44'/145'/0'".to_string(), + curve: "secp256k1".to_string(), + }, + PublicKeyDerivation { + chain_type: "ETHEREUM".to_string(), + path: "m/44'/60'/0'".to_string(), + curve: "secp256k1".to_string(), + }, + PublicKeyDerivation { + chain_type: "EOS".to_string(), + path: "m/44'/194'/0'".to_string(), + curve: "secp256k1".to_string(), + }, + PublicKeyDerivation { + chain_type: "COSMOS".to_string(), + path: "m/44'/118'/0'".to_string(), + curve: "secp256k1".to_string(), + }, + PublicKeyDerivation { + chain_type: "BITCOINCASH".to_string(), + path: "m/44'/145'/0'".to_string(), + curve: "secp256k1".to_string(), + }, + PublicKeyDerivation { + chain_type: "LITECOIN".to_string(), + path: "m/44'/2'/0'".to_string(), + curve: "secp256k1".to_string(), + }, + PublicKeyDerivation { + chain_type: "TRON".to_string(), + path: "m/44'/195'/0'".to_string(), + curve: "secp256k1".to_string(), + }, + PublicKeyDerivation { + chain_type: "NERVOS".to_string(), + path: "m/44'/309'/0'".to_string(), + curve: "secp256k1".to_string(), + }, + PublicKeyDerivation { + chain_type: "POLKADOT".to_string(), + path: "m/44'/354'/0'".to_string(), + curve: "ed25519".to_string(), + }, + PublicKeyDerivation { + chain_type: "KUSAMA".to_string(), + path: "m/44'/434'/0'".to_string(), + curve: "ed25519".to_string(), + }, + PublicKeyDerivation { + chain_type: "FILECOIN".to_string(), + path: "m/44'/461'/0'".to_string(), + curve: "secp256k1".to_string(), + }, + PublicKeyDerivation { + chain_type: "BITCOIN".to_string(), + path: "m/0/0".to_string(), + curve: "secp256k1".to_string(), + }, + PublicKeyDerivation { + chain_type: "POLKADOT".to_string(), + path: "m/0'/0'".to_string(), + curve: "ed25519".to_string(), + }, + ]; + let param = GetPublicKeysParam { derivations }; + let action: ImkeyAction = ImkeyAction { + method: "get_public_keys".to_string(), + param: Some(::prost_types::Any { + type_url: "get_public_keys".to_string(), + value: encode_message(param).unwrap(), + }), + }; + let action = hex::encode(encode_message(action).unwrap()); + let ret_hex = unsafe { _to_str(call_imkey_api(_to_c_char(action.as_str()))) }; + let ret_bytes = hex::decode(ret_hex).unwrap(); + let result: GetPublicKeysResult = + GetPublicKeysResult::decode(ret_bytes.as_slice()).unwrap(); + assert_eq!( + result.public_keys[0], + "0x0303f2f84851514bf2f40a46b5bb9dbf4e5913fbacde1a96968cda08f9fd882caa" + ); + assert_eq!( + result.public_keys[1], + "0x03f3175613d999d15e6fde436825a3cc2c568f8f5082275f06eb4bd6e561f503ac" + ); + assert_eq!( + result.public_keys[2], + "EOS7ik8DKrvmBBKZHePgRSJiFhKoG1r3w8wNBwtRE9rTntW8yYmSk" + ); + assert_eq!( + result.public_keys[3], + "0x03cca080a087467a703b01a1f87a65f3e4e566508c6f85f5582a6973a77c80c35e" + ); + assert_eq!( + result.public_keys[4], + "0x0303f2f84851514bf2f40a46b5bb9dbf4e5913fbacde1a96968cda08f9fd882caa" + ); + assert_eq!( + result.public_keys[5], + "0x02c7709248e6205fefa7366efb0269021f1f2f1e04fdc334fe7c7fd2628d7451e8" + ); + assert_eq!( + result.public_keys[6], + "0x03349ff19e96c1aa7f568e493f85fa506320410245b4e69146bb0d3d8b5df3b901" + ); + assert_eq!( + result.public_keys[7], + "0x03ad9d0e2d9181e23c7075a56ed4f10e249aaf38a2bb7aa0cb604f8b768ea84b86" + ); + assert_eq!( + result.public_keys[8], + "0x2d9aecea337e9eee9d9a86f2d81aadafa88557fe5fb49efa187ce8ca3bc4e2a2" + ); + assert_eq!( + result.public_keys[9], + "0x5fcd1bec698400671d396c7f3507441a9b62340731b53aebf0a58c57512b5c45" + ); + assert_eq!( + result.public_keys[10], + "0x02611325073f61ae5feb6c8dce96857d007cdb765937e53e43e6f91374dac62edb" + ); + assert_eq!( + result.public_keys[11], + "0x0330f3b39c1a4278db118d2a8e8cd1fd5bd574d6fac43040f5ec514ad6cc776892" + ); + assert_eq!( + result.public_keys[12], + "0x4f3f24c064893a591d5a5b31990de9d12ed9da0c8650bcf98ede27e3da141401" + ); + } + + #[test] + fn test_get_public_keys_error_case() { + connect_and_bind(); + + let derivations = vec![PublicKeyDerivation { + chain_type: "POLKADOT".to_string(), + path: "m/44'/354'/0'/0'/0'".to_string(), + curve: "sr25519".to_string(), + }]; + let param = GetPublicKeysParam { derivations }; + let action: ImkeyAction = ImkeyAction { + method: "get_public_keys".to_string(), + param: Some(::prost_types::Any { + type_url: "get_public_keys".to_string(), + value: encode_message(param).unwrap(), + }), + }; + let action = hex::encode(encode_message(action).unwrap()); + let _ = unsafe { _to_str(call_imkey_api(_to_c_char(action.as_str()))) }; + let err = unsafe { _to_str(imkey_get_last_err_message()) }; + assert!(!err.is_empty()); + let error_ret: ErrorResponse = + ErrorResponse::decode(hex::decode(err).unwrap().as_slice()).unwrap(); + assert_eq!(error_ret.error, "unsupported_curve_type"); + } + fn derive_account(derivation: Derivation) -> DeriveAccountsResult { connect_and_bind();