From 83c9667baf3cd735b55f9010385d0e79f7b3314a Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Wed, 7 Jan 2026 18:33:12 +0000 Subject: [PATCH 1/4] chore: add endpoints to filter round data by requester --- examples/CRISP/server/src/server/models.rs | 14 ++++++++ examples/CRISP/server/src/server/repo.rs | 33 +++++++++++++++++++ .../CRISP/server/src/server/routes/rounds.rs | 22 +++++++++++-- .../CRISP/server/src/server/routes/state.rs | 33 ++++++++++++++----- 4 files changed, 91 insertions(+), 11 deletions(-) diff --git a/examples/CRISP/server/src/server/models.rs b/examples/CRISP/server/src/server/models.rs index ae5001c807..6c2912295a 100644 --- a/examples/CRISP/server/src/server/models.rs +++ b/examples/CRISP/server/src/server/models.rs @@ -105,6 +105,18 @@ pub struct GetRoundRequest { pub round_id: u64, } +#[derive(Debug, Deserialize, Serialize)] +pub struct CurrentRoundRequestWithRequester { + #[serde(skip_serializing_if = "Option::is_none")] + pub requester: Option>, +} + +#[derive(Debug, Deserialize, Serialize)] +pub struct RoundRequestWithRequester { + #[serde(skip_serializing_if = "Option::is_none")] + pub requesters: Option>, +} + #[derive(Debug, Deserialize, Serialize)] pub struct PreviousCiphertextRequest { pub round_id: u64, @@ -156,6 +168,7 @@ pub struct WebResultRequest { pub option_2_emoji: String, pub total_votes: u64, pub end_time: u64, + pub requester: String, } #[derive(Debug, Deserialize, Serialize)] @@ -245,6 +258,7 @@ impl From for WebResultRequest { option_2_emoji: e3.emojis[1].clone(), total_votes: e3.vote_count, end_time: e3.expiration, + requester: e3.requester, } } } diff --git a/examples/CRISP/server/src/server/repo.rs b/examples/CRISP/server/src/server/repo.rs index 8919a24008..de9b79d205 100644 --- a/examples/CRISP/server/src/server/repo.rs +++ b/examples/CRISP/server/src/server/repo.rs @@ -40,9 +40,41 @@ impl CurrentRoundRepository { .get::(&key) .await .map_err(|_| eyre::eyre!("Could get e3 at '{key}'"))?; + Ok(round) } + /// Get the current (most recent) round for a specific requester + /// + /// # Arguments + /// * `requester` - The requester address to find the current round for + /// + /// # Returns + /// * The CurrentRound object for the most recent round by this requester, or None if not found + pub async fn get_current_round_for_requester(&self, requester: String) -> Result> { + // Get the current round count to iterate through all rounds + let round_count = self.get_current_round_id().await?; + + // Iterate backwards from the most recent round to find the latest one for this requester + for round_id in (0..=round_count).rev() { + let crisp_repo = CrispE3Repository::new(self.store.clone(), round_id); + + match crisp_repo.get_e3_state_lite().await { + Ok(state) => { + if state.requester == requester { + return Ok(Some(CurrentRound { id: round_id })); + } + } + Err(e) => { + info!("Error retrieving state for round {}: {:?}", round_id, e); + continue; + } + } + } + + Ok(None) + } + pub async fn get_current_round_id(&self) -> Result { let round = self .get_current_round() @@ -219,6 +251,7 @@ impl CrispE3Repository { option_2_emoji: e3_crisp.emojis[1].clone(), end_time: e3.expiration, total_votes: self.get_vote_count().await?, + requester: e3_crisp.requester, }) } diff --git a/examples/CRISP/server/src/server/routes/rounds.rs b/examples/CRISP/server/src/server/routes/rounds.rs index a7edb4ae1b..248bb1dceb 100644 --- a/examples/CRISP/server/src/server/routes/rounds.rs +++ b/examples/CRISP/server/src/server/routes/rounds.rs @@ -8,6 +8,7 @@ use crate::config::CONFIG; use crate::server::app_data::AppData; use crate::server::models::{ CTRequest, ComputeProviderParams, JsonResponse, PKRequest, RoundRequest, + RoundRequestWithRequester, }; use actix_web::{web, HttpResponse, Responder}; @@ -74,8 +75,25 @@ async fn request_new_round(data: web::Json) -> impl Responder { /// # Returns /// /// * A JSON response containing the current round -async fn get_current_round(store: web::Data) -> impl Responder { - match store.current_round().get_current_round().await { +async fn get_current_round( + data: web::Json, + store: web::Data, +) -> impl Responder { + let incoming = data.into_inner(); + + // Determine if we should filter by requester + let requester = incoming + .requesters + .and_then(|r| if r.is_empty() { None } else { Some(r[0].clone()) }); + + // Get the current round (either overall or for specific requester) + let result = if let Some(req) = requester { + store.current_round().get_current_round_for_requester(req).await + } else { + store.current_round().get_current_round().await + }; + + match result { Ok(Some(current_round)) => HttpResponse::Ok().json(current_round), Ok(None) => HttpResponse::NotFound().json(JsonResponse { response: "No current round found".to_string(), diff --git a/examples/CRISP/server/src/server/routes/state.rs b/examples/CRISP/server/src/server/routes/state.rs index 69a27e4f02..cea2ffbcff 100644 --- a/examples/CRISP/server/src/server/routes/state.rs +++ b/examples/CRISP/server/src/server/routes/state.rs @@ -7,15 +7,12 @@ use std::str::FromStr; use crate::server::{ - app_data::AppData, - models::{ - GetRoundRequest, IsSlotEmptyRequest, IsSlotEmptyResponse, PreviousCiphertextRequest, - PreviousCiphertextResponse, WebhookPayload, - }, - CONFIG, + CONFIG, app_data::AppData, models::{ + GetRoundRequest, IsSlotEmptyRequest, IsSlotEmptyResponse, PreviousCiphertextRequest, PreviousCiphertextResponse, RoundRequestWithRequester, WebhookPayload + } }; use actix_web::{web, HttpResponse, Responder}; -use alloy::primitives::{Address, Bytes, U256}; +use alloy::{primitives::{Address, Bytes, U256}, rlp::Encodable}; use e3_sdk::evm_helpers::contracts::{ EnclaveContract, EnclaveContractFactory, EnclaveWrite, ReadWrite, }; @@ -222,7 +219,9 @@ async fn get_round_result( /// # Returns /// /// * A JSON response containing the results for all rounds -async fn get_all_round_results(store: web::Data) -> impl Responder { +async fn get_all_round_results(data: web::Json::, store: web::Data) -> impl Responder { + let incoming = data.into_inner(); + let round_count = match store.current_round().get_current_round_id().await { Ok(count) => count, Err(e) => { @@ -233,10 +232,26 @@ async fn get_all_round_results(store: web::Data) -> impl Responder { let mut states = Vec::new(); + let requesters_array; + if let Some(requesters) = incoming.requesters { + requesters_array = requesters; + } else { + requesters_array = vec![]; + } + // FIXME: This assumes ids are ordered for i in 0..round_count + 1 { match store.e3(i).get_web_result_request().await { - Ok(w) => states.push(w), + Ok(w) => { + if requesters_array.length() > 0 { + // if we have any requesters to filter by, do it + if requesters_array.contains(&w.requester) { + states.push(w); + } + } else { + states.push(w); + } + } Err(e) => { info!("Error retrieving state for round {}: {:?}", i, e); continue; From 36172af090a8ffbfae26419615be67b8bc668bb3 Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Wed, 7 Jan 2026 21:14:34 +0000 Subject: [PATCH 2/4] chore: fix filtering in frontend --- examples/CRISP/client/.env.example | 2 ++ .../src/hooks/enclave/useEnclaveServer.ts | 6 ++++-- examples/CRISP/client/src/utils/constants.ts | 1 + examples/CRISP/server/src/server/models.rs | 9 +-------- .../CRISP/server/src/server/routes/rounds.rs | 19 ++++++++----------- .../CRISP/server/src/server/routes/state.rs | 14 ++++---------- 6 files changed, 20 insertions(+), 31 deletions(-) diff --git a/examples/CRISP/client/.env.example b/examples/CRISP/client/.env.example index 0a9c39b75e..569a701973 100644 --- a/examples/CRISP/client/.env.example +++ b/examples/CRISP/client/.env.example @@ -3,3 +3,5 @@ VITE_WALLETCONNECT_PROJECT_ID= # A token with a public mint function. Also used as the fee token for Enclave. # This is its default address in Hardhat node VITE_CRISP_TOKEN=0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0 +# Addresses of requesters for which to show rounds in the UI. Comma separated Ethereum addresses. +VITE_E3_REQUESTERS=0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266,0x70997970C51812dc3A010C7d01b50e0d17dc79C8 diff --git a/examples/CRISP/client/src/hooks/enclave/useEnclaveServer.ts b/examples/CRISP/client/src/hooks/enclave/useEnclaveServer.ts index 0d1190314e..bd05124f06 100644 --- a/examples/CRISP/client/src/hooks/enclave/useEnclaveServer.ts +++ b/examples/CRISP/client/src/hooks/enclave/useEnclaveServer.ts @@ -16,6 +16,7 @@ import { } from '@/model/vote.model' import { useApi } from '../generic/useFetchApi' import { PollRequestResult } from '@/model/poll.model' +import { ROUND_REQUESTERS } from '@/utils/constants' const ENCLAVE_API = import.meta.env.VITE_ENCLAVE_API @@ -35,10 +36,11 @@ const EnclaveEndpoints = { export const useEnclaveServer = () => { const { GetCurrentRound, GetWebAllResult, BroadcastVote, GetRoundStateLite, GetWebResult, GetVoteStatus } = EnclaveEndpoints const { fetchData, isLoading } = useApi() - const getCurrentRound = () => fetchData(GetCurrentRound) + const getCurrentRound = () => fetchData(GetCurrentRound, 'post', { requesters: ROUND_REQUESTERS }) const getRoundStateLite = (round_id: number) => fetchData(GetRoundStateLite, 'post', { round_id }) const broadcastVote = (vote: BroadcastVoteRequest) => fetchData(BroadcastVote, 'post', vote) - const getWebResult = () => fetchData(GetWebAllResult, 'get') + const getWebResult = () => + fetchData(GetWebAllResult, 'post', { requesters: ROUND_REQUESTERS }) const getWebResultByRound = (round_id: number) => fetchData(GetWebResult, 'post', { round_id }) const getVoteStatus = (request: VoteStatusRequest) => fetchData(GetVoteStatus, 'post', request) const getEligibleVoters = (round_id: number) => diff --git a/examples/CRISP/client/src/utils/constants.ts b/examples/CRISP/client/src/utils/constants.ts index 2ad8707314..8c09ecf2ff 100644 --- a/examples/CRISP/client/src/utils/constants.ts +++ b/examples/CRISP/client/src/utils/constants.ts @@ -5,3 +5,4 @@ // or FITNESS FOR A PARTICULAR PURPOSE. export const ROUND_TOKEN = import.meta.env.VITE_CRISP_TOKEN +export const ROUND_REQUESTERS = import.meta.env.VITE_E3_REQUESTERS ? import.meta.env.VITE_E3_REQUESTERS.split(',') : [] diff --git a/examples/CRISP/server/src/server/models.rs b/examples/CRISP/server/src/server/models.rs index 6c2912295a..c7c05f6e56 100644 --- a/examples/CRISP/server/src/server/models.rs +++ b/examples/CRISP/server/src/server/models.rs @@ -105,16 +105,9 @@ pub struct GetRoundRequest { pub round_id: u64, } -#[derive(Debug, Deserialize, Serialize)] -pub struct CurrentRoundRequestWithRequester { - #[serde(skip_serializing_if = "Option::is_none")] - pub requester: Option>, -} - #[derive(Debug, Deserialize, Serialize)] pub struct RoundRequestWithRequester { - #[serde(skip_serializing_if = "Option::is_none")] - pub requesters: Option>, + pub requesters: Vec, } #[derive(Debug, Deserialize, Serialize)] diff --git a/examples/CRISP/server/src/server/routes/rounds.rs b/examples/CRISP/server/src/server/routes/rounds.rs index 248bb1dceb..976d27d079 100644 --- a/examples/CRISP/server/src/server/routes/rounds.rs +++ b/examples/CRISP/server/src/server/routes/rounds.rs @@ -7,8 +7,7 @@ use crate::config::CONFIG; use crate::server::app_data::AppData; use crate::server::models::{ - CTRequest, ComputeProviderParams, JsonResponse, PKRequest, RoundRequest, - RoundRequestWithRequester, + CTRequest, ComputeProviderParams, JsonResponse, PKRequest, RoundRequest, RoundRequestWithRequester }; use actix_web::{web, HttpResponse, Responder}; @@ -23,7 +22,7 @@ use log::{error, info}; pub fn setup_routes(config: &mut web::ServiceConfig) { config.service( web::scope("/rounds") - .route("/current", web::get().to(get_current_round)) + .route("/current", web::post().to(get_current_round)) .route("/public-key", web::post().to(get_public_key)) .route("/ciphertext", web::post().to(get_ciphertext)) .route("/request", web::post().to(request_new_round)), @@ -81,15 +80,13 @@ async fn get_current_round( ) -> impl Responder { let incoming = data.into_inner(); - // Determine if we should filter by requester - let requester = incoming - .requesters - .and_then(|r| if r.is_empty() { None } else { Some(r[0].clone()) }); - - // Get the current round (either overall or for specific requester) - let result = if let Some(req) = requester { - store.current_round().get_current_round_for_requester(req).await + // Get the first requester if any exist + // .get(0) returns Option<&String>, so we need to handle that + let result = if let Some(requester) = incoming.requesters.get(0) { + // We have a requester, filter by it + store.current_round().get_current_round_for_requester(requester.clone()).await } else { + // No requester provided (empty array) store.current_round().get_current_round().await }; diff --git a/examples/CRISP/server/src/server/routes/state.rs b/examples/CRISP/server/src/server/routes/state.rs index cea2ffbcff..7778d9ebdd 100644 --- a/examples/CRISP/server/src/server/routes/state.rs +++ b/examples/CRISP/server/src/server/routes/state.rs @@ -23,7 +23,7 @@ pub fn setup_routes(config: &mut web::ServiceConfig) { config.service( web::scope("/state") .route("/result", web::post().to(get_round_result)) - .route("/all", web::get().to(get_all_round_results)) + .route("/all", web::post().to(get_all_round_results)) .route("/lite", web::post().to(get_round_state_lite)) // Do we need protection on this endpoint? technically they would need to send a valid proof for it to // be included on chain @@ -231,21 +231,15 @@ async fn get_all_round_results(data: web::Json::, sto }; let mut states = Vec::new(); - - let requesters_array; - if let Some(requesters) = incoming.requesters { - requesters_array = requesters; - } else { - requesters_array = vec![]; - } + let requesters = incoming.requesters; // FIXME: This assumes ids are ordered for i in 0..round_count + 1 { match store.e3(i).get_web_result_request().await { Ok(w) => { - if requesters_array.length() > 0 { + if !requesters.is_empty() { // if we have any requesters to filter by, do it - if requesters_array.contains(&w.requester) { + if requesters.contains(&w.requester) { states.push(w); } } else { From fb4bf0f4d92c5561e6c28651216758efb82169af Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Wed, 7 Jan 2026 21:25:17 +0000 Subject: [PATCH 3/4] chore: pr comments --- examples/CRISP/client/src/utils/constants.ts | 4 +++- examples/CRISP/server/src/server/routes/state.rs | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/examples/CRISP/client/src/utils/constants.ts b/examples/CRISP/client/src/utils/constants.ts index 8c09ecf2ff..630c778fa3 100644 --- a/examples/CRISP/client/src/utils/constants.ts +++ b/examples/CRISP/client/src/utils/constants.ts @@ -5,4 +5,6 @@ // or FITNESS FOR A PARTICULAR PURPOSE. export const ROUND_TOKEN = import.meta.env.VITE_CRISP_TOKEN -export const ROUND_REQUESTERS = import.meta.env.VITE_E3_REQUESTERS ? import.meta.env.VITE_E3_REQUESTERS.split(',') : [] +export const ROUND_REQUESTERS = import.meta.env.VITE_E3_REQUESTERS + ? import.meta.env.VITE_E3_REQUESTERS.split(',').map((s: string) => s.trim()) + : [] diff --git a/examples/CRISP/server/src/server/routes/state.rs b/examples/CRISP/server/src/server/routes/state.rs index 7778d9ebdd..384bfd9209 100644 --- a/examples/CRISP/server/src/server/routes/state.rs +++ b/examples/CRISP/server/src/server/routes/state.rs @@ -12,7 +12,7 @@ use crate::server::{ } }; use actix_web::{web, HttpResponse, Responder}; -use alloy::{primitives::{Address, Bytes, U256}, rlp::Encodable}; +use alloy::{primitives::{Address, Bytes, U256}}; use e3_sdk::evm_helpers::contracts::{ EnclaveContract, EnclaveContractFactory, EnclaveWrite, ReadWrite, }; From 8ed5fabc124c99e46b3809df903f2c3389bdada5 Mon Sep 17 00:00:00 2001 From: ctrlc03 <93448202+ctrlc03@users.noreply.github.com> Date: Wed, 7 Jan 2026 22:16:10 +0000 Subject: [PATCH 4/4] chore: ensure we copy client env --- examples/CRISP/scripts/dev_client.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/CRISP/scripts/dev_client.sh b/examples/CRISP/scripts/dev_client.sh index e78886cadc..70def73c98 100755 --- a/examples/CRISP/scripts/dev_client.sh +++ b/examples/CRISP/scripts/dev_client.sh @@ -4,4 +4,4 @@ set -euo pipefail echo "CLIENT SCRIPT RUNNING..." -(cd ./client && pnpm dev-static) +(cd ./client && if [[ ! -f .env ]]; then cp .env.example .env; fi && pnpm dev-static)