verity_verify_remote/
ic.rs

1use std::error::Error;
2
3use crate::config::Config;
4use candid::{CandidType, Decode};
5use ic_agent::{export::Principal, Agent};
6use serde::Deserialize;
7use verity_verify_local::{self, ecdsa::validate_ecdsa_signature, merkle::validate_merkle_tree};
8pub const DEFAULT_IC_GATEWAY_LOCAL: &str = "http://127.0.0.1:4943";
9pub const DEFAULT_IC_GATEWAY_MAINNET: &str = "https://icp0.io";
10pub const DEFAULT_IC_GATEWAY_MAINNET_TRAILING_SLASH: &str = "https://icp0.io/";
11
12pub struct Verifier {
13    pub agent: Agent,
14    pub canister: Principal,
15}
16
17#[derive(CandidType, Deserialize, Debug)]
18pub struct PublicKeyReply {
19    pub sec1_pk: String,
20    pub etherum_pk: String,
21}
22
23#[derive(CandidType, Deserialize, Debug, Clone)]
24pub struct VerificationResponse {
25    pub results: Vec<ProofResponse>,
26    pub root: String,
27    pub signature: String,
28}
29
30#[derive(CandidType, Deserialize, Debug, Clone)]
31pub enum ProofResponse {
32    SessionProof(String), //takes in reques response
33    FullProof(String),    //takes in
34}
35
36impl ProofResponse {
37    /// Fetches the content of a proof
38    pub fn get_content(&self) -> String {
39        match self {
40            // Returns the content of a session proof
41            ProofResponse::SessionProof(content) => content.clone(),
42            // Returns the content of a full proof
43            ProofResponse::FullProof(content) => content.clone(),
44        }
45    }
46}
47
48type CanisterResponseType = Result<VerificationResponse, String>;
49
50/// A proxy verifier to interact with the managed verifier contract
51impl Verifier {
52    /// Creates a new verifier from a config struct
53    pub async fn from_config(config: &Config) -> anyhow::Result<Self> {
54        let agent = config.create_agent().await?;
55        Ok(Self {
56            agent,
57            canister: config.canister_principal,
58        })
59    }
60
61    /// Verifies a canister's response by checking the Merkle root hash
62    /// and validating the ECDSA signature
63    async fn verify_canister_response(
64        &self,
65        verification_response: &VerificationResponse,
66    ) -> Result<bool, Box<dyn Error>> {
67        // Extract parameters needed for verification
68        let signature_hex = &verification_response.signature;
69        let root_hash = &verification_response.root;
70        let leaves: Vec<String> = verification_response
71            .results
72            .iter()
73            .map(|proof_response| proof_response.get_content())
74            .collect();
75        let canister_public_key = self.get_public_key().await?;
76
77        // Verify the signature and the Merkle tree root
78        let is_signature_valid =
79            validate_ecdsa_signature(signature_hex, &root_hash, &canister_public_key)?;
80        let is_merkle_valid = validate_merkle_tree(&leaves, root_hash);
81
82        // Return the verification result
83        let is_response_valid = is_signature_valid && is_merkle_valid;
84        Ok(is_response_valid)
85    }
86
87    /// Retrieves the public key of the specified canister
88    pub async fn get_public_key(&self) -> Result<String, Box<dyn Error>> {
89        let method_name = "public_key";
90
91        // Calls the public key method on the specified canister
92        let response = self
93            .agent
94            .update(&self.canister, method_name)
95            .with_arg(candid::encode_args(())?)
96            .call_and_wait()
97            .await?;
98
99        let public_key_response = Decode!(&response, PublicKeyReply)?;
100
101        Ok(public_key_response.etherum_pk)
102    }
103
104    /// Verifies a proof on-chain and validates the response locally
105    pub async fn verify_proof(
106        &self,
107        string_proofs: Vec<String>,
108        notary_pub_key: String,
109    ) -> Result<VerificationResponse, Box<dyn Error>> {
110        let verifier_method = "verify_proof_direct";
111
112        // Makes a call to IC using the agent to verify the proof via the direct interface
113        let response = self
114            .agent
115            .update(&self.canister, verifier_method)
116            .with_arg(candid::encode_args((string_proofs, notary_pub_key))?)
117            .call_and_wait()
118            .await
119            .unwrap();
120
121        // Parses the response into the appropriate struct and returns it
122        let verification_response = Decode!(&response, CanisterResponseType)??;
123
124        // Validates the signature and Merkle tree
125        let is_response_valid = self
126            .verify_canister_response(&verification_response)
127            .await?;
128
129        assert!(is_response_valid, "INVALID_CANISTER_RESPONSE");
130
131        Ok(verification_response)
132    }
133}