diff --git a/crates/delegator/src/api.rs b/crates/delegator/src/api.rs index 0418192..17b008a 100644 --- a/crates/delegator/src/api.rs +++ b/crates/delegator/src/api.rs @@ -6,7 +6,7 @@ use axum::{ }; use futures::StreamExt; use hyper::StatusCode; -use libp2p::{kad, PeerId}; +use libp2p::kad; use serde::{Deserialize, Serialize}; use std::hash::{DefaultHasher, Hash, Hasher}; use std::{io, time::Duration}; @@ -39,7 +39,7 @@ pub struct DelegateRequest { #[derive(Debug, Serialize)] pub struct DelegateResponse { - job_hash: kad::RecordKey, + job_key: String, } pub async fn deletage_handler( @@ -49,19 +49,19 @@ pub async fn deletage_handler( let job_data = JobData::new(input.pie); let job_data_hash = kad::RecordKey::new(&hash!(job_data).to_be_bytes()); state.delegate_tx.send(job_data).await.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; - Ok(Json(DelegateResponse { job_hash: job_data_hash })) + Ok(Json(DelegateResponse { job_key: hex::encode(job_data_hash) })) } #[derive(Debug, Deserialize)] pub struct JobEventsRequest { - job_hash: kad::RecordKey, + job_key: String, } #[derive(Debug, Serialize)] #[serde(tag = "type", content = "data")] pub enum JobEventsResponse { - BidReceived(PeerId), - Delegated(PeerId), + BidReceived(String), + Delegated(String), Finished(Vec), } @@ -70,16 +70,19 @@ pub async fn job_events_handler( Query(input): Query, ) -> Sse>> { let stream = stream! { - let job_hash = input.job_hash; + let job_key = kad::RecordKey::new( + &hex::decode(input.job_key) + .map_err(|e| io::Error::new(io::ErrorKind::BrokenPipe, e.to_string()))? + ); loop { tokio::select! { - Ok((hash, event)) = state.events_rx.recv() => { - if hash == job_hash { + Ok((key, event)) = state.events_rx.recv() => { + if key == job_key { yield Event::default() .json_data( match event { - DelegatorEvent::BidReceived(peer_id) => { JobEventsResponse::BidReceived(peer_id) }, - DelegatorEvent::Delegated(peer_id) => { JobEventsResponse::Delegated(peer_id) }, + DelegatorEvent::BidReceived(peer_id) => { JobEventsResponse::BidReceived(peer_id.to_base58()) }, + DelegatorEvent::Delegated(peer_id) => { JobEventsResponse::Delegated(peer_id.to_base58()) }, DelegatorEvent::Finished(data) => { JobEventsResponse::Finished(data) }, } ) diff --git a/crates/peer/src/swarm.rs b/crates/peer/src/swarm.rs index dc649d0..ff90649 100644 --- a/crates/peer/src/swarm.rs +++ b/crates/peer/src/swarm.rs @@ -3,8 +3,8 @@ use futures::stream::Stream; use libp2p::futures::StreamExt; use libp2p::gossipsub::{self, IdentTopic, TopicHash}; use libp2p::identity::Keypair; -use libp2p::kad::store::MemoryStore; -use libp2p::kad::Mode; +use libp2p::kad::store::{MemoryStore, MemoryStoreConfig}; +use libp2p::kad::{Config, Mode}; use libp2p::swarm::{DialError, NetworkBehaviour, SwarmEvent}; use libp2p::{kad, noise, tcp, yamux, Multiaddr, Swarm, SwarmBuilder}; use serde::{Deserialize, Serialize}; @@ -92,6 +92,8 @@ impl SwarmRunner { p2p_keypair: Keypair, p2p_multiaddr: Multiaddr, ) -> Result> { + let mut config = Config::default(); + config.set_max_packet_size(1024*1024*100); let mut swarm = SwarmBuilder::with_existing_identity(p2p_keypair) .with_tokio() .with_tcp( @@ -101,13 +103,20 @@ impl SwarmRunner { )? .with_quic() .with_behaviour(|p2p_keypair| PeerBehaviour { - kademlia: kad::Behaviour::new( + kademlia: kad::Behaviour::with_config( p2p_keypair.public().to_peer_id(), - MemoryStore::new(p2p_keypair.public().to_peer_id()), + MemoryStore::with_config( + p2p_keypair.public().to_peer_id(), + MemoryStoreConfig { + max_value_bytes: 1024*1024*100, + ..Default::default() + }, + ), + config ), gossipsub: Self::init_gossip(p2p_keypair).unwrap(), })? - .with_swarm_config(|c| c.with_idle_connection_timeout(Duration::from_secs(60))) + .with_swarm_config(|c| c.with_idle_connection_timeout(Duration::from_secs(10))) .build(); swarm.behaviour_mut().gossipsub.subscribe(&IdentTopic::new(Topic::Networking.as_str()))?; @@ -163,7 +172,7 @@ impl SwarmRunner { let record = kad::Record { key: kad::RecordKey::new(&key), value: data, - publisher: Some(*self.swarm.local_peer_id()), + publisher: None, expires: None, }; if let Err(e) = self.swarm.behaviour_mut().kademlia.put_record(record, kad::Quorum::One) { diff --git a/crates/prover/src/stone_prover/mod.rs b/crates/prover/src/stone_prover/mod.rs index 4986858..358100d 100644 --- a/crates/prover/src/stone_prover/mod.rs +++ b/crates/prover/src/stone_prover/mod.rs @@ -124,10 +124,10 @@ pub fn params(n_steps: u64) -> Params { ) .collect(), last_layer_degree_bound, - n_queries: 1, - proof_of_work_bits: 1, + n_queries: 20, + proof_of_work_bits: 30, }, - log_n_cosets: 1, + log_n_cosets: 2, }, ..Default::default() } diff --git a/dashboard/package-lock.json b/dashboard/package-lock.json index 7c676c9..c52bcd8 100644 --- a/dashboard/package-lock.json +++ b/dashboard/package-lock.json @@ -16,6 +16,18 @@ "react": "^18", "react-dom": "^18", "react-dropzone": "^14.2.3", + "swiftness-dex-blake2s": "^0.0.7", + "swiftness-dex-keccak": "^0.0.7", + "swiftness-recursive-blake2s": "^0.0.7", + "swiftness-recursive-keccak": "^0.0.7", + "swiftness-recursive-with-poseidon-blake2s": "^0.0.7", + "swiftness-recursive-with-poseidon-keccak": "^0.0.7", + "swiftness-small-blake2s": "^0.0.7", + "swiftness-small-keccak": "^0.0.7", + "swiftness-starknet-blake2s": "^0.0.7", + "swiftness-starknet-keccak": "^0.0.7", + "swiftness-starknet-with-keccak-blake2s": "^0.0.7", + "swiftness-starknet-with-keccak-keccak": "^0.0.7", "zod": "^3.23.8" }, "devDependencies": { @@ -5282,6 +5294,66 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/swiftness-dex-blake2s": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/swiftness-dex-blake2s/-/swiftness-dex-blake2s-0.0.7.tgz", + "integrity": "sha512-jGYc9emS65RLK/6+iPtjKT1j2lEKKkYAMM/hGT87Idd46AuYlePEL8Qjn8mUljkoTz5G3eu5X+Z+Rs2fXGoiYQ==" + }, + "node_modules/swiftness-dex-keccak": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/swiftness-dex-keccak/-/swiftness-dex-keccak-0.0.7.tgz", + "integrity": "sha512-WhR8jo08RptvnJabTiqpwcAslXRQmjHpImHpOx+V8E7u82AXUMNuF4Q5cHx9+KY4U8G7eLVXJpNGUwr1SDbsyg==" + }, + "node_modules/swiftness-recursive-blake2s": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/swiftness-recursive-blake2s/-/swiftness-recursive-blake2s-0.0.7.tgz", + "integrity": "sha512-aqVJhrkcjJjD96tBTvLctVSJGzVbBks1ahmIwTtfhtD30vJ58IlW+gmHF0lXaea475holswbYhBBVF8vXIMcfA==" + }, + "node_modules/swiftness-recursive-keccak": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/swiftness-recursive-keccak/-/swiftness-recursive-keccak-0.0.7.tgz", + "integrity": "sha512-UO2A3iVkdvWhl5JgGt+B1c+tuJR1Xnx42pQjrWiFcTPkmxHCJo2MMfopz4H9mYe/Vj04S2dCUl9S8/bdkmJcdA==" + }, + "node_modules/swiftness-recursive-with-poseidon-blake2s": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/swiftness-recursive-with-poseidon-blake2s/-/swiftness-recursive-with-poseidon-blake2s-0.0.7.tgz", + "integrity": "sha512-mWhi2c4PPU0ANTPrDl+CIWNJIdo3GoGKXsfEbngcRh/2rHm+Ufc+6JnldNVP7FRc7Dr58Ym7pwmhbbZzXRRf5w==" + }, + "node_modules/swiftness-recursive-with-poseidon-keccak": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/swiftness-recursive-with-poseidon-keccak/-/swiftness-recursive-with-poseidon-keccak-0.0.7.tgz", + "integrity": "sha512-QEXte36px+sbcUZtPHilGk/qyFUKt1NsBU81T892N/m9TDccxvG2fGM9cObNMuRscKy8fsdMppA8AQcC88NBgA==" + }, + "node_modules/swiftness-small-blake2s": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/swiftness-small-blake2s/-/swiftness-small-blake2s-0.0.7.tgz", + "integrity": "sha512-bVKFaBlG1wMXc5dTFthivgMwg8H5LzStifjme3cA51198NfPCaEZv8vDTa+pvMa1iSgkM0jInK3VVW652FuABw==" + }, + "node_modules/swiftness-small-keccak": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/swiftness-small-keccak/-/swiftness-small-keccak-0.0.7.tgz", + "integrity": "sha512-gceslp9dpsSJxEqUn7E1uosyxB3ITKoWmCrcGPmIysB8Ff7/L1D8UxG4F5gsAdMsz5pdhAs0qKjMHl85953rhg==" + }, + "node_modules/swiftness-starknet-blake2s": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/swiftness-starknet-blake2s/-/swiftness-starknet-blake2s-0.0.7.tgz", + "integrity": "sha512-4FCoVxuNLgdwFMOcfvYsCjnHy8XpODlIpScgaAtBtDkNK3X4i+PwtJSxKDEIf++jIfJJXQL0Wz/IL25pyPGg7Q==" + }, + "node_modules/swiftness-starknet-keccak": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/swiftness-starknet-keccak/-/swiftness-starknet-keccak-0.0.7.tgz", + "integrity": "sha512-dU4UB10CGq/Me4ZGdSK+IegZPLsEEruHO+2lxrqScaVhi+Kpkkp4IYBRbMfwEeSA8ppISZR7MGzxujrLCqzCLQ==" + }, + "node_modules/swiftness-starknet-with-keccak-blake2s": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/swiftness-starknet-with-keccak-blake2s/-/swiftness-starknet-with-keccak-blake2s-0.0.7.tgz", + "integrity": "sha512-Eeok1U3qbRDpJKL78WVwRo43x/C8dngnHzQl4CV5ILWdU3WIWPFjDilR7PeoVUTmGUOyUORYTyH/txA768UnfQ==" + }, + "node_modules/swiftness-starknet-with-keccak-keccak": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/swiftness-starknet-with-keccak-keccak/-/swiftness-starknet-with-keccak-keccak-0.0.7.tgz", + "integrity": "sha512-2axDJTodoxTRXSAqiBie84woX5/WwC14Rf9g7lK9A7sQkmMyDZSkEuqUIIsP6l6PmqYUjkXIJGOMrNkP7YW4uA==" + }, "node_modules/tailwindcss": { "version": "3.4.4", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.4.tgz", diff --git a/dashboard/package.json b/dashboard/package.json index 46b0c5a..510dbbd 100644 --- a/dashboard/package.json +++ b/dashboard/package.json @@ -18,7 +18,19 @@ "react": "^18", "react-dom": "^18", "react-dropzone": "^14.2.3", - "zod": "^3.23.8" + "zod": "^3.23.8", + "swiftness-dex-blake2s": "^0.0.7", + "swiftness-dex-keccak": "^0.0.7", + "swiftness-recursive-blake2s": "^0.0.7", + "swiftness-recursive-keccak": "^0.0.7", + "swiftness-recursive-with-poseidon-blake2s": "^0.0.7", + "swiftness-recursive-with-poseidon-keccak": "^0.0.7", + "swiftness-small-blake2s": "^0.0.7", + "swiftness-small-keccak": "^0.0.7", + "swiftness-starknet-blake2s": "^0.0.7", + "swiftness-starknet-keccak": "^0.0.7", + "swiftness-starknet-with-keccak-blake2s": "^0.0.7", + "swiftness-starknet-with-keccak-keccak": "^0.0.7" }, "devDependencies": { "@types/node": "^20", diff --git a/dashboard/src/app/api.ts b/dashboard/src/app/api.ts index b7087d3..25733cb 100644 --- a/dashboard/src/app/api.ts +++ b/dashboard/src/app/api.ts @@ -1,31 +1,36 @@ import { z } from "zod"; +const hexStringSchema = z.string().regex(/^[0-9a-fA-F]+$/, 'Invalid hex string'); +const base58Pattern = /^[123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]+$/; +const base58Schema = z.string().regex(base58Pattern, 'Invalid Base58 string'); +const bytesSchema = z.array(z.number()); + // Zod for DelegateRequest export const DelegateRequest = z.object({ - pie: z.array(z.number()), + pie: bytesSchema, }); export type DelegateRequest = z.infer; // Zod for DelegateResponse export const DelegateResponse = z.object({ - job_hash: z.coerce.bigint(), + job_key: hexStringSchema }); export type DelegateResponse = z.infer; // Zod for JobEventsRequest export const JobEventsRequest = z.object({ - job_hash: z.coerce.bigint(), + job_key: hexStringSchema }); export type JobEventsRequest = z.infer; export const JobEventsResponse = z.object({ - type: z.literal("Finished"), + type: z.literal("Finished").or(z.literal("Delegated")).or(z.literal("BidReceived")), data: z.any(), }); export type JobEventsResponse = z.infer; -export const JobHash = z.coerce.bigint(); -export type JobHash = z.infer; - -export const Proof = z.array(z.number()); +export const Proof = bytesSchema; export type Proof = z.infer; + +export const PeerId = base58Schema; +export type PeerId = z.infer; \ No newline at end of file diff --git a/dashboard/src/app/globals.css b/dashboard/src/app/globals.css index 0af548e..52df725 100644 --- a/dashboard/src/app/globals.css +++ b/dashboard/src/app/globals.css @@ -51,3 +51,13 @@ body { filter: blur(5px); z-index: -1; /* Ensure the image is behind any content in .background */ } + +/* Hide scrollbar for WebKit browsers (Chrome, Safari) */ +.scroll-container::-webkit-scrollbar { + display: none; /* Hide scrollbar */ +} + +/* Hide scrollbar for Firefox */ +.scroll-container { + scrollbar-width: none; /* Hide scrollbar */ +} diff --git a/dashboard/src/app/page.tsx b/dashboard/src/app/page.tsx index 80973be..1216a41 100644 --- a/dashboard/src/app/page.tsx +++ b/dashboard/src/app/page.tsx @@ -16,14 +16,18 @@ import { DelegateRequest, DelegateResponse, JobEventsResponse, + PeerId, Proof, } from "./api"; -import { useState } from "react"; +import { useEffect, useRef, useState } from "react"; import subscribeEvents from "./subscribeEvents"; -import assert from "assert"; +import { WorkerMessage, WorkerResponse } from "@/utils/types"; +import { matchCommitment, matchLayout } from "@/utils/loadModule"; const steps = [ "Job propagated to network", + "Job bidding", + "Job delegated", "Proof received", ]; @@ -80,7 +84,9 @@ function QontoStepIcon(props: StepIconProps) { ); } + export default function Home() { + const workerRef = useRef(); const darkTheme = createTheme({ palette: { mode: "dark", @@ -88,6 +94,58 @@ export default function Home() { }, }); + // Function to add a new log entry + const addLog = (message: string) => { + const now = new Date(); + const timestamp = now.toLocaleString(); // Get current date and time as a string + const logEntry = ( +
+ LOG: {timestamp} - {message} +
+ ); + setLogs((prevLogs) => [...prevLogs, logEntry]); + }; + + const verifyProof = async (proof: string) => { + const parsedProof = JSON.parse(proof); + + const layout = matchLayout(parsedProof.public_input.layout); + const commitment = matchCommitment(parsedProof.proof_parameters.pow_hash); + + workerRef.current = new Worker(new URL("../worker.ts", import.meta.url), { + type: "module", + }); + + workerRef.current.onmessage = (event: MessageEvent) => { + const { programHash, programOutput, error } = event.data; + + if (error) { + console.error(error); + addLog("Verification Failed"); + setButtonColor("error"); + } else { + addLog(`programHash: ${programHash}`); + addLog(`programOutput: ${programOutput}`); + addLog(`layout: ${layout}`); + addLog(`commitment: ${commitment}`); + addLog("Proof Verified"); + setButtonColor("success"); + } + + workerRef.current?.terminate(); + }; + + if (layout && commitment) { + const message: WorkerMessage = { + proof, + layout, + commitment, + }; + + workerRef.current.postMessage(message); + } + }; + const ondrop = ( acceptedFiles: T[], _fileRejections: FileRejection[], @@ -104,8 +162,6 @@ export default function Home() { pie: Array.from(fileBytes), }); - console.log(requestBody); - let subscriber: EventSource | null = null; try { @@ -127,25 +183,36 @@ export default function Home() { const data: DelegateResponse = DelegateResponse.parse( await response.json(), ); - console.log("Job hash:", data.job_hash); - + addLog(`Job ${data.job_key} sent to the p2p network`) setActiveStep(1); - setIsProcessing(data.job_hash); + setIsProcessing(data.job_key); subscriber = subscribeEvents( `${process.env.NEXT_PUBLIC_API_URL}/job_events`, - `job_hash=${data.job_hash.toString()}`, - (event) => { + `job_key=${data.job_key.toString()}`, + async (event) => { let job_event = JobEventsResponse.parse(event); + if (job_event.type == "BidReceived") { + let peer_id = PeerId.parse(job_event.data); + addLog(`Recived bid for job ${data.job_key} from peer ${peer_id}`) + setActiveStep(2); + } + if (job_event.type == "Delegated") { + let peer_id = PeerId.parse(job_event.data); + addLog(`Job ${data.job_key} delegated to peer ${peer_id}`) + setActiveStep(3); + } if (job_event.type == "Finished") { let proof = Proof.parse(job_event.data); - setActiveStep(3); + addLog(`Job ${data.job_key} proof received`) + setActiveStep(4); setDownloadBlob([ new Blob([new Uint8Array(proof)]), - `${data.job_hash}_proof.json`, + `${data.job_key}_proof.json`, ]); setIsProcessing(null); subscriber?.close(); + await verifyProof(new TextDecoder().decode(new Uint8Array(job_event.data))) } }, ); @@ -160,14 +227,32 @@ export default function Home() { reader.readAsArrayBuffer(file); }; - const [isProcessing, setIsProcessing] = useState(null); + const [isProcessing, setIsProcessing] = useState(null); + const [logs, setLogs] = useState([]); const [activeStep, setActiveStep] = useState(0); const [downloadBlob, setDownloadBlob] = useState<[Blob, string] | null>(null); + const [buttonColor, setButtonColor] = useState< + | "inherit" + | "primary" + | "secondary" + | "success" + | "error" + | "info" + | "warning" + >("primary"); const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop: ondrop, }); + const scrollContainerRef = useRef(null); + + useEffect(() => { + if (scrollContainerRef.current) { + scrollContainerRef.current.scrollTop = scrollContainerRef.current.scrollHeight; + } + }, [logs]); + return (
@@ -213,10 +298,16 @@ export default function Home() { ))} +
+ {logs.map((log, index) => ( +
{log}
+ ))} +