1use ic_cdk::export::{
4 candid::CandidType,
5 serde::{Deserialize, Serialize},
6 Principal,
7};
8use std::str::FromStr;
9use crate::types::Address;
10use crate::signing;
11use libsecp256k1::{PublicKey, PublicKeyFormat, Message, Signature, RecoveryId, recover};
12
13const ECDSA_SIGN_CYCLES : u64 = 10_000_000_000;
14use ic_cdk::api::management_canister::ecdsa::*;
23
24#[derive(CandidType, Serialize, Debug, Clone)]
25pub struct KeyInfo {
26 pub derivation_path: Vec<Vec<u8>>,
27 pub key_name: String,
28}
29
30pub async fn get_public_key(
33 canister_id: Option<Principal>,
34 derivation_path: Vec<Vec<u8>>,
35 key_name: String
36) -> Result<Vec<u8>, String> {
37 let key_id = EcdsaKeyId {
38 curve: EcdsaCurve::Secp256k1,
39 name: key_name,
40 };
41 let ic_canister_id = "aaaaa-aa";
42 let ic = Principal::from_str(&ic_canister_id).unwrap();
43
44
45 let request = EcdsaPublicKeyArgument {
46 canister_id: canister_id,
47 derivation_path: derivation_path,
48 key_id: key_id.clone(),
49 };
50 let (res,): (EcdsaPublicKeyResponse,) = ic_cdk::call(ic, "ecdsa_public_key", (request,))
51 .await
52 .map_err(|e| format!("Failed to call ecdsa_public_key {}", e.1))?;
53
54 Ok(res.public_key)
55}
56
57pub fn pubkey_to_address(pubkey: &[u8]) -> Result<Address, String> {
59 let uncompressed_pubkey = match PublicKey::parse_slice(pubkey, Some(PublicKeyFormat::Compressed)) {
60 Ok(key) => { key.serialize() },
61 Err(_) => { return Err("uncompress public key failed: ".to_string()); },
62 };
63 let hash = signing::keccak256(&uncompressed_pubkey[1..65]);
64 let mut result = [0u8; 20];
65 result.copy_from_slice(&hash[12..]);
66 Ok(Address::from(result))
67}
68
69pub async fn get_eth_addr(
71 canister_id: Option<Principal>,
72 derivation_path: Option<Vec<Vec<u8>>>,
73 name: String
74) -> Result<Address, String> {
75 let path = if let Some(v) = derivation_path { v } else { vec![ic_cdk::id().as_slice().to_vec()] };
76 match get_public_key(canister_id, path, name).await {
77 Ok(pubkey) => { return pubkey_to_address(&pubkey); },
78 Err(e) => { return Err(e); },
79 };
80}
81
82pub async fn ic_raw_sign(
84 message: Vec<u8>,
85 derivation_path: Vec<Vec<u8>>,
86 key_name: String
87) -> Result<Vec<u8>, String> {
88 assert!(message.len() == 32);
89
90 let key_id = EcdsaKeyId {
91 curve: EcdsaCurve::Secp256k1,
92 name: key_name,
93 };
94 let ic = Principal::management_canister();
95
96 let request = SignWithEcdsaArgument {
97 message_hash: message.clone(),
98 derivation_path: derivation_path,
99 key_id,
100 };
101 let (res,): (SignWithEcdsaResponse,) =
102 ic_cdk::api::call::call_with_payment(ic, "sign_with_ecdsa", (request,), ECDSA_SIGN_CYCLES)
103 .await
104 .map_err(|e| format!("Failed to call sign_with_ecdsa {}", e.1))?;
105
106 Ok(res.signature)
107}
108
109
110pub fn recover_address(msg: Vec<u8>, sig: Vec<u8>, rec_id: u8) -> String {
113 let message = Message::parse_slice(&msg).unwrap();
114 let signature = Signature::parse_overflowing_slice(&sig).unwrap();
115 let recovery_id = RecoveryId::parse(rec_id).unwrap();
116
117 match recover(&message, &signature, &recovery_id) {
118 Ok(pubkey) => {
119 let uncompressed_pubkey = pubkey.serialize();
120 let hash = signing::keccak256(&uncompressed_pubkey[1..65]);
122 let mut result = [0u8; 20];
123 result.copy_from_slice(&hash[12..]);
124 hex::encode(result)
125 },
126 Err(_) => { "".into() }
127 }
128}
129
130