From 7385b9a47c3f8aedf44b7fa1e82a7fa27564148c Mon Sep 17 00:00:00 2001 From: Dimitri Date: Fri, 13 Sep 2024 13:09:33 +0700 Subject: [PATCH] Add Re: to subject for reply emails --- packages/relayer/src/chain.rs | 14 +++- packages/relayer/src/core.rs | 65 +++++++++++-------- packages/relayer/src/modules/mail.rs | 38 +++++++---- .../relayer/src/modules/web_server/errors.rs | 2 +- .../src/modules/web_server/rest_api.rs | 43 +++++++++++- packages/relayer/src/utils/mod.rs | 4 +- ...mand_templates.rs => subject_templates.rs} | 3 +- 7 files changed, 126 insertions(+), 43 deletions(-) rename packages/relayer/src/utils/{command_templates.rs => subject_templates.rs} (99%) diff --git a/packages/relayer/src/chain.rs b/packages/relayer/src/chain.rs index b124488f..8de83d4e 100644 --- a/packages/relayer/src/chain.rs +++ b/packages/relayer/src/chain.rs @@ -152,7 +152,7 @@ impl ChainClient { .call() .await .map_err(|e| { - ChainError::contract_error("Failed to get recovery subject templates", e) + ChainError::contract_error("Failed to get recovery command templates", e) })?; Ok(templates[template_idx as usize].clone()) } @@ -163,19 +163,29 @@ impl ChainClient { account_eth_addr: &String, complete_calldata: &String, ) -> Result { + println!("doing complete recovery"); let controller_eth_addr: H160 = controller_eth_addr.parse().map_err(ChainError::HexError)?; + println!("controller_eth_addr: {:?}", controller_eth_addr); + let contract = EmailAccountRecovery::new(controller_eth_addr, self.client.clone()); let decoded_calldata = hex::decode(&complete_calldata.trim_start_matches("0x")).expect("Decoding failed"); + println!("decoded_calldata : {:?}", decoded_calldata); + let account_eth_addr = account_eth_addr .parse::() .map_err(ChainError::HexError)?; + println!("account_eth_addr : {:?}", account_eth_addr); + let call = contract.complete_recovery(account_eth_addr, Bytes::from(decoded_calldata)); + println!("call: {:?}", call); + let tx = call .send() .await .map_err(|e| ChainError::contract_error("Failed to call complete_recovery", e))?; + println!("tx: {:?}", tx); // If the transaction is successful, the function will return true and false otherwise. let receipt = tx .log() @@ -188,6 +198,8 @@ impl ChainClient { ) })? .ok_or(anyhow!("No receipt"))?; + println!("receipt : {:?}", receipt); + Ok(receipt .status .map(|status| status == U64::from(1)) diff --git a/packages/relayer/src/core.rs b/packages/relayer/src/core.rs index 67fe5194..7af369ae 100644 --- a/packages/relayer/src/core.rs +++ b/packages/relayer/src/core.rs @@ -25,26 +25,25 @@ pub async fn handle_email(email: String) -> Result { let request_decomposed_def = serde_json::from_str(include_str!("./regex_json/request_def.json")) .map_err(|e| EmailError::Parse(format!("Failed to parse request_def.json: {}", e)))?; - println!("request_decomposed_def: {:?}", request_decomposed_def); let request_idxes = extract_substr_idxes(&email, &request_decomposed_def)?; - println!("request_idxes: {:?}", request_idxes); if request_idxes.is_empty() { return Err(EmailError::Body(WRONG_COMMAND_FORMAT.to_string())); } info!(LOG, "Request idxes: {:?}", request_idxes); let request_id = &email[request_idxes[0].0..request_idxes[0].1]; - println!("request_id: {:?}", request_id); let request_id_u32 = request_id .parse::() .map_err(|e| EmailError::Parse(format!("Failed to parse request_id to u64: {}", e)))?; - println!("request_id_u32: {:?}", request_id_u32); let request = match DB.get_request(request_id_u32).await? { Some(req) => req, None => { + let original_subject = parsed_email.get_subject_all()?; return Ok(EmailAuthEvent::Error { email_addr: guardian_email_addr, error: format!("Request {} not found", request_id), - }) + original_subject, + original_message_id: parsed_email.get_message_id().ok(), + }); } }; if request.guardian_email_addr != guardian_email_addr { @@ -104,14 +103,24 @@ async fn handle_email_request( accept(params, invitation_code).await } (None, is_for_recovery) if is_for_recovery => recover(params).await, - (Some(_), _) => Ok(EmailAuthEvent::Error { - email_addr: params.request.guardian_email_addr, - error: "Account code found and for recovery".to_string(), - }), - (None, _) => Ok(EmailAuthEvent::Error { - email_addr: params.request.guardian_email_addr, - error: "No account code found and not for recovery".to_string(), - }), + (Some(_), _) => { + let original_subject = params.parsed_email.get_subject_all()?; + Ok(EmailAuthEvent::Error { + email_addr: params.request.guardian_email_addr, + error: "Account code found and for recovery".to_string(), + original_subject, + original_message_id: params.parsed_email.get_message_id().ok(), + }) + } + (None, _) => { + let original_subject = params.parsed_email.get_subject_all()?; + Ok(EmailAuthEvent::Error { + email_addr: params.request.guardian_email_addr, + error: "No account code found and not for recovery".to_string(), + original_subject, + original_message_id: params.parsed_email.get_message_id().ok(), + }) + } } } @@ -140,6 +149,7 @@ async fn accept( ) .await?; + let original_subject = params.parsed_email.get_subject_all()?; if is_accepted { let creds = Credentials { account_code: invitation_code, @@ -153,11 +163,16 @@ async fn accept( account_eth_addr: params.request.account_eth_addr, guardian_email_addr: params.request.guardian_email_addr, request_id: params.request.request_id, + original_subject, + original_message_id: params.parsed_email.get_message_id().ok(), }) } else { + let original_subject = params.parsed_email.get_subject_all()?; Ok(EmailAuthEvent::Error { email_addr: params.request.guardian_email_addr, error: "Failed to handle acceptance".to_string(), + original_subject, + original_message_id: params.parsed_email.get_message_id().ok(), }) } } @@ -184,16 +199,22 @@ async fn recover(params: EmailRequestContext) -> Result = command_params - // .iter() - // .map(|param| param.abi_encode(None).unwrap()) - // .collect(); + let command_params = extract_template_vals_from_command(¶ms.email_body, command_template) + .map_err(|e| EmailError::Body(format!("Invalid commad: {}", e)))?; let command_params_encoded = command_params .iter() diff --git a/packages/relayer/src/modules/mail.rs b/packages/relayer/src/modules/mail.rs index ab792c39..dfb237dc 100644 --- a/packages/relayer/src/modules/mail.rs +++ b/packages/relayer/src/modules/mail.rs @@ -20,6 +20,8 @@ pub enum EmailAuthEvent { Error { email_addr: String, error: String, + original_subject: String, + original_message_id: Option, }, RecoveryRequest { account_eth_addr: String, @@ -31,11 +33,15 @@ pub enum EmailAuthEvent { account_eth_addr: String, guardian_email_addr: String, request_id: u32, + original_subject: String, + original_message_id: Option, }, RecoverySuccess { account_eth_addr: String, guardian_email_addr: String, request_id: u32, + original_subject: String, + original_message_id: Option, }, GuardianNotSet { account_eth_addr: String, @@ -116,8 +122,14 @@ pub async fn handle_email_event(event: EmailAuthEvent) -> Result<(), EmailError> send_email(email).await?; } - EmailAuthEvent::Error { email_addr, error } => { - let subject = "Error"; + EmailAuthEvent::Error { + email_addr, + error, + original_subject, + original_message_id, + } => { + let subject = format!("Re: {}", original_subject); + let body_plain = format!( "An error occurred while processing your request. \ Error: {}", @@ -132,9 +144,9 @@ pub async fn handle_email_event(event: EmailAuthEvent) -> Result<(), EmailError> let email = EmailMessage { to: email_addr, - subject: subject.to_string(), - reference: None, - reply_to: None, + subject, + reference: original_message_id.clone(), + reply_to: original_message_id, body_plain, body_html, body_attachments: None, @@ -211,8 +223,10 @@ pub async fn handle_email_event(event: EmailAuthEvent) -> Result<(), EmailError> account_eth_addr, guardian_email_addr, request_id, + original_subject, + original_message_id, } => { - let subject = "Acceptance Success"; + let subject = format!("Re: {}", original_subject); let body_plain = format!( "Your guardian request for the wallet address {} has been set. \ Your request ID is #{} is now complete.", @@ -229,8 +243,8 @@ pub async fn handle_email_event(event: EmailAuthEvent) -> Result<(), EmailError> let email = EmailMessage { to: guardian_email_addr, subject: subject.to_string(), - reference: None, - reply_to: None, + reference: original_message_id.clone(), + reply_to: original_message_id, body_plain, body_html, body_attachments: None, @@ -242,8 +256,10 @@ pub async fn handle_email_event(event: EmailAuthEvent) -> Result<(), EmailError> account_eth_addr, guardian_email_addr, request_id, + original_subject, + original_message_id, } => { - let subject = "Recovery Success"; + let subject = format!("Re: {}", original_subject); let body_plain = format!( "Your recovery request for the wallet address {} is successful. \ Your request ID is #{}.", @@ -260,8 +276,8 @@ pub async fn handle_email_event(event: EmailAuthEvent) -> Result<(), EmailError> let email = EmailMessage { to: guardian_email_addr, subject: subject.to_string(), - reference: None, - reply_to: None, + reference: original_message_id.clone(), + reply_to: original_message_id, body_plain, body_html, body_attachments: None, diff --git a/packages/relayer/src/modules/web_server/errors.rs b/packages/relayer/src/modules/web_server/errors.rs index 85c4d178..10959c21 100644 --- a/packages/relayer/src/modules/web_server/errors.rs +++ b/packages/relayer/src/modules/web_server/errors.rs @@ -190,7 +190,7 @@ impl IntoResponse for ApiError { ApiError::Database(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()), ApiError::Chain(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()), ApiError::SqlxError(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()), - ApiError::Validation(e) => (StatusCode::BAD_REQUEST, e), + ApiError::Validation(e) => (StatusCode::BAD_REQUEST, e.to_string()), ApiError::Anyhow(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()), ApiError::Internal(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()), ApiError::Email(e) => match e { diff --git a/packages/relayer/src/modules/web_server/rest_api.rs b/packages/relayer/src/modules/web_server/rest_api.rs index f099b5fb..823eb6e0 100644 --- a/packages/relayer/src/modules/web_server/rest_api.rs +++ b/packages/relayer/src/modules/web_server/rest_api.rs @@ -11,6 +11,7 @@ use std::str; pub async fn request_status_api( Json(payload): Json, ) -> Result, ApiError> { + println!("requesting status"); let row = DB.get_request(payload.request_id).await?; let status = if let Some(ref row) = row { if row.is_processed { @@ -35,6 +36,7 @@ pub async fn request_status_api( pub async fn handle_acceptance_request( Json(payload): Json, ) -> Result, ApiError> { + println!("handle_acceptance_request"); let command_template = CLIENT .get_acceptance_command_templates(&payload.controller_eth_addr, payload.template_idx) .await?; @@ -194,12 +196,15 @@ pub async fn handle_acceptance_request( pub async fn handle_recovery_request( Json(payload): Json, ) -> Result, ApiError> { + println!("handle_recovery_request: {:?}", payload); let command_template = CLIENT .get_recovery_command_templates(&payload.controller_eth_addr, payload.template_idx) .await?; + println!("command_template: {:?}", command_template); let command_params = extract_template_vals(&payload.command, command_template) .map_err(|_| ApiError::Validation("Invalid command".to_string()))?; + println!("command_params"); let account_eth_addr = CLIENT .get_recovered_account_from_recovery_command( @@ -209,15 +214,22 @@ pub async fn handle_recovery_request( ) .await?; + println!("account_eth_addr"); + let account_eth_addr = format!("0x{:x}", account_eth_addr); + println!("account_eth_addr"); if !CLIENT.is_wallet_deployed(&account_eth_addr).await? { return Err(ApiError::Validation("Wallet not deployed".to_string())); } + println!("wallet is deployed"); + // Check if hash of bytecode of proxy contract is equal or not let bytecode = CLIENT.get_bytecode(&account_eth_addr).await?; + println!("bytecode"); let bytecode_hash = format!("0x{}", hex::encode(keccak256(bytecode.as_ref()))); + println!("bytecode_hash"); // let permitted_wallets: Vec = // serde_json::from_str(include_str!("../../permitted_wallets.json")).unwrap(); @@ -266,24 +278,37 @@ pub async fn handle_recovery_request( // } let mut request_id = rand::thread_rng().gen::(); + println!("got request_id"); while let Ok(Some(request)) = DB.get_request(request_id).await { request_id = rand::thread_rng().gen::(); } + println!("got request: {:?}", request_id); + println!("account_eth_addr: {:?}", account_eth_addr); + println!( + "payload.guardian_email_addr: {:?}", + payload.guardian_email_addr + ); + let account = DB .get_credentials_from_wallet_and_email(&account_eth_addr, &payload.guardian_email_addr) .await?; + println!("got account: {:?}", account); + let account_salt = if let Some(account_details) = account { calculate_account_salt(&payload.guardian_email_addr, &account_details.account_code) } else { return Err(ApiError::Validation("Wallet not deployed".to_string())); }; + println!("got account_salt"); + if !DB .is_wallet_and_email_registered(&account_eth_addr, &payload.guardian_email_addr) .await? { + println!("email and wallet are not registered"); DB.insert_request(&Request { request_id: request_id.clone(), account_eth_addr: account_eth_addr.clone(), @@ -326,10 +351,13 @@ pub async fn handle_recovery_request( }) .await?; + println!("inserted request"); + if DB .is_guardian_set(&account_eth_addr, &payload.guardian_email_addr) .await? { + println!("guardian is set"); handle_email_event(EmailAuthEvent::RecoveryRequest { account_eth_addr, guardian_email_addr: payload.guardian_email_addr.clone(), @@ -340,6 +368,7 @@ pub async fn handle_recovery_request( // TODO: Add custom error for handle_email_event .expect("Failed to send Recovery event"); } else { + println!("guardian is not set"); handle_email_event(EmailAuthEvent::GuardianNotSet { account_eth_addr, guardian_email_addr: payload.guardian_email_addr.clone(), @@ -349,6 +378,8 @@ pub async fn handle_recovery_request( .expect("Failed to send Recovery event"); } + println!("all done"); + Ok(Json(RecoveryResponse { request_id, command_params, @@ -358,9 +389,11 @@ pub async fn handle_recovery_request( pub async fn handle_complete_recovery_request( Json(payload): Json, ) -> Result { + println!("handle_complete_recovery_request"); if !CLIENT.is_wallet_deployed(&payload.account_eth_addr).await? { return Err(ApiError::Validation("Wallet not deployed".to_string())); } + println!("wallet is deployed"); match CLIENT .complete_recovery( @@ -395,6 +428,7 @@ pub async fn handle_complete_recovery_request( pub async fn get_account_salt( Json(payload): Json, ) -> Result { + println!("get_account_salt"); let account_salt = calculate_account_salt(&payload.email_addr, &payload.account_code); Ok(account_salt) } @@ -402,6 +436,7 @@ pub async fn get_account_salt( pub async fn inactive_guardian( Json(payload): Json, ) -> Result { + println!("inactive_guardian"); let is_activated = CLIENT .get_is_activated(&payload.controller_eth_addr, &payload.account_eth_addr) .await?; @@ -438,6 +473,7 @@ fn parse_error_message(error_data: String) -> String { } pub async fn receive_email_api_fn(email: String) -> Result<(), ApiError> { + println!("receive_email_api_fn"); let parsed_email = ParsedEmail::new_from_raw_email(&email).await?; let from_addr = parsed_email.get_from_addr()?; let original_subject = parsed_email.get_subject_all()?; @@ -466,9 +502,14 @@ pub async fn receive_email_api_fn(email: String) -> Result<(), ApiError> { }, Err(e) => { error!(LOG, "Error handling email: {:?}", e); + let original_subject = parsed_email + .get_subject_all() + .unwrap_or("Unknown Error".to_string()); match handle_email_event(EmailAuthEvent::Error { email_addr: from_addr, error: e.to_string(), + original_subject, + original_message_id: parsed_email.get_message_id().ok(), }) .await { @@ -519,7 +560,7 @@ pub struct AcceptanceResponse { pub command_params: Vec, } -#[derive(Serialize, Deserialize)] +#[derive(Serialize, Deserialize, Debug)] pub struct RecoveryRequest { pub controller_eth_addr: String, pub guardian_email_addr: String, diff --git a/packages/relayer/src/utils/mod.rs b/packages/relayer/src/utils/mod.rs index 11b1e85f..361a451a 100644 --- a/packages/relayer/src/utils/mod.rs +++ b/packages/relayer/src/utils/mod.rs @@ -1,7 +1,7 @@ -pub mod command_templates; pub mod strings; +pub mod subject_templates; pub mod utils; -pub use command_templates::*; pub use strings::*; +pub use subject_templates::*; pub use utils::*; diff --git a/packages/relayer/src/utils/command_templates.rs b/packages/relayer/src/utils/subject_templates.rs similarity index 99% rename from packages/relayer/src/utils/command_templates.rs rename to packages/relayer/src/utils/subject_templates.rs index c742a323..e8ccd734 100644 --- a/packages/relayer/src/utils/command_templates.rs +++ b/packages/relayer/src/utils/subject_templates.rs @@ -49,7 +49,7 @@ impl TemplateValue { } } -pub fn extract_template_vals_from_command_template( +pub fn extract_template_vals_from_command( input: &str, templates: Vec, ) -> Result, anyhow::Error> { @@ -178,6 +178,7 @@ 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();