diff --git a/Cargo.lock b/Cargo.lock index 4fcb1904..cad9b58c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4666,7 +4666,7 @@ dependencies = [ ] [[package]] -name = "prism-bin" +name = "prism-cli" version = "0.1.0" dependencies = [ "anyhow", diff --git a/Cargo.toml b/Cargo.toml index f0787d0f..3ef89583 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ readme = "README.md" [workspace] default-members = [ - "crates/bin", + "crates/cli", "crates/tests", "crates/node_types/prover", "crates/node_types/lightclient", @@ -26,7 +26,7 @@ default-members = [ ] members = [ - "crates/bin", + "crates/cli", "crates/tests", "crates/node_types/prover", "crates/node_types/lightclient", @@ -93,7 +93,7 @@ prism-storage = { path = "crates/storage" } prism-nova = { path = "crates/zk/nova" } prism-da = { path = "crates/da" } prism-errors = { path = "crates/errors" } -prism-bin = { path = "crates/bin" } +prism-cli = { path = "crates/cli" } prism-groth16 = { path = "crates/zk/groth16" } prism-prover = { path = "crates/node_types/prover" } prism-tests = { path = "crates/tests" } diff --git a/README.md b/README.md index 84ed16b1..d041ace6 100644 --- a/README.md +++ b/README.md @@ -37,32 +37,55 @@ We are currently experimenting with various proof systems and have handwritten g ### Prerequisites -### Install Redis +### Install Dependencies -Redis serves as a powerful in-memory database that is used to store the label-value pairs. Follow these steps to install Redis: +We use `just` as a task runner. Once installed, you can install the rest of the dependencies with: -1. Download Redis from [Redis Download Page](https://redis.io/download/). -2. Follow the installation instructions for your operating system. +```bash +just install-deps +``` + +### Building + +To build the project, run: + +```bash +just build +``` -### Install Celestia +This will compile the `prism-cli` binary and sp1 `ELF` that are used to run the prover, light-client, and full-node. -A DA layer such as Celestia is an important component for data security and availability. It stores the cryptographic commitments and parameters of the zkSNARKs and ideally enables them to be verified. Follow the instructions [here](https://github.com/celestiaorg/apollo) to deploy a local testnet. +### Running a local DA layer + +To run a local Celestia network for testing, use: + +```bash +just celestia-up +``` ### Starting the prover -If Redis is installed and the local devnet is running, Prism can be started. Prism can be started in two different ways, as a prover (service provider and proof generator) or as a light-client (to verify the proofs posted on Celestia using the cryptographic commitments). To start the prover, run the following command: +If the dependencies are installed and the local devnet is running, a prism node can be started. + +Prism can be started in three different ways: +1. as a prover (service provider and proof generator) +2. as a light-client (to verify the proofs posted on Celestia using the cryptographic commitments) +3. as a full-node (acts as a service provider, processing all transactions and making the state available to the light-clients) +To start the prover, run: ```bash -cargo run prover +prism-cli prover ``` +This will output the prover's verifying key in the logs, which you can use along with the light-client and full-node to verify the proofs. + to start the light-client, run the following command: ```bash -cargo run light-client +prism-cli light-client|full-node --verifying-key ``` -You can then interact with Prism via the interfaces defined in [webserver.rs](https://github.com/deltadevsde/prism/blob/main/src/webserver.rs). Based on the data exchanged or stored via the interface the global indexed merkle tree changes and proofs based on these changes then are created in defined epochs (currently 60 seconds) and cryptographic commitments including the proof parameters are posted in the Celestia namespace. +You can then interact with Prism via the interfaces defined in [webserver.rs](https://github.com/deltadevsde/prism/blob/main/crates/prover/src/webserver.rs). ## Contributions diff --git a/crates/bin/src/main.rs b/crates/bin/src/main.rs deleted file mode 100644 index f360f9b8..00000000 --- a/crates/bin/src/main.rs +++ /dev/null @@ -1,86 +0,0 @@ -mod cfg; -mod node_types; - -use cfg::{initialize_da_layer, load_config, CommandLineArgs, Commands}; -use clap::Parser; -use keystore_rs::{KeyChain, KeyStore, KeyStoreType}; -use prism_common::keys::VerifyingKey; - -use node_types::NodeType; -use prism_lightclient::LightClient; -use prism_prover::Prover; -use prism_storage::RedisConnection; -use std::sync::Arc; - -#[macro_use] -extern crate log; - -pub const PRISM_ELF: &[u8] = include_bytes!("../../../elf/riscv32im-succinct-zkvm-elf"); - -/// The main function that initializes and runs a prism client. -#[tokio::main()] -async fn main() -> std::io::Result<()> { - let args = CommandLineArgs::parse(); - - let config = load_config(args.clone()) - .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?; - - let da = initialize_da_layer(&config) - .await - .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?; - let node: Arc = match args.command { - Commands::LightClient {} => { - let celestia_config = config.celestia_config.ok_or_else(|| { - std::io::Error::new( - std::io::ErrorKind::NotFound, - "celestia configuration not found", - ) - })?; - - let prover_vk = config - .verifying_key - .and_then(|s| s.try_into().ok()) - .and_then(|vk: VerifyingKey| match vk { - VerifyingKey::Ed25519(key) => Some(key), - _ => None, - }); - - Arc::new(LightClient::new(da, celestia_config, prover_vk)) - } - Commands::Prover {} => { - let redis_config = config.clone().redis_config.ok_or_else(|| { - std::io::Error::new( - std::io::ErrorKind::NotFound, - "redis configuration not found", - ) - })?; - let redis_connections = RedisConnection::new(&redis_config) - .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?; - - let signing_key = KeyStoreType::KeyChain(KeyChain) - .get_signing_key() - .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?; - - let prover_cfg = prism_prover::Config { - prover: true, - batcher: true, - webserver: config.webserver.unwrap_or_default(), - key: signing_key.clone(), - start_height: config.celestia_config.unwrap_or_default().start_height, - }; - - Arc::new( - Prover::new(Arc::new(Box::new(redis_connections)), da, &prover_cfg).map_err( - |e| { - error!("error initializing prover: {}", e); - std::io::Error::new(std::io::ErrorKind::Other, e.to_string()) - }, - )?, - ) - } - }; - - node.start() - .await - .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string())) -} diff --git a/crates/bin/Cargo.toml b/crates/cli/Cargo.toml similarity index 98% rename from crates/bin/Cargo.toml rename to crates/cli/Cargo.toml index 4edb35f0..ba2a9ad3 100644 --- a/crates/bin/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "prism-bin" +name = "prism-cli" version.workspace = true edition.workspace = true license.workspace = true diff --git a/crates/bin/src/cfg.rs b/crates/cli/src/cfg.rs similarity index 59% rename from crates/bin/src/cfg.rs rename to crates/cli/src/cfg.rs index f4f283d0..cea3ab63 100644 --- a/crates/bin/src/cfg.rs +++ b/crates/cli/src/cfg.rs @@ -1,10 +1,10 @@ -use anyhow::{anyhow, Context, Result}; -use clap::{Parser, Subcommand}; +use anyhow::{Context, Result}; +use clap::{Args, Parser, Subcommand}; use config::{builder::DefaultState, ConfigBuilder, File}; use dirs::home_dir; use dotenvy::dotenv; use log::{error, warn}; -use prism_errors::{DataAvailabilityError, GeneralError, PrismError}; +use prism_errors::{DataAvailabilityError, GeneralError}; use prism_prover::webserver::WebServerConfig; use prism_storage::redis::RedisConfig; use serde::{Deserialize, Serialize}; @@ -19,52 +19,73 @@ use prism_da::{ #[derive(Clone, Debug, Subcommand, Deserialize)] pub enum Commands { - LightClient, - Prover, + LightClient(CommandArgs), + FullNode(CommandArgs), + Prover(CommandArgs), +} + +#[derive(Args, Deserialize, Clone, Debug)] +pub struct CommandArgs { + /// Log level + #[arg(short, long, default_value = "INFO")] + log_level: String, + + #[arg(short = 'r', long)] + redis_client: Option, + + #[arg(long)] + verifying_key: Option, + + #[arg(long)] + config_path: Option, + + #[command(flatten)] + celestia: CelestiaArgs, + + #[command(flatten)] + webserver: WebserverArgs, } #[derive(Parser, Clone, Debug, Deserialize)] #[command(author, version, about, long_about = None)] -pub struct CommandLineArgs { - /// Log level - #[arg(short, long)] - log_level: Option, +pub struct Cli { + #[command(subcommand)] + pub command: Commands, +} +#[derive(Args, Deserialize, Clone, Debug)] +#[group(required = false, multiple = true)] +struct CelestiaArgs { /// Celestia Client websocket URL #[arg(short = 'c', long)] celestia_client: Option, - #[arg(short = 'r', long)] - redis_client: Option, - /// Celestia Snark Namespace ID #[arg(long)] snark_namespace_id: Option, /// Celestia Transaction Namespace ID #[arg(long)] - transaction_namespace_id: Option, + operation_namespace_id: Option, // Height to start searching the DA layer for SNARKs on #[arg(short = 's', long)] celestia_start_height: Option, +} - /// IP address for the webserver to listen on +#[derive(Args, Deserialize, Clone, Debug)] +#[group(required = false, multiple = true)] +struct WebserverArgs { #[arg(long)] + webserver_active: Option, + + /// IP address for the webserver to listen on + #[arg(long, requires = "webserver_active", default_value = "127.0.0.1")] host: Option, /// Port number for the webserver to listen on - #[arg(short, long)] + #[arg(short, long, requires = "webserver_active")] port: Option, - - #[arg(long)] - verifying_key: Option, - - #[arg(long)] - config_path: Option, - - #[command(subcommand)] - pub command: Commands, } #[derive(Debug, Serialize, Deserialize, Clone)] @@ -73,37 +94,33 @@ pub struct Config { pub webserver: Option, #[serde(skip_serializing_if = "Option::is_none")] pub celestia_config: Option, - pub da_layer: Option, + pub da_layer: DALayerOption, pub redis_config: Option, pub verifying_key: Option, } -#[derive(Debug, Default, Clone, Eq, PartialEq, Serialize, Deserialize)] -pub enum DALayerOption { - #[default] - Celestia, - InMemory, - None, -} - impl Default for Config { fn default() -> Self { Config { webserver: Some(WebServerConfig::default()), - da_layer: Some(DALayerOption::default()), celestia_config: Some(CelestiaConfig::default()), + da_layer: DALayerOption::default(), redis_config: Some(RedisConfig::default()), verifying_key: None, } } } -pub fn load_config(args: CommandLineArgs) -> Result { +#[derive(Debug, Default, Clone, Eq, PartialEq, Serialize, Deserialize)] +pub enum DALayerOption { + #[default] + Celestia, + InMemory, +} + +pub fn load_config(args: CommandArgs) -> Result { dotenv().ok(); - std::env::set_var( - "RUST_LOG", - args.clone().log_level.unwrap_or_else(|| "INFO".to_string()), - ); + std::env::set_var("RUST_LOG", args.clone().log_level); pretty_env_logger::init(); let config_path = get_config_path(&args).context("Failed to determine config path")?; @@ -114,12 +131,10 @@ pub fn load_config(args: CommandLineArgs) -> Result { .build() .context("Failed to build config")?; - let default_config = Config::default(); let loaded_config: Config = config_source.try_deserialize().context("Failed to deserialize config file")?; - let merged_config = merge_configs(loaded_config, default_config); - let final_config = apply_command_line_args(merged_config, args); + let final_config = apply_command_line_args(loaded_config, args); if final_config.verifying_key.is_none() { warn!("prover's public key was not provided. this is not recommended and epoch signatures will not be verified."); @@ -128,7 +143,7 @@ pub fn load_config(args: CommandLineArgs) -> Result { Ok(final_config) } -fn get_config_path(args: &CommandLineArgs) -> Result { +fn get_config_path(args: &CommandArgs) -> Result { args.config_path .clone() .or_else(|| home_dir().map(|path| format!("{}/.prism/config.toml", path.to_string_lossy()))) @@ -152,74 +167,37 @@ fn ensure_config_file_exists(config_path: &str) -> Result<()> { Ok(()) } -fn merge_configs(loaded: Config, default: Config) -> Config { - Config { - webserver: loaded.webserver.or(default.webserver), - redis_config: loaded.redis_config.or(default.redis_config), - celestia_config: loaded.celestia_config.or(default.celestia_config), - da_layer: loaded.da_layer.or(default.da_layer), - verifying_key: loaded.verifying_key.or(default.verifying_key), - } -} +fn apply_command_line_args(config: Config, args: CommandArgs) -> Config { + let webserver_config = &config.webserver.unwrap_or_default(); + let redis_config = &config.redis_config.unwrap_or_default(); + let celestia_config = &config.celestia_config.unwrap_or_default(); -fn apply_command_line_args(config: Config, args: CommandLineArgs) -> Config { Config { webserver: Some(WebServerConfig { - enabled: true, - host: args.host.unwrap_or_else(|| { - config - .webserver - .as_ref() - .map(|w| w.host.clone()) - .unwrap_or_else(|| WebServerConfig::default().host) - }), - port: args.port.unwrap_or_else(|| { - config - .webserver - .as_ref() - .map(|w| w.port) - .unwrap_or_else(|| WebServerConfig::default().port) - }), + enabled: args.webserver.webserver_active.unwrap_or(webserver_config.enabled), + host: args.webserver.host.unwrap_or(webserver_config.host.clone()), + port: args.webserver.port.unwrap_or(webserver_config.port), }), redis_config: Some(RedisConfig { - connection_string: args.redis_client.unwrap_or_else(|| { - config - .redis_config - .as_ref() - .map(|r| r.connection_string.clone()) - .unwrap_or_else(|| RedisConfig::default().connection_string) - }), + connection_string: args.redis_client.unwrap_or(redis_config.connection_string.clone()), }), celestia_config: Some(CelestiaConfig { - connection_string: args.celestia_client.unwrap_or_else(|| { - config - .celestia_config - .as_ref() - .map(|c| c.connection_string.clone()) - .unwrap_or_else(|| CelestiaConfig::default().connection_string) - }), - start_height: args.celestia_start_height.unwrap_or_else(|| { - config - .celestia_config - .as_ref() - .map(|c| c.start_height) - .unwrap_or_else(|| CelestiaConfig::default().start_height) - }), - snark_namespace_id: args.snark_namespace_id.unwrap_or_else(|| { - config - .celestia_config - .as_ref() - .map(|c| c.snark_namespace_id.clone()) - .unwrap_or_else(|| CelestiaConfig::default().snark_namespace_id) - }), - transaction_namespace_id: Some(args.transaction_namespace_id.unwrap_or_else(|| { - config - .celestia_config - .as_ref() - .map(|c| c.transaction_namespace_id.clone()) - .unwrap_or_else(|| CelestiaConfig::default().transaction_namespace_id) - .unwrap() - })), + connection_string: args + .celestia + .celestia_client + .unwrap_or(celestia_config.connection_string.clone()), + start_height: args + .celestia + .celestia_start_height + .unwrap_or(celestia_config.start_height), + snark_namespace_id: args + .celestia + .snark_namespace_id + .unwrap_or(celestia_config.snark_namespace_id.clone()), + operation_namespace_id: args + .celestia + .operation_namespace_id + .unwrap_or(celestia_config.operation_namespace_id.clone()), }), da_layer: config.da_layer, verifying_key: args.verifying_key.or(config.verifying_key), @@ -229,9 +207,7 @@ fn apply_command_line_args(config: Config, args: CommandLineArgs) -> Config { pub async fn initialize_da_layer( config: &Config, ) -> Result> { - let da_layer = config.da_layer.as_ref().context("DA Layer not specified")?; - - match da_layer { + match config.da_layer { DALayerOption::Celestia => { let celestia_conf = config.celestia_config.clone().context("Celestia configuration not found")?; @@ -258,8 +234,5 @@ pub async fn initialize_da_layer( let (da_layer, _height_rx, _block_rx) = InMemoryDataAvailabilityLayer::new(30); Ok(Arc::new(da_layer) as Arc) } - DALayerOption::None => Err(anyhow!(PrismError::ConfigError( - "No DA Layer specified".into() - ))), } } diff --git a/crates/bin/src/lib.rs b/crates/cli/src/lib.rs similarity index 100% rename from crates/bin/src/lib.rs rename to crates/cli/src/lib.rs diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs new file mode 100644 index 00000000..a582a9b5 --- /dev/null +++ b/crates/cli/src/main.rs @@ -0,0 +1,149 @@ +mod cfg; +mod node_types; + +use cfg::{initialize_da_layer, load_config, Cli, Commands}; +use clap::Parser; +use keystore_rs::{KeyChain, KeyStore, KeyStoreType}; +use prism_common::keys::VerifyingKey; + +use node_types::NodeType; +use prism_lightclient::LightClient; +use prism_prover::Prover; +use prism_storage::RedisConnection; +use std::sync::Arc; + +#[macro_use] +extern crate log; + +pub const PRISM_ELF: &[u8] = include_bytes!("../../../elf/riscv32im-succinct-zkvm-elf"); + +/// The main function that initializes and runs a prism client. +#[tokio::main()] +async fn main() -> std::io::Result<()> { + let cli = Cli::parse(); + let node: Arc = match cli.command { + Commands::LightClient(args) => { + let config = load_config(args.clone()) + .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?; + + let da = initialize_da_layer(&config) + .await + .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?; + + let celestia_config = config.celestia_config.ok_or_else(|| { + std::io::Error::new( + std::io::ErrorKind::NotFound, + "celestia configuration not found", + ) + })?; + + let prover_vk = config.verifying_key.and_then(|s| s.try_into().ok()).and_then( + |vk: VerifyingKey| match vk { + VerifyingKey::Ed25519(key) => Some(key), + _ => None, + }, + ); + + Arc::new(LightClient::new(da, celestia_config, prover_vk)) + } + Commands::Prover(args) => { + let config = load_config(args.clone()) + .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?; + + let da = initialize_da_layer(&config) + .await + .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?; + + let redis_config = config.clone().redis_config.ok_or_else(|| { + std::io::Error::new( + std::io::ErrorKind::NotFound, + "redis configuration not found", + ) + })?; + let redis_connections = RedisConnection::new(&redis_config) + .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?; + + let signing_key = KeyStoreType::KeyChain(KeyChain) + .get_signing_key() + .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?; + + let prover_cfg = prism_prover::Config { + prover: true, + batcher: true, + webserver: config.webserver.unwrap_or_default(), + signing_key: signing_key.clone(), + verifying_key: signing_key.verification_key(), + start_height: config.celestia_config.unwrap_or_default().start_height, + }; + + info!( + "prover verifying key: {}", + VerifyingKey::from(prover_cfg.verifying_key) + ); + + Arc::new( + Prover::new(Arc::new(Box::new(redis_connections)), da, &prover_cfg).map_err( + |e| { + error!("error initializing prover: {}", e); + std::io::Error::new(std::io::ErrorKind::Other, e.to_string()) + }, + )?, + ) + } + Commands::FullNode(args) => { + let config = load_config(args.clone()) + .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?; + + let da = initialize_da_layer(&config) + .await + .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?; + + let redis_config = config.clone().redis_config.ok_or_else(|| { + std::io::Error::new( + std::io::ErrorKind::NotFound, + "redis configuration not found", + ) + })?; + let redis_connections = RedisConnection::new(&redis_config) + .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?; + + let signing_key = KeyStoreType::KeyChain(KeyChain) + .get_signing_key() + .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?; + + let prover_vk = config + .verifying_key + .and_then(|s| s.try_into().ok()) + .and_then(|vk: VerifyingKey| match vk { + VerifyingKey::Ed25519(key) => Some(key), + _ => None, + }) + .ok_or_else(|| { + std::io::Error::new( + std::io::ErrorKind::NotFound, + "prover verifying key not found", + ) + })?; + + let prover_cfg = prism_prover::Config { + prover: false, + batcher: true, + webserver: config.webserver.unwrap_or_default(), + signing_key: signing_key.clone(), + verifying_key: prover_vk, + start_height: config.celestia_config.unwrap_or_default().start_height, + }; + + Arc::new( + Prover::new(Arc::new(Box::new(redis_connections)), da, &prover_cfg).map_err( + |e| { + error!("error initializing prover: {}", e); + std::io::Error::new(std::io::ErrorKind::Other, e.to_string()) + }, + )?, + ) + } + }; + + node.start().await.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string())) +} diff --git a/crates/bin/src/node_types.rs b/crates/cli/src/node_types.rs similarity index 100% rename from crates/bin/src/node_types.rs rename to crates/cli/src/node_types.rs diff --git a/crates/common/src/keys.rs b/crates/common/src/keys.rs index f281d112..a0e1252a 100644 --- a/crates/common/src/keys.rs +++ b/crates/common/src/keys.rs @@ -124,6 +124,13 @@ impl TryFrom for VerifyingKey { } } +impl std::fmt::Display for VerifyingKey { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + let encoded = engine.encode(self.as_bytes()); + write!(f, "{}", encoded) + } +} + #[derive(Clone)] pub enum SigningKey { Ed25519(Box), diff --git a/crates/da/src/celestia.rs b/crates/da/src/celestia.rs index ff279880..e26ef540 100644 --- a/crates/da/src/celestia.rs +++ b/crates/da/src/celestia.rs @@ -33,16 +33,16 @@ pub struct CelestiaConfig { pub connection_string: String, pub start_height: u64, pub snark_namespace_id: String, - pub transaction_namespace_id: Option, + pub operation_namespace_id: String, } impl Default for CelestiaConfig { fn default() -> Self { CelestiaConfig { connection_string: "ws://localhost:26658".to_string(), - start_height: 0, + start_height: 1, snark_namespace_id: "00000000000000de1008".to_string(), - transaction_namespace_id: Some("00000000000000de1009".to_string()), + operation_namespace_id: "00000000000000de1009".to_string(), } } } @@ -50,7 +50,7 @@ impl Default for CelestiaConfig { pub struct CelestiaConnection { pub client: celestia_rpc::Client, pub snark_namespace: Namespace, - pub transaction_namespace: Namespace, + pub operation_namespace: Namespace, height_update_tx: broadcast::Sender, sync_target: Arc, @@ -68,20 +68,18 @@ impl CelestiaConnection { &config.snark_namespace_id ))?; - let transaction_namespace = match &config.transaction_namespace_id { - Some(id) => create_namespace(id).context(format!( - "Failed to create transaction namespace from: '{}'", - id - ))?, - None => snark_namespace, - }; + let operation_namespace = + create_namespace(&config.operation_namespace_id).context(format!( + "Failed to create operation namespace from: '{}'", + &config.operation_namespace_id + ))?; let (height_update_tx, _) = broadcast::channel(100); Ok(CelestiaConnection { client, snark_namespace, - transaction_namespace, + operation_namespace, height_update_tx, sync_target: Arc::new(AtomicU64::new(0)), }) @@ -174,7 +172,7 @@ impl DataAvailabilityLayer for CelestiaConnection { height ); let maybe_blobs = - BlobClient::blob_get_all(&self.client, height, &[self.transaction_namespace]) + BlobClient::blob_get_all(&self.client, height, &[self.operation_namespace]) .await .map_err(|e| { anyhow!(DataAvailabilityError::DataRetrievalError( @@ -218,7 +216,7 @@ impl DataAvailabilityLayer for CelestiaConnection { )) })?; - Blob::new(self.transaction_namespace, data) + Blob::new(self.operation_namespace, data) .context(format!( "Failed to create blob for transaction {:?}", transaction diff --git a/crates/node_types/lightclient/src/lightclient.rs b/crates/node_types/lightclient/src/lightclient.rs index c3ab367e..ee0ea9a6 100644 --- a/crates/node_types/lightclient/src/lightclient.rs +++ b/crates/node_types/lightclient/src/lightclient.rs @@ -70,7 +70,7 @@ impl LightClient { Ok(Some(finalized_epoch)) => { debug!("light client: got epochs at height {}", i + 1); - // Signature verification + // TODO: Issue #144 if let Some(pubkey) = &self.prover_pubkey { match finalized_epoch.verify_signature(*pubkey) { Ok(_) => trace!("valid signature for epoch {}", finalized_epoch.height), diff --git a/crates/node_types/prover/src/prover/mod.rs b/crates/node_types/prover/src/prover/mod.rs index 64ab4d29..a1f19774 100644 --- a/crates/node_types/prover/src/prover/mod.rs +++ b/crates/node_types/prover/src/prover/mod.rs @@ -1,5 +1,5 @@ use anyhow::{anyhow, bail, Context, Result}; -use ed25519_consensus::SigningKey; +use ed25519_consensus::{SigningKey, VerificationKey}; use jmt::KeyHash; use keystore_rs::create_signing_key; use prism_common::{ @@ -30,9 +30,9 @@ pub const PRISM_ELF: &[u8] = include_bytes!("../../../../../elf/riscv32im-succin #[derive(Clone)] pub struct Config { - /// Enables generating FinalizedEpochs and posting them to the DA + /// Enables generating [`FinalizedEpoch`]s and posting them to the DA /// layer. When deactivated, the node will simply sync historical and - /// incoming FinalizedEpochs. + /// incoming [`FinalizedEpoch`]s. pub prover: bool, /// Enables accepting incoming transactions from the webserver and posting batches to the DA layer. @@ -42,8 +42,12 @@ pub struct Config { /// Configuration for the webserver. pub webserver: WebServerConfig, - /// Key used to sign new FinalizedEpochs. - pub key: SigningKey, + /// Key used to sign new [`FinalizedEpochs`]. + pub signing_key: SigningKey, + + /// Key used to verify incoming [`FinalizedEpochs`]. + /// This is not necessarily the counterpart to signing_key, as fullnodes must use the [`verifying_key`] of the prover. + pub verifying_key: VerificationKey, /// DA layer height the prover should start syncing transactions from. pub start_height: u64, @@ -51,11 +55,14 @@ pub struct Config { impl Default for Config { fn default() -> Self { + let signing_key = create_signing_key(); + Config { prover: true, batcher: true, webserver: WebServerConfig::default(), - key: create_signing_key(), + signing_key: signing_key.clone(), + verifying_key: signing_key.verification_key(), start_height: 1, } } @@ -262,6 +269,12 @@ impl Prover { return Ok(()); } + // TODO: Issue #144 + match epoch.verify_signature(self.cfg.verifying_key) { + Ok(_) => trace!("valid signature for epoch {}", epoch.height), + Err(e) => panic!("invalid signature in epoch {}: {:?}", epoch.height, e), + } + let prev_commitment = self.db.get_commitment(¤t_epoch)?; if epoch.height != current_epoch { @@ -292,6 +305,18 @@ impl Prover { )); } + let client = self.prover_client.read().await; + match client.verify(&epoch.proof, &self.verifying_key) { + Ok(_) => info!( + "zkSNARK for epoch {} was validated successfully", + epoch.height + ), + Err(err) => panic!( + "failed to validate epoch at height {}: {:?}", + epoch.height, err + ), + } + debug!( "processed epoch {}. new commitment: {:?}", current_epoch, new_commitment @@ -386,7 +411,7 @@ impl Prover { signature: None, }; - epoch_json.insert_signature(&self.cfg.key); + epoch_json.insert_signature(&self.cfg.signing_key); Ok(epoch_json) } diff --git a/crates/tests/src/lib.rs b/crates/tests/src/lib.rs index c41d4252..f79423f5 100644 --- a/crates/tests/src/lib.rs +++ b/crates/tests/src/lib.rs @@ -49,7 +49,7 @@ async fn test_light_client_prover_talking() -> Result<()> { let mut test_state = TestTreeState::new(); let _service = test_state.register_service("test_service".to_string()); let prover_cfg = prism_prover::Config { - key: signing_key, + signing_key, ..prism_prover::Config::default() }; diff --git a/elf/riscv32im-succinct-zkvm-elf b/elf/riscv32im-succinct-zkvm-elf index eb447764..feef67d3 100755 Binary files a/elf/riscv32im-succinct-zkvm-elf and b/elf/riscv32im-succinct-zkvm-elf differ diff --git a/justfile b/justfile index 815fa604..934de0d7 100644 --- a/justfile +++ b/justfile @@ -87,7 +87,7 @@ unit-test: coverage: @echo "Generating coverage report..." - cargo llvm-cov nextest --html --output-dir coverage_report --lib --features "mock_prover" --release --workspace --exclude prism-bin --exclude-from-report prism-sp1 --ignore-filename-regex sp1 + cargo llvm-cov nextest --html --output-dir coverage_report --lib --features "mock_prover" --release --workspace --exclude prism-cli --exclude-from-report prism-sp1 --ignore-filename-regex sp1 @echo "Coverage report generated in 'coverage_report' directory"