-
Notifications
You must be signed in to change notification settings - Fork 396
feat(aggregation-mode): receipt endpoint in agg mode batcher #2188
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 54 commits
7e0f1a0
a24e6de
bdea05c
fe53ac3
4f48a7f
770601d
8c78c41
26d8ab7
b9abb7d
d3adcf8
fcb5426
c987d70
23ba2f4
79f9460
ea80262
8a76723
e68cbb5
0f1c0d9
b67367d
edfa55b
33c8bb9
7e52fa8
b79bd9f
37ad00c
555be57
31230f1
ad969e4
d9847df
a88dd3d
da5dce6
7d6425e
e16d14c
7d8e37a
ccc95e4
b29e4d7
15f1bc8
dc4c9a0
25ad0f6
d91c748
f4e8143
6be7129
e61073b
1e7006b
7ae7cae
1ccd9d2
73a0ec2
26ed7a8
8e37e1f
aa714ac
2305a4c
13d985e
8bca829
b4a3da4
c3017eb
b4e2a76
6383138
cf2be49
49c440c
fa23887
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -14,13 +14,13 @@ use sqlx::types::BigDecimal; | |
|
|
||
| use super::{ | ||
| helpers::format_merkle_path, | ||
| types::{AppResponse, GetProofMerklePathQueryParams}, | ||
| types::{AppResponse, GetReceiptsQueryParams}, | ||
| }; | ||
|
|
||
| use crate::{ | ||
| config::Config, | ||
| db::Db, | ||
| server::types::{SubmitProofRequestRisc0, SubmitProofRequestSP1}, | ||
| server::types::{GetReceiptsResponse, SubmitProofRequestRisc0, SubmitProofRequestSP1}, | ||
| verifiers::{verify_sp1_proof, VerificationError}, | ||
| }; | ||
|
|
||
|
|
@@ -35,6 +35,14 @@ impl BatcherServer { | |
| Self { db, config } | ||
| } | ||
|
|
||
| fn is_valid_eth_address(addr: &str) -> bool { | ||
| let addr = addr.strip_prefix("0x").unwrap_or(addr); | ||
| if addr.len() != 40 { | ||
| return false; | ||
| } | ||
| addr.chars().all(|c| c.is_ascii_hexdigit()) | ||
| } | ||
|
|
||
| pub async fn start(&self) { | ||
| // Note: BatcherServer is thread safe so we can just clone it (no need to add mutexes) | ||
| let port = self.config.port; | ||
|
|
@@ -45,7 +53,7 @@ impl BatcherServer { | |
| App::new() | ||
| .app_data(Data::new(state.clone())) | ||
| .route("/nonce/{address}", web::get().to(Self::get_nonce)) | ||
| .route("/proof/merkle", web::get().to(Self::get_proof_merkle_path)) | ||
| .route("/receipts", web::get().to(Self::get_receipts)) | ||
| .route("/proof/sp1", web::post().to(Self::post_proof_sp1)) | ||
| .route("/proof/risc0", web::post().to(Self::post_proof_risc0)) | ||
| }) | ||
|
|
@@ -56,20 +64,28 @@ impl BatcherServer { | |
| .expect("Server to never end"); | ||
| } | ||
|
|
||
| // Returns the nonce (number of submitted tasks) for a given address | ||
| async fn get_nonce(req: HttpRequest) -> impl Responder { | ||
| let Some(address) = req.match_info().get("address") else { | ||
| let Some(address_raw) = req.match_info().get("address") else { | ||
| return HttpResponse::BadRequest() | ||
| .json(AppResponse::new_unsucessfull("Missing address", 400)); | ||
| }; | ||
|
|
||
| if !Self::is_valid_eth_address(address_raw) { | ||
| return HttpResponse::BadRequest() | ||
| .json(AppResponse::new_unsucessfull("Invalid address", 400)); | ||
| } | ||
|
|
||
|
MarcosNicolau marked this conversation as resolved.
Outdated
|
||
| let address = address_raw.to_lowercase(); | ||
|
|
||
| // TODO: validate valid ethereum address | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Either this comment should be updated or removed, if the signature is not checked it could be changed to Validate signature |
||
| let Some(state) = req.app_data::<Data<BatcherServer>>() else { | ||
| return HttpResponse::InternalServerError() | ||
| .json(AppResponse::new_unsucessfull("Internal server error", 500)); | ||
| }; | ||
|
|
||
| let state = state.get_ref(); | ||
| match state.db.count_tasks_by_address(address).await { | ||
| match state.db.count_tasks_by_address(&address).await { | ||
| Ok(count) => HttpResponse::Ok().json(AppResponse::new_sucessfull(serde_json::json!( | ||
| { | ||
| "nonce": count | ||
|
|
@@ -80,6 +96,7 @@ impl BatcherServer { | |
| } | ||
| } | ||
|
|
||
| // Posts an SP1 proof to the batcher, recovering the address from the signature | ||
| async fn post_proof_sp1( | ||
| req: HttpRequest, | ||
| MultipartForm(data): MultipartForm<SubmitProofRequestSP1>, | ||
|
|
@@ -172,6 +189,7 @@ impl BatcherServer { | |
| &proof_content, | ||
| &vk_content, | ||
| None, | ||
| data.nonce.0 as i64, | ||
| ) | ||
| .await | ||
| { | ||
|
|
@@ -184,67 +202,88 @@ impl BatcherServer { | |
| } | ||
|
|
||
| /// TODO: complete for risc0 (see `post_proof_sp1`) | ||
| // Posts a Risc0 proof to the batcher, recovering the address from the signature | ||
| async fn post_proof_risc0( | ||
| _req: HttpRequest, | ||
| MultipartForm(_): MultipartForm<SubmitProofRequestRisc0>, | ||
| ) -> impl Responder { | ||
| HttpResponse::Ok().json(AppResponse::new_sucessfull(serde_json::json!({}))) | ||
| } | ||
|
|
||
| async fn get_proof_merkle_path( | ||
| // Returns the last 100 receipt merkle proofs for the address received in the URL. | ||
| // In case of also receiving a nonce on the query param, it returns only the merkle proof for that nonce. | ||
| async fn get_receipts( | ||
| req: HttpRequest, | ||
| params: web::Query<GetProofMerklePathQueryParams>, | ||
| params: web::Query<GetReceiptsQueryParams>, | ||
| ) -> impl Responder { | ||
| let Some(state) = req.app_data::<Data<BatcherServer>>() else { | ||
| return HttpResponse::InternalServerError() | ||
| .json(AppResponse::new_unsucessfull("Internal server error", 500)); | ||
| return HttpResponse::InternalServerError().json(AppResponse::new_unsucessfull( | ||
| "Internal server error: Failed to get app data", | ||
| 500, | ||
| )); | ||
| }; | ||
|
|
||
| let state = state.get_ref(); | ||
|
|
||
| // TODO: maybe also accept proof commitment in query param | ||
| let Some(id) = params.id.clone() else { | ||
| return HttpResponse::BadRequest().json(AppResponse::new_unsucessfull( | ||
| "Provide task `id` query param", | ||
| 400, | ||
| )); | ||
| if !Self::is_valid_eth_address(¶ms.address.clone()) { | ||
| return HttpResponse::BadRequest() | ||
| .json(AppResponse::new_unsucessfull("Invalid address", 400)); | ||
| } | ||
|
|
||
| let limit = match params.limit { | ||
| Some(received_limit) => received_limit.min(100), | ||
| None => 100, | ||
| }; | ||
|
|
||
| if id.is_empty() { | ||
| return HttpResponse::BadRequest().json(AppResponse::new_unsucessfull( | ||
| "Proof id cannot be empty", | ||
| 400, | ||
| )); | ||
| } | ||
| let address = params.address.to_lowercase(); | ||
|
|
||
| let query = if let Some(nonce) = params.nonce { | ||
| state | ||
| .db | ||
| .get_tasks_by_address_and_nonce(&address, nonce) | ||
| .await | ||
| } else { | ||
| state | ||
| .db | ||
| .get_tasks_by_address_with_limit(&address, limit) | ||
| .await | ||
| }; | ||
|
|
||
| let Ok(proof_id) = sqlx::types::Uuid::parse_str(&id) else { | ||
| return HttpResponse::BadRequest() | ||
| .json(AppResponse::new_unsucessfull("Proof id invalid uuid", 400)); | ||
| let Ok(receipts) = query else { | ||
| return HttpResponse::InternalServerError().json(AppResponse::new_unsucessfull( | ||
| "Internal server error: Failed to get tasks by address and nonce", | ||
| 500, | ||
| )); | ||
| }; | ||
|
|
||
| let db_result = state.db.get_merkle_path_by_task_id(proof_id).await; | ||
| let merkle_path = match db_result { | ||
| Ok(Some(merkle_path)) => merkle_path, | ||
| Ok(None) => { | ||
| return HttpResponse::NotFound().json(AppResponse::new_unsucessfull( | ||
| "Proof merkle path not found", | ||
| 404, | ||
| )) | ||
| } | ||
| Err(_) => { | ||
| let mut responses: Vec<GetReceiptsResponse> = Vec::new(); | ||
| for receipt in receipts { | ||
|
MarcosNicolau marked this conversation as resolved.
Outdated
|
||
| let Some(merkle_path) = receipt.merkle_path else { | ||
| responses.push(GetReceiptsResponse { | ||
| status: receipt.status, | ||
| merkle_path: Vec::new(), | ||
| nonce: receipt.nonce, | ||
| address: receipt.address, | ||
| }); | ||
|
|
||
| continue; | ||
| }; | ||
|
|
||
| let Ok(formatted_merkle_path) = format_merkle_path(&merkle_path) else { | ||
| return HttpResponse::InternalServerError() | ||
| .json(AppResponse::new_unsucessfull("Internal server error", 500)); | ||
| } | ||
| }; | ||
| }; | ||
|
|
||
| match format_merkle_path(&merkle_path) { | ||
| Ok(merkle_path) => { | ||
| HttpResponse::Ok().json(AppResponse::new_sucessfull(serde_json::json!({ | ||
| "merkle_path": merkle_path | ||
| }))) | ||
| } | ||
| Err(_) => HttpResponse::InternalServerError() | ||
| .json(AppResponse::new_unsucessfull("Internal server error", 500)), | ||
| responses.push(GetReceiptsResponse { | ||
| status: receipt.status, | ||
| merkle_path: formatted_merkle_path, | ||
| nonce: receipt.nonce, | ||
| address: receipt.address, | ||
| }); | ||
| } | ||
|
|
||
| HttpResponse::Ok().json(AppResponse::new_sucessfull(serde_json::json!({ | ||
| "receipts": responses | ||
| }))) | ||
| } | ||
| } | ||
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will get removed after #2186 as it encapsulates all db tables in one common shared file