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