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() {
)}
- {
- if (downloadBlob != null) {
- const download_url = window.URL.createObjectURL(downloadBlob[0]);
- const a = document.createElement('a');
- a.href = download_url;
- a.download = downloadBlob[1];
- document.body.appendChild(a);
- a.click();
- document.body.removeChild(a);
- window.URL.revokeObjectURL(download_url);
- }
- }}>
+ {
+ if (downloadBlob != null) {
+ const download_url = window.URL.createObjectURL(
+ downloadBlob[0],
+ );
+ const a = document.createElement("a");
+ a.href = download_url;
+ a.download = downloadBlob[1];
+ document.body.appendChild(a);
+ a.click();
+ document.body.removeChild(a);
+ window.URL.revokeObjectURL(download_url);
+ }
+ }}
+ >
Download Proof
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