From b23875cd92634c36c7dbcfc000d4f91a5984e89f Mon Sep 17 00:00:00 2001 From: cpu Date: Thu, 23 Nov 2023 07:42:45 -0800 Subject: [PATCH] Overhaul leader CLI such that flags are grouped by subcommand --- README.md | 175 ++++++++++++++++++++++++++++++--------------- leader/src/cli.rs | 82 ++++++++++++--------- leader/src/main.rs | 33 ++++----- rpc/src/cli.rs | 2 +- 4 files changed, 182 insertions(+), 110 deletions(-) diff --git a/README.md b/README.md index 212ad7f2..18fccd0e 100644 --- a/README.md +++ b/README.md @@ -7,18 +7,19 @@ A composition of [`paladin`](https://github.com/0xPolygonZero/paladin) and [`plo - [Ops](#ops) - [Worker](#worker) - [Leader](#leader) - - [Usage](#usage) + - [RPC](#rpc) + - [Verifier](#verifier) + - [Leader Usage](#leader-usage) + - [stdio](#stdio) + - [Jerigon](#jerigon) + - [HTTP](#http) - [Paladin Runtime](#paladin-runtime) - [Starting an AMQP enabled cluster](#starting-an-amqp-enabled-cluster) - [Start worker(s)](#start-workers) - [Start leader](#start-leader) - [Starting an in-memory (single process) cluster](#starting-an-in-memory-single-process-cluster) - - [Input mode](#input-mode) - - [stdin](#stdin) - - [HTTP](#http) - - [Jerigon](#jerigon) - - [Verifier](#verifier) - - [RPC](#rpc) + - [Verifier Usage](#verifier-usage) + - [RPC Usage](#rpc-usage) - [Docker](#docker) @@ -34,6 +35,14 @@ worker └── main.rs leader ├── Cargo.toml +└── src + └── main.rs +rpc +├── Cargo.toml +└── src + └── main.rs +verifier +├── Cargo.toml └── src └── main.rs ``` @@ -46,98 +55,150 @@ The worker process. Receives proof operations from the leader, and returns the r ### Leader The leader process. Receives proof generation requests, and distributes them to workers. -## Usage +### RPC + +A binary to generate the block trace format expected by the leader. + +### Verifier -Leader binary arguments and options are comprised of paladin configuration and input mode: +A binary to verify the correctness of the generated proof. + +## Leader Usage + +The leader has various subcommands for different io modes. The leader binary arguments are as follows: ``` -cargo r --bin leader -- --help +cargo r --release --bin leader -- --help -Usage: leader [OPTIONS] +Usage: leader [OPTIONS] + +Commands: + stdio Reads input from stdin and writes output to stdout + jerigon Reads input from a Jerigon node and writes output to stdout + http Reads input from HTTP and writes output to a directory + help Print this message or the help of the given subcommand(s) Options: - -m, --mode - The input mode. If `std-io`, the input is read from stdin. If `http`, the input is read from HTTP requests. If `jerigon`, the input is read from the `debug_traceBlockByNumber` and `eth_getBlockByNumber` RPC methods from Jerigon [default: std-io] [possible values: std-io, http, jerigon] - -p, --port - The port to listen on when using the `http` mode [default: 8080] - -o, --output-dir - The directory to which output should be written (`http` mode only) - --rpc-url - The RPC URL to use when using the `jerigon` mode - -b, --block-number - The block number to use when using the `jerigon` mode - -r, --runtime - Specifies the paladin runtime to use [default: amqp] [possible values: amqp, in-memory] - -n, --num-workers - Specifies the number of worker threads to spawn (in memory runtime only) - -p, --previous-proof - The previous proof output - -h, --help - Print help + -n, --num-workers Specifies the number of worker threads to spawn (in memory runtime only) + -r, --runtime Specifies the paladin runtime mode [default: amqp] [possible values: amqp, in-memory] + --amqp-uri Specifies the URI for the AMQP broker (AMQP runtime only) + -h, --help Print help ``` -### Paladin Runtime +### stdio +The stdio command reads proof input from stdin and writes output to stdout. -Paladin supports both an AMQP and in-memory runtime. The in-memory runtime will emulate a cluster in memory within a single process, and is useful for testing. The AMQP runtime is geared for a production environment. The AMQP runtime requires a running AMQP broker and spinning up worker processes. The AMQP uri can be specified with the `--amqp-uri` flag or be set with the `AMQP_URI` environment variable. +``` +cargo r --release --bin leader stdio --help -#### Starting an AMQP enabled cluster +Reads input from stdin and writes output to stdout -##### Start worker(s) +Usage: leader stdio [OPTIONS] -Start worker process(es). The default paladin runtime is AMQP, so no additional flags are required to enable it. +Options: + -f, --previous-proof The previous proof output + -h, --help Print help +``` +Pull prover input from the rpc binary. ```bash -RUST_LOG=debug cargo r --release --bin worker +cargo r --release --bin rpc fetch --rpc-url -b 6 > ./input/block_6.json ``` -##### Start leader +Pipe the block input to the leader binary. +```bash +cat ./input/block_6.json | cargo r --release --bin leader -- -r in-memory stdio > ./output/proof_6.json +``` -Start the leader process with the desired [input mode](#input-mode). The default paladin runtime is AMQP, so no additional flags are required to enable it. +### Jerigon + +The Jerigon command reads proof input from a Jerigon node and writes output to stdout. -```bash -RUST_LOG=debug cargo r --release --bin leader -- --mode http --output-dir ./output ``` +cargo r --release --bin leader jerigon --help -#### Starting an in-memory (single process) cluster +Reads input from a Jerigon node and writes output to stdout -Paladin can emulate a cluster in memory within a single process. Useful for testing purposes. +Usage: leader jerigon [OPTIONS] --rpc-url --block-number + +Options: + -u, --rpc-url + -b, --block-number The block number for which to generate a proof + -f, --previous-proof The previous proof output + -h, --help Print help +``` +Prove a block. ```bash -RUST_LOG=debug cargo r --release --bin leader -- --mode http --runtime in-memory --output-dir ./output +cargo r --release --bin leader -- -r in-memory jerigon -u -b 16 > ./output/proof_16.json +``` + +### HTTP + +The HTTP command reads proof input from HTTP and writes output to a directory. ``` +cargo r --release --bin leader http --help -### Input mode -Pass JSON encoded prover input to stdin or over HTTP, or point the leader to a Jerigon RPC endpoint to retrieve the prover input from the `debug_traceBlockByNumber` and `eth_getBlockByNumber` RPC methods. +Reads input from HTTP and writes output to a directory -See [`prover_input.rs`](/leader/src/prover_input.rs) for the input format. +Usage: leader http [OPTIONS] --output-dir -The `std-io` and `http` examples below assume some prover input is stored in `./block_121.json`. +Options: + -p, --port The port on which to listen [default: 8080] + -o, --output-dir The directory to which output should be written + -h, --help Print help +``` -#### stdin +Pull prover input from the rpc binary. +```bash +cargo r --release --bin rpc fetch -u -b 6 > ./input/block_6.json +``` +Start the server. ```bash -cat ./block_121.json | RUST_LOG=debug cargo r --release --bin leader > ./output/proof_121.json +RUST_LOG=debug cargo r --release --bin leader http --output-dir ./output ``` -#### HTTP +Note that HTTP mode requires a [slightly modified input format](./leader/src/http.rs#L56) from the rest of the commands. In particular, [the previous proof is expected to be part of the payload](./leader/src/http.rs#L58). This is due to the fact that the HTTP mode may handle multiple requests concurrently, and thus the previous proof cannot reasonably be given by a command line argument like the other modes. + + +Using `jq` we can merge the previous proof and the block input into a single JSON object. -Start the server ```bash -RUST_LOG=debug cargo r --release --bin leader -- --mode http --output-dir ./output +jq -s '{prover_input: .[0], previous: .[1]}' ./input/block_6.json ./output/proof_5.json | curl -X POST -H "Content-Type: application/json" -d @- http://localhost:8080/prove + ``` -Once initialized, send a request: +### Paladin Runtime + +Paladin supports both an AMQP and in-memory runtime. The in-memory runtime will emulate a cluster in memory within a single process, and is useful for testing. The AMQP runtime is geared for a production environment. The AMQP runtime requires a running AMQP broker and spinning up worker processes. The AMQP uri can be specified with the `--amqp-uri` flag or be set with the `AMQP_URI` environment variable. + +#### Starting an AMQP enabled cluster + +##### Start worker(s) + +Start worker process(es). The default paladin runtime is AMQP, so no additional flags are required to enable it. + ```bash -curl -X POST -H "Content-Type: application/json" -d @./block_121.json http://localhost:8080/prove +RUST_LOG=debug cargo r --release --bin worker ``` -#### Jerigon +##### Start leader + +Start the leader process with the desired [command](#leader-usage). The default paladin runtime is AMQP, so no additional flags are required to enable it. ```bash -RUST_LOG=debug cargo r --release --bin leader -- --mode jerigon --runtime in-memory --rpc-url --block-number 16 > ./output/proof_16.json +RUST_LOG=debug cargo r --release --bin leader jerigon -u -b 16 > ./output/proof_16.json ``` +#### Starting an in-memory (single process) cluster + +Paladin can emulate a cluster in memory within a single process. Useful for testing purposes. + +```bash +cat ./input/block_6.json | cargo r --release --bin leader -- -r in-memory stdio > ./output/proof_6.json +``` -## Verifier +## Verifier Usage A verifier binary is provided to verify the correctness of the generated proof. The verifier expects output in the format generated by the leader. The verifier binary arguments are as follows: ``` @@ -155,7 +216,7 @@ Example: cargo r --release --bin verifier -- -f ./output/proof_16.json ``` -## RPC +## RPC Usage An rpc binary is provided to generate the block trace format expected by the leader. ``` diff --git a/leader/src/cli.rs b/leader/src/cli.rs index 80f65f34..e671600d 100644 --- a/leader/src/cli.rs +++ b/leader/src/cli.rs @@ -1,44 +1,58 @@ use std::path::PathBuf; -use clap::{Parser, ValueEnum, ValueHint}; +use clap::{Args, Parser, Subcommand, ValueHint}; use paladin::config::Runtime; #[derive(Parser)] pub(crate) struct Cli { - /// The input mode. If `std-io`, the input is read from stdin. If `http`, - /// the input is read from HTTP requests. If `jerigon`, the input is - /// read from the `debug_traceBlockByNumber` and `eth_getBlockByNumber` - /// RPC methods from Jerigon. - #[arg(short, long, value_enum, default_value_t = Mode::StdIo)] - pub(crate) mode: Mode, - /// The port to listen on when using the `http` mode. - #[arg(short, long, default_value_t = 8080)] - pub(crate) port: u16, - /// The directory to which output should be written (`http` mode only). - #[arg(short, long, required_if_eq("mode", "http"), value_hint = ValueHint::DirPath)] - pub(crate) output_dir: Option, - /// The RPC URL to use when using the `jerigon` mode. - #[arg(long, required_if_eq("mode", "jerigon"), value_hint = ValueHint::Url)] - pub(crate) rpc_url: Option, - /// The block number to use when using the `jerigon` mode. - #[arg(short, long, required_if_eq("mode", "jerigon"))] - pub(crate) block_number: Option, - /// Specifies the paladin runtime to use. - #[arg(long, short, value_enum, default_value_t = Runtime::Amqp)] - pub(crate) runtime: Runtime, + #[command(subcommand)] + pub(crate) command: Command, + + #[clap(flatten)] + pub(crate) runtime: RuntimeGroup, +} + +#[derive(Subcommand)] +pub(crate) enum Command { + /// Reads input from stdin and writes output to stdout. + Stdio { + /// The previous proof output. + #[arg(long, short = 'f', value_hint = ValueHint::FilePath)] + previous_proof: Option, + }, + /// Reads input from a Jerigon node and writes output to stdout. + Jerigon { + // The Jerigon RPC URL. + #[arg(long, short = 'u', value_hint = ValueHint::Url)] + rpc_url: String, + /// The block number for which to generate a proof. + #[arg(short, long)] + block_number: u64, + /// The previous proof output. + #[arg(long, short = 'f', value_hint = ValueHint::FilePath)] + previous_proof: Option, + }, + /// Reads input from HTTP and writes output to a directory. + Http { + /// The port on which to listen. + #[arg(short, long, default_value_t = 8080)] + port: u16, + /// The directory to which output should be written. + #[arg(short, long, value_hint = ValueHint::DirPath)] + output_dir: PathBuf, + }, +} + +#[derive(Args)] +pub(crate) struct RuntimeGroup { /// Specifies the number of worker threads to spawn (in memory runtime /// only). #[arg(long, short)] - pub num_workers: Option, - /// The previous proof output. - #[arg(long, short = 'f')] - pub previous_proof: Option, -} - -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, ValueEnum, Default)] -pub(crate) enum Mode { - #[default] - StdIo, - Http, - Jerigon, + pub(crate) num_workers: Option, + /// Specifies the paladin runtime mode. + #[arg(long, short, value_enum, default_value_t = Runtime::Amqp)] + pub(crate) runtime: Runtime, + /// Specifies the URI for the AMQP broker (AMQP runtime only). + #[arg(long, env = "AMQP_URI", value_hint = ValueHint::Url, required_if_eq("runtime", "amqp"))] + pub amqp_uri: Option, } diff --git a/leader/src/main.rs b/leader/src/main.rs index ee9eee84..ad398173 100644 --- a/leader/src/main.rs +++ b/leader/src/main.rs @@ -2,7 +2,7 @@ use std::{fs::File, path::PathBuf}; use anyhow::Result; use clap::Parser; -use cli::Mode; +use cli::Command; use dotenvy::dotenv; use ops::Ops; use paladin::runtime::Runtime; @@ -33,36 +33,33 @@ async fn main() -> Result<()> { let args = cli::Cli::parse(); let runtime = Runtime::from_config::(&paladin::config::Config { - runtime: args.runtime, - num_workers: args.num_workers, + runtime: args.runtime.runtime, + num_workers: args.runtime.num_workers, + amqp_uri: args.runtime.amqp_uri, ..Default::default() }) .await?; - match args.mode { - Mode::StdIo => { - let previous_proof = get_previous_proof(args.previous_proof)?; + match args.command { + Command::Stdio { previous_proof } => { + let previous_proof = get_previous_proof(previous_proof)?; stdio::stdio_main(runtime, previous_proof).await?; } - Mode::Http => { - let output_dir = args - .output_dir - .expect("output-dir is required in http mode"); - + Command::Http { port, output_dir } => { // check if output_dir exists, is a directory, and is writable let output_dir_metadata = std::fs::metadata(&output_dir)?; if !output_dir.is_dir() || output_dir_metadata.permissions().readonly() { panic!("output-dir is not a writable directory"); } - http::http_main(runtime, args.port, output_dir).await?; + http::http_main(runtime, port, output_dir).await?; } - Mode::Jerigon => { - let rpc_url = args.rpc_url.expect("rpc-url is required in jerigon mode"); - let block_number = args - .block_number - .expect("block-number is required in jerigon mode"); - let previous_proof = get_previous_proof(args.previous_proof)?; + Command::Jerigon { + rpc_url, + block_number, + previous_proof, + } => { + let previous_proof = get_previous_proof(previous_proof)?; jerigon::jerigon_main(runtime, &rpc_url, block_number, previous_proof).await?; } diff --git a/rpc/src/cli.rs b/rpc/src/cli.rs index 7d491597..c339e194 100644 --- a/rpc/src/cli.rs +++ b/rpc/src/cli.rs @@ -11,7 +11,7 @@ pub(crate) enum Commands { /// Fetch and generate prover input from the RPC endpoint Fetch { /// The RPC URL - #[arg(short, long, value_hint = ValueHint::Url)] + #[arg(short = 'u', long, value_hint = ValueHint::Url)] rpc_url: String, /// The block number #[arg(short, long)]