diff --git a/.dockerignore b/.dockerignore index 569b45c..edfadae 100644 --- a/.dockerignore +++ b/.dockerignore @@ -13,6 +13,8 @@ Scarb.lock .snfoundry_cache/ stone-prover bootloader*.json +*.dockerfile +*docker-compose*.yaml # Ignore Python-specific files *.pyc @@ -29,4 +31,4 @@ build/ .dockerignore.template .dockerignore.generated .dockerignore.custom -.dockerignore.local +.dockerignore.local \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 2a28df6..9346623 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -70,6 +70,7 @@ hyper = { version = "1.0", features = [] } hyper-util = { version = "0.1", features = ["tokio", "server-auto", "http1"] } tower = { version = "0.4", features = ["util"] } tower-http = { version = "0.5", features = ["timeout", "trace", "cors"] } +clap = { version = "4.0", features = ["derive"] } zetina-common = { path = "crates/common" } zetina-compiler = { path = "crates/compiler" } diff --git a/crates/common/src/job_record.rs b/crates/common/src/job_record.rs index 4f61eb8..8784f5d 100644 --- a/crates/common/src/job_record.rs +++ b/crates/common/src/job_record.rs @@ -26,7 +26,7 @@ where // add random wait to simulate network overhead let random = { let mut rng = rand::thread_rng(); - rng.gen_range(0..1000) + rng.gen_range(0..2000) }; tokio::time::sleep(std::time::Duration::from_millis(random)).await; self.ordered_set.pop_last() diff --git a/crates/common/src/node_account.rs b/crates/common/src/node_account.rs index efbc5b0..f480c9e 100644 --- a/crates/common/src/node_account.rs +++ b/crates/common/src/node_account.rs @@ -1,30 +1,17 @@ use starknet::{ - accounts::{ConnectedAccount, ExecutionEncoding, SingleOwnerAccount}, core::types::FieldElement, - providers::Provider, - signers::{LocalWallet, SigningKey, VerifyingKey}, + signers::{SigningKey, VerifyingKey}, }; -use crate::network::Network; - -pub struct NodeAccount

-where - P: Provider + Sync + Send + 'static, -{ +pub struct NodeAccount { /// Key pair for the p2p network. /// This represents the identity of the node in the network. p2p_keypair: libp2p::identity::Keypair, - /// The account for the StarkNet network. - /// This account is used to interact with the Registry contract. - account: SingleOwnerAccount, signing_key: SigningKey, } -impl

NodeAccount

-where - P: Provider + Sync + Send + 'static, -{ - pub fn new(private_key: Vec, address: Vec, network: Network, provider: P) -> Self { +impl NodeAccount { + pub fn new(private_key: Vec) -> Self { let secret_key = libp2p::identity::ecdsa::SecretKey::try_from_bytes(private_key.as_slice()) .expect("Failed to create secret key from private key."); let p2p_keypair = @@ -32,27 +19,14 @@ where let signing_key = SigningKey::from_secret_scalar( FieldElement::from_byte_slice_be(private_key.as_slice()).unwrap(), ); - let signer = LocalWallet::from(signing_key.clone()); - let address = FieldElement::from_byte_slice_be(address.as_slice()).unwrap(); - let network = network.to_field_element(); - let account = - SingleOwnerAccount::new(provider, signer, address, network, ExecutionEncoding::New); - Self { p2p_keypair, account, signing_key } + Self { p2p_keypair, signing_key } } pub fn get_keypair(&self) -> &libp2p::identity::Keypair { &self.p2p_keypair } - pub fn get_account(&self) -> &SingleOwnerAccount { - &self.account - } - - pub fn get_provider(&self) -> &P { - self.account.provider() - } - pub fn get_signing_key(&self) -> &SigningKey { &self.signing_key } diff --git a/crates/delegator/Cargo.toml b/crates/delegator/Cargo.toml index 487c7aa..1e9544d 100644 --- a/crates/delegator/Cargo.toml +++ b/crates/delegator/Cargo.toml @@ -17,7 +17,6 @@ libp2p.workspace = true serde.workspace = true serde_json.workspace = true zetina-common.workspace = true -zetina-compiler.workspace = true zetina-peer.workspace = true tokio.workspace = true tracing-subscriber.workspace = true @@ -31,4 +30,5 @@ axum.workspace = true hyper.workspace = true tower.workspace = true hyper-util.workspace = true -tower-http.workspace = true \ No newline at end of file +tower-http.workspace = true +clap.workspace = true \ No newline at end of file diff --git a/crates/delegator/src/main.rs b/crates/delegator/src/main.rs index a7a03fb..44e0a44 100644 --- a/crates/delegator/src/main.rs +++ b/crates/delegator/src/main.rs @@ -7,9 +7,9 @@ use axum::{ routing::{get, post}, Router, }; +use clap::Parser; use delegator::Delegator; use libp2p::gossipsub; -use starknet::providers::{jsonrpc::HttpTransport, JsonRpcClient, Url}; use std::time::Duration; use swarm::SwarmRunner; use tokio::{ @@ -31,24 +31,28 @@ use zetina_common::{ topic::{gossipsub_ident_topic, Topic}, }; +#[derive(Parser)] +struct Cli { + /// The private key as a hex string + #[arg(short, long)] + private_key: String, + + #[arg(short, long)] + dial_addresses: Vec, +} + #[tokio::main] async fn main() -> Result<(), Box> { let _ = tracing_subscriber::fmt().with_env_filter(EnvFilter::from_default_env()).try_init(); + // Parse command line arguments + let cli = Cli::parse(); + // TODO: common setup in node initiate binary let network = Network::Sepolia; - let private_key = - hex::decode("018ef9563461ec2d88236d59039babf44c97d8bf6200d01d81170f1f60a78f32")?; - let account_address = - hex::decode("cdd51fbc4e008f4ef807eaf26f5043521ef5931bbb1e04032a25bd845d286b")?; - let url = "https://starknet-sepolia.public.blastapi.io"; + let private_key = hex::decode(cli.private_key)?; - let node_account = NodeAccount::new( - private_key, - account_address, - network, - JsonRpcClient::new(HttpTransport::new(Url::parse(url)?)), - ); + let node_account = NodeAccount::new(private_key); // Generate topic let job_topic = gossipsub_ident_topic(network, Topic::NewJob); @@ -62,6 +66,7 @@ async fn main() -> Result<(), Box> { let (job_topic_tx, job_topic_rx) = mpsc::channel::>(100); SwarmRunner::new( + cli.dial_addresses, node_account.get_keypair(), vec![job_topic.to_owned(), picked_job_topic, finished_job_topic], vec![(job_topic, job_topic_rx)], diff --git a/crates/delegator/src/swarm.rs b/crates/delegator/src/swarm.rs index 0682280..1e1b592 100644 --- a/crates/delegator/src/swarm.rs +++ b/crates/delegator/src/swarm.rs @@ -2,18 +2,18 @@ use futures::StreamExt; use libp2p::gossipsub::{self, IdentTopic}; use libp2p::identity::Keypair; use libp2p::swarm::{NetworkBehaviour, SwarmEvent}; -use libp2p::{mdns, noise, tcp, yamux, SwarmBuilder}; +use libp2p::{noise, tcp, yamux, Multiaddr, SwarmBuilder}; use std::error::Error; +use std::str::FromStr; use std::time::Duration; use tokio::sync::mpsc; use tokio::task::JoinHandle; -use tracing::{debug, error}; +use tracing::{debug, error, info}; use zetina_common::graceful_shutdown::shutdown_signal; #[derive(NetworkBehaviour)] pub struct PeerBehaviour { gossipsub: gossipsub::Behaviour, - mdns: mdns::tokio::Behaviour, } pub struct SwarmRunner { @@ -22,17 +22,14 @@ pub struct SwarmRunner { impl SwarmRunner { pub fn new( + dial_addresses: Vec, p2p_local_keypair: &Keypair, subscribe_topics: Vec, mut transmit_topics: Vec<(IdentTopic, mpsc::Receiver>)>, swarm_events_tx: mpsc::Sender, ) -> Result> { - let mdns = mdns::tokio::Behaviour::new( - mdns::Config::default(), - p2p_local_keypair.public().to_peer_id(), - )?; let gossipsub = Self::init_gossip(p2p_local_keypair)?; - let behaviour = PeerBehaviour { gossipsub, mdns }; + let behaviour = PeerBehaviour { gossipsub }; let local_keypair = p2p_local_keypair.clone(); let mut swarm = SwarmBuilder::with_existing_identity(local_keypair) .with_tokio() @@ -50,6 +47,13 @@ impl SwarmRunner { swarm.behaviour_mut().gossipsub.subscribe(&topic)?; } + // Reach out to other nodes if specified + for to_dial in dial_addresses { + let addr: Multiaddr = Multiaddr::from_str(&to_dial)?; + swarm.dial(addr)?; + info!("Dialed {to_dial:?}") + } + swarm.listen_on("/ip4/0.0.0.0/udp/5678/quic-v1".parse()?)?; swarm.listen_on("/ip4/0.0.0.0/tcp/5679".parse()?)?; @@ -66,18 +70,6 @@ impl SwarmRunner { } }, event = swarm.select_next_some() => match event { - SwarmEvent::Behaviour(PeerBehaviourEvent::Mdns(mdns::Event::Discovered(list))) => { - for (peer_id, _multiaddr) in list { - debug!("mDNS discovered a new peer: {peer_id}"); - swarm.behaviour_mut().gossipsub.add_explicit_peer(&peer_id); - } - }, - SwarmEvent::Behaviour(PeerBehaviourEvent::Mdns(mdns::Event::Expired(list))) => { - for (peer_id, _multiaddr) in list { - debug!("mDNS discover peer has expired: {peer_id}"); - swarm.behaviour_mut().gossipsub.remove_explicit_peer(&peer_id); - } - }, SwarmEvent::Behaviour(PeerBehaviourEvent::Gossipsub(gossipsub::Event::Message { propagation_source, message_id, @@ -87,6 +79,14 @@ impl SwarmRunner { }, SwarmEvent::NewListenAddr { address, .. } => { debug!("Local node is listening on {address}"); + }, + SwarmEvent::ConnectionEstablished { peer_id, connection_id, num_established, .. } => { + info!{"ConnectionEstablished: peer_id {}, connection_id {}, num_established {}", peer_id, connection_id, num_established}; + swarm.behaviour_mut().gossipsub.add_explicit_peer(&peer_id); + } + SwarmEvent::ConnectionClosed { peer_id, connection_id, num_established, .. } => { + info!{"ConnectionClosed: peer_id {}, connection_id {}, num_established {}", peer_id, connection_id, num_established}; + swarm.behaviour_mut().gossipsub.remove_explicit_peer(&peer_id); } _ => {} }, diff --git a/crates/executor/Cargo.toml b/crates/executor/Cargo.toml index 6aa7259..e011888 100644 --- a/crates/executor/Cargo.toml +++ b/crates/executor/Cargo.toml @@ -32,4 +32,5 @@ axum.workspace = true hyper.workspace = true tower.workspace = true hyper-util.workspace = true -tower-http.workspace = true \ No newline at end of file +tower-http.workspace = true +clap.workspace = true \ No newline at end of file diff --git a/crates/executor/src/executor.rs b/crates/executor/src/executor.rs index d6df707..3e4cc44 100644 --- a/crates/executor/src/executor.rs +++ b/crates/executor/src/executor.rs @@ -92,12 +92,64 @@ impl Executor { && !job_record.is_empty() { if let Some(job) = job_record.take_job().await { - let serialized_job = serde_json::to_string(&job)?; - picked_job_topic_tx.send(serialized_job.into()).await?; - info!("Sent picked job event: {}", hash!(&job)); + let mut flag = false; + if let Ok(event) = events_rx.try_recv() { + match event { + Event::Message { message, .. } => { + // Received a new-job message from the network + if message.topic + == gossipsub_ident_topic( + Network::Sepolia, + Topic::NewJob, + ) + .into() + { + let job: Job = serde_json::from_slice(&message.data)?; + info!("Received a new job event: {}", hash!(&job)); + job_record.register_job(job); + } + // Received a picked-job message from the network + if message.topic + == gossipsub_ident_topic( + Network::Sepolia, + Topic::PickedJob, + ) + .into() + { + let job_removed: Job = + serde_json::from_slice(&message.data)?; + info!("Received picked job event: {}", hash!(&job)); + job_record.remove_job(&job_removed); + if hash!(job_removed) == hash!(job) { + flag = true; + } + } + } + Event::Subscribed { peer_id, topic } => { + info!( + "{} subscribed to the topic {}", + peer_id.to_string(), + topic.to_string() + ); + } + Event::Unsubscribed { peer_id, topic } => { + info!( + "{} unsubscribed to the topic {}", + peer_id.to_string(), + topic.to_string() + ); + } + _ => {} + } + }; + if flag == false { + let serialized_job = serde_json::to_string(&job)?; + picked_job_topic_tx.send(serialized_job.into()).await?; + info!("Sent picked job event: {}", hash!(&job)); - info!("Scheduled run of job: {}", hash!(&job)); - runner_scheduler.push(runner.run(job)?); + info!("Scheduled run of job: {}", hash!(&job)); + runner_scheduler.push(runner.run(job)?); + } } } } diff --git a/crates/executor/src/main.rs b/crates/executor/src/main.rs index 7f058fb..ff467a4 100644 --- a/crates/executor/src/main.rs +++ b/crates/executor/src/main.rs @@ -2,9 +2,9 @@ pub mod executor; pub mod swarm; use axum::Router; +use clap::Parser; use executor::Executor; use libp2p::gossipsub; -use starknet::providers::{jsonrpc::HttpTransport, JsonRpcClient, Url}; use std::time::Duration; use swarm::SwarmRunner; use tokio::{net::TcpListener, sync::mpsc}; @@ -19,10 +19,23 @@ use zetina_common::{ use zetina_prover::stone_prover::StoneProver; use zetina_runner::cairo_runner::CairoRunner; +#[derive(Parser)] +struct Cli { + /// The private key as a hex string + #[arg(short, long)] + private_key: String, + + #[arg(short, long)] + dial_addresses: Vec, +} + #[tokio::main] async fn main() -> Result<(), Box> { let _ = tracing_subscriber::fmt().with_env_filter(EnvFilter::from_default_env()).try_init(); + // Parse command line arguments + let cli = Cli::parse(); + let ws_root = std::path::PathBuf::from( std::env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR env not present"), ) @@ -31,18 +44,9 @@ async fn main() -> Result<(), Box> { // TODO: common setup in node initiate binary let network = Network::Sepolia; - let private_key = - hex::decode("07c7a41c77c7a3b19e7c77485854fc88b09ed7041361595920009f81236d55d2")?; - let account_address = - hex::decode("cdd51fbc4e008f4ef807eaf26f5043521ef5931bbb1e04032a25bd845d286b")?; - let url = "https://starknet-sepolia.public.blastapi.io"; + let private_key = hex::decode(cli.private_key)?; - let node_account = NodeAccount::new( - private_key, - account_address, - network, - JsonRpcClient::new(HttpTransport::new(Url::parse(url)?)), - ); + let node_account = NodeAccount::new(private_key); // Generate topic let new_job_topic = gossipsub_ident_topic(network, Topic::NewJob); @@ -54,6 +58,7 @@ async fn main() -> Result<(), Box> { let (finished_job_topic_tx, finished_job_topic_rx) = mpsc::channel::>(1000); SwarmRunner::new( + cli.dial_addresses, node_account.get_keypair(), vec![new_job_topic, picked_job_topic.to_owned(), finished_job_topic.to_owned()], vec![ @@ -70,7 +75,7 @@ async fn main() -> Result<(), Box> { Executor::new(swarm_events_rx, finished_job_topic_tx, picked_job_topic_tx, runner, prover); // Create a `TcpListener` using tokio. - let listener = TcpListener::bind("0.0.0.0:3020").await.unwrap(); + let listener = TcpListener::bind("0.0.0.0:3010").await.unwrap(); // Run the server with graceful shutdown axum::serve( diff --git a/crates/executor/src/swarm.rs b/crates/executor/src/swarm.rs index 1600dca..39858d6 100644 --- a/crates/executor/src/swarm.rs +++ b/crates/executor/src/swarm.rs @@ -2,18 +2,18 @@ use futures::StreamExt; use libp2p::gossipsub::{self, IdentTopic}; use libp2p::identity::Keypair; use libp2p::swarm::{NetworkBehaviour, SwarmEvent}; -use libp2p::{mdns, noise, tcp, yamux, SwarmBuilder}; +use libp2p::{noise, tcp, yamux, Multiaddr, SwarmBuilder}; use std::error::Error; +use std::str::FromStr; use std::time::Duration; use tokio::sync::mpsc; use tokio::task::JoinHandle; -use tracing::{debug, error}; +use tracing::{debug, error, info}; use zetina_common::graceful_shutdown::shutdown_signal; #[derive(NetworkBehaviour)] pub struct PeerBehaviour { gossipsub: gossipsub::Behaviour, - mdns: mdns::tokio::Behaviour, } pub struct SwarmRunner { @@ -22,17 +22,14 @@ pub struct SwarmRunner { impl SwarmRunner { pub fn new( + dial_addresses: Vec, p2p_local_keypair: &Keypair, subscribe_topics: Vec, mut transmit_topics: Vec<(IdentTopic, mpsc::Receiver>)>, swarm_events_tx: mpsc::Sender, ) -> Result> { - let mdns = mdns::tokio::Behaviour::new( - mdns::Config::default(), - p2p_local_keypair.public().to_peer_id(), - )?; let gossipsub = Self::init_gossip(p2p_local_keypair)?; - let behaviour = PeerBehaviour { gossipsub, mdns }; + let behaviour = PeerBehaviour { gossipsub }; let local_keypair = p2p_local_keypair.clone(); let mut swarm = SwarmBuilder::with_existing_identity(local_keypair) .with_tokio() @@ -50,8 +47,15 @@ impl SwarmRunner { swarm.behaviour_mut().gossipsub.subscribe(&topic)?; } - swarm.listen_on("/ip4/0.0.0.0/udp/5680/quic-v1".parse()?)?; - swarm.listen_on("/ip4/0.0.0.0/tcp/5681".parse()?)?; + swarm.listen_on("/ip4/0.0.0.0/udp/5678/quic-v1".parse()?)?; + swarm.listen_on("/ip4/0.0.0.0/tcp/5679".parse()?)?; + + // Reach out to other nodes if specified + for to_dial in dial_addresses { + let addr: Multiaddr = Multiaddr::from_str(&to_dial)?; + swarm.dial(addr)?; + info!("Dialed {to_dial:?}") + } Ok(SwarmRunner { handle: Some(tokio::spawn(async move { @@ -77,18 +81,6 @@ impl SwarmRunner { } }, event = swarm.select_next_some() => match event { - SwarmEvent::Behaviour(PeerBehaviourEvent::Mdns(mdns::Event::Discovered(list))) => { - for (peer_id, _multiaddr) in list { - debug!("mDNS discovered a new peer: {peer_id}"); - swarm.behaviour_mut().gossipsub.add_explicit_peer(&peer_id); - } - }, - SwarmEvent::Behaviour(PeerBehaviourEvent::Mdns(mdns::Event::Expired(list))) => { - for (peer_id, _multiaddr) in list { - debug!("mDNS discover peer has expired: {peer_id}"); - swarm.behaviour_mut().gossipsub.remove_explicit_peer(&peer_id); - } - }, SwarmEvent::Behaviour(PeerBehaviourEvent::Gossipsub(gossipsub::Event::Message { propagation_source, message_id, @@ -99,6 +91,14 @@ impl SwarmRunner { SwarmEvent::NewListenAddr { address, .. } => { debug!("Local node is listening on {address}"); } + SwarmEvent::ConnectionEstablished { peer_id, connection_id, num_established, .. } => { + info!{"ConnectionEstablished: peer_id {}, connection_id {}, num_established {}", peer_id, connection_id, num_established}; + swarm.behaviour_mut().gossipsub.add_explicit_peer(&peer_id); + } + SwarmEvent::ConnectionClosed { peer_id, connection_id, num_established, .. } => { + info!{"ConnectionClosed: peer_id {}, connection_id {}, num_established {}", peer_id, connection_id, num_established}; + swarm.behaviour_mut().gossipsub.remove_explicit_peer(&peer_id); + } _ => {} }, _ = shutdown_signal() => { diff --git a/dashboard/.dockerignore b/dashboard/.dockerignore new file mode 100644 index 0000000..caab220 --- /dev/null +++ b/dashboard/.dockerignore @@ -0,0 +1,40 @@ +# Ignore node_modules +node_modules + +# Ignore logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Ignore Docker files +Dockerfile +*.dockerfile +.dockerignore + +# Ignore build output +.next +out + +# Ignore miscellaneous files +*.swp +*.swo +*.bak +*.tmp +*.temp + +# Ignore IDE and editor config files +.idea +.vscode +*.iml + +# Ignore test directories and files +__tests__ +coverage +*.test.js +*.spec.js +*.test.ts +*.spec.ts + +k8s/ \ No newline at end of file diff --git a/dashboard/.env.development b/dashboard/.env.development new file mode 100644 index 0000000..1d12d29 --- /dev/null +++ b/dashboard/.env.development @@ -0,0 +1 @@ +NEXT_PUBLIC_API_URL=http://localhost:3010 \ No newline at end of file diff --git a/dashboard/.env.production b/dashboard/.env.production new file mode 100644 index 0000000..1d12d29 --- /dev/null +++ b/dashboard/.env.production @@ -0,0 +1 @@ +NEXT_PUBLIC_API_URL=http://localhost:3010 \ No newline at end of file diff --git a/dashboard/.gitignore b/dashboard/.gitignore index fd3dbb5..5ab2f9b 100644 --- a/dashboard/.gitignore +++ b/dashboard/.gitignore @@ -25,9 +25,6 @@ npm-debug.log* yarn-debug.log* yarn-error.log* -# local env files -.env*.local - # vercel .vercel diff --git a/dashboard/cairo_pie_large.zip b/dashboard/cairo_pie_large.zip new file mode 100644 index 0000000..6188b97 Binary files /dev/null and b/dashboard/cairo_pie_large.zip differ diff --git a/dashboard/cairo_pie_medium.zip b/dashboard/cairo_pie_medium.zip new file mode 100644 index 0000000..17b7110 Binary files /dev/null and b/dashboard/cairo_pie_medium.zip differ diff --git a/dashboard/cairo_pie.zip b/dashboard/cairo_pie_small.zip similarity index 100% rename from dashboard/cairo_pie.zip rename to dashboard/cairo_pie_small.zip diff --git a/dashboard/dashboard.dockerfile b/dashboard/dashboard.dockerfile new file mode 100644 index 0000000..2b38331 --- /dev/null +++ b/dashboard/dashboard.dockerfile @@ -0,0 +1,37 @@ +# Use the official Node.js image as the base image +FROM node:18-alpine AS builder + +# Set the working directory +WORKDIR /app + +# Copy the package.json and package-lock.json files +COPY package*.json ./ + +# Install dependencies +RUN npm install + +# Copy the rest of the application code +COPY . . + +# Build the Next.js app +RUN npm run build + +# Use a smaller base image for the final stage +FROM node:18-alpine AS runner + +# Set the working directory +WORKDIR /app + +# Copy the built files from the builder stage +COPY --from=builder /app/.next ./.next +COPY --from=builder /app/public ./public +COPY --from=builder /app/package*.json ./ + +# Install only production dependencies +RUN npm install --omit=dev + +# Expose the port that the app runs on +EXPOSE 3000 + +# Start the application using Next.js built-in server +CMD ["npm", "start"] diff --git a/dashboard/src/app/layout.tsx b/dashboard/src/app/layout.tsx index 2813e35..a8c624a 100644 --- a/dashboard/src/app/layout.tsx +++ b/dashboard/src/app/layout.tsx @@ -16,7 +16,9 @@ export default function RootLayout({ }>) { return ( - + + + {children} ); diff --git a/dashboard/src/app/page.tsx b/dashboard/src/app/page.tsx index ab1b2ab..5413497 100644 --- a/dashboard/src/app/page.tsx +++ b/dashboard/src/app/page.tsx @@ -12,7 +12,13 @@ import Check from "@mui/icons-material/Check"; import StepConnector, { stepConnectorClasses, } from "@mui/material/StepConnector"; -import { DelegateRequest, DelegateResponse, JobEventsResponse, JobHash, Proof } from "./api"; +import { + DelegateRequest, + DelegateResponse, + JobEventsResponse, + JobHash, + Proof, +} from "./api"; import { useState } from "react"; import subscribeEvents from "./subscribeEvents"; import assert from "assert"; @@ -80,7 +86,7 @@ export default function Home() { const darkTheme = createTheme({ palette: { mode: "dark", - primary: { main: "#784af4", dark: "#784af4" } + primary: { main: "#784af4", dark: "#784af4" }, }, }); @@ -102,16 +108,19 @@ export default function Home() { console.log(requestBody); - let subscriber: EventSource | null = null + let subscriber: EventSource | null = null; try { - const response = await fetch("http://localhost:3010/delegate", { - method: "POST", - headers: { - "Content-Type": "application/json", + const response = await fetch( + `${process.env.NEXT_PUBLIC_API_URL}/delegate`, + { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(requestBody), }, - body: JSON.stringify(requestBody), - }); + ); if (!response.ok) { throw new Error(`Error: ${response.statusText}`); @@ -122,32 +131,35 @@ export default function Home() { ); console.log("Job hash:", data.job_hash); - setActiveStep(1) + setActiveStep(1); setIsProcessing(data.job_hash); subscriber = subscribeEvents( - "http://localhost:3010/job_events", + `${process.env.NEXT_PUBLIC_API_URL}/job_events`, `job_hash=${data.job_hash.toString()}`, (event) => { let job_event = JobEventsResponse.parse(event); if (job_event.type == "Picked") { let job_hash = JobHash.parse(job_event.data); - assert(job_hash == data.job_hash) - setActiveStep(2) + assert(job_hash == data.job_hash); + setActiveStep(2); } if (job_event.type == "Witness") { let proof = Proof.parse(job_event.data); - setActiveStep(3) - setDownloadBlob([new Blob([new Uint8Array(proof)]), `${data.job_hash}_proof.json`]) - setIsProcessing(null) - subscriber?.close() + setActiveStep(3); + setDownloadBlob([ + new Blob([new Uint8Array(proof)]), + `${data.job_hash}_proof.json`, + ]); + setIsProcessing(null); + subscriber?.close(); } }, ); } catch (error) { console.error("Failed to upload file:", error); setIsProcessing(null); - subscriber?.close() + subscriber?.close(); } } }; @@ -190,7 +202,10 @@ export default function Home() { )}

-
diff --git a/delegator.dockerfile b/delegator.dockerfile index cd6c4a1..cff9a4d 100644 --- a/delegator.dockerfile +++ b/delegator.dockerfile @@ -1,11 +1,14 @@ -# Use the base runtime image -FROM runtime +# Use the official Rust image from the Docker Hub +FROM rust:latest -# Build +# Set the working directory inside the container +WORKDIR /zetina-delegator + +# Copy the rest of the application source code +COPY . . + +# Build the application in release mode RUN cargo build --release --bin zetina-delegator # Expose necessary ports -EXPOSE 5678/udp 5679/tcp 50051/tcp - -# Set the default command to run when the container starts -CMD ["bash", "-ci", "cargo run --release --bin zetina-delegator"] +EXPOSE 5678/udp 5679/tcp 3010/tcp \ No newline at end of file diff --git a/devcontainer.dockerfile b/devcontainer.dockerfile index 52eb797..527b5af 100644 --- a/devcontainer.dockerfile +++ b/devcontainer.dockerfile @@ -1,5 +1,5 @@ # Use the base runtime image -FROM runtime +FROM zetina-runtime # Set the default command to run when the container starts CMD ["bash"] diff --git a/docker-compose.demo.yaml b/docker-compose.demo.yaml new file mode 100644 index 0000000..d0e70a7 --- /dev/null +++ b/docker-compose.demo.yaml @@ -0,0 +1,40 @@ +services: + zetina-delegator: + build: + dockerfile: delegator.dockerfile + image: zetina-delegator + hostname: zetina-delegator + deploy: + resources: + limits: + cpus: '1' + memory: '1G' + environment: + - RUST_LOG=info + networks: + - zetina-network + command: ["bash", "-ci", "cargo run --release --bin zetina-delegator -- -p 018ef9563461ec2d88236d59039babf44c97d8bf6200d01d81170f1f60a78f34 -d /ip4/83.238.171.134/tcp/5679/p2p/QmVUbnW8PjFw2yUt1DDukUTZrK1t2vPGkNb1SQgserjaXV -d /ip4/83.238.171.134/tcp/5681/p2p/QmUEi69CXN8SXEYhkzoqJzQ6aEHTKqJzewqirBiT8FKmBu -d /ip4/83.238.171.134/tcp/5683/p2p/QmYaed2psXu4UMTXqrrRUE7PaYnwGFkvGvKvTUBCQSry4S"] + ports: + - "3010:3010" + + zetina-delegator-dashboard: + build: + context: dashboard + dockerfile: dashboard.dockerfile + image: zetina-delegator-dashboard + hostname: zetina-delegator-dashboard + depends_on: + - zetina-delegator + deploy: + resources: + limits: + cpus: '1' + memory: '1G' + networks: + - zetina-network + ports: + - "3000:3000" + +networks: + zetina-network: + driver: bridge \ No newline at end of file diff --git a/docker-compose.yaml b/docker-compose.yaml index aa89440..acb7dd1 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -1,51 +1,60 @@ services: - stone-prover: + zetina-stone-prover: build: context: stone-prover dockerfile: Dockerfile - image: stone-prover + image: zetina-stone-prover - runtime: + zetina-runtime: build: dockerfile: runtime.dockerfile - image: runtime + image: zetina-runtime depends_on: - - stone-prover + - zetina-stone-prover - delegator: + zetina-delegator: build: dockerfile: delegator.dockerfile + image: zetina-delegator + hostname: zetina-delegator depends_on: - - runtime + - zetina-runtime + - zetina-executor deploy: resources: limits: - cpus: '8' + cpus: '10' memory: '10G' - ports: - - "3010:3010/tcp" environment: - RUST_LOG=info networks: - - zetina-network + zetina-network: + ipv4_address: 172.16.238.3 + ports: + - "3010:3010" + command: ["bash", "-ci", "cargo run --release --bin zetina-delegator -- -p 018ef9563461ec2d88236d59039babf44c97d8bf6200d01d81170f1f60a78f32 -d /ip4/172.16.238.2/tcp/5679/p2p/QmVUbnW8PjFw2yUt1DDukUTZrK1t2vPGkNb1SQgserjaXV"] - executor: + zetina-executor: build: dockerfile: executor.dockerfile + image: zetina-executor depends_on: - - runtime + - zetina-runtime deploy: resources: limits: - cpus: '8' + cpus: '10' memory: '10G' - ports: - - "3020:3020/tcp" environment: - RUST_LOG=info networks: - - zetina-network + zetina-network: + ipv4_address: 172.16.238.2 + command: ["bash", "-ci", "cargo run --release --bin zetina-executor -- -p 018ef9563461ec2d88236d59039babf44c97d8bf6200d01d81170f1f60a78f31"] networks: zetina-network: - driver: bridge \ No newline at end of file + driver: bridge + ipam: + config: + - subnet: 172.16.238.0/24 \ No newline at end of file diff --git a/executor.dockerfile b/executor.dockerfile index 64a05d7..d74802c 100644 --- a/executor.dockerfile +++ b/executor.dockerfile @@ -1,11 +1,8 @@ # Use the base runtime image -FROM runtime +FROM zetina-runtime:latest # Build RUN cargo build --release --bin zetina-executor # Expose necessary ports -EXPOSE 5678/udp 5679/tcp 50052/tcp - -# Set the default command to run when the container starts -CMD ["bash", "-ci", "cargo run --release --bin zetina-executor"] +EXPOSE 5678/udp 5679/tcp \ No newline at end of file diff --git a/k8s/certificates.yaml b/k8s/certificates.yaml new file mode 100644 index 0000000..b98ee06 --- /dev/null +++ b/k8s/certificates.yaml @@ -0,0 +1,14 @@ +apiVersion: cert-manager.io/v1 +kind: Certificate +metadata: + name: zetina-delegator-cert + namespace: zetina +spec: + secretName: zetina-tls + + dnsNames: + - delegator.zetina.iosis.tech + - dashboard.delegator.zetina.iosis.tech + issuerRef: + name: letsencrypt-http01 + kind: Issuer diff --git a/k8s/delegator-dashboard-deployment.yaml b/k8s/delegator-dashboard-deployment.yaml new file mode 100644 index 0000000..db955a3 --- /dev/null +++ b/k8s/delegator-dashboard-deployment.yaml @@ -0,0 +1,63 @@ + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: delegator-dashboard + namespace: zetina +spec: + selector: + matchLabels: + app: delegator-dashboard + replicas: 1 + template: + metadata: + labels: + app: delegator-dashboard + spec: + containers: + - name: delegator-dashboard + image: registry.internal.iosis.tech/zetina-delegator-dashboard + resources: + limits: + cpu: "1000m" + memory: "1Gi" + ports: + - containerPort: 3000 +--- +apiVersion: v1 +kind: Service +metadata: + name: delegator-dashboard-service + namespace: zetina +spec: + selector: + app: delegator-dashboard + ports: + - protocol: TCP + port: 80 + targetPort: 3000 +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: delegator-dashboard-ingress + namespace: zetina + annotations: + nginx.ingress.kubernetes.io/proxy-body-size: "0" +spec: + ingressClassName: nginx-ingress-prod + tls: + - hosts: + - dashboard.delegator.zetina.iosis.tech + secretName: zetina-tls + rules: + - host: dashboard.delegator.zetina.iosis.tech + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: delegator-dashboard-service + port: + number: 80 diff --git a/k8s/delegator-deployment.yaml b/k8s/delegator-deployment.yaml new file mode 100644 index 0000000..84a4692 --- /dev/null +++ b/k8s/delegator-deployment.yaml @@ -0,0 +1,70 @@ + +apiVersion: apps/v1 +kind: Deployment +metadata: + name: delegator + namespace: zetina +spec: + selector: + matchLabels: + app: delegator + replicas: 1 + template: + metadata: + labels: + app: delegator + spec: + containers: + - name: delegator + image: registry.internal.iosis.tech/zetina-delegator + command: ["bash", "-ci", "cargo run --release --bin zetina-delegator -- -p 018ef9563461ec2d88236d59039babf44c97d8bf6200d01d81170f1f60a78f34 -d /ip4/83.238.171.134/tcp/5679/p2p/QmVUbnW8PjFw2yUt1DDukUTZrK1t2vPGkNb1SQgserjaXV -d /ip4/83.238.171.134/tcp/5681/p2p/QmUEi69CXN8SXEYhkzoqJzQ6aEHTKqJzewqirBiT8FKmBu -d /ip4/83.238.171.134/tcp/5683/p2p/QmYaed2psXu4UMTXqrrRUE7PaYnwGFkvGvKvTUBCQSry4S"] + resources: + requests: + cpu: "1" + memory: "1Gi" + limits: + cpu: "1" + memory: "1Gi" + env: + - name: RUST_LOG + value: "info" + ports: + - containerPort: 3010 +--- +apiVersion: v1 +kind: Service +metadata: + name: delegator-service + namespace: zetina +spec: + selector: + app: delegator + ports: + - protocol: TCP + port: 80 + targetPort: 3010 +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: delegator-ingress + namespace: zetina + annotations: + nginx.ingress.kubernetes.io/proxy-body-size: "0" +spec: + ingressClassName: nginx-ingress-prod + tls: + - hosts: + - delegator.zetina.iosis.tech + secretName: zetina-tls + rules: + - host: delegator.zetina.iosis.tech + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: delegator-service + port: + number: 80 diff --git a/k8s/executor-deployment-1.yaml b/k8s/executor-deployment-1.yaml new file mode 100644 index 0000000..098074e --- /dev/null +++ b/k8s/executor-deployment-1.yaml @@ -0,0 +1,56 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: executor-1 + namespace: zetina +spec: + selector: + matchLabels: + app: executor-1 + replicas: 1 + template: + metadata: + labels: + app: executor-1 + spec: + containers: + - name: executor-1 + image: registry.internal.iosis.tech/zetina-executor + command: ["bash", "-ci", "cargo run --release --bin zetina-executor -- -p 018ef9563461ec2d88236d59039babf44c97d8bf6200d01d81170f1f60a78f31"] + resources: + requests: + cpu: "1" + memory: "2Gi" + limits: + cpu: "10" + memory: "10Gi" + env: + - name: RUST_LOG + value: "info" + ports: + - containerPort: 3010 + - containerPort: 5678 + - containerPort: 5679 +--- +apiVersion: v1 +kind: Service +metadata: + name: executor-1-service + namespace: zetina + annotations: + kube-vip.io/loadbalancerIPs: "0.0.0.0" + kube-vip.io/hwaddr: "00:00:6C:32:98:82" + kube-vip.io/loadbalancerHostname: "zetina-executor-1" +spec: + ports: + - port: 5678 + targetPort: 5678 + protocol: UDP + name: p2p-quic-port + - port: 5679 + targetPort: 5679 + protocol: TCP + name: p2p-tcp-port + selector: + app: executor-1 + type: LoadBalancer diff --git a/k8s/executor-deployment-2.yaml b/k8s/executor-deployment-2.yaml new file mode 100644 index 0000000..9dbe04a --- /dev/null +++ b/k8s/executor-deployment-2.yaml @@ -0,0 +1,56 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: executor-2 + namespace: zetina +spec: + selector: + matchLabels: + app: executor-2 + replicas: 1 + template: + metadata: + labels: + app: executor-2 + spec: + containers: + - name: executor-2 + image: registry.internal.iosis.tech/zetina-executor + command: ["bash", "-ci", "cargo run --release --bin zetina-executor -- -p 018ef9563461ec2d88236d59039babf44c97d8bf6200d01d81170f1f60a78f32"] + resources: + requests: + cpu: "1" + memory: "2Gi" + limits: + cpu: "10" + memory: "10Gi" + env: + - name: RUST_LOG + value: "info" + ports: + - containerPort: 3010 + - containerPort: 5678 + - containerPort: 5679 +--- +apiVersion: v1 +kind: Service +metadata: + name: executor-2-service + namespace: zetina + annotations: + kube-vip.io/loadbalancerIPs: "0.0.0.0" + kube-vip.io/hwaddr: "00:00:6C:32:98:83" + kube-vip.io/loadbalancerHostname: "zetina-executor-2" +spec: + ports: + - port: 5680 + targetPort: 5678 + protocol: UDP + name: p2p-quic-port + - port: 5681 + targetPort: 5679 + protocol: TCP + name: p2p-tcp-port + selector: + app: executor-2 + type: LoadBalancer diff --git a/k8s/executor-deployment-3.yaml b/k8s/executor-deployment-3.yaml new file mode 100644 index 0000000..88daee4 --- /dev/null +++ b/k8s/executor-deployment-3.yaml @@ -0,0 +1,56 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: executor-3 + namespace: zetina +spec: + selector: + matchLabels: + app: executor-3 + replicas: 1 + template: + metadata: + labels: + app: executor-3 + spec: + containers: + - name: executor-3 + image: registry.internal.iosis.tech/zetina-executor + command: ["bash", "-ci", "cargo run --release --bin zetina-executor -- -p 018ef9563461ec2d88236d59039babf44c97d8bf6200d01d81170f1f60a78f33"] + resources: + requests: + cpu: "1" + memory: "2Gi" + limits: + cpu: "10" + memory: "10Gi" + env: + - name: RUST_LOG + value: "info" + ports: + - containerPort: 3010 + - containerPort: 5678 + - containerPort: 5679 +--- +apiVersion: v1 +kind: Service +metadata: + name: executor-3-service + namespace: zetina + annotations: + kube-vip.io/loadbalancerIPs: "0.0.0.0" + kube-vip.io/hwaddr: "00:00:6C:32:98:84" + kube-vip.io/loadbalancerHostname: "zetina-executor-3" +spec: + ports: + - port: 5682 + targetPort: 5678 + protocol: UDP + name: p2p-quic-port + - port: 5683 + targetPort: 5679 + protocol: TCP + name: p2p-tcp-port + selector: + app: executor-3 + type: LoadBalancer diff --git a/k8s/issuer.yaml b/k8s/issuer.yaml new file mode 100644 index 0000000..c49ed3e --- /dev/null +++ b/k8s/issuer.yaml @@ -0,0 +1,14 @@ +apiVersion: cert-manager.io/v1 +kind: Issuer +metadata: + name: letsencrypt-http01 + namespace: zetina +spec: + acme: + server: https://acme-v02.api.letsencrypt.org/directory + privateKeySecretRef: + name: letsencrypt-http01-issuer-account-key + solvers: + - http01: + ingress: + ingressClassName: nginx-ingress-prod diff --git a/runtime.dockerfile b/runtime.dockerfile index aeb1ee1..c41d10d 100644 --- a/runtime.dockerfile +++ b/runtime.dockerfile @@ -1,5 +1,5 @@ # Stage 1: Use the stone-prover image to copy the executable -FROM stone-prover AS stone-prover +FROM zetina-stone-prover AS zetina-stone-prover # Stage 2: Use a Debian-based Linux distribution as the base image FROM --platform=linux/amd64 debian:stable-slim @@ -60,8 +60,8 @@ RUN mkdir -p /root/.local/bin && \ echo 'export PATH="/root/.local/bin:$PATH"' >> /root/.bashrc # Copy the executable from stone-prover image -COPY --from=stone-prover /bin/cpu_air_prover /root/.local/bin/ -COPY --from=stone-prover /bin/cpu_air_verifier /root/.local/bin/ +COPY --from=zetina-stone-prover /bin/cpu_air_prover /root/.local/bin/ +COPY --from=zetina-stone-prover /bin/cpu_air_verifier /root/.local/bin/ # Set the working directory WORKDIR /zetina diff --git a/skaffold.yaml b/skaffold.yaml new file mode 100644 index 0000000..87ca209 --- /dev/null +++ b/skaffold.yaml @@ -0,0 +1,25 @@ +apiVersion: skaffold/v4beta3 +kind: Config +build: + artifacts: + # - image: registry.internal.iosis.tech/zetina-delegator + # context: . + # docker: + # dockerfile: delegator.dockerfile + # - image: registry.internal.iosis.tech/zetina-delegator-dashboard + # context: dashboard + # docker: + # dockerfile: dashboard.dockerfile + - image: registry.internal.iosis.tech/zetina-executor + context: . + docker: + dockerfile: executor.dockerfile +manifests: + rawYaml: + - ./k8s/executor-deployment-1.yaml + - ./k8s/executor-deployment-2.yaml + - ./k8s/executor-deployment-3.yaml + # - ./k8s/delegator-deployment.yaml + # - ./k8s/delegator-dashboard-deployment.yaml + # - ./k8s/certificates.yaml + # - ./k8s/issuer.yaml