From 45947363f3778e83dea6ad3050edbe3a1de54982 Mon Sep 17 00:00:00 2001 From: Dimitri Date: Thu, 19 Sep 2024 13:33:36 +0700 Subject: [PATCH] Add AI generated comments --- src/command_templates.rs | 66 ++++++++++++++++++++++++++++++++++++++-- src/parse_email.rs | 43 +++++++++++++++++++++----- src/proof.rs | 64 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 163 insertions(+), 10 deletions(-) diff --git a/src/command_templates.rs b/src/command_templates.rs index af7cb23..637d1b3 100644 --- a/src/command_templates.rs +++ b/src/command_templates.rs @@ -12,17 +12,33 @@ const INT_REGEX: &str = r"-?\d+"; const ETH_ADDR_REGEX: &str = r"0x[a-fA-F0-9]{40}"; const DECIMALS_REGEX: &str = r"\d+\.\d+"; +/// Represents different types of template values. #[derive(Debug, Clone, Serialize, Deserialize)] pub enum TemplateValue { + /// A string value. String(String), + /// An unsigned integer value. Uint(U256), + /// A signed integer value. Int(I256), + /// A decimal value represented as a string. Decimals(String), + /// An Ethereum address. EthAddr(Address), + /// A fixed value represented as a string. Fixed(String), } impl TemplateValue { + /// Encodes the template value into ABI format. + /// + /// # Arguments + /// + /// * `decimal_size` - An optional value specifying the number of decimal places for Decimals type. + /// + /// # Returns + /// + /// A `Result` containing the encoded bytes or an error. pub fn abi_encode(&self, decimal_size: Option) -> Result { match self { Self::String(string) => Ok(Bytes::from(abi::encode(&[Token::String(string.clone())]))), @@ -36,7 +52,17 @@ impl TemplateValue { } } - pub fn decimals_str_to_uint(str: &str, decimal_size: u8) -> U256 { + /// Converts a decimal string to a U256 integer. + /// + /// # Arguments + /// + /// * `str` - The decimal string to convert. + /// * `decimal_size` - The number of decimal places. + /// + /// # Returns + /// + /// A `U256` representing the decimal value. + fn decimals_str_to_uint(str: &str, decimal_size: u8) -> U256 { let decimal_size = decimal_size as usize; let dot = Regex::new("\\.").unwrap().find(str); let (before_dot_str, mut after_dot_str) = match dot { @@ -54,6 +80,16 @@ impl TemplateValue { } } +/// Extracts template values from a command input string. +/// +/// # Arguments +/// +/// * `input` - The input string to extract values from. +/// * `templates` - A vector of template strings. +/// +/// # Returns +/// +/// A `Result` containing a vector of `TemplateValue`s or an error. pub fn extract_template_vals_from_command( input: &str, templates: Vec, @@ -88,13 +124,24 @@ pub fn extract_template_vals_from_command( } } -pub fn extract_template_vals(input: &str, templates: Vec) -> Result> { +/// Extracts template values from an input string. +/// +/// # Arguments +/// +/// * `input` - The input string to extract values from. +/// * `templates` - A vector of template strings. +/// +/// # Returns +/// +/// A `Result` containing a vector of `TemplateValue`s or an error. +fn extract_template_vals(input: &str, templates: Vec) -> Result> { let input_decomposed: Vec<&str> = input.split_whitespace().collect(); let mut template_vals = Vec::new(); for (input_idx, template) in templates.iter().enumerate() { match template.as_str() { "{string}" => { + // Extract and validate string value let string_match = Regex::new(STRING_REGEX) .unwrap() .find(input_decomposed[input_idx]) @@ -109,6 +156,7 @@ pub fn extract_template_vals(input: &str, templates: Vec) -> Result { + // Extract and validate unsigned integer value let uint_match = Regex::new(UINT_REGEX) .unwrap() .find(input_decomposed[input_idx]) @@ -125,6 +173,7 @@ pub fn extract_template_vals(input: &str, templates: Vec) -> Result { + // Extract and validate signed integer value let int_match = Regex::new(INT_REGEX) .unwrap() .find(input_decomposed[input_idx]) @@ -140,6 +189,7 @@ pub fn extract_template_vals(input: &str, templates: Vec) -> Result { + // Extract and validate decimal value let decimals_match = Regex::new(DECIMALS_REGEX) .unwrap() .find(input_decomposed[input_idx]) @@ -156,6 +206,7 @@ pub fn extract_template_vals(input: &str, templates: Vec) -> Result { + // Extract and validate Ethereum address let address_match = Regex::new(ETH_ADDR_REGEX) .unwrap() .find(input_decomposed[input_idx]) @@ -173,7 +224,16 @@ pub fn extract_template_vals(input: &str, templates: Vec) -> Result String { // Convert amount to string in wei format (no decimals) let uint_str = uint.to_string(); diff --git a/src/parse_email.rs b/src/parse_email.rs index f85b424..66f8be6 100644 --- a/src/parse_email.rs +++ b/src/parse_email.rs @@ -15,14 +15,20 @@ use zk_regex_apis::extract_substrs::{ extract_substr_idxes, extract_timestamp_idxes, extract_to_addr_idxes, }; -#[derive(Debug, Clone, Serialize, Deserialize)] /// `ParsedEmail` holds the canonicalized parts of an email along with its signature and public key. +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct ParsedEmail { - pub canonicalized_header: String, // The canonicalized email header. - pub canonicalized_body: String, // The canonicalized email body. - pub signature: Vec, // The email signature bytes. - pub public_key: Vec, // The public key bytes associated with the email. - pub cleaned_body: String, // The cleaned email body. + /// The canonicalized email header. + pub canonicalized_header: String, + /// The canonicalized email body. + pub canonicalized_body: String, + /// The email signature bytes. + pub signature: Vec, + /// The public key bytes associated with the email. + pub public_key: Vec, + /// The cleaned email body. + pub cleaned_body: String, + /// The email headers. pub headers: EmailHeaders, } @@ -57,6 +63,7 @@ impl ParsedEmail { // Extract all headers let parsed_mail = parse_mail(raw_email.as_bytes())?; let headers: EmailHeaders = EmailHeaders::new_from_mail(&parsed_mail); + // Construct the `ParsedEmail` instance. let parsed_email = ParsedEmail { canonicalized_header: String::from_utf8(canonicalized_header)?, // Convert bytes to string, may return an error if not valid UTF-8. @@ -270,12 +277,14 @@ pub(crate) fn remove_quoted_printable_soft_breaks(body: Vec) -> Vec { while let Some((i, &byte)) = iter.next() { if byte == b'=' && body.get(i + 1..i + 3) == Some(&[b'\r', b'\n']) { - iter.nth(1); // Skip the next two bytes + // Skip the next two bytes (soft line break) + iter.nth(1); } else { result.push(byte); } } + // Resize the result to match the original body length result.resize(body.len(), 0); result } @@ -296,6 +305,7 @@ pub(crate) fn remove_quoted_printable_soft_breaks(body: Vec) -> Vec { pub(crate) fn find_index_in_body(body: Option<&Vec>, pattern: &str) -> usize { body.and_then(|body_bytes| { if !pattern.is_empty() { + // Search for the pattern in the body body_bytes .windows(pattern.len()) .position(|w| w == pattern.as_bytes()) @@ -306,10 +316,20 @@ pub(crate) fn find_index_in_body(body: Option<&Vec>, pattern: &str) -> usize .unwrap_or(0) // Default to 0 if not found or pattern is empty } +/// Represents the email headers as a collection of key-value pairs. #[derive(Debug, Clone, Serialize, Deserialize)] pub struct EmailHeaders(HashMap>); impl EmailHeaders { + /// Creates a new `EmailHeaders` instance from a parsed email. + /// + /// # Arguments + /// + /// * `parsed_mail` - A reference to a `ParsedMail` instance. + /// + /// # Returns + /// + /// A new `EmailHeaders` instance containing the headers from the parsed email. pub fn new_from_mail(parsed_mail: &ParsedMail) -> Self { let mut headers = HashMap::new(); for header in &parsed_mail.headers { @@ -320,6 +340,15 @@ impl EmailHeaders { Self(headers) } + /// Retrieves the value(s) of a specific header. + /// + /// # Arguments + /// + /// * `name` - The name of the header to retrieve. + /// + /// # Returns + /// + /// An `Option` containing a `Vec` of header values if the header exists, or `None` if it doesn't. pub fn get_header(&self, name: &str) -> Option> { self.0.get(name).cloned() } diff --git a/src/proof.rs b/src/proof.rs index 477df6b..74e31bf 100644 --- a/src/proof.rs +++ b/src/proof.rs @@ -12,25 +12,40 @@ use std::hash::{Hash, Hasher}; use crate::{field_to_hex, hex_to_field, AccountCode, AccountSalt, PaddedEmailAddr}; +/// Represents the response from the prover. #[derive(Debug, Clone, Deserialize)] pub struct ProverRes { + /// The proof in JSON format. proof: ProofJson, + /// The public signals associated with the proof. pub_signals: Vec, } +/// Represents the proof in JSON format. #[derive(Debug, Clone, Deserialize)] pub struct ProofJson { + /// The pi_a component of the proof. pi_a: Vec, + /// The pi_b component of the proof. pi_b: Vec>, + /// The pi_c component of the proof. pi_c: Vec, } impl ProofJson { + /// Converts the proof to Ethereum-compatible bytes. + /// + /// # Returns + /// + /// A `Result` containing the Ethereum-compatible bytes or an error. pub fn to_eth_bytes(&self) -> Result { + // Convert pi_a to Token::FixedArray let pi_a = Token::FixedArray(vec![ Token::Uint(U256::from_dec_str(self.pi_a[0].as_str())?), Token::Uint(U256::from_dec_str(self.pi_a[1].as_str())?), ]); + + // Convert pi_b to nested Token::FixedArray let pi_b = Token::FixedArray(vec![ Token::FixedArray(vec![ Token::Uint(U256::from_dec_str(self.pi_b[0][1].as_str())?), @@ -41,36 +56,69 @@ impl ProofJson { Token::Uint(U256::from_dec_str(self.pi_b[1][0].as_str())?), ]), ]); + + // Convert pi_c to Token::FixedArray let pi_c = Token::FixedArray(vec![ Token::Uint(U256::from_dec_str(self.pi_c[0].as_str())?), Token::Uint(U256::from_dec_str(self.pi_c[1].as_str())?), ]); + + // Encode the tokens and return as Bytes Ok(Bytes::from(abi::encode(&[pi_a, pi_b, pi_c]))) } } +/// Generates a proof for the given input. +/// +/// # Arguments +/// +/// * `input` - The input string for proof generation. +/// * `request` - The request string. +/// * `address` - The address string. +/// +/// # Returns +/// +/// A `Result` containing a tuple of `Bytes` (the proof) and `Vec` (public signals) or an error. pub async fn generate_proof( input: &str, request: &str, address: &str, ) -> Result<(Bytes, Vec)> { let client = reqwest::Client::new(); + + // Send POST request to the prover let res = client .post(format!("{}/prove/{}", address, request)) .json(&serde_json::json!({ "input": input })) .send() .await? .error_for_status()?; + + // Parse the response JSON let res_json = res.json::().await?; + + // Convert the proof to Ethereum-compatible bytes let proof = res_json.proof.to_eth_bytes()?; + + // Convert public signals to U256 let pub_signals = res_json .pub_signals .into_iter() .map(|str| U256::from_dec_str(&str).expect("pub signal should be u256")) .collect(); + Ok((proof, pub_signals)) } +/// Calculates a default hash for the given input string. +/// +/// # Arguments +/// +/// * `input` - The input string to hash. +/// +/// # Returns +/// +/// A string representation of the calculated hash. pub fn calculate_default_hash(input: &str) -> String { let mut hasher = DefaultHasher::new(); input.hash(&mut hasher); @@ -79,15 +127,31 @@ pub fn calculate_default_hash(input: &str) -> String { hash_code.to_string() } +/// Calculates the account salt based on the email address and account code. +/// +/// # Arguments +/// +/// * `email_addr` - The email address string. +/// * `account_code` - The account code string. +/// +/// # Returns +/// +/// A string representation of the calculated account salt. pub fn calculate_account_salt(email_addr: &str, account_code: &str) -> String { + // Pad the email address let padded_email_addr = PaddedEmailAddr::from_email_addr(email_addr); + + // Convert account code to field element let account_code = if account_code.starts_with("0x") { hex_to_field(account_code).unwrap() } else { hex_to_field(&format!("0x{}", account_code)).unwrap() }; let account_code = AccountCode::from(account_code); + + // Generate account salt let account_salt = AccountSalt::new(&padded_email_addr, account_code).unwrap(); + // Convert account salt to hexadecimal representation field_to_hex(&account_salt.0) }