diff --git a/prover/Cargo.toml b/prover/Cargo.toml index 75adec6d..e517b2c3 100644 --- a/prover/Cargo.toml +++ b/prover/Cargo.toml @@ -52,6 +52,8 @@ ethers = { version = "2.0.7", default_features = false, features = [ "ethers-solc", "abigen", ] } +primitive-types = "0.12.2" +reqwest = "0.11.22" [build-dependencies] cli-batteries = "=0.4" diff --git a/prover/src/cli.rs b/prover/src/cli.rs new file mode 100644 index 00000000..d162a7f9 --- /dev/null +++ b/prover/src/cli.rs @@ -0,0 +1,247 @@ +use crate::args::{Args, Out, Proof}; + +use ethers::abi::Address; +use ethers::providers::{Http, Provider}; +use halo2curves::bn256::{Bn256, Fr, G1Affine}; +use itertools::Itertools; +use lightclient_circuits::{ + committee_update_circuit::CommitteeUpdateCircuit, + sync_step_circuit::SyncStepCircuit, + util::{gen_srs, AppCircuit}, +}; +use primitive_types::U256; +use snark_verifier::{ + loader::halo2::halo2_ecc::halo2_base::halo2_proofs::{ + plonk::VerifyingKey, poly::kzg::commitment::ParamsKZG, + }, + system::halo2::Config, +}; +use snark_verifier_sdk::{halo2::aggregation::AggregationCircuit, read_instances, Snark}; +use std::str::FromStr; +use std::{ + fs::{self, File}, + future::Future, + io::Write, + path::Path, + sync::Arc, +}; + +use preprocessor::{fetch_rotation_args, fetch_step_args}; +ethers::contract::abigen!( + SnarkVerifierSol, + r#"[ + function verify(uint256[1] calldata pubInputs,bytes calldata proof) public view returns (bool) + ]"#, +); +pub(crate) async fn spec_app(proof: &Proof) -> eyre::Result<()> { + match proof { + Proof::CommitteeUpdate(args) => { + generic_circuit_cli::, _, _>( + args, + fetch_rotation_args, + "committee_update", + as AppCircuit>::Witness::default(), + ) + .await + } + Proof::SyncStep(args) => { + generic_circuit_cli::, _, _>( + args, + fetch_step_args, + "sync_step", + as AppCircuit>::Witness::default(), + ) + .await + } + Proof::Aggregation(args) => { + let params = gen_srs(CommitteeUpdateCircuit::::get_degree( + &args.app_config_path, + )); + + let app_pk = CommitteeUpdateCircuit::::read_pk( + ¶ms, + &args.app_pk_path, + & as AppCircuit>::Witness::default(), + ); + + let snark = read_snark( + ¶ms, + app_pk.get_vk(), + args.aggregation + .input_path + .as_ref() + .expect("path to SNARK is required"), + )?; + + generic_circuit_cli::( + &args.aggregation, + |_| async { Ok(vec![snark.clone()]) }, + "aggregation", + vec![snark.clone()], + ) + .await + } + } +} + +pub(crate) async fn generic_circuit_cli< + Circuit: AppCircuit, + FnFetch: FnOnce(String) -> Fut, + Fut: Future>, +>( + args: &Args, + fetch: FnFetch, + name: &str, + default_witness: Circuit::Witness, +) -> eyre::Result<()> { + let k = args + .k + .unwrap_or_else(|| Circuit::get_degree(&args.config_path)); + let params = gen_srs(k); + let pk_filename = format!("{}.pkey", name); + + match args.out { + Out::Snark => { + let pk = Circuit::read_or_create_pk( + ¶ms, + args.build_dir.join(&pk_filename), + &args.config_path, + true, + &default_witness, + ); + let witness = fetch(args.beacon_api_url.clone()).await?; + Circuit::gen_snark_shplonk( + ¶ms, + &pk, + &args.config_path, + Some(&args.path_out), + &witness, + ) + .map_err(|e| eyre::eyre!("Failed to generate proof: {}", e))?; + } + Out::Artifacts => { + Circuit::create_pk( + ¶ms, + args.build_dir.join(&pk_filename), + &args.config_path, + &default_witness, + ); + } + Out::EvmVerifier => { + let pk = Circuit::read_or_create_pk( + ¶ms, + args.build_dir.join(&pk_filename), + &args.config_path, + true, + &default_witness, + ); + let deplyment_code = Circuit::gen_evm_verifier_shplonk( + ¶ms, + &pk, + Some(&args.path_out), + &default_witness, + ) + .map_err(|e| eyre::eyre!("Failed to EVM verifier: {}", e))?; + println!("yul size: {}", deplyment_code.len()); + let sol_contract = halo2_solidity_verifier::fix_verifier_sol(args.path_out.clone(), 1) + .map_err(|e| eyre::eyre!("Failed to generate Solidity verifier: {}", e))?; + let mut sol_contract_path = args.path_out.clone(); + sol_contract_path.set_extension("sol"); + let mut f = File::create(sol_contract_path).unwrap(); + f.write(sol_contract.as_bytes()) + .map_err(|e| eyre::eyre!("Failed to write Solidity verifier: {}", e))?; + } + Out::Calldata => { + let pk = Circuit::read_or_create_pk( + ¶ms, + args.build_dir.join(&pk_filename), + &args.config_path, + true, + &default_witness, + ); + let witness = fetch(args.beacon_api_url.clone()).await?; + + let deplyment_code = + Circuit::gen_evm_verifier_shplonk(¶ms, &pk, None::<&Path>, &default_witness) + .map_err(|e| eyre::eyre!("Failed to EVM verifier: {}", e))?; + + Circuit::gen_calldata( + ¶ms, + &pk, + &args.config_path, + &args.path_out, + Some(deplyment_code), + &witness, + ) + .map_err(|e| eyre::eyre!("Failed to generate calldata: {}", e))?; + } + Out::Tx => { + let provider = Arc::new(Provider::new(Http::new( + args.ethereum_rpc.parse::().unwrap(), + ))); + + let pk = Circuit::read_or_create_pk( + ¶ms, + args.build_dir.join(&pk_filename), + &args.config_path, + true, + &default_witness, + ); + let witness = fetch(args.beacon_api_url.clone()).await?; + + let (proof, instances) = + Circuit::gen_evm_proof_shplonk(¶ms, &pk, &args.config_path, None, &witness) + .map_err(|e| eyre::eyre!("Failed to generate calldata: {}", e))?; + + let public_inputs = instances[0] + .iter() + .map(|pi| U256::from_little_endian(&pi.to_bytes())) + .collect_vec() + .try_into() + .unwrap(); + + let contract_addr = Address::from_str( + args.verifier_address + .as_ref() + .expect("verifier address is required"), + ) + .unwrap(); + let snark_verifier = SnarkVerifierSol::new(contract_addr, provider); + + let result = snark_verifier + .verify(public_inputs, proof.into()) + .await + .unwrap(); + + assert!(result); + } + } + Ok(()) +} + +fn read_snark( + params: &ParamsKZG, + vk: &VerifyingKey, + path: &Path, +) -> eyre::Result { + let path_str = path.to_str().unwrap(); + + let proof = fs::read(format!("{path_str}.proof")) + .map_err(|e| eyre::eyre!("Error reading proof file: {}", e))?; + let instances = read_instances(format!("{path_str}.instances")) + .map_err(|e| eyre::eyre!("Error reading instances file: {}", e))?; + + let protocol = snark_verifier::system::halo2::compile( + params, + vk, + Config::kzg() + .with_num_instance(instances.iter().map(|i| i.len()).collect()) + .with_accumulator_indices(None), // FIXME: use >::accumulator_indices() + ); + + Ok(Snark { + protocol, + proof, + instances, + }) +} diff --git a/prover/src/main.rs b/prover/src/main.rs index 7af4a213..e049e2b9 100644 --- a/prover/src/main.rs +++ b/prover/src/main.rs @@ -1,69 +1,33 @@ #![allow(incomplete_features)] #![feature(associated_type_bounds)] +mod cli; mod rpc; -use args::{Args, Cli, Out, Proof}; +mod rpc_api; +pub mod rpc_client; + +use args::Cli; use axum::{response::IntoResponse, routing::post, Router}; use cli_batteries::version; -use ethers::prelude::*; -use halo2curves::bn256::{Bn256, Fr, G1Affine}; + use http::StatusCode; -use itertools::Itertools; use jsonrpc_v2::{MapRouter as JsonRpcMapRouter, Server as JsonRpcServer}; -use lightclient_circuits::{ - committee_update_circuit::CommitteeUpdateCircuit, - sync_step_circuit::SyncStepCircuit, - util::{gen_srs, AppCircuit}, -}; -use preprocessor::{fetch_rotation_args, fetch_step_args}; -use rpc::{gen_evm_proof_rotation_circuit_handler, gen_evm_proof_step_circuit_handler}; -use snark_verifier::{ - loader::halo2::halo2_ecc::halo2_base::halo2_proofs::{ - plonk::VerifyingKey, poly::kzg::commitment::ParamsKZG, - }, - system::halo2::Config, -}; -use snark_verifier_sdk::{halo2::aggregation::AggregationCircuit, read_instances, Snark}; -use std::str::FromStr; -use std::{ - fs::{self, File}, - future::Future, - io::Write, - net::TcpListener, - path::Path, - sync::Arc, -}; +use crate::{cli::spec_app, rpc::jsonrpc_server}; + +use std::{net::TcpListener, sync::Arc}; mod args; use jsonrpc_v2::RequestObject as JsonRpcRequestObject; use crate::args::RpcOptions; pub type JsonRpcServerState = Arc>; -ethers::contract::abigen!( - SnarkVerifierSol, - r#"[ - function verify(uint256[1] calldata pubInputs,bytes calldata proof) public view returns (bool) - ]"#, -); - async fn app(options: Cli) -> eyre::Result<()> { match options.subcommand { args::Subcommands::Rpc(op) => { let RpcOptions { port } = op; let tcp_listener = TcpListener::bind(format!("0.0.0.0:{}", port)).unwrap(); - let rpc_server = Arc::new( - JsonRpcServer::new() - .with_method( - "genEvmProofAndInstancesStepSyncCircuit", - gen_evm_proof_step_circuit_handler, - ) - .with_method( - "genEvmProofAndInstancesRotationCircuit", - gen_evm_proof_rotation_circuit_handler, - ) - .finish_unwrapped(), - ); + let rpc_server = Arc::new(jsonrpc_server()); let router = Router::new() .route("/rpc", post(handler)) .with_state(rpc_server); @@ -85,192 +49,6 @@ async fn app(options: Cli) -> eyre::Result<()> { Ok(()) } -async fn spec_app(proof: &Proof) -> eyre::Result<()> { - match proof { - Proof::CommitteeUpdate(args) => { - generic_circuit_cli::, _, _>( - args, - fetch_rotation_args, - "committee_update", - as AppCircuit>::Witness::default(), - ) - .await - } - Proof::SyncStep(args) => { - generic_circuit_cli::, _, _>( - args, - fetch_step_args, - "sync_step", - as AppCircuit>::Witness::default(), - ) - .await - } - Proof::Aggregation(args) => { - let params = gen_srs(CommitteeUpdateCircuit::::get_degree( - &args.app_config_path, - )); - - let app_pk = CommitteeUpdateCircuit::::read_pk( - ¶ms, - &args.app_pk_path, - & as AppCircuit>::Witness::default(), - ); - - let snark = read_snark( - ¶ms, - app_pk.get_vk(), - args.aggregation - .input_path - .as_ref() - .expect("path to SNARK is required"), - )?; - - generic_circuit_cli::( - &args.aggregation, - |_| async { Ok(vec![snark.clone()]) }, - "aggregation", - vec![snark.clone()], - ) - .await - } - } -} - -async fn generic_circuit_cli< - Circuit: AppCircuit, - FnFetch: FnOnce(String) -> Fut, - Fut: Future>, ->( - args: &Args, - fetch: FnFetch, - name: &str, - default_witness: Circuit::Witness, -) -> eyre::Result<()> { - let k = args - .k - .unwrap_or_else(|| Circuit::get_degree(&args.config_path)); - let params = gen_srs(k); - let pk_filename = format!("{}.pkey", name); - - match args.out { - Out::Snark => { - let pk = Circuit::read_or_create_pk( - ¶ms, - args.build_dir.join(&pk_filename), - &args.config_path, - true, - &default_witness, - ); - let witness = fetch(args.beacon_api_url.clone()).await?; - Circuit::gen_snark_shplonk( - ¶ms, - &pk, - &args.config_path, - Some(&args.path_out), - &witness, - ) - .map_err(|e| eyre::eyre!("Failed to generate proof: {}", e))?; - } - Out::Artifacts => { - Circuit::create_pk( - ¶ms, - args.build_dir.join(&pk_filename), - &args.config_path, - &default_witness, - ); - } - Out::EvmVerifier => { - let pk = Circuit::read_or_create_pk( - ¶ms, - args.build_dir.join(&pk_filename), - &args.config_path, - true, - &default_witness, - ); - let deplyment_code = Circuit::gen_evm_verifier_shplonk( - ¶ms, - &pk, - Some(&args.path_out), - &default_witness, - ) - .map_err(|e| eyre::eyre!("Failed to EVM verifier: {}", e))?; - println!("yul size: {}", deplyment_code.len()); - let sol_contract = halo2_solidity_verifier::fix_verifier_sol(args.path_out.clone(), 1) - .map_err(|e| eyre::eyre!("Failed to generate Solidity verifier: {}", e))?; - let mut sol_contract_path = args.path_out.clone(); - sol_contract_path.set_extension("sol"); - let mut f = File::create(sol_contract_path).unwrap(); - f.write(sol_contract.as_bytes()) - .map_err(|e| eyre::eyre!("Failed to write Solidity verifier: {}", e))?; - } - Out::Calldata => { - let pk = Circuit::read_or_create_pk( - ¶ms, - args.build_dir.join(&pk_filename), - &args.config_path, - true, - &default_witness, - ); - let witness = fetch(args.beacon_api_url.clone()).await?; - - let deplyment_code = - Circuit::gen_evm_verifier_shplonk(¶ms, &pk, None::<&Path>, &default_witness) - .map_err(|e| eyre::eyre!("Failed to EVM verifier: {}", e))?; - - Circuit::gen_calldata( - ¶ms, - &pk, - &args.config_path, - &args.path_out, - Some(deplyment_code), - &witness, - ) - .map_err(|e| eyre::eyre!("Failed to generate calldata: {}", e))?; - } - Out::Tx => { - let provider = Arc::new(Provider::new(Http::new( - args.ethereum_rpc.parse::().unwrap(), - ))); - - let pk = Circuit::read_or_create_pk( - ¶ms, - args.build_dir.join(&pk_filename), - &args.config_path, - true, - &default_witness, - ); - let witness = fetch(args.beacon_api_url.clone()).await?; - - let (proof, instances) = - Circuit::gen_evm_proof_shplonk(¶ms, &pk, &args.config_path, None, &witness) - .map_err(|e| eyre::eyre!("Failed to generate calldata: {}", e))?; - - let public_inputs = instances[0] - .iter() - .map(|pi| U256::from_little_endian(&pi.to_bytes())) - .collect_vec() - .try_into() - .unwrap(); - - let contract_addr = Address::from_str( - args.verifier_address - .as_ref() - .expect("verifier address is required"), - ) - .unwrap(); - let snark_verifier = SnarkVerifierSol::new(contract_addr, provider); - - let result = snark_verifier - .verify(public_inputs, proof.into()) - .await - .unwrap(); - - assert!(result); - } - } - Ok(()) -} - async fn handler( axum::extract::State(rpc_server): axum::extract::State, axum::Json(rpc_call): axum::Json, @@ -292,30 +70,3 @@ async fn handler( fn main() { cli_batteries::run(version!(), app); } - -fn read_snark( - params: &ParamsKZG, - vk: &VerifyingKey, - path: &Path, -) -> eyre::Result { - let path_str = path.to_str().unwrap(); - - let proof = fs::read(format!("{path_str}.proof")) - .map_err(|e| eyre::eyre!("Error reading proof file: {}", e))?; - let instances = read_instances(format!("{path_str}.instances")) - .map_err(|e| eyre::eyre!("Error reading instances file: {}", e))?; - - let protocol = snark_verifier::system::halo2::compile( - params, - vk, - Config::kzg() - .with_num_instance(instances.iter().map(|i| i.len()).collect()) - .with_accumulator_indices(None), // FIXME: use >::accumulator_indices() - ); - - Ok(Snark { - protocol, - proof, - instances, - }) -} diff --git a/prover/src/rpc.rs b/prover/src/rpc.rs index 5da9f37c..7c4fb054 100644 --- a/prover/src/rpc.rs +++ b/prover/src/rpc.rs @@ -1,4 +1,4 @@ -use super::{args, args::Spec}; +use super::args::Spec; use ethers::prelude::*; use halo2curves::bn256::Fr; @@ -11,45 +11,20 @@ use lightclient_circuits::{ util::{gen_srs, AppCircuit, Halo2ConfigPinning}, }; use preprocessor::{fetch_rotation_args, fetch_step_args}; -use serde::{Deserialize, Serialize}; +use jsonrpc_v2::{MapRouter as JsonRpcMapRouter, Server as JsonRpcServer}; use snark_verifier::loader::halo2::halo2_ecc::halo2_base::gates::builder::CircuitBuilderStage; use snark_verifier_sdk::{ - evm::gen_evm_verifier_shplonk, gen_pk, halo2::{aggregation::AggregationCircuit, gen_snark_shplonk}, CircuitExt, Snark, SHPLONK, }; - use std::path::PathBuf; -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct GenProofRotationParams { - spec: args::Spec, - - k: Option, - #[serde(default = "default_beacon_api")] - beacon_api: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct GenProofStepParams { - spec: args::Spec, - - k: Option, - #[serde(default = "default_beacon_api")] - beacon_api: String, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct EvmProofResult { - proof: Vec, - public_inputs: Vec, -} - -fn default_beacon_api() -> String { - String::from("http://127.0.0.1:5052") -} +use crate::rpc_api::{ + EvmProofResult, GenProofRotationParams, GenProofStepParams, EVM_PROOF_ROTATION_CIRCUIT, + EVM_PROOF_STEP_CIRCUIT, +}; fn gen_app_snark( app_config_path: PathBuf, @@ -60,9 +35,10 @@ fn gen_app_snark( &app_config_path, )); - let app_pk = CommitteeUpdateCircuit::::read_pk( + let app_pk = CommitteeUpdateCircuit::::create_pk( ¶ms, app_pk_path, + &app_config_path, & as AppCircuit>::Witness::default(), ); @@ -85,7 +61,7 @@ fn gen_evm_proof( let k = k.unwrap_or_else(|| C::get_degree(&config_path)); let params = gen_srs(k); - let pk = C::read_pk(¶ms, build_dir.join(pk_filename), &witness); + let pk = C::create_pk(¶ms, build_dir.join(pk_filename), &config_path, &witness); let (proof, instances) = C::gen_evm_proof_shplonk(¶ms, &pk, &config_path, None, &witness) .map_err(|e| eyre::eyre!("Failed to generate calldata: {}", e)) @@ -104,16 +80,16 @@ pub(crate) async fn gen_evm_proof_rotation_circuit_handler( } = params; // TODO: use config/build paths from CLI flags - let app_config_path = PathBuf::from("./lightclient-circuits/config/committee_update.json"); + let app_config_path = PathBuf::from("../lightclient-circuits/config/committee_update.json"); let app_pk_path = PathBuf::from("./build/committee_update_circuit.pkey"); let agg_l2_config_path = - PathBuf::from("./lightclient-circuits/config/committee_update_aggregation_2.json"); + PathBuf::from("../lightclient-circuits/config/committee_update_aggregation_2.json"); let agg_l1_config_path = - PathBuf::from("./lightclient-circuits/config/committee_update_aggregation_1.json"); - let build_dir = PathBuf::from("./build"); + PathBuf::from("../lightclient-circuits/config/committee_update_aggregation_1.json"); + let _build_dir = PathBuf::from("./build"); - let (l0_snark, pk_filename) = match spec { + let (l0_snark, _pk_filename) = match spec { Spec::Minimal => { let witness = fetch_rotation_args(beacon_api).await?; ( @@ -153,7 +129,7 @@ pub(crate) async fn gen_evm_proof_rotation_circuit_handler( Some(pinning.break_points), lookup_bits, &p1, - std::iter::once(l0_snark.clone()), + std::iter::once(l0_snark), ); circuit.expose_previous_instances(false); @@ -176,7 +152,7 @@ pub(crate) async fn gen_evm_proof_rotation_circuit_handler( let mut circuit = AggregationCircuit::prover::( &p2, - std::iter::once(l1_snark.clone()), + std::iter::once(l1_snark), pinning.break_points, ); circuit.expose_previous_instances(true); @@ -208,7 +184,7 @@ pub(crate) async fn gen_evm_proof_step_circuit_handler( beacon_api, } = params.clone(); - let config_path = PathBuf::from("./lightclient-circuits/config/step_sync.json"); + let config_path = PathBuf::from("../lightclient-circuits/config/sync_step.json"); let build_dir = PathBuf::from("./build"); let (proof, instances) = match spec { @@ -258,3 +234,13 @@ pub(crate) async fn gen_evm_proof_step_circuit_handler( public_inputs, }) } + +pub(crate) fn jsonrpc_server() -> JsonRpcServer { + JsonRpcServer::new() + .with_method(EVM_PROOF_STEP_CIRCUIT, gen_evm_proof_step_circuit_handler) + .with_method( + EVM_PROOF_ROTATION_CIRCUIT, + gen_evm_proof_rotation_circuit_handler, + ) + .finish_unwrapped() +} diff --git a/prover/src/rpc_api.rs b/prover/src/rpc_api.rs new file mode 100644 index 00000000..7526af57 --- /dev/null +++ b/prover/src/rpc_api.rs @@ -0,0 +1,34 @@ +use crate::args; +use primitive_types::U256; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct GenProofRotationParams { + pub spec: args::Spec, + + pub k: Option, + #[serde(default = "default_beacon_api")] + pub beacon_api: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct GenProofStepParams { + pub spec: args::Spec, + + pub k: Option, + #[serde(default = "default_beacon_api")] + pub beacon_api: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct EvmProofResult { + pub proof: Vec, + pub public_inputs: Vec, +} + +fn default_beacon_api() -> String { + String::from("http://127.0.0.1:5052") +} + +pub const EVM_PROOF_STEP_CIRCUIT: &str = "genEvmProofAndInstancesStepSyncCircuit"; +pub const EVM_PROOF_ROTATION_CIRCUIT: &str = "genEvmProofAndInstancesRotationCircuit"; diff --git a/prover/src/rpc_client.rs b/prover/src/rpc_client.rs new file mode 100644 index 00000000..97c6b881 --- /dev/null +++ b/prover/src/rpc_client.rs @@ -0,0 +1,122 @@ +use jsonrpc_v2::{Error, Id, RequestObject, V2}; +use reqwest::IntoUrl; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; +use url::Url; + +use crate::rpc_api::{ + EvmProofResult, GenProofRotationParams, GenProofStepParams, EVM_PROOF_ROTATION_CIRCUIT, + EVM_PROOF_STEP_CIRCUIT, +}; + +/// Error object in a response +#[derive(Deserialize)] +pub struct JsonRpcError { + pub code: i64, + pub message: String, +} + +#[derive(Deserialize)] +#[serde(untagged)] +pub enum JsonRpcResponse { + Result { + jsonrpc: V2, + result: R, + id: Id, + }, + Error { + jsonrpc: V2, + error: JsonRpcError, + id: Id, + }, +} + +/// RPC client to send JSON-RPC requests to the prover +pub struct Client { + client: reqwest::Client, + api_url: Url, +} + +impl Client { + pub fn new(url: impl IntoUrl) -> Self { + Self { + client: reqwest::Client::new(), + api_url: url.into_url().unwrap(), + } + } + /// Generates a proof along with instance values for committee Rotation circuit + pub async fn gen_evm_proof_rotation_circuit( + &self, + params: GenProofRotationParams, + ) -> Result { + self.call(EVM_PROOF_ROTATION_CIRCUIT, params).await + } + + /// Generates a proof along with instance values for Step circuit + pub async fn gen_evm_proof_step_circuit( + &self, + params: GenProofStepParams, + ) -> Result { + self.call(EVM_PROOF_STEP_CIRCUIT, params).await + } + + /// Utility method for sending RPC requests over HTTP + async fn call(&self, method_name: &str, params: P) -> Result + where + P: Serialize, + R: DeserializeOwned, + { + let rpc_req = RequestObject::request() + .with_method(method_name) + .with_params(serde_json::to_value(params)?) + .finish(); + + let request = self.client.post(self.api_url.clone()).json(&rpc_req); + + let rpc_res = request.send().await?.error_for_status()?.json().await?; + + match rpc_res { + JsonRpcResponse::Result { result, .. } => Ok(result), + JsonRpcResponse::Error { error, .. } => Err(Error::Full { + data: None, + code: error.code, + message: error.message, + }), + } + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::args; + use jsonrpc_v2::Error; + + #[tokio::test] + #[ignore = "requires a running prover"] + async fn test_rpc_client() { + let client = Client::new("http://localhost:3000/rpc"); + + let p = GenProofStepParams { + spec: args::Spec::Testnet, + k: Some(21), + beacon_api: String::from("http://3.128.78.74:5052"), + }; + let r = client.gen_evm_proof_step_circuit(p).await; + + match r { + Ok(r) => { + println!("res: {:?}", r); + } + Err(Error::Full { + data: _, + code, + message, + }) => { + println!("Error: {}, Code: {}", message, code); + } + Err(Error::Provided { code, message }) => { + println!("Error: {}, Code: {}", message, code); + } + } + } +}