Skip to content

Commit

Permalink
Feature: gRPC server and client (#4)
Browse files Browse the repository at this point in the history
Implemented a gRPC server to call the Stone prover from a remote client.
This server provides a single endpoint at the moment, `Prove`, which
returns the proof. The protocol uses server-side streaming to
communicate the proof to the client once it is computed.

This server is still limited, the following points need to be discussed:

* Efficiency: the protocol passes JSON data (public input, prover config
  and parameters) as raw strings. These strings are then deserialized on
  server-side to validate them, but end up being serialized again for
  use by the prover process. This is clearly inefficient.

* Even if the client process cancels the request, the prover will keep
  on running until the proof is generated.
  • Loading branch information
odesenfans authored Nov 20, 2023
1 parent 57347e7 commit 54aca74
Show file tree
Hide file tree
Showing 13 changed files with 372 additions and 24 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ jobs:
with:
submodules: recursive

- name: Install system dependencies
run: |
sudo apt-get install protobuf-compiler
- name: Install Rust
uses: actions-rs/toolchain@v1
with:
Expand Down
6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
[workspace]
resolver = "2"

members = ["stone-prover"]
members = ["madara-prover-rpc-client", "madara-prover-rpc-server", "stone-prover"]

[workspace.dependencies]
prost = "0.12.1"
serde = { version = "1.0.192", features = ["derive"] }
serde_json = "1.0.108"
tokio = { version = "1.34.0", features = ["macros", "process", "rt-multi-thread"] }
tonic = "0.10.2"
tonic-build = "0.10.2"
15 changes: 15 additions & 0 deletions madara-prover-rpc-client/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "madara-prover-rpc-client"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
prost = { workspace = true }
serde_json = { workspace = true }
tokio = { workspace = true }
tonic = { workspace = true }

[build-dependencies]
tonic-build = { workspace = true }
4 changes: 4 additions & 0 deletions madara-prover-rpc-client/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
fn main() -> Result<(), Box<dyn std::error::Error>> {
tonic_build::compile_protos("../protocols/prover.proto")?;
Ok(())
}
28 changes: 28 additions & 0 deletions madara-prover-rpc-client/src/client.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use tonic::codegen::tokio_stream::StreamExt;
use tonic::Status;

use crate::prover::prover_client::ProverClient;
use crate::prover::{ProverRequest, ProverResponse};

pub async fn call_prover(
client: &mut ProverClient<tonic::transport::Channel>,
public_input: String,
memory: Vec<u8>,
trace: Vec<u8>,
prover_config: String,
prover_parameters: String,
) -> Result<ProverResponse, Status> {
let request = tonic::Request::new(ProverRequest {
public_input,
memory,
trace,
prover_config,
prover_parameters,
});
let prover_stream = client.prove(request).await?.into_inner();
if let Some(prover_result) = prover_stream.take(1).next().await {
return prover_result;
}

Err(Status::cancelled("Server-side stream was dropped"))
}
33 changes: 33 additions & 0 deletions madara-prover-rpc-client/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use crate::client::call_prover;
use prover::prover_client::ProverClient;
use std::path::Path;

pub mod client;
mod prover;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut client = ProverClient::connect("http://[::1]:8080").await?;

let fixtures_dir = Path::new("../stone-prover/tests/fixtures/fibonacci");
let public_input =
std::fs::read_to_string(fixtures_dir.join("fibonacci_public_input.json")).unwrap();
let memory = std::fs::read(fixtures_dir.join("fibonacci_memory.bin")).unwrap();
let trace = std::fs::read(fixtures_dir.join("fibonacci_trace.bin")).unwrap();
let prover_config =
std::fs::read_to_string(fixtures_dir.join("cpu_air_prover_config.json")).unwrap();
let prover_parameters =
std::fs::read_to_string(fixtures_dir.join("cpu_air_params.json")).unwrap();

let response = call_prover(
&mut client,
public_input,
memory,
trace,
prover_config,
prover_parameters,
)
.await?;
println!("Got: '{}' from service", response.proof_hex);
Ok(())
}
1 change: 1 addition & 0 deletions madara-prover-rpc-client/src/prover.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
tonic::include_proto!("prover");
18 changes: 18 additions & 0 deletions madara-prover-rpc-server/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[package]
name = "madara-prover-rpc-server"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
prost = { workspace = true }
stone-prover = { path = "../stone-prover" }
tokio = { workspace = true }
tonic = { workspace = true }
serde_json = { workspace = true }
tokio-stream = "0.1.14"

[build-dependencies]
tonic-build = { workspace = true }

4 changes: 4 additions & 0 deletions madara-prover-rpc-server/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
fn main() -> Result<(), Box<dyn std::error::Error>> {
tonic_build::compile_protos("../protocols/prover.proto")?;
Ok(())
}
70 changes: 70 additions & 0 deletions madara-prover-rpc-server/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
use tokio_stream::wrappers::ReceiverStream;
use tonic::{transport::Server, Request, Response, Status};

use prover::ProverRequest;
use stone_prover::error::ProverError;
use stone_prover::models::{Proof, ProverConfig, ProverParameters, PublicInput};
use stone_prover::prover::run_prover_async;

use crate::prover::prover_server::{Prover, ProverServer};
use crate::prover::ProverResponse;

pub mod prover {
tonic::include_proto!("prover");
}

async fn call_prover(prover_request: &ProverRequest) -> Result<Proof, ProverError> {
let public_input: PublicInput = serde_json::from_str(&prover_request.public_input)?;
let prover_config: ProverConfig = serde_json::from_str(&prover_request.prover_config)?;
let prover_parameters: ProverParameters =
serde_json::from_str(&prover_request.prover_parameters)?;

run_prover_async(
&public_input,
&prover_request.memory,
&prover_request.trace,
&prover_config,
&prover_parameters,
)
.await
}

#[derive(Debug, Default)]
pub struct ProverService {}

#[tonic::async_trait]
impl Prover for ProverService {
type ProveStream = ReceiverStream<Result<ProverResponse, Status>>;

async fn prove(
&self,
request: Request<ProverRequest>,
) -> Result<Response<Self::ProveStream>, Status> {
let r = request.into_inner();
let (tx, rx) = tokio::sync::mpsc::channel(1);

tokio::spawn(async move {
let prover_result = call_prover(&r)
.await
.map(|proof| ProverResponse {
proof_hex: proof.proof_hex,
})
.map_err(|e| Status::invalid_argument(format!("Prover run failed: {e}")));
let _ = tx.send(prover_result).await;
});

Ok(Response::new(ReceiverStream::new(rx)))
}
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let address = "[::1]:8080".parse().unwrap();
let prover_service = ProverService::default();

Server::builder()
.add_service(ProverServer::new(prover_service))
.serve(address)
.await?;
Ok(())
}
19 changes: 19 additions & 0 deletions protocols/prover.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
syntax = "proto3";
package prover;

service Prover {
rpc Prove (ProverRequest) returns (stream ProverResponse);
}

message ProverRequest {

string public_input = 1;
bytes memory = 2;
bytes trace = 3;
string prover_config = 4;
string prover_parameters = 5;
}

message ProverResponse {
string proof_hex = 1;
}
2 changes: 2 additions & 0 deletions stone-prover/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true }
tempfile = "3.8.1"
thiserror = "1.0.50"
tokio = { workspace = true }

Loading

0 comments on commit 54aca74

Please sign in to comment.