Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: ikc add get_extended_public_key api [R2D2-10567] #70

Merged
merged 5 commits into from
Mar 1, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions imkey-core/ikc-common/src/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(())
Expand All @@ -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(())
Expand Down
14 changes: 14 additions & 0 deletions imkey-core/ikc-proto/src/api.proto
Original file line number Diff line number Diff line change
Expand Up @@ -141,4 +141,18 @@ message DeriveSubAccountsParam {

message DeriveSubAccountsResult {
repeated AccountResponse accounts = 1;
}

message GetExtendedPublicKeysParam {
repeated PublicKeyDerivation derivations = 4;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个 tag 数字要填对。不然后续容易对不上

}

message GetExtendedPublicKeysResult {
repeated string extendedPublicKeys = 1;
}

message PublicKeyDerivation {
string chainType = 1;
string path = 2;
string curve = 3;
}
2 changes: 1 addition & 1 deletion imkey-core/ikc-wallet/coin-substrate/src/address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ impl SubstrateAddress {
return Err(CoinError::ImkeySignatureVerifyFail.into());
}

Ok(pubkey.to_string())
Ok(pubkey.to_lowercase())
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里为什么要改成小写?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

现在返回给的公钥信息不都要统一成小写嘛?而且我看tcx这个接口返回的都是小写,为了保持统一。

}

pub fn get_address(path: &str, address_type: &AddressType) -> Result<String> {
Expand Down
22 changes: 22 additions & 0 deletions imkey-core/ikc/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -235,3 +235,25 @@ pub struct DeriveSubAccountsResult {
#[prost(message, repeated, tag = "1")]
pub accounts: ::prost::alloc::vec::Vec<AccountResponse>,
}
#[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<PublicKeyDerivation>,
}
#[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,
}
24 changes: 20 additions & 4 deletions imkey-core/ikc/src/handler.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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<Vec<u8>> {
let param: DeriveAccountsParam =
Expand Down Expand Up @@ -168,6 +166,7 @@ pub(crate) fn derive_sub_accounts(data: &[u8]) -> Result<Vec<u8>> {
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" => {
Expand Down Expand Up @@ -195,6 +194,23 @@ pub(crate) fn derive_sub_accounts(data: &[u8]) -> Result<Vec<u8>> {
})
}

pub(crate) fn get_extended_public_key(data: &[u8]) -> Result<Vec<u8>> {
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)?,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

下面这个为什么方法叫做 get_public_key ?tcx 我记得不返回 ed25519 的 xpub 。因为ed25519 没法使用 xpub 派生子地址来着。

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tcx不支持支持ed25519的xpub,但是可以直接返回派生的公钥。这个get_public_key方法是通过path来获取公钥,之前就已经有了,命名上可能有些歧义。

_ => return Err(anyhow!("unsupported_chain_type")),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个报错信息应该是 unsupported_curve ?

};
extended_public_keys.push(extended_public_key);
}
encode_message(GetExtendedPublicKeysResult{
extended_public_keys
})
}

#[cfg(test)]
mod test {
use crate::api::derive_accounts_param::Derivation;
Expand Down
176 changes: 172 additions & 4 deletions imkey-core/ikc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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)),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tcx 的名字应该叫 get_extended_public_keys。你上面参数名称也叫 GetExtendedPublicKeys

_ => landingpad(|| Err(anyhow!("unsupported_method"))),
};
match reply {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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(),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

测试最好是到 Account 级别的。如果全用 index 级别的似乎意义不大

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

已添加account级别path测试

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();

Expand Down