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