Refactor docs and make Input API more explicit (#235)

This commit is contained in:
Han
2025-12-06 12:02:14 +09:00
committed by GitHub
parent 3d844b9f8b
commit 8f6cee8a32
64 changed files with 1297 additions and 456 deletions

View File

@@ -34,16 +34,16 @@ ere-zisk = { workspace = true, features = ["zkvm"], optional = true }
ere-zkvm-interface = { workspace = true, features = ["clap"] }
[dev-dependencies]
prost-build.workspace = true
tempfile.workspace = true
twirp-build.workspace = true
[lints]
workspace = true
[build-dependencies]
prost-build.workspace = true
twirp-build.workspace = true
[features]
default = []
default = ["client"]
client = []
server = ["dep:clap", "dep:tower-http", "dep:tracing", "dep:tracing-subscriber", "tokio/macros", "tokio/rt-multi-thread", "tokio/signal"]
# zkVM

View File

@@ -1,8 +0,0 @@
fn main() -> Result<(), Box<dyn std::error::Error>> {
prost_build::Config::new()
.type_attribute(".", "#[derive(serde::Serialize, serde::Deserialize)]") // enable support for JSON encoding
.service_generator(twirp_build::service_generator())
.compile_protos(&["./proto/api.proto"], &["./proto"])
.expect("error compiling protos");
Ok(())
}

View File

@@ -0,0 +1,278 @@
// This file is @generated by prost-build.
#[derive(serde::Serialize, serde::Deserialize)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct ExecuteRequest {
#[prost(bytes = "vec", tag = "1")]
pub input_stdin: ::prost::alloc::vec::Vec<u8>,
#[prost(bytes = "vec", optional, tag = "2")]
pub input_proofs: ::core::option::Option<::prost::alloc::vec::Vec<u8>>,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct ExecuteResponse {
#[prost(oneof = "execute_response::Result", tags = "1, 2")]
pub result: ::core::option::Option<execute_response::Result>,
}
/// Nested message and enum types in `ExecuteResponse`.
pub mod execute_response {
#[derive(serde::Serialize, serde::Deserialize)]
#[derive(Clone, PartialEq, ::prost::Oneof)]
pub enum Result {
#[prost(message, tag = "1")]
Ok(super::ExecuteOk),
#[prost(string, tag = "2")]
Err(::prost::alloc::string::String),
}
}
#[derive(serde::Serialize, serde::Deserialize)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct ExecuteOk {
#[prost(bytes = "vec", tag = "1")]
pub public_values: ::prost::alloc::vec::Vec<u8>,
#[prost(bytes = "vec", tag = "2")]
pub report: ::prost::alloc::vec::Vec<u8>,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct ProveRequest {
#[prost(bytes = "vec", tag = "1")]
pub input_stdin: ::prost::alloc::vec::Vec<u8>,
#[prost(bytes = "vec", optional, tag = "2")]
pub input_proofs: ::core::option::Option<::prost::alloc::vec::Vec<u8>>,
#[prost(enumeration = "ProofKind", tag = "3")]
pub proof_kind: i32,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct ProveResponse {
#[prost(oneof = "prove_response::Result", tags = "1, 2")]
pub result: ::core::option::Option<prove_response::Result>,
}
/// Nested message and enum types in `ProveResponse`.
pub mod prove_response {
#[derive(serde::Serialize, serde::Deserialize)]
#[derive(Clone, PartialEq, ::prost::Oneof)]
pub enum Result {
#[prost(message, tag = "1")]
Ok(super::ProveOk),
#[prost(string, tag = "2")]
Err(::prost::alloc::string::String),
}
}
#[derive(serde::Serialize, serde::Deserialize)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct ProveOk {
#[prost(bytes = "vec", tag = "1")]
pub public_values: ::prost::alloc::vec::Vec<u8>,
#[prost(bytes = "vec", tag = "2")]
pub proof: ::prost::alloc::vec::Vec<u8>,
#[prost(bytes = "vec", tag = "3")]
pub report: ::prost::alloc::vec::Vec<u8>,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct VerifyRequest {
#[prost(bytes = "vec", tag = "1")]
pub proof: ::prost::alloc::vec::Vec<u8>,
#[prost(enumeration = "ProofKind", tag = "2")]
pub proof_kind: i32,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct VerifyResponse {
#[prost(oneof = "verify_response::Result", tags = "1, 2")]
pub result: ::core::option::Option<verify_response::Result>,
}
/// Nested message and enum types in `VerifyResponse`.
pub mod verify_response {
#[derive(serde::Serialize, serde::Deserialize)]
#[derive(Clone, PartialEq, ::prost::Oneof)]
pub enum Result {
#[prost(message, tag = "1")]
Ok(super::VerifyOk),
#[prost(string, tag = "2")]
Err(::prost::alloc::string::String),
}
}
#[derive(serde::Serialize, serde::Deserialize)]
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct VerifyOk {
#[prost(bytes = "vec", tag = "1")]
pub public_values: ::prost::alloc::vec::Vec<u8>,
}
#[derive(serde::Serialize, serde::Deserialize)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
#[repr(i32)]
pub enum ProofKind {
Compressed = 0,
Groth16 = 1,
}
impl ProofKind {
/// String value of the enum field names used in the ProtoBuf definition.
///
/// The values are not transformed in any way and thus are considered stable
/// (if the ProtoBuf definition does not change) and safe for programmatic use.
pub fn as_str_name(&self) -> &'static str {
match self {
Self::Compressed => "Compressed",
Self::Groth16 => "Groth16",
}
}
/// Creates an enum from field names used in the ProtoBuf definition.
pub fn from_str_name(value: &str) -> ::core::option::Option<Self> {
match value {
"Compressed" => Some(Self::Compressed),
"Groth16" => Some(Self::Groth16),
_ => None,
}
}
}
pub use twirp;
#[twirp::async_trait::async_trait]
pub trait ZkvmService: Send + Sync {
async fn execute(
&self,
req: twirp::Request<ExecuteRequest>,
) -> twirp::Result<twirp::Response<ExecuteResponse>>;
async fn prove(
&self,
req: twirp::Request<ProveRequest>,
) -> twirp::Result<twirp::Response<ProveResponse>>;
async fn verify(
&self,
req: twirp::Request<VerifyRequest>,
) -> twirp::Result<twirp::Response<VerifyResponse>>;
}
#[twirp::async_trait::async_trait]
impl<T> ZkvmService for std::sync::Arc<T>
where
T: ZkvmService + Sync + Send,
{
async fn execute(
&self,
req: twirp::Request<ExecuteRequest>,
) -> twirp::Result<twirp::Response<ExecuteResponse>> {
T::execute(&*self, req).await
}
async fn prove(
&self,
req: twirp::Request<ProveRequest>,
) -> twirp::Result<twirp::Response<ProveResponse>> {
T::prove(&*self, req).await
}
async fn verify(
&self,
req: twirp::Request<VerifyRequest>,
) -> twirp::Result<twirp::Response<VerifyResponse>> {
T::verify(&*self, req).await
}
}
pub fn router<T>(api: T) -> twirp::Router
where
T: ZkvmService + Clone + Send + Sync + 'static,
{
twirp::details::TwirpRouterBuilder::new("/api.ZkvmService", api)
.route(
"/Execute",
|api: T, req: twirp::Request<ExecuteRequest>| async move {
api.execute(req).await
},
)
.route(
"/Prove",
|api: T, req: twirp::Request<ProveRequest>| async move {
api.prove(req).await
},
)
.route(
"/Verify",
|api: T, req: twirp::Request<VerifyRequest>| async move {
api.verify(req).await
},
)
.build()
}
#[twirp::async_trait::async_trait]
impl ZkvmService for twirp::client::Client {
async fn execute(
&self,
req: twirp::Request<ExecuteRequest>,
) -> twirp::Result<twirp::Response<ExecuteResponse>> {
self.request("api.ZkvmService/Execute", req).await
}
async fn prove(
&self,
req: twirp::Request<ProveRequest>,
) -> twirp::Result<twirp::Response<ProveResponse>> {
self.request("api.ZkvmService/Prove", req).await
}
async fn verify(
&self,
req: twirp::Request<VerifyRequest>,
) -> twirp::Result<twirp::Response<VerifyResponse>> {
self.request("api.ZkvmService/Verify", req).await
}
}
#[allow(dead_code)]
pub mod handler {
use super::*;
pub struct ZkvmServiceHandler {
inner: std::sync::Arc<dyn ZkvmService>,
}
impl ZkvmServiceHandler {
#[allow(clippy::new_ret_no_self)]
pub fn new<M: ZkvmService + 'static>(inner: M) -> Self {
Self {
inner: std::sync::Arc::new(inner),
}
}
}
#[twirp::async_trait::async_trait]
impl twirp::client::DirectHandler for ZkvmServiceHandler {
fn service(&self) -> &str {
"api.ZkvmService"
}
async fn handle(
&self,
method: &str,
req: twirp::reqwest::Request,
) -> twirp::Result<twirp::reqwest::Response> {
match method {
"Execute" => {
twirp::details::encode_response(
self
.inner
.execute(twirp::details::decode_request(req).await?)
.await?,
)
}
"Prove" => {
twirp::details::encode_response(
self
.inner
.prove(twirp::details::decode_request(req).await?)
.await?,
)
}
"Verify" => {
twirp::details::encode_response(
self
.inner
.verify(twirp::details::decode_request(req).await?)
.await?,
)
}
_ => {
Err(
twirp::bad_route(
format!(
"unknown rpc `{method}` for service `{}`, url: {:?}",
"api.ZkvmService", req.url()
),
),
)
}
}
}
}
}

View File

@@ -1,9 +1,11 @@
pub mod client;
#[rustfmt::skip]
pub mod api;
#[allow(dead_code)]
pub(crate) mod api {
include!(concat!(env!("OUT_DIR"), "/api.rs"));
}
#[cfg(feature = "client")]
pub mod client;
#[cfg(feature = "server")]
pub mod server;
#[cfg(test)]
mod test;

View File

@@ -0,0 +1,28 @@
use std::{env, fs, path::PathBuf};
/// To sync generated `api.rs`, run:
///
/// ```
/// cargo test --package ere-server --no-default-features --lib -- test::api_generation --exact
/// ```
#[test]
fn api_generation() {
let tempdir = tempfile::tempdir().unwrap();
let dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
prost_build::Config::new()
.out_dir(tempdir.path())
.type_attribute(".", "#[derive(serde::Serialize, serde::Deserialize)]") // enable support for JSON encoding
.service_generator(twirp_build::service_generator())
.compile_protos(&[dir.join("proto").join("api.proto")], &[dir.join("proto")])
.unwrap();
let latest = tempdir.path().join("api.rs");
let current = dir.join("src").join("api.rs");
// If it's in CI env, don't overwrite but only check if it's up-to-date.
if env::var_os("GITHUB_ACTIONS").is_none() {
fs::copy(&latest, &current).unwrap();
}
assert_eq!(fs::read(&latest).unwrap(), fs::read(&current).unwrap());
}