From 2d25d4cb56f18748cb0bdc4f98f528f0619a56d7 Mon Sep 17 00:00:00 2001 From: "Odysseas.eth" Date: Thu, 13 Feb 2025 21:40:45 +0200 Subject: [PATCH 01/25] ref: code organization --- Cargo.lock | 5 +- Cargo.toml | 4 +- bin/pcl/Cargo.toml | 2 +- bin/pcl/src/main.rs | 4 +- crates/{da => core}/Cargo.toml | 3 +- .../submit.rs => core/src/assertion_da.rs} | 43 ++++------ crates/core/src/error.rs | 13 +++ crates/core/src/lib.rs | 2 + crates/da/src/lib.rs | 1 - crates/phoundry/src/error.rs | 12 +++ crates/phoundry/src/lib.rs | 79 ++----------------- crates/phoundry/src/phorge.rs | 70 ++++++++++++++++ 12 files changed, 131 insertions(+), 107 deletions(-) rename crates/{da => core}/Cargo.toml (91%) rename crates/{da/src/submit.rs => core/src/assertion_da.rs} (83%) create mode 100644 crates/core/src/error.rs create mode 100644 crates/core/src/lib.rs delete mode 100644 crates/da/src/lib.rs create mode 100644 crates/phoundry/src/error.rs create mode 100644 crates/phoundry/src/phorge.rs diff --git a/Cargo.lock b/Cargo.lock index 390fdb8..ebd17cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2370,7 +2370,7 @@ dependencies = [ "clap", "eyre", "pcl-common", - "pcl-da", + "pcl-core", "pcl-phoundry", "tokio", "vergen-gix", @@ -2385,7 +2385,7 @@ dependencies = [ ] [[package]] -name = "pcl-da" +name = "pcl-core" version = "0.0.1" dependencies = [ "alloy-primitives", @@ -2397,6 +2397,7 @@ dependencies = [ "serde", "serde_json", "thiserror", + "tokio", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index fa79c86..e32bae2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ members = [ "bin/pcl", "crates/phoundry", "crates/common", - "crates/da", + "crates/core" ] default-members = ["bin/pcl"] resolver = '2' @@ -22,7 +22,7 @@ repository = "https://github.com/phylaxsystems/pcl" [workspace.dependencies] pcl-phoundry = { path = "crates/phoundry" } pcl-common = { path = "crates/common" } -pcl-da = { path = "crates/da" } +pcl-core= { path = "crates/core" } tokio = { version = "1.39.0", features = ["full"] } clap = { version = "4.5.23", features = ["derive", "env"] } eyre = "0.6.12" diff --git a/bin/pcl/Cargo.toml b/bin/pcl/Cargo.toml index a7ab922..96fdca6 100644 --- a/bin/pcl/Cargo.toml +++ b/bin/pcl/Cargo.toml @@ -11,7 +11,7 @@ build = "build.rs" [dependencies] pcl-phoundry = { workspace = true } -pcl-da = { workspace = true } +pcl-core = { workspace = true } pcl-common = { workspace = true } clap = { workspace = true } diff --git a/bin/pcl/src/main.rs b/bin/pcl/src/main.rs index ea8d3da..31ef058 100644 --- a/bin/pcl/src/main.rs +++ b/bin/pcl/src/main.rs @@ -1,7 +1,7 @@ use clap::{command, Parser}; use eyre::Result; use pcl_common::args::CliArgs; -use pcl_da::submit::DASubmitArgs; +use pcl_core::assertion_da::DASubmitArgs; use pcl_phoundry::{build::BuildArgs, Phorge, PhoundryError}; const VERSION_MESSAGE: &str = concat!( env!("CARGO_PKG_VERSION"), @@ -47,7 +47,7 @@ async fn main() -> Result<()> { Ok::<(), PhoundryError>(()) } Commands::DASubmit(submit) => { - submit.run(cli.args.clone())?; + submit.run(cli.args.clone()).await?; Ok::<(), PhoundryError>(()) } }?; diff --git a/crates/da/Cargo.toml b/crates/core/Cargo.toml similarity index 91% rename from crates/da/Cargo.toml rename to crates/core/Cargo.toml index 99a1bb4..839e32b 100644 --- a/crates/da/Cargo.toml +++ b/crates/core/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "pcl-da" +name = "pcl-core" version.workspace = true authors.workspace = true edition.workspace = true @@ -24,3 +24,4 @@ alloy-primitives = "0.6" [dev-dependencies] mockito = "1.2" +tokio = { workspace = true } diff --git a/crates/da/src/submit.rs b/crates/core/src/assertion_da.rs similarity index 83% rename from crates/da/src/submit.rs rename to crates/core/src/assertion_da.rs index 49e6ae4..95e2364 100644 --- a/crates/da/src/submit.rs +++ b/crates/core/src/assertion_da.rs @@ -1,10 +1,9 @@ use alloy_primitives::keccak256; use pcl_common::{args::CliArgs, utils::bytecode}; use pcl_phoundry::build::BuildArgs; -use pcl_phoundry::PhoundryError; -use reqwest::{blocking::Client, Error as ReqwestError}; +use reqwest::Client; use serde::{Deserialize, Serialize}; -use thiserror::Error; +use crate::error::SubmitError; #[derive(Deserialize)] struct JsonRpcResponse { @@ -21,7 +20,7 @@ struct SubmissionResponse { #[derive(Serialize)] struct JsonRpcRequest { - jsonrpc: String, + json_rpc: String, method: String, params: Vec, id: u64, @@ -37,7 +36,7 @@ pub struct DASubmitArgs { } impl DASubmitArgs { - pub fn run(&self, cli_args: CliArgs) -> Result<(), SubmitError> { + pub async fn run(&self, cli_args: CliArgs) -> Result<(), SubmitError> { let build_args = BuildArgs { assertions: vec![self.assertion.clone()], }; @@ -46,7 +45,7 @@ impl DASubmitArgs { let bytecode = self.get_bytecode(&self.assertion)?; let id = self.calculate_id(&bytecode)?; let request = self.create_jsonrpc_request(&id, &bytecode)?; - self.submit_request(&request) + self.submit_request(&request).await } fn get_bytecode(&self, assertion: &str) -> Result { @@ -66,7 +65,7 @@ impl DASubmitArgs { bytecode: &str, ) -> Result { Ok(JsonRpcRequest { - jsonrpc: "2.0".to_string(), + json_rpc: "2.0".to_string(), method: "da_submit_assertion".to_string(), params: vec![ format!("0x{}", id), // keccak256 hash as id @@ -76,15 +75,15 @@ impl DASubmitArgs { }) } - fn submit_request(&self, request: &JsonRpcRequest) -> Result<(), SubmitError> { + async fn submit_request(&self, request: &JsonRpcRequest) -> Result<(), SubmitError> { let client = Client::new(); - let response = client.post(&self.url).json(request).send()?; + let response = client.post(&self.url).json(request).send().await?; if !response.status().is_success() { return Err(SubmitError::SubmissionFailed(response.status().to_string())); } - let result: JsonRpcResponse = response.json()?; + let result: JsonRpcResponse = response.json().await?; println!( "Submitted assertion '{}': ID {}: Status {}", self.assertion, result.result.id, result.result.status @@ -94,15 +93,7 @@ impl DASubmitArgs { } } -#[derive(Error, Debug)] -pub enum SubmitError { - #[error("HTTP request failed: {0}")] - RequestFailed(#[from] ReqwestError), - #[error("Submission failed: {0}")] - SubmissionFailed(String), - #[error("Build failed: {0}")] - BuildError(#[from] PhoundryError), -} + #[cfg(test)] mod tests { @@ -129,15 +120,15 @@ mod tests { let request = args .create_jsonrpc_request("test_id", "test_bytecode") .unwrap(); - assert_eq!(request.jsonrpc, "2.0"); + assert_eq!(request.json_rpc, "2.0"); assert_eq!(request.method, "da_submit_assertion"); assert_eq!(request.params.len(), 2); assert_eq!(request.params[0], "0xtest_id"); assert_eq!(request.params[1], "0xtest_bytecode"); } - #[test] - fn test_submit_request() { + #[tokio::test] + async fn test_submit_request() { let mut server = Server::new(); let mock = server .mock("POST", "/") @@ -155,13 +146,13 @@ mod tests { let request = args .create_jsonrpc_request("test_id", "test_bytecode") .unwrap(); - let result = args.submit_request(&request); + let result = args.submit_request(&request).await; assert!(result.is_ok()); mock.assert(); } - #[test] - fn test_submit_request_failure() { + #[tokio::test] + async fn test_submit_request_failure() { let mut server = Server::new(); let mock = server .mock("POST", "/") @@ -179,7 +170,7 @@ mod tests { let request = args .create_jsonrpc_request("test_id", "test_bytecode") .unwrap(); - let result = args.submit_request(&request); + let result = args.submit_request(&request).await; assert!(matches!(result, Err(SubmitError::SubmissionFailed(_)))); mock.assert(); } diff --git a/crates/core/src/error.rs b/crates/core/src/error.rs new file mode 100644 index 0000000..404a587 --- /dev/null +++ b/crates/core/src/error.rs @@ -0,0 +1,13 @@ +use reqwest::Error as ReqwestError; +use pcl_phoundry::PhoundryError; +use thiserror::Error; + +#[derive(Error, Debug)] +pub enum SubmitError { + #[error("HTTP request failed: {0}")] + RequestFailed(#[from] ReqwestError), + #[error("Submission failed: {0}")] + SubmissionFailed(String), + #[error("Build failed: {0}")] + BuildError(#[from] PhoundryError), +} \ No newline at end of file diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs new file mode 100644 index 0000000..78ca213 --- /dev/null +++ b/crates/core/src/lib.rs @@ -0,0 +1,2 @@ +pub mod assertion_da; +pub mod error; \ No newline at end of file diff --git a/crates/da/src/lib.rs b/crates/da/src/lib.rs deleted file mode 100644 index d53a5d1..0000000 --- a/crates/da/src/lib.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod submit; diff --git a/crates/phoundry/src/error.rs b/crates/phoundry/src/error.rs new file mode 100644 index 0000000..0abdc82 --- /dev/null +++ b/crates/phoundry/src/error.rs @@ -0,0 +1,12 @@ +use thiserror::Error; +use std::fmt::Debug; + +#[derive(Error, Debug)] +pub enum PhoundryError { + #[error("forge is not installed or not available in PATH")] + ForgeNotInstalled, + #[error("forge command failed")] + ForgeCommandFailed(#[from] std::io::Error), + #[error("invalid forge output")] + InvalidForgeOutput(&'static str), +} diff --git a/crates/phoundry/src/lib.rs b/crates/phoundry/src/lib.rs index 8f0e5e3..cc51575 100644 --- a/crates/phoundry/src/lib.rs +++ b/crates/phoundry/src/lib.rs @@ -7,78 +7,13 @@ use std::{ use pcl_common::args::CliArgs; use thiserror::Error; -pub mod build; +mod build; +mod error; +mod phorge; -const FORGE_BINARY_NAME: &str = "phorge"; +// re-export the public items +pub use build::*; +pub use error::*; +pub use phorge::*; -// Remove the const and add a function to get the forge binary path -fn get_forge_binary_path() -> PathBuf { - let exe_path = env::current_exe().expect("Failed to get current executable path"); - exe_path - .parent() - .expect("Failed to get executable directory") - .join(FORGE_BINARY_NAME) -} -#[derive(clap::Parser)] -pub struct Phorge { - pub args: Vec, -} - -impl Phorge { - /// Run the forge command with the given arguments. - /// Phoundry should be installed as part of the pcl workspace, meaning that we - /// can assume that forge is available in the PATH. - /// We do this so that we don't have to re-write the forge command in the CLI, as - /// a lot of the functionality is implemented as part of the forge binary, which we can't import - /// as a crate. - pub fn run(&self, cli_args: CliArgs, print_output: bool) -> Result { - // Execute forge and pass through all output exactly as-is - let mut command = Command::new(get_forge_binary_path()); - - command.args(self.args.clone()); - - // Only valid for the context of this binary execution - env::set_var( - "FOUNDRY_SRC", - cli_args.assertions_src().as_os_str().to_str().unwrap(), - ); - env::set_var( - "FOUNDRY_TEST", - cli_args.assertions_test().as_os_str().to_str().unwrap(), - ); - - let output = command.output()?; - - // Pass through stdout/stderr exactly as forge produced them - if print_output && !output.stdout.is_empty() { - print!("{}", String::from_utf8_lossy(&output.stdout)); - } - if !output.stderr.is_empty() { - eprint!("{}", String::from_utf8_lossy(&output.stderr)); - } - Ok(output) - } - - /// Check if forge is installed and available in the PATH. - pub fn forge_must_be_installed() -> Result<(), PhoundryError> { - if Command::new(get_forge_binary_path()) - .arg("--version") - .output() - .is_err() - { - return Err(PhoundryError::ForgeNotInstalled); - } - Ok(()) - } -} - -#[derive(Error, Debug)] -pub enum PhoundryError { - #[error("forge is not installed or not available in PATH")] - ForgeNotInstalled, - #[error("forge command failed")] - ForgeCommandFailed(#[from] std::io::Error), - #[error("invalid forge output")] - InvalidForgeOutput(&'static str), -} diff --git a/crates/phoundry/src/phorge.rs b/crates/phoundry/src/phorge.rs new file mode 100644 index 0000000..980b86d --- /dev/null +++ b/crates/phoundry/src/phorge.rs @@ -0,0 +1,70 @@ +use std::{ + env, + path::PathBuf, + process::{Command, Output}, +}; + +const FORGE_BINARY_NAME: &str = "phorge"; + +// Remove the const and add a function to get the forge binary path +fn get_forge_binary_path() -> PathBuf { + let exe_path = env::current_exe().expect("Failed to get current executable path"); + exe_path + .parent() + .expect("Failed to get executable directory") + .join(FORGE_BINARY_NAME) +} + +#[derive(clap::Parser)] +pub struct Phorge { + pub args: Vec, +} + +impl Phorge { + /// Run the forge command with the given arguments. + /// Phoundry should be installed as part of the pcl workspace, meaning that we + /// can assume that forge is available in the PATH. + /// We do this so that we don't have to re-write the forge command in the CLI, as + /// a lot of the functionality is implemented as part of the forge binary, which we can't import + /// as a crate. + pub fn run(&self, cli_args: CliArgs, print_output: bool) -> Result { + // Execute forge and pass through all output exactly as-is + let mut command = Command::new(get_forge_binary_path()); + + command.args(self.args.clone()); + + // Only valid for the context of this binary execution + env::set_var( + "FOUNDRY_SRC", + cli_args.assertions_src().as_os_str().to_str().unwrap(), + ); + env::set_var( + "FOUNDRY_TEST", + cli_args.assertions_test().as_os_str().to_str().unwrap(), + ); + + let output = command.output()?; + + // Pass through stdout/stderr exactly as forge produced them + if print_output && !output.stdout.is_empty() { + print!("{}", String::from_utf8_lossy(&output.stdout)); + } + if !output.stderr.is_empty() { + eprint!("{}", String::from_utf8_lossy(&output.stderr)); + } + + Ok(output) + } + + /// Check if forge is installed and available in the PATH. + pub fn forge_must_be_installed() -> Result<(), PhoundryError> { + if Command::new(get_forge_binary_path()) + .arg("--version") + .output() + .is_err() + { + return Err(PhoundryError::ForgeNotInstalled); + } + Ok(()) + } +} \ No newline at end of file From a0816f1c517577c6b3a6016b3581bb16a4bff43e Mon Sep 17 00:00:00 2001 From: "Odysseas.eth" Date: Thu, 13 Feb 2025 22:00:31 +0200 Subject: [PATCH 02/25] fix: not require user to add da url --- crates/core/src/assertion_da.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/core/src/assertion_da.rs b/crates/core/src/assertion_da.rs index 95e2364..9b2ffb7 100644 --- a/crates/core/src/assertion_da.rs +++ b/crates/core/src/assertion_da.rs @@ -28,8 +28,9 @@ struct JsonRpcRequest { #[derive(clap::Parser)] pub struct DASubmitArgs { + // FIXME(Odysseas): Replace localhost with the actual DA URL from our infrastructure /// URL of the assertion-DA - #[clap(long, env = "PCL_DA_URL")] + #[clap(long, env = "PCL_DA_URL", default_value = "http://localhost:3000")] url: String, /// Name of the assertion contract to submit assertion: String, From c1afb882513eb3a8d140374772ec76a8d38d826c Mon Sep 17 00:00:00 2001 From: "Odysseas.eth" Date: Thu, 13 Feb 2025 22:55:11 +0200 Subject: [PATCH 03/25] wip: submission skeleton --- Cargo.lock | 493 ++++++++++++++++++------ bin/pcl/src/main.rs | 13 +- crates/core/Cargo.toml | 6 +- crates/core/src/assertion_da.rs | 16 +- crates/core/src/assertion_submission.rs | 44 +++ crates/core/src/config.rs | 25 ++ crates/core/src/error.rs | 22 +- crates/core/src/lib.rs | 4 +- 8 files changed, 477 insertions(+), 146 deletions(-) create mode 100644 crates/core/src/assertion_submission.rs create mode 100644 crates/core/src/config.rs diff --git a/Cargo.lock b/Cargo.lock index ebd17cd..fcde563 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -46,23 +46,28 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "alloy-primitives" -version = "0.6.4" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "600d34d8de81e23b6d909c094e23b3d357e01ca36b78a8c5424c501eedbe86f0" +checksum = "478bedf4d24e71ea48428d1bc278553bd7c6ae07c30ca063beb0b09fe58a9e74" dependencies = [ "alloy-rlp", "bytes", "cfg-if", "const-hex", "derive_more", - "hex-literal", + "foldhash", + "hashbrown 0.15.2", + "indexmap", "itoa", "k256", "keccak-asm", + "paste", "proptest", "rand", "ruint", + "rustc-hash", "serde", + "sha3", "tiny-keccak", ] @@ -323,9 +328,9 @@ checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" [[package]] name = "base64" -version = "0.21.7" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "base64ct" @@ -544,12 +549,6 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" -[[package]] -name = "convert_case" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" - [[package]] name = "core-foundation" version = "0.9.4" @@ -609,6 +608,31 @@ version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +[[package]] +name = "crossterm" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e64e6c0fbe2c17357405f7c758c1ef960fce08bdfb2c03d88d2a18d7e09c4b67" +dependencies = [ + "bitflags 1.3.2", + "crossterm_winapi", + "libc", + "mio 0.8.11", + "parking_lot", + "signal-hook", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" +dependencies = [ + "winapi", +] + [[package]] name = "crunchy" version = "0.2.3" @@ -735,15 +759,23 @@ dependencies = [ [[package]] name = "derive_more" -version = "0.99.18" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" +checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" dependencies = [ - "convert_case", "proc-macro2", "quote", - "rustc_version 0.4.1", "syn 2.0.90", + "unicode-xid", ] [[package]] @@ -784,6 +816,12 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" +[[package]] +name = "dyn-clone" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "feeef44e73baff3a26d371801df019877a9866a8c493d315ab00177843314f35" + [[package]] name = "ecdsa" version = "0.16.9" @@ -945,6 +983,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" + [[package]] name = "foreign-types" version = "0.3.2" @@ -982,6 +1026,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", + "futures-sink", ] [[package]] @@ -1016,6 +1061,7 @@ checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-core", "futures-io", + "futures-sink", "futures-task", "memchr", "pin-project-lite", @@ -1023,6 +1069,24 @@ dependencies = [ "slab", ] +[[package]] +name = "fuzzy-matcher" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54614a3312934d066701a80f20f15fa3b56d67ac7722b39eea5b4c9dd1d66c94" +dependencies = [ + "thread_local", +] + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + [[package]] name = "generic-array" version = "0.14.7" @@ -1559,25 +1623,6 @@ dependencies = [ "subtle", ] -[[package]] -name = "h2" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http 0.2.12", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - [[package]] name = "h2" version = "0.4.7" @@ -1589,7 +1634,7 @@ dependencies = [ "fnv", "futures-core", "futures-sink", - "http 1.2.0", + "http", "indexmap", "slab", "tokio", @@ -1612,6 +1657,9 @@ name = "hashbrown" version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" +dependencies = [ + "foldhash", +] [[package]] name = "heck" @@ -1625,12 +1673,6 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" -[[package]] -name = "hex-literal" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" - [[package]] name = "hmac" version = "0.12.1" @@ -1649,17 +1691,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "http" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - [[package]] name = "http" version = "1.2.0" @@ -1671,17 +1702,6 @@ dependencies = [ "itoa", ] -[[package]] -name = "http-body" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" -dependencies = [ - "bytes", - "http 0.2.12", - "pin-project-lite", -] - [[package]] name = "http-body" version = "1.0.1" @@ -1689,7 +1709,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.2.0", + "http", ] [[package]] @@ -1700,8 +1720,8 @@ checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes", "futures-util", - "http 1.2.0", - "http-body 1.0.1", + "http", + "http-body", "pin-project-lite", ] @@ -1719,59 +1739,56 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "0.14.32" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" +checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0" dependencies = [ "bytes", "futures-channel", - "futures-core", "futures-util", - "h2 0.3.26", - "http 0.2.12", - "http-body 0.4.6", + "h2", + "http", + "http-body", "httparse", "httpdate", "itoa", "pin-project-lite", - "socket2", + "smallvec", "tokio", - "tower-service", - "tracing", "want", ] [[package]] -name = "hyper" -version = "1.5.2" +name = "hyper-rustls" +version = "0.27.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0" +checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" dependencies = [ - "bytes", - "futures-channel", "futures-util", - "h2 0.4.7", - "http 1.2.0", - "http-body 1.0.1", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "smallvec", + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", "tokio", + "tokio-rustls", + "tower-service", ] [[package]] name = "hyper-tls" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", - "hyper 0.14.32", + "http-body-util", + "hyper", + "hyper-util", "native-tls", "tokio", "tokio-native-tls", + "tower-service", ] [[package]] @@ -1781,12 +1798,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" dependencies = [ "bytes", + "futures-channel", "futures-util", - "http 1.2.0", - "http-body 1.0.1", - "hyper 1.5.2", + "http", + "http-body", + "hyper", "pin-project-lite", + "socket2", "tokio", + "tower-service", + "tracing", ] [[package]] @@ -1970,6 +1991,23 @@ dependencies = [ "hashbrown 0.15.2", ] +[[package]] +name = "inquire" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fddf93031af70e75410a2511ec04d49e758ed2f26dad3404a934e0fb45cc12a" +dependencies = [ + "bitflags 2.6.0", + "crossterm", + "dyn-clone", + "fuzzy-matcher", + "fxhash", + "newline-converter", + "once_cell", + "unicode-segmentation", + "unicode-width", +] + [[package]] name = "ipnet" version = "2.11.0" @@ -2043,7 +2081,15 @@ dependencies = [ "elliptic-curve", "once_cell", "sha2", - "signature", +] + +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", ] [[package]] @@ -2143,6 +2189,18 @@ dependencies = [ "adler2", ] +[[package]] +name = "mio" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys 0.48.0", +] + [[package]] name = "mio" version = "1.0.3" @@ -2164,10 +2222,10 @@ dependencies = [ "bytes", "colored", "futures-util", - "http 1.2.0", - "http-body 1.0.1", + "http", + "http-body", "http-body-util", - "hyper 1.5.2", + "hyper", "hyper-util", "log", "rand", @@ -2195,6 +2253,15 @@ dependencies = [ "tempfile", ] +[[package]] +name = "newline-converter" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b6b097ecb1cbfed438542d16e84fd7ad9b0c76c8a65b7f9039212a3d14dc7f" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "ntapi" version = "0.4.1" @@ -2390,6 +2457,7 @@ version = "0.0.1" dependencies = [ "alloy-primitives", "clap", + "inquire", "mockito", "pcl-common", "pcl-phoundry", @@ -2650,20 +2718,24 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.11.27" +version = "0.12.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da" dependencies = [ "base64", "bytes", "encoding_rs", + "futures-channel", "futures-core", "futures-util", - "h2 0.3.26", - "http 0.2.12", - "http-body 0.4.6", - "hyper 0.14.32", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", "hyper-tls", + "hyper-util", "ipnet", "js-sys", "log", @@ -2680,12 +2752,13 @@ dependencies = [ "system-configuration", "tokio", "tokio-native-tls", + "tower", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "winreg", + "windows-registry", ] [[package]] @@ -2698,6 +2771,21 @@ dependencies = [ "subtle", ] +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys 0.52.0", +] + [[package]] name = "rlp" version = "0.5.2" @@ -2746,6 +2834,12 @@ version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + [[package]] name = "rustc-hex" version = "2.1.0" @@ -2783,13 +2877,43 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "rustls" +version = "0.23.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47796c98c480fce5406ef69d1c76378375492c3b0a0de587be0c1d9feb12f395" +dependencies = [ + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + [[package]] name = "rustls-pemfile" -version = "1.0.4" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" dependencies = [ - "base64", + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" + +[[package]] +name = "rustls-webpki" +version = "0.102.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", ] [[package]] @@ -2965,6 +3089,16 @@ dependencies = [ "digest 0.10.7", ] +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest 0.10.7", + "keccak", +] + [[package]] name = "sha3-asm" version = "0.1.4" @@ -2991,6 +3125,17 @@ dependencies = [ "signal-hook-registry", ] +[[package]] +name = "signal-hook-mio" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" +dependencies = [ + "libc", + "mio 0.8.11", + "signal-hook", +] + [[package]] name = "signal-hook-registry" version = "1.4.2" @@ -3041,6 +3186,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + [[package]] name = "spki" version = "0.7.3" @@ -3099,9 +3250,12 @@ dependencies = [ [[package]] name = "sync_wrapper" -version = "0.1.2" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] [[package]] name = "synstructure" @@ -3130,20 +3284,20 @@ dependencies = [ [[package]] name = "system-configuration" -version = "0.5.1" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.6.0", "core-foundation", "system-configuration-sys", ] [[package]] name = "system-configuration-sys" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" dependencies = [ "core-foundation-sys", "libc", @@ -3188,6 +3342,16 @@ dependencies = [ "syn 2.0.90", ] +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if", + "once_cell", +] + [[package]] name = "time" version = "0.3.37" @@ -3264,7 +3428,7 @@ dependencies = [ "backtrace", "bytes", "libc", - "mio", + "mio 1.0.3", "parking_lot", "pin-project-lite", "signal-hook-registry", @@ -3294,6 +3458,16 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-rustls" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" +dependencies = [ + "rustls", + "tokio", +] + [[package]] name = "tokio-util" version = "0.7.13" @@ -3324,6 +3498,27 @@ dependencies = [ "winnow", ] +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + [[package]] name = "tower-service" version = "0.3.3" @@ -3406,6 +3601,30 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "unicode-width" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "url" version = "2.5.4" @@ -3660,7 +3879,7 @@ checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" dependencies = [ "windows-implement", "windows-interface", - "windows-result", + "windows-result 0.1.2", "windows-targets 0.52.6", ] @@ -3686,6 +3905,17 @@ dependencies = [ "syn 2.0.90", ] +[[package]] +name = "windows-registry" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +dependencies = [ + "windows-result 0.2.0", + "windows-strings", + "windows-targets 0.52.6", +] + [[package]] name = "windows-result" version = "0.1.2" @@ -3695,6 +3925,25 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result 0.2.0", + "windows-targets 0.52.6", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -3852,16 +4101,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "winreg" -version = "0.50.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - [[package]] name = "write16" version = "1.0.0" diff --git a/bin/pcl/src/main.rs b/bin/pcl/src/main.rs index 31ef058..887df10 100644 --- a/bin/pcl/src/main.rs +++ b/bin/pcl/src/main.rs @@ -1,7 +1,7 @@ use clap::{command, Parser}; use eyre::Result; use pcl_common::args::CliArgs; -use pcl_core::assertion_da::DASubmitArgs; +use pcl_core::{assertion_da::DASubmitArgs, error::DappSubmitError}; use pcl_phoundry::{build::BuildArgs, Phorge, PhoundryError}; const VERSION_MESSAGE: &str = concat!( env!("CARGO_PKG_VERSION"), @@ -29,6 +29,7 @@ enum Commands { Phorge(Phorge), Build(BuildArgs), DASubmit(DASubmitArgs), + DappSubmit(DappSubmitArgs), } #[tokio::main] @@ -39,17 +40,17 @@ async fn main() -> Result<()> { let cli = Cli::parse(); match cli.command { Commands::Phorge(phorge) => { - let _ = phorge.run(cli.args.clone(), true)?; - Ok::<(), PhoundryError>(()) + phorge.run(cli.args.clone(), true)?; } Commands::Build(build) => { build.run(cli.args.clone())?; - Ok::<(), PhoundryError>(()) } Commands::DASubmit(submit) => { submit.run(cli.args.clone()).await?; - Ok::<(), PhoundryError>(()) } - }?; + Commands::DappSubmit(submit) => { + submit.run(cli.args.clone()).await?; + } + }; Ok(()) } diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index 839e32b..df7a377 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -18,9 +18,11 @@ thiserror = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } -reqwest = { version = "0.11", features = ["json", "blocking"] } +reqwest = { version = "0.12", features = ["json", "blocking"] } -alloy-primitives = "0.6" +alloy-primitives = "0.8.21" + +inquire = "0.7.5" [dev-dependencies] mockito = "1.2" diff --git a/crates/core/src/assertion_da.rs b/crates/core/src/assertion_da.rs index 9b2ffb7..f2b9d42 100644 --- a/crates/core/src/assertion_da.rs +++ b/crates/core/src/assertion_da.rs @@ -3,7 +3,7 @@ use pcl_common::{args::CliArgs, utils::bytecode}; use pcl_phoundry::build::BuildArgs; use reqwest::Client; use serde::{Deserialize, Serialize}; -use crate::error::SubmitError; +use crate::error::DaSubmitError; #[derive(Deserialize)] struct JsonRpcResponse { @@ -37,7 +37,7 @@ pub struct DASubmitArgs { } impl DASubmitArgs { - pub async fn run(&self, cli_args: CliArgs) -> Result<(), SubmitError> { + pub async fn run(&self, cli_args: CliArgs) -> Result<(), DaSubmitError> { let build_args = BuildArgs { assertions: vec![self.assertion.clone()], }; @@ -49,12 +49,12 @@ impl DASubmitArgs { self.submit_request(&request).await } - fn get_bytecode(&self, assertion: &str) -> Result { + fn get_bytecode(&self, assertion: &str) -> Result { let artifact_path = format!("{}.sol:{}", assertion, assertion); Ok(bytecode(&artifact_path)) } - fn calculate_id(&self, bytecode: &str) -> Result { + fn calculate_id(&self, bytecode: &str) -> Result { // TODO: Need to align with the correct calculation of the id let id = keccak256(bytecode.as_bytes()); Ok(id.to_string()) @@ -64,7 +64,7 @@ impl DASubmitArgs { &self, id: &str, bytecode: &str, - ) -> Result { + ) -> Result { Ok(JsonRpcRequest { json_rpc: "2.0".to_string(), method: "da_submit_assertion".to_string(), @@ -76,12 +76,12 @@ impl DASubmitArgs { }) } - async fn submit_request(&self, request: &JsonRpcRequest) -> Result<(), SubmitError> { + async fn submit_request(&self, request: &JsonRpcRequest) -> Result<(), DaSubmitError> { let client = Client::new(); let response = client.post(&self.url).json(request).send().await?; if !response.status().is_success() { - return Err(SubmitError::SubmissionFailed(response.status().to_string())); + return Err(DaSubmitError::SubmissionFailed(response.status().to_string())); } let result: JsonRpcResponse = response.json().await?; @@ -172,7 +172,7 @@ mod tests { .create_jsonrpc_request("test_id", "test_bytecode") .unwrap(); let result = args.submit_request(&request).await; - assert!(matches!(result, Err(SubmitError::SubmissionFailed(_)))); + assert!(matches!(result, Err(DaSubmitError::SubmissionFailed(_)))); mock.assert(); } } diff --git a/crates/core/src/assertion_submission.rs b/crates/core/src/assertion_submission.rs new file mode 100644 index 0000000..948dc6b --- /dev/null +++ b/crates/core/src/assertion_submission.rs @@ -0,0 +1,44 @@ +use pcl_common::args::CliArgs; +use serde::{Deserialize, Serialize}; +use inquire::Select; +use crate::{config::CliConfig, error::DappSubmitError}; + +#[derive(Deserialize)] +struct Project { + id: String, + name: String, +} + +#[derive(clap::Parser)] +pub struct AssertionSubmitArgs { + #[clap(short, long, default_value = "https://credible-layer-dapp.pages.dev/api/v1")] + dapp_url: String, +} + + +impl AssertionSubmitArgs { + pub async fn run(&self, cli_args: CliArgs, config: CliConfig) -> Result<(), DappSubmitError> { + let client = reqwest::Client::new(); + let projects: Vec = client + .get(format!("{}/projects?user={}", self.dapp_url, config.auth.unwrap().user_address)) + .send() + .await? + .json() + .await?; + + // Create selection options + let project_options: Vec = projects + .iter() + .map(|p| format!("{} ({})", p.name, p.id)) + .collect(); + + // Show interactive selection + let selection = Select::new( + "Select a project to submit the assertion to:", + project_options, + ) + .prompt() + .map_err(|_| DappSubmitError::ProjectSelectionCancelled)?; + Ok(()) + } +} diff --git a/crates/core/src/config.rs b/crates/core/src/config.rs new file mode 100644 index 0000000..aa6fe15 --- /dev/null +++ b/crates/core/src/config.rs @@ -0,0 +1,25 @@ +use crate::error::ConfigError; + + +#[derive(Debug, Default)] +pub struct CliConfig { + pub auth: Option, + pub assertions_for_submission: Vec +} + +impl CliConfig { +} + + +#[derive(Debug, Default)] +pub struct UserAuth { + pub access_token: String, + pub refresh_token: String, + pub user_address: String, +} + +#[derive(Debug, Default)] +pub struct AssertionForSubmission { + assertion: String, + id: String, +} \ No newline at end of file diff --git a/crates/core/src/error.rs b/crates/core/src/error.rs index 404a587..f76b62e 100644 --- a/crates/core/src/error.rs +++ b/crates/core/src/error.rs @@ -3,11 +3,29 @@ use pcl_phoundry::PhoundryError; use thiserror::Error; #[derive(Error, Debug)] -pub enum SubmitError { +pub enum DaSubmitError { #[error("HTTP request failed: {0}")] RequestFailed(#[from] ReqwestError), #[error("Submission failed: {0}")] SubmissionFailed(String), #[error("Build failed: {0}")] BuildError(#[from] PhoundryError), -} \ No newline at end of file +} + +#[derive(Error, Debug)] +pub enum DappSubmitError { + #[error("No auth token found")] + NoAuthToken, + #[error("Project selection cancelled")] + ProjectSelectionCancelled, + #[error("Failed to connect to the dApp API")] + ApiConnectionError(#[from] ReqwestError), +} + +#[derive(Error, Debug)] +pub enum ConfigError { + #[error("Failed to read config file: {0}")] + ReadError(std::io::Error), + #[error("Failed to write config file: {0}")] + WriteError(std::io::Error), +} diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index 78ca213..5c888ad 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -1,2 +1,4 @@ pub mod assertion_da; -pub mod error; \ No newline at end of file +pub mod error; +pub mod config; +pub mod assertion_submission; \ No newline at end of file From 7b139e54a1f7a454793ef22eba0f174f751015dc Mon Sep 17 00:00:00 2001 From: "Odysseas.eth" Date: Thu, 13 Feb 2025 23:30:58 +0200 Subject: [PATCH 04/25] feat: write/read config file --- Cargo.lock | 89 +++++++++++++++++++++++-- bin/pcl/src/main.rs | 7 +- crates/core/Cargo.toml | 7 +- crates/core/src/assertion_submission.rs | 22 ++++-- crates/core/src/config.rs | 28 ++++++-- crates/core/src/error.rs | 2 +- crates/phoundry/src/build.rs | 2 +- crates/phoundry/src/lib.rs | 22 +----- crates/phoundry/src/phorge.rs | 3 + 9 files changed, 136 insertions(+), 46 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fcde563..19b7d75 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -799,6 +799,27 @@ dependencies = [ "subtle", ] +[[package]] +name = "dirs" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.59.0", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -1167,7 +1188,7 @@ dependencies = [ "gix-utils", "itoa", "thiserror", - "winnow", + "winnow 0.6.20", ] [[package]] @@ -1220,7 +1241,7 @@ dependencies = [ "smallvec", "thiserror", "unicode-bom", - "winnow", + "winnow 0.6.20", ] [[package]] @@ -1395,7 +1416,7 @@ dependencies = [ "itoa", "smallvec", "thiserror", - "winnow", + "winnow 0.6.20", ] [[package]] @@ -1479,7 +1500,7 @@ dependencies = [ "gix-validate", "memmap2", "thiserror", - "winnow", + "winnow 0.6.20", ] [[package]] @@ -2374,6 +2395,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + [[package]] name = "parity-scale-codec" version = "3.6.12" @@ -2457,6 +2484,7 @@ version = "0.0.1" dependencies = [ "alloy-primitives", "clap", + "dirs", "inquire", "mockito", "pcl-common", @@ -2466,6 +2494,7 @@ dependencies = [ "serde_json", "thiserror", "tokio", + "toml", ] [[package]] @@ -2687,6 +2716,17 @@ dependencies = [ "bitflags 2.6.0", ] +[[package]] +name = "redox_users" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + [[package]] name = "regex" version = "1.11.1" @@ -3060,6 +3100,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -3481,21 +3530,38 @@ dependencies = [ "tokio", ] +[[package]] +name = "toml" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + [[package]] name = "toml_datetime" version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] [[package]] name = "toml_edit" -version = "0.22.22" +version = "0.22.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" dependencies = [ "indexmap", + "serde", + "serde_spanned", "toml_datetime", - "winnow", + "winnow 0.7.2", ] [[package]] @@ -4101,6 +4167,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "winnow" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59690dea168f2198d1a3b0cac23b8063efcd11012f10ae4698f284808c8ef603" +dependencies = [ + "memchr", +] + [[package]] name = "write16" version = "1.0.0" diff --git a/bin/pcl/src/main.rs b/bin/pcl/src/main.rs index 887df10..eb96b25 100644 --- a/bin/pcl/src/main.rs +++ b/bin/pcl/src/main.rs @@ -1,8 +1,8 @@ use clap::{command, Parser}; use eyre::Result; use pcl_common::args::CliArgs; -use pcl_core::{assertion_da::DASubmitArgs, error::DappSubmitError}; -use pcl_phoundry::{build::BuildArgs, Phorge, PhoundryError}; +use pcl_core::{assertion_da::DASubmitArgs, assertion_submission::DappSubmitArgs, error::DappSubmitError}; +use pcl_phoundry::{build::BuildArgs, phorge::Phorge}; const VERSION_MESSAGE: &str = concat!( env!("CARGO_PKG_VERSION"), "\nCommit: ", @@ -22,6 +22,7 @@ struct Cli { command: Commands, #[command(flatten)] args: CliArgs, + } #[derive(clap::Subcommand)] @@ -49,7 +50,7 @@ async fn main() -> Result<()> { submit.run(cli.args.clone()).await?; } Commands::DappSubmit(submit) => { - submit.run(cli.args.clone()).await?; + submit.run(cli.args.clone(), Default::default()).await?; } }; Ok(()) diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index df7a377..795f847 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -14,15 +14,14 @@ pcl-common = { workspace = true } pcl-phoundry = { workspace = true } clap = { workspace = true } thiserror = { workspace = true } - serde = { workspace = true } serde_json = { workspace = true } - reqwest = { version = "0.12", features = ["json", "blocking"] } - alloy-primitives = "0.8.21" - inquire = "0.7.5" +toml = "0.8.2" +dirs = "6.0.0" + [dev-dependencies] mockito = "1.2" diff --git a/crates/core/src/assertion_submission.rs b/crates/core/src/assertion_submission.rs index 948dc6b..65e0f12 100644 --- a/crates/core/src/assertion_submission.rs +++ b/crates/core/src/assertion_submission.rs @@ -5,22 +5,29 @@ use crate::{config::CliConfig, error::DappSubmitError}; #[derive(Deserialize)] struct Project { - id: String, - name: String, + project_id: String, + project_name: String, + project_description: Option, + profile_image_url: Option, + project_networks: Vec, + project_manager: String, + assertion_adopters: Vec, + created_at: String, + updated_at: String, } #[derive(clap::Parser)] -pub struct AssertionSubmitArgs { +pub struct DappSubmitArgs { #[clap(short, long, default_value = "https://credible-layer-dapp.pages.dev/api/v1")] dapp_url: String, } -impl AssertionSubmitArgs { +impl DappSubmitArgs { pub async fn run(&self, cli_args: CliArgs, config: CliConfig) -> Result<(), DappSubmitError> { let client = reqwest::Client::new(); let projects: Vec = client - .get(format!("{}/projects?user={}", self.dapp_url, config.auth.unwrap().user_address)) + .get(format!("{}/projects?user={}", self.dapp_url, "0x702352bc4fc5a3C1e7ef8D96C6d51d5352998c2B")) .send() .await? .json() @@ -29,7 +36,7 @@ impl AssertionSubmitArgs { // Create selection options let project_options: Vec = projects .iter() - .map(|p| format!("{} ({})", p.name, p.id)) + .map(|p| p.project_name.clone()) .collect(); // Show interactive selection @@ -39,6 +46,9 @@ impl AssertionSubmitArgs { ) .prompt() .map_err(|_| DappSubmitError::ProjectSelectionCancelled)?; + + let project = projects.iter().find(|p| p.project_name == selection).unwrap(); + Ok(()) } } diff --git a/crates/core/src/config.rs b/crates/core/src/config.rs index aa6fe15..4ec1c7f 100644 --- a/crates/core/src/config.rs +++ b/crates/core/src/config.rs @@ -1,24 +1,42 @@ use crate::error::ConfigError; +use dirs::home_dir; +use serde::{Deserialize, Serialize}; +pub const CONFIG_DIR: &str = ".pcl"; +pub const CONFIG_FILE: &str = "config.toml"; -#[derive(Debug, Default)] -pub struct CliConfig { +#[derive(Debug, Default, Serialize, Deserialize)] +pub struct CliConfig{ pub auth: Option, pub assertions_for_submission: Vec } -impl CliConfig { +impl CliConfig{ + pub fn write_to_file(&self) -> Result<(), ConfigError> { + let config_dir = home_dir().unwrap().join(CONFIG_DIR); + let config_file = config_dir.join(CONFIG_FILE); + let config_str = toml::to_string(self).unwrap(); + std::fs::write(config_file, config_str).unwrap(); + Ok(()) + } + + pub fn read_from_file() -> Result { + let config_dir = home_dir().unwrap().join(CONFIG_DIR); + let config_file = config_dir.join(CONFIG_FILE); + let config_str = std::fs::read_to_string(config_file).unwrap(); + Ok(toml::from_str(&config_str).unwrap()) + } } -#[derive(Debug, Default)] +#[derive(Debug, Default, Serialize, Deserialize)] pub struct UserAuth { pub access_token: String, pub refresh_token: String, pub user_address: String, } -#[derive(Debug, Default)] +#[derive(Debug, Default, Serialize, Deserialize)] pub struct AssertionForSubmission { assertion: String, id: String, diff --git a/crates/core/src/error.rs b/crates/core/src/error.rs index f76b62e..3b2099e 100644 --- a/crates/core/src/error.rs +++ b/crates/core/src/error.rs @@ -1,5 +1,5 @@ use reqwest::Error as ReqwestError; -use pcl_phoundry::PhoundryError; +use pcl_phoundry::error::PhoundryError; use thiserror::Error; #[derive(Error, Debug)] diff --git a/crates/phoundry/src/build.rs b/crates/phoundry/src/build.rs index ba72ef0..958c468 100644 --- a/crates/phoundry/src/build.rs +++ b/crates/phoundry/src/build.rs @@ -1,7 +1,7 @@ use clap::Parser; use pcl_common::args::CliArgs; -use crate::{Phorge, PhoundryError}; +use crate::{phorge::Phorge, error::PhoundryError}; #[derive(Debug)] pub struct AssertionBuildOutput { diff --git a/crates/phoundry/src/lib.rs b/crates/phoundry/src/lib.rs index cc51575..575c26f 100644 --- a/crates/phoundry/src/lib.rs +++ b/crates/phoundry/src/lib.rs @@ -1,19 +1,3 @@ -use std::{ - env, - path::PathBuf, - process::{Command, Output}, -}; - -use pcl_common::args::CliArgs; -use thiserror::Error; - -mod build; -mod error; -mod phorge; - -// re-export the public items -pub use build::*; -pub use error::*; -pub use phorge::*; - - +pub mod build; +pub mod error; +pub mod phorge; \ No newline at end of file diff --git a/crates/phoundry/src/phorge.rs b/crates/phoundry/src/phorge.rs index 980b86d..6482516 100644 --- a/crates/phoundry/src/phorge.rs +++ b/crates/phoundry/src/phorge.rs @@ -3,6 +3,9 @@ use std::{ path::PathBuf, process::{Command, Output}, }; +use pcl_common::args::CliArgs; + +use crate::error::PhoundryError; const FORGE_BINARY_NAME: &str = "phorge"; From 1f288e37f8d1c574e08313fe6189856ac405d33c Mon Sep 17 00:00:00 2001 From: "Odysseas.eth" Date: Thu, 13 Feb 2025 23:41:46 +0200 Subject: [PATCH 05/25] chore: fix errors --- bin/pcl/src/main.rs | 9 ++++++--- crates/core/src/assertion_submission.rs | 2 +- crates/core/src/config.rs | 15 +++++++++++++-- crates/core/src/error.rs | 2 ++ 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/bin/pcl/src/main.rs b/bin/pcl/src/main.rs index eb96b25..58a4bbd 100644 --- a/bin/pcl/src/main.rs +++ b/bin/pcl/src/main.rs @@ -1,7 +1,7 @@ use clap::{command, Parser}; -use eyre::Result; +use eyre::{Context, Result}; use pcl_common::args::CliArgs; -use pcl_core::{assertion_da::DASubmitArgs, assertion_submission::DappSubmitArgs, error::DappSubmitError}; +use pcl_core::{assertion_da::DASubmitArgs, assertion_submission::DappSubmitArgs, config::CliConfig, error::DappSubmitError}; use pcl_phoundry::{build::BuildArgs, phorge::Phorge}; const VERSION_MESSAGE: &str = concat!( env!("CARGO_PKG_VERSION"), @@ -37,6 +37,7 @@ enum Commands { async fn main() -> Result<()> { // Check if forge is installed Phorge::forge_must_be_installed()?; + let mut config = CliConfig::read_or_default(); let cli = Cli::parse(); match cli.command { @@ -50,8 +51,10 @@ async fn main() -> Result<()> { submit.run(cli.args.clone()).await?; } Commands::DappSubmit(submit) => { - submit.run(cli.args.clone(), Default::default()).await?; + config.must_be_authenticated().wrap_err("Authentication required for dapp submission. Please authenticate first using 'pcl auth'")?; + submit.run(cli.args.clone(), &mut config).await?; } }; + config.write_to_file()?; Ok(()) } diff --git a/crates/core/src/assertion_submission.rs b/crates/core/src/assertion_submission.rs index 65e0f12..1dca505 100644 --- a/crates/core/src/assertion_submission.rs +++ b/crates/core/src/assertion_submission.rs @@ -24,7 +24,7 @@ pub struct DappSubmitArgs { impl DappSubmitArgs { - pub async fn run(&self, cli_args: CliArgs, config: CliConfig) -> Result<(), DappSubmitError> { + pub async fn run(&self, cli_args: CliArgs, config: &mut CliConfig) -> Result<(), DappSubmitError> { let client = reqwest::Client::new(); let projects: Vec = client .get(format!("{}/projects?user={}", self.dapp_url, "0x702352bc4fc5a3C1e7ef8D96C6d51d5352998c2B")) diff --git a/crates/core/src/config.rs b/crates/core/src/config.rs index 4ec1c7f..17465a2 100644 --- a/crates/core/src/config.rs +++ b/crates/core/src/config.rs @@ -16,16 +16,27 @@ impl CliConfig{ let config_dir = home_dir().unwrap().join(CONFIG_DIR); let config_file = config_dir.join(CONFIG_FILE); let config_str = toml::to_string(self).unwrap(); - std::fs::write(config_file, config_str).unwrap(); + std::fs::write(config_file, config_str).map_err(|e| ConfigError::WriteError(e))?; Ok(()) } + pub fn read_or_default() -> Self { + Self::read_from_file().unwrap_or_default() + } + pub fn read_from_file() -> Result { let config_dir = home_dir().unwrap().join(CONFIG_DIR); let config_file = config_dir.join(CONFIG_FILE); - let config_str = std::fs::read_to_string(config_file).unwrap(); + let config_str = std::fs::read_to_string(config_file).map_err(|e| ConfigError::ReadError(e))?; Ok(toml::from_str(&config_str).unwrap()) } + + pub fn must_be_authenticated(&self) -> Result<(), ConfigError> { + if self.auth.is_none() { + return Err(ConfigError::NotAuthenticated); + } + Ok(()) + } } diff --git a/crates/core/src/error.rs b/crates/core/src/error.rs index 3b2099e..2d1d729 100644 --- a/crates/core/src/error.rs +++ b/crates/core/src/error.rs @@ -28,4 +28,6 @@ pub enum ConfigError { ReadError(std::io::Error), #[error("Failed to write config file: {0}")] WriteError(std::io::Error), + #[error("No Authentication Token Found")] + NotAuthenticated, } From 7ad9355f41e6217ebc15deaa4c416c1ba107f2a2 Mon Sep 17 00:00:00 2001 From: "Odysseas.eth" Date: Thu, 13 Feb 2025 23:42:29 +0200 Subject: [PATCH 06/25] chore: fmt --- bin/pcl/src/main.rs | 6 ++- crates/core/src/assertion_da.rs | 8 ++-- crates/core/src/assertion_submission.rs | 51 +++++++++++++++---------- crates/core/src/config.rs | 13 +++---- crates/core/src/error.rs | 2 +- crates/core/src/lib.rs | 4 +- crates/phoundry/src/build.rs | 2 +- crates/phoundry/src/error.rs | 2 +- crates/phoundry/src/lib.rs | 2 +- crates/phoundry/src/phorge.rs | 4 +- 10 files changed, 53 insertions(+), 41 deletions(-) diff --git a/bin/pcl/src/main.rs b/bin/pcl/src/main.rs index 58a4bbd..9359f62 100644 --- a/bin/pcl/src/main.rs +++ b/bin/pcl/src/main.rs @@ -1,7 +1,10 @@ use clap::{command, Parser}; use eyre::{Context, Result}; use pcl_common::args::CliArgs; -use pcl_core::{assertion_da::DASubmitArgs, assertion_submission::DappSubmitArgs, config::CliConfig, error::DappSubmitError}; +use pcl_core::{ + assertion_da::DASubmitArgs, assertion_submission::DappSubmitArgs, config::CliConfig, + error::DappSubmitError, +}; use pcl_phoundry::{build::BuildArgs, phorge::Phorge}; const VERSION_MESSAGE: &str = concat!( env!("CARGO_PKG_VERSION"), @@ -22,7 +25,6 @@ struct Cli { command: Commands, #[command(flatten)] args: CliArgs, - } #[derive(clap::Subcommand)] diff --git a/crates/core/src/assertion_da.rs b/crates/core/src/assertion_da.rs index f2b9d42..1fbef92 100644 --- a/crates/core/src/assertion_da.rs +++ b/crates/core/src/assertion_da.rs @@ -1,9 +1,9 @@ +use crate::error::DaSubmitError; use alloy_primitives::keccak256; use pcl_common::{args::CliArgs, utils::bytecode}; use pcl_phoundry::build::BuildArgs; use reqwest::Client; use serde::{Deserialize, Serialize}; -use crate::error::DaSubmitError; #[derive(Deserialize)] struct JsonRpcResponse { @@ -81,7 +81,9 @@ impl DASubmitArgs { let response = client.post(&self.url).json(request).send().await?; if !response.status().is_success() { - return Err(DaSubmitError::SubmissionFailed(response.status().to_string())); + return Err(DaSubmitError::SubmissionFailed( + response.status().to_string(), + )); } let result: JsonRpcResponse = response.json().await?; @@ -94,8 +96,6 @@ impl DASubmitArgs { } } - - #[cfg(test)] mod tests { use super::*; diff --git a/crates/core/src/assertion_submission.rs b/crates/core/src/assertion_submission.rs index 1dca505..6e561bf 100644 --- a/crates/core/src/assertion_submission.rs +++ b/crates/core/src/assertion_submission.rs @@ -1,43 +1,51 @@ -use pcl_common::args::CliArgs; -use serde::{Deserialize, Serialize}; -use inquire::Select; use crate::{config::CliConfig, error::DappSubmitError}; +use inquire::Select; +use pcl_common::args::CliArgs; +use serde::Deserialize; #[derive(Deserialize)] struct Project { - project_id: String, + _project_id: String, project_name: String, - project_description: Option, - profile_image_url: Option, - project_networks: Vec, - project_manager: String, - assertion_adopters: Vec, - created_at: String, - updated_at: String, + _project_description: Option, + _profile_image_url: Option, + _project_networks: Vec, + _project_manager: String, + _assertion_adopters: Vec, + _created_at: String, + _updated_at: String, } #[derive(clap::Parser)] pub struct DappSubmitArgs { - #[clap(short, long, default_value = "https://credible-layer-dapp.pages.dev/api/v1")] + #[clap( + short, + long, + default_value = "https://credible-layer-dapp.pages.dev/api/v1" + )] dapp_url: String, } - impl DappSubmitArgs { - pub async fn run(&self, cli_args: CliArgs, config: &mut CliConfig) -> Result<(), DappSubmitError> { + pub async fn run( + &self, + cli_args: CliArgs, + config: &mut CliConfig, + ) -> Result<(), DappSubmitError> { let client = reqwest::Client::new(); let projects: Vec = client - .get(format!("{}/projects?user={}", self.dapp_url, "0x702352bc4fc5a3C1e7ef8D96C6d51d5352998c2B")) + .get(format!( + "{}/projects?user={}", + self.dapp_url, "0x702352bc4fc5a3C1e7ef8D96C6d51d5352998c2B" + )) .send() .await? .json() .await?; // Create selection options - let project_options: Vec = projects - .iter() - .map(|p| p.project_name.clone()) - .collect(); + let project_options: Vec = + projects.iter().map(|p| p.project_name.clone()).collect(); // Show interactive selection let selection = Select::new( @@ -47,7 +55,10 @@ impl DappSubmitArgs { .prompt() .map_err(|_| DappSubmitError::ProjectSelectionCancelled)?; - let project = projects.iter().find(|p| p.project_name == selection).unwrap(); + let project = projects + .iter() + .find(|p| p.project_name == selection) + .unwrap(); Ok(()) } diff --git a/crates/core/src/config.rs b/crates/core/src/config.rs index 17465a2..bbaec48 100644 --- a/crates/core/src/config.rs +++ b/crates/core/src/config.rs @@ -4,14 +4,13 @@ use serde::{Deserialize, Serialize}; pub const CONFIG_DIR: &str = ".pcl"; pub const CONFIG_FILE: &str = "config.toml"; - #[derive(Debug, Default, Serialize, Deserialize)] -pub struct CliConfig{ +pub struct CliConfig { pub auth: Option, - pub assertions_for_submission: Vec + pub assertions_for_submission: Vec, } -impl CliConfig{ +impl CliConfig { pub fn write_to_file(&self) -> Result<(), ConfigError> { let config_dir = home_dir().unwrap().join(CONFIG_DIR); let config_file = config_dir.join(CONFIG_FILE); @@ -27,7 +26,8 @@ impl CliConfig{ pub fn read_from_file() -> Result { let config_dir = home_dir().unwrap().join(CONFIG_DIR); let config_file = config_dir.join(CONFIG_FILE); - let config_str = std::fs::read_to_string(config_file).map_err(|e| ConfigError::ReadError(e))?; + let config_str = + std::fs::read_to_string(config_file).map_err(|e| ConfigError::ReadError(e))?; Ok(toml::from_str(&config_str).unwrap()) } @@ -39,7 +39,6 @@ impl CliConfig{ } } - #[derive(Debug, Default, Serialize, Deserialize)] pub struct UserAuth { pub access_token: String, @@ -51,4 +50,4 @@ pub struct UserAuth { pub struct AssertionForSubmission { assertion: String, id: String, -} \ No newline at end of file +} diff --git a/crates/core/src/error.rs b/crates/core/src/error.rs index 2d1d729..2fcd2ed 100644 --- a/crates/core/src/error.rs +++ b/crates/core/src/error.rs @@ -1,5 +1,5 @@ -use reqwest::Error as ReqwestError; use pcl_phoundry::error::PhoundryError; +use reqwest::Error as ReqwestError; use thiserror::Error; #[derive(Error, Debug)] diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index 5c888ad..fa6a322 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -1,4 +1,4 @@ pub mod assertion_da; -pub mod error; +pub mod assertion_submission; pub mod config; -pub mod assertion_submission; \ No newline at end of file +pub mod error; diff --git a/crates/phoundry/src/build.rs b/crates/phoundry/src/build.rs index 958c468..8897d46 100644 --- a/crates/phoundry/src/build.rs +++ b/crates/phoundry/src/build.rs @@ -1,7 +1,7 @@ use clap::Parser; use pcl_common::args::CliArgs; -use crate::{phorge::Phorge, error::PhoundryError}; +use crate::{error::PhoundryError, phorge::Phorge}; #[derive(Debug)] pub struct AssertionBuildOutput { diff --git a/crates/phoundry/src/error.rs b/crates/phoundry/src/error.rs index 0abdc82..1434fed 100644 --- a/crates/phoundry/src/error.rs +++ b/crates/phoundry/src/error.rs @@ -1,5 +1,5 @@ -use thiserror::Error; use std::fmt::Debug; +use thiserror::Error; #[derive(Error, Debug)] pub enum PhoundryError { diff --git a/crates/phoundry/src/lib.rs b/crates/phoundry/src/lib.rs index 575c26f..67c91ee 100644 --- a/crates/phoundry/src/lib.rs +++ b/crates/phoundry/src/lib.rs @@ -1,3 +1,3 @@ pub mod build; pub mod error; -pub mod phorge; \ No newline at end of file +pub mod phorge; diff --git a/crates/phoundry/src/phorge.rs b/crates/phoundry/src/phorge.rs index 6482516..8f36cea 100644 --- a/crates/phoundry/src/phorge.rs +++ b/crates/phoundry/src/phorge.rs @@ -1,9 +1,9 @@ +use pcl_common::args::CliArgs; use std::{ env, path::PathBuf, process::{Command, Output}, }; -use pcl_common::args::CliArgs; use crate::error::PhoundryError; @@ -70,4 +70,4 @@ impl Phorge { } Ok(()) } -} \ No newline at end of file +} From cad8430f911aa792a9ca340a77b7f4a326de97ac Mon Sep 17 00:00:00 2001 From: "Odysseas.eth" Date: Thu, 13 Feb 2025 23:42:56 +0200 Subject: [PATCH 07/25] chore: clippy --- bin/pcl/src/main.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/bin/pcl/src/main.rs b/bin/pcl/src/main.rs index 9359f62..a717884 100644 --- a/bin/pcl/src/main.rs +++ b/bin/pcl/src/main.rs @@ -3,7 +3,6 @@ use eyre::{Context, Result}; use pcl_common::args::CliArgs; use pcl_core::{ assertion_da::DASubmitArgs, assertion_submission::DappSubmitArgs, config::CliConfig, - error::DappSubmitError, }; use pcl_phoundry::{build::BuildArgs, phorge::Phorge}; const VERSION_MESSAGE: &str = concat!( From 491558635f814248afd2e69639e80591eb306103 Mon Sep 17 00:00:00 2001 From: "Odysseas.eth" Date: Fri, 14 Feb 2025 00:02:44 +0200 Subject: [PATCH 08/25] feat: select project --- crates/core/src/assertion_submission.rs | 68 +++++++++++++++++-------- crates/core/src/config.rs | 5 +- 2 files changed, 51 insertions(+), 22 deletions(-) diff --git a/crates/core/src/assertion_submission.rs b/crates/core/src/assertion_submission.rs index 6e561bf..0b01418 100644 --- a/crates/core/src/assertion_submission.rs +++ b/crates/core/src/assertion_submission.rs @@ -24,6 +24,10 @@ pub struct DappSubmitArgs { default_value = "https://credible-layer-dapp.pages.dev/api/v1" )] dapp_url: String, + #[clap(short, long)] + project_name: Option, + #[clap(short, long)] + assertion_name: Option, } impl DappSubmitArgs { @@ -32,34 +36,58 @@ impl DappSubmitArgs { cli_args: CliArgs, config: &mut CliConfig, ) -> Result<(), DappSubmitError> { + let projects = self.get_projects(config).await?; + let project = self.select_project(projects)?; + + Ok(()) + } + + fn select_assertion(){} + + fn submit_assertion(){} + + fn select_project(&self, projects: Vec) -> Result { + match &self.project_name { + None => { + let project_options: Vec = projects + .iter() + .map(|p| p.project_name.clone()) + .collect(); + + Select::new( + "Select a project to submit the assertion to:", + project_options, + ) + .prompt() + .map_err(|_| DappSubmitError::ProjectSelectionCancelled) + } + Some(name) => { + let exists = projects.iter().any(|p| { + p.project_name.to_lowercase() == name.to_lowercase() + }); + + if !exists { + println!( + "The project {} does not exist. Please create it or choose from the list of existing projects.", + name + ); + } + Ok(name.clone()) + } + } + } + async fn get_projects(&self, config: &mut CliConfig) -> Result, DappSubmitError> { let client = reqwest::Client::new(); let projects: Vec = client .get(format!( "{}/projects?user={}", - self.dapp_url, "0x702352bc4fc5a3C1e7ef8D96C6d51d5352998c2B" + self.dapp_url, config.auth.as_ref().unwrap().user_address )) .send() .await? .json() .await?; - - // Create selection options - let project_options: Vec = - projects.iter().map(|p| p.project_name.clone()).collect(); - - // Show interactive selection - let selection = Select::new( - "Select a project to submit the assertion to:", - project_options, - ) - .prompt() - .map_err(|_| DappSubmitError::ProjectSelectionCancelled)?; - - let project = projects - .iter() - .find(|p| p.project_name == selection) - .unwrap(); - - Ok(()) + Ok(projects) } + } diff --git a/crates/core/src/config.rs b/crates/core/src/config.rs index bbaec48..6726200 100644 --- a/crates/core/src/config.rs +++ b/crates/core/src/config.rs @@ -1,6 +1,7 @@ use crate::error::ConfigError; use dirs::home_dir; use serde::{Deserialize, Serialize}; + pub const CONFIG_DIR: &str = ".pcl"; pub const CONFIG_FILE: &str = "config.toml"; @@ -48,6 +49,6 @@ pub struct UserAuth { #[derive(Debug, Default, Serialize, Deserialize)] pub struct AssertionForSubmission { - assertion: String, - id: String, + assertion_id: String, + signature: String, } From ebc2871aa9bdd7abcf8d84141b2858f6f5f32a37 Mon Sep 17 00:00:00 2001 From: "Odysseas.eth" Date: Fri, 14 Feb 2025 00:31:27 +0200 Subject: [PATCH 09/25] ref: move justfile logic into build script --- README.md | 3 +- bin/pcl/build.rs | 64 +++++++++++++++++++++++++++++++-- bin/pcl/src/main.rs | 4 ++- crates/core/src/assertion_da.rs | 4 +-- justfile | 25 ------------- 5 files changed, 67 insertions(+), 33 deletions(-) delete mode 100644 justfile diff --git a/README.md b/README.md index 73c3c01..2ccb599 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,6 @@ The Credible CLI is a command-line interface for the Credible Layer. It requires the following: -- `just` - `Rust >= 1.86 nightly` - `git` @@ -17,7 +16,7 @@ After you have installed the above, you can build the CLI by running the followi ```bash git clone git@github.com:phylaxsystems/pcl.git cd pcl -just build-all +cargo build --release ``` This will build the CLI and install it in the `target/release` directory. diff --git a/bin/pcl/build.rs b/bin/pcl/build.rs index df5c23b..e806d9a 100644 --- a/bin/pcl/build.rs +++ b/bin/pcl/build.rs @@ -1,14 +1,72 @@ +use std::{env, fs, path::Path, process::Command}; + use anyhow::Result; use vergen_gix::{BuildBuilder, CargoBuilder, Emitter, GixBuilder, RustcBuilder, SysinfoBuilder}; pub fn main() -> Result<()> { - println!("cargo:rerun-if-changed=build.rs"); - Emitter::default() .add_instructions(&BuildBuilder::all_build()?)? .add_instructions(&CargoBuilder::all_cargo()?)? .add_instructions(&GixBuilder::all_git()?)? .add_instructions(&RustcBuilder::all_rustc()?)? .add_instructions(&SysinfoBuilder::all_sysinfo()?)? - .emit() + .emit()?; + + let out_dir = env::var("OUT_DIR").unwrap(); + let profile = env::var("PROFILE").unwrap(); + println!("cargo:warning=Building in {} mode", profile); + + // Get the workspace root directory (where Cargo.toml is located) + let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); + let workspace_root = Path::new(&manifest_dir) + .parent() // up from bin/pcl + .unwrap() + .parent() // up to workspace root + .unwrap(); + + // Update phoundry submodule + update_phoundry(workspace_root).expect("Failed to update phoundry submodule"); + + // Build phoundry/forge + build_phoundry(workspace_root, &profile).expect("Failed to build phoundry"); + + // Copy the forge binary to the main target directory instead of OUT_DIR + let source = workspace_root + .join("phoundry") + .join("target") + .join(&profile) + .join("forge"); + + let dest = workspace_root + .join("target") + .join(&profile) + .join("phorge"); + + println!("cargo:warning=Copying {} to {}", source.display(), dest.display()); + fs::copy(&source, &dest).expect("Failed to copy forge binary"); + + println!("cargo:rerun-if-changed={}", source.display()); + println!("cargo:rerun-if-changed=phoundry"); + Ok(()) +} + +fn update_phoundry(workspace_root: &Path) -> std::io::Result<()> { + Command::new("git") + .current_dir(workspace_root) + .args(["submodule", "update", "--init", "--recursive", "--remote"]) + .status()?; + Ok(()) } + +fn build_phoundry(workspace_root: &Path, mode: &str) -> std::io::Result<()> { + let mut args = vec!["build", "--bin", "forge"]; + if mode == "release" { + args.push("--release"); + } + + Command::new("cargo") + .current_dir(workspace_root.join("phoundry")) + .args(&args) + .status()?; + Ok(()) +} \ No newline at end of file diff --git a/bin/pcl/src/main.rs b/bin/pcl/src/main.rs index a717884..05729cd 100644 --- a/bin/pcl/src/main.rs +++ b/bin/pcl/src/main.rs @@ -5,6 +5,7 @@ use pcl_core::{ assertion_da::DASubmitArgs, assertion_submission::DappSubmitArgs, config::CliConfig, }; use pcl_phoundry::{build::BuildArgs, phorge::Phorge}; + const VERSION_MESSAGE: &str = concat!( env!("CARGO_PKG_VERSION"), "\nCommit: ", @@ -49,7 +50,8 @@ async fn main() -> Result<()> { build.run(cli.args.clone())?; } Commands::DASubmit(submit) => { - submit.run(cli.args.clone()).await?; + config.must_be_authenticated().wrap_err("Authentication required for DA submission. Please authenticate first using 'pcl auth'")?; + submit.run(cli.args.clone(), &mut config).await?; } Commands::DappSubmit(submit) => { config.must_be_authenticated().wrap_err("Authentication required for dapp submission. Please authenticate first using 'pcl auth'")?; diff --git a/crates/core/src/assertion_da.rs b/crates/core/src/assertion_da.rs index 1fbef92..b8e6a39 100644 --- a/crates/core/src/assertion_da.rs +++ b/crates/core/src/assertion_da.rs @@ -1,4 +1,4 @@ -use crate::error::DaSubmitError; +use crate::{config::CliConfig, error::DaSubmitError}; use alloy_primitives::keccak256; use pcl_common::{args::CliArgs, utils::bytecode}; use pcl_phoundry::build::BuildArgs; @@ -37,7 +37,7 @@ pub struct DASubmitArgs { } impl DASubmitArgs { - pub async fn run(&self, cli_args: CliArgs) -> Result<(), DaSubmitError> { + pub async fn run(&self, cli_args: CliArgs, _config: &mut CliConfig) -> Result<(), DaSubmitError> { let build_args = BuildArgs { assertions: vec![self.assertion.clone()], }; diff --git a/justfile b/justfile deleted file mode 100644 index d438bed..0000000 --- a/justfile +++ /dev/null @@ -1,25 +0,0 @@ -phoundry-repo := "https://github.com/phylaxsystems/phoundry" -phoundry-dir := "phoundry" - -cargo-mode mode="release": - if [ {{mode}} == "release" ]; then echo "--release"; else echo ""; fi - -setup-phoundry mode="release": - #!/usr/bin/env sh - cd {{phoundry-dir}} && cargo build --bin forge `just cargo-mode {{mode}}` - -build-all skip_update="false" mode="release": - cargo build `just cargo-mode {{mode}}` - if [ "{{skip_update}}" = "false" ]; then just update-phoundry; fi - just setup-phoundry {{mode}} - just place-phoundry-bin {{mode}} - -update-phoundry: - git submodule update --init --recursive --remote - cd {{phoundry-dir}} - -place-phoundry-bin mode="release": - cp {{phoundry-dir}}/target/{{mode}}/forge target/{{mode}}/phorge - -test: - cargo test --workspace From bc40e2d736f3a223b9bcc24cd3f465848e645ea4 Mon Sep 17 00:00:00 2001 From: "Odysseas.eth" Date: Fri, 14 Feb 2025 00:35:35 +0200 Subject: [PATCH 10/25] chore: clippy --- bin/pcl/build.rs | 22 ++++++++++----------- crates/core/src/assertion_da.rs | 10 +++++++--- crates/core/src/assertion_submission.rs | 26 ++++++++++++------------- crates/core/src/config.rs | 5 ++--- 4 files changed, 33 insertions(+), 30 deletions(-) diff --git a/bin/pcl/build.rs b/bin/pcl/build.rs index e806d9a..ce81e43 100644 --- a/bin/pcl/build.rs +++ b/bin/pcl/build.rs @@ -12,7 +12,6 @@ pub fn main() -> Result<()> { .add_instructions(&SysinfoBuilder::all_sysinfo()?)? .emit()?; - let out_dir = env::var("OUT_DIR").unwrap(); let profile = env::var("PROFILE").unwrap(); println!("cargo:warning=Building in {} mode", profile); @@ -23,7 +22,7 @@ pub fn main() -> Result<()> { .unwrap() .parent() // up to workspace root .unwrap(); - + // Update phoundry submodule update_phoundry(workspace_root).expect("Failed to update phoundry submodule"); @@ -36,15 +35,16 @@ pub fn main() -> Result<()> { .join("target") .join(&profile) .join("forge"); - - let dest = workspace_root - .join("target") - .join(&profile) - .join("phorge"); - - println!("cargo:warning=Copying {} to {}", source.display(), dest.display()); + + let dest = workspace_root.join("target").join(&profile).join("phorge"); + + println!( + "cargo:warning=Copying {} to {}", + source.display(), + dest.display() + ); fs::copy(&source, &dest).expect("Failed to copy forge binary"); - + println!("cargo:rerun-if-changed={}", source.display()); println!("cargo:rerun-if-changed=phoundry"); Ok(()) @@ -69,4 +69,4 @@ fn build_phoundry(workspace_root: &Path, mode: &str) -> std::io::Result<()> { .args(&args) .status()?; Ok(()) -} \ No newline at end of file +} diff --git a/crates/core/src/assertion_da.rs b/crates/core/src/assertion_da.rs index b8e6a39..d2bbb85 100644 --- a/crates/core/src/assertion_da.rs +++ b/crates/core/src/assertion_da.rs @@ -7,9 +7,9 @@ use serde::{Deserialize, Serialize}; #[derive(Deserialize)] struct JsonRpcResponse { - jsonrpc: String, + _json_rpc: String, result: SubmissionResponse, - id: u64, + _id: u64, } #[derive(Deserialize)] @@ -37,7 +37,11 @@ pub struct DASubmitArgs { } impl DASubmitArgs { - pub async fn run(&self, cli_args: CliArgs, _config: &mut CliConfig) -> Result<(), DaSubmitError> { + pub async fn run( + &self, + cli_args: CliArgs, + _config: &mut CliConfig, + ) -> Result<(), DaSubmitError> { let build_args = BuildArgs { assertions: vec![self.assertion.clone()], }; diff --git a/crates/core/src/assertion_submission.rs b/crates/core/src/assertion_submission.rs index 0b01418..8c1d648 100644 --- a/crates/core/src/assertion_submission.rs +++ b/crates/core/src/assertion_submission.rs @@ -33,26 +33,26 @@ pub struct DappSubmitArgs { impl DappSubmitArgs { pub async fn run( &self, - cli_args: CliArgs, + _cli_args: CliArgs, config: &mut CliConfig, ) -> Result<(), DappSubmitError> { let projects = self.get_projects(config).await?; - let project = self.select_project(projects)?; + let _project = self.select_project(projects)?; Ok(()) } - fn select_assertion(){} + fn _select_assertion(&self) { + todo!() + } - fn submit_assertion(){} + fn _submit_assertion(&self) {} fn select_project(&self, projects: Vec) -> Result { match &self.project_name { None => { - let project_options: Vec = projects - .iter() - .map(|p| p.project_name.clone()) - .collect(); + let project_options: Vec = + projects.iter().map(|p| p.project_name.clone()).collect(); Select::new( "Select a project to submit the assertion to:", @@ -62,9 +62,9 @@ impl DappSubmitArgs { .map_err(|_| DappSubmitError::ProjectSelectionCancelled) } Some(name) => { - let exists = projects.iter().any(|p| { - p.project_name.to_lowercase() == name.to_lowercase() - }); + let exists = projects + .iter() + .any(|p| p.project_name.to_lowercase() == name.to_lowercase()); if !exists { println!( @@ -81,7 +81,8 @@ impl DappSubmitArgs { let projects: Vec = client .get(format!( "{}/projects?user={}", - self.dapp_url, config.auth.as_ref().unwrap().user_address + self.dapp_url, + config.auth.as_ref().unwrap().user_address )) .send() .await? @@ -89,5 +90,4 @@ impl DappSubmitArgs { .await?; Ok(projects) } - } diff --git a/crates/core/src/config.rs b/crates/core/src/config.rs index 6726200..b370ca7 100644 --- a/crates/core/src/config.rs +++ b/crates/core/src/config.rs @@ -16,7 +16,7 @@ impl CliConfig { let config_dir = home_dir().unwrap().join(CONFIG_DIR); let config_file = config_dir.join(CONFIG_FILE); let config_str = toml::to_string(self).unwrap(); - std::fs::write(config_file, config_str).map_err(|e| ConfigError::WriteError(e))?; + std::fs::write(config_file, config_str).map_err(ConfigError::WriteError)?; Ok(()) } @@ -27,8 +27,7 @@ impl CliConfig { pub fn read_from_file() -> Result { let config_dir = home_dir().unwrap().join(CONFIG_DIR); let config_file = config_dir.join(CONFIG_FILE); - let config_str = - std::fs::read_to_string(config_file).map_err(|e| ConfigError::ReadError(e))?; + let config_str = std::fs::read_to_string(config_file).map_err(ConfigError::ReadError)?; Ok(toml::from_str(&config_str).unwrap()) } From dcf508f69ca097f9c26b08851c6ad47f95e7c85f Mon Sep 17 00:00:00 2001 From: "Odysseas.eth" Date: Fri, 14 Feb 2025 14:07:35 +0200 Subject: [PATCH 11/25] wip: submit assert --- crates/core/src/assertion_submission.rs | 98 +++++++++++++++++++------ crates/core/src/config.rs | 5 +- crates/core/src/error.rs | 2 + 3 files changed, 79 insertions(+), 26 deletions(-) diff --git a/crates/core/src/assertion_submission.rs b/crates/core/src/assertion_submission.rs index 8c1d648..9a01c80 100644 --- a/crates/core/src/assertion_submission.rs +++ b/crates/core/src/assertion_submission.rs @@ -1,5 +1,5 @@ -use crate::{config::CliConfig, error::DappSubmitError}; -use inquire::Select; +use crate::{config::{AssertionForSubmission, CliConfig}, error::DappSubmitError}; +use inquire::{MultiSelect, Select}; use pcl_common::args::CliArgs; use serde::Deserialize; @@ -27,7 +27,7 @@ pub struct DappSubmitArgs { #[clap(short, long)] project_name: Option, #[clap(short, long)] - assertion_name: Option, + assertion_name: Option>, } impl DappSubmitArgs { @@ -37,42 +37,92 @@ impl DappSubmitArgs { config: &mut CliConfig, ) -> Result<(), DappSubmitError> { let projects = self.get_projects(config).await?; - let _project = self.select_project(projects)?; + let assertions_for_submission = config.assertions_for_submission.iter().map(|a| a.assertion_contract.clone()).collect(); + + let project_name = self.provide_or_select(self.project_name.clone(), projects.iter().map(|p| p.project_name.clone()).collect(), "Select a project to submit the assertion to:".to_string())?; + let project = projects.iter().find(|p| p.project_name == project_name).unwrap(); + + let assertion_names= self.provide_or_multi_select(self.assertion_name.clone(), assertions_for_submission, "Select an assertion to submit:".to_string())?; + + let assertions = assertion_names.iter().map(|n| config.assertions_for_submission.iter().find(|a| a.assertion_contract == n).unwrap()).collect(); + + self.submit_assertion(project, assertions).await?; Ok(()) } - fn _select_assertion(&self) { - todo!() + async fn submit_assertion(&self, project: &Project, assertions: Vec<&AssertionForSubmission>) -> Result<(), DappSubmitError> { + let client = reqwest::Client::new(); + let body = json!{{}}; + let response = client.post(format!("{}/assertions", self.dapp_url)); + .json(&body) + .send() + .await?; + if response.status().is_success() { + Ok(()) + } else { + Err(DappSubmitError::SubmissionFailed(response.text().await?)) + } } - fn _submit_assertion(&self) {} - - fn select_project(&self, projects: Vec) -> Result { - match &self.project_name { + fn provide_or_select(&self, maybe_key: Option, values: Vec, message: String) -> Result { + match maybe_key{ None => { - let project_options: Vec = - projects.iter().map(|p| p.project_name.clone()).collect(); - Select::new( - "Select a project to submit the assertion to:", - project_options, + message.as_str(), + values, ) .prompt() .map_err(|_| DappSubmitError::ProjectSelectionCancelled) } - Some(name) => { - let exists = projects + Some(key) => { + let exists = values .iter() - .any(|p| p.project_name.to_lowercase() == name.to_lowercase()); + .any(|p| key.to_lowercase() == p.to_lowercase()); + if exists { + Ok(key.to_string()) + } + else { + println!("{} does not exist", key); + let choice = Select::new( + message.as_str(), + values, + ) + .prompt() + .map_err(|_| DappSubmitError::ProjectSelectionCancelled)?; + Ok(choice) + } + } + } + } - if !exists { - println!( - "The project {} does not exist. Please create it or choose from the list of existing projects.", - name - ); + fn provide_or_multi_select(&self, maybe_keys: Option>, values: Vec, message: String) -> Result, DappSubmitError> { + match maybe_keys{ + None => { + MultiSelect::new( + message.as_str(), + values, + ) + .prompt() + .map_err(|_| DappSubmitError::ProjectSelectionCancelled) + } + Some(key) => { + let exists = key.iter().all(|k| { + values.iter().any(|v| k.to_lowercase() == v.to_lowercase()) + }); + if exists { + Ok(values) + } + else { + println!("{} does not exist", key.join(", ")); + let choice = MultiSelect::new( + message.as_str(), + values, + ) + .prompt() + .map_err(|_| DappSubmitError::ProjectSelectionCancelled)?; + Ok(choice) } - Ok(name.clone()) } } } diff --git a/crates/core/src/config.rs b/crates/core/src/config.rs index b370ca7..2b24aaa 100644 --- a/crates/core/src/config.rs +++ b/crates/core/src/config.rs @@ -48,6 +48,7 @@ pub struct UserAuth { #[derive(Debug, Default, Serialize, Deserialize)] pub struct AssertionForSubmission { - assertion_id: String, - signature: String, + pub assertion_contract: String, + pub assertion_id: String, + pub signature: String, } diff --git a/crates/core/src/error.rs b/crates/core/src/error.rs index 2fcd2ed..0e228f2 100644 --- a/crates/core/src/error.rs +++ b/crates/core/src/error.rs @@ -20,6 +20,8 @@ pub enum DappSubmitError { ProjectSelectionCancelled, #[error("Failed to connect to the dApp API")] ApiConnectionError(#[from] ReqwestError), + #[error("Submission failed: {0}")] + SubmissionFailed(String), } #[derive(Error, Debug)] From fe508bf6477425fd5c0af9b4ca41497dbc7c36b3 Mon Sep 17 00:00:00 2001 From: "Odysseas.eth" Date: Fri, 14 Feb 2025 14:22:57 +0200 Subject: [PATCH 12/25] chore: fmt, comments --- crates/core/src/assertion_submission.rs | 191 ++++++++++++++++++------ crates/core/src/error.rs | 21 +++ 2 files changed, 165 insertions(+), 47 deletions(-) diff --git a/crates/core/src/assertion_submission.rs b/crates/core/src/assertion_submission.rs index 9a01c80..b3400ec 100644 --- a/crates/core/src/assertion_submission.rs +++ b/crates/core/src/assertion_submission.rs @@ -1,7 +1,11 @@ -use crate::{config::{AssertionForSubmission, CliConfig}, error::DappSubmitError}; +use crate::{ + config::{AssertionForSubmission, CliConfig}, + error::DappSubmitError, +}; use inquire::{MultiSelect, Select}; use pcl_common::args::CliArgs; use serde::Deserialize; +use serde_json::json; #[derive(Deserialize)] struct Project { @@ -16,48 +20,109 @@ struct Project { _updated_at: String, } +/// Arguments for submitting assertions to the Credible Layer dApp +/// +/// This struct handles CLI arguments for the assertion submission process, +/// including the dApp URL, project name, and assertion names. #[derive(clap::Parser)] +#[clap(about = "Submit assertions to the Credible Layer dApp")] pub struct DappSubmitArgs { + /// Base URL for the Credible Layer dApp API #[clap( short, long, default_value = "https://credible-layer-dapp.pages.dev/api/v1" )] dapp_url: String, + + /// Optional project name to skip interactive selection #[clap(short, long)] project_name: Option, + + /// Optional list of assertion names to skip interactive selection #[clap(short, long)] assertion_name: Option>, } impl DappSubmitArgs { + /// Executes the assertion submission workflow + /// + /// # Arguments + /// * `_cli_args` - General CLI arguments + /// * `config` - Configuration containing assertions and auth details + /// + /// # Returns + /// * `Result<(), DappSubmitError>` - Success or specific error pub async fn run( &self, _cli_args: CliArgs, config: &mut CliConfig, ) -> Result<(), DappSubmitError> { let projects = self.get_projects(config).await?; - let assertions_for_submission = config.assertions_for_submission.iter().map(|a| a.assertion_contract.clone()).collect(); + let assertions_for_submission = config + .assertions_for_submission + .iter() + .map(|a| a.assertion_contract.clone()) + .collect(); - let project_name = self.provide_or_select(self.project_name.clone(), projects.iter().map(|p| p.project_name.clone()).collect(), "Select a project to submit the assertion to:".to_string())?; - let project = projects.iter().find(|p| p.project_name == project_name).unwrap(); + let project_name = self.provide_or_select( + self.project_name.clone(), + projects.iter().map(|p| p.project_name.clone()).collect(), + "Select a project to submit the assertion to:".to_string(), + )?; + let project = projects + .iter() + .find(|p| p.project_name == project_name) + .unwrap(); - let assertion_names= self.provide_or_multi_select(self.assertion_name.clone(), assertions_for_submission, "Select an assertion to submit:".to_string())?; + let assertion_names = self.provide_or_multi_select( + self.assertion_name.clone(), + assertions_for_submission, + "Select an assertion to submit:".to_string(), + )?; - let assertions = assertion_names.iter().map(|n| config.assertions_for_submission.iter().find(|a| a.assertion_contract == n).unwrap()).collect(); + let assertions = assertion_names + .iter() + .map(|n| { + config + .assertions_for_submission + .iter() + .find(|a| a.assertion_contract == *n) + .unwrap() + }) + .collect(); self.submit_assertion(project, assertions).await?; Ok(()) } - async fn submit_assertion(&self, project: &Project, assertions: Vec<&AssertionForSubmission>) -> Result<(), DappSubmitError> { + /// Submits selected assertions to the specified project + /// + /// # Arguments + /// * `project` - Target project for submission + /// * `assertions` - List of assertions to submit + /// + /// # Returns + /// * `Result<(), DappSubmitError>` - Success or API error + async fn submit_assertion( + &self, + project: &Project, + assertions: Vec<&AssertionForSubmission>, + ) -> Result<(), DappSubmitError> { let client = reqwest::Client::new(); - let body = json!{{}}; - let response = client.post(format!("{}/assertions", self.dapp_url)); + // TODO: Update payload structure once API spec is finalized + let body = json!({ + "project_id": project._project_id, + "assertions": assertions.iter().map(|a| &a.assertion_contract).collect::>() + }); + + let response = client + .post(format!("{}/assertions", self.dapp_url)) .json(&body) .send() .await?; + if response.status().is_success() { Ok(()) } else { @@ -65,62 +130,72 @@ impl DappSubmitArgs { } } - fn provide_or_select(&self, maybe_key: Option, values: Vec, message: String) -> Result { - match maybe_key{ - None => { - Select::new( - message.as_str(), - values, - ) + /// Handles interactive or direct selection of a single value + /// + /// # Arguments + /// * `maybe_key` - Optional pre-selected value + /// * `values` - Available options + /// * `message` - Prompt message for interactive selection + /// + /// # Returns + /// * `Result` - Selected value or error + fn provide_or_select( + &self, + maybe_key: Option, + values: Vec, + message: String, + ) -> Result { + match maybe_key { + None => Select::new(message.as_str(), values) .prompt() - .map_err(|_| DappSubmitError::ProjectSelectionCancelled) - } + .map_err(|_| DappSubmitError::ProjectSelectionCancelled), Some(key) => { - let exists = values + let exists = values .iter() .any(|p| key.to_lowercase() == p.to_lowercase()); if exists { Ok(key.to_string()) - } - else { + } else { println!("{} does not exist", key); - let choice = Select::new( - message.as_str(), - values, - ) - .prompt() - .map_err(|_| DappSubmitError::ProjectSelectionCancelled)?; + let choice = Select::new(message.as_str(), values) + .prompt() + .map_err(|_| DappSubmitError::ProjectSelectionCancelled)?; Ok(choice) } } } } - fn provide_or_multi_select(&self, maybe_keys: Option>, values: Vec, message: String) -> Result, DappSubmitError> { - match maybe_keys{ - None => { - MultiSelect::new( - message.as_str(), - values, - ) + /// Handles interactive or direct selection of multiple values + /// + /// # Arguments + /// * `maybe_keys` - Optional pre-selected values + /// * `values` - Available options + /// * `message` - Prompt message for interactive selection + /// + /// # Returns + /// * `Result, DappSubmitError>` - Selected values or error + fn provide_or_multi_select( + &self, + maybe_keys: Option>, + values: Vec, + message: String, + ) -> Result, DappSubmitError> { + match maybe_keys { + None => MultiSelect::new(message.as_str(), values) .prompt() - .map_err(|_| DappSubmitError::ProjectSelectionCancelled) - } + .map_err(|_| DappSubmitError::ProjectSelectionCancelled), Some(key) => { - let exists = key.iter().all(|k| { - values.iter().any(|v| k.to_lowercase() == v.to_lowercase()) - }); + let exists = key + .iter() + .all(|k| values.iter().any(|v| k.to_lowercase() == v.to_lowercase())); if exists { Ok(values) - } - else { + } else { println!("{} does not exist", key.join(", ")); - let choice = MultiSelect::new( - message.as_str(), - values, - ) - .prompt() - .map_err(|_| DappSubmitError::ProjectSelectionCancelled)?; + let choice = MultiSelect::new(message.as_str(), values) + .prompt() + .map_err(|_| DappSubmitError::ProjectSelectionCancelled)?; Ok(choice) } } @@ -141,3 +216,25 @@ impl DappSubmitArgs { Ok(projects) } } + +/// TODO(ODYSSEAS): Add tests for the DappSubmitArgs struct +#[cfg(test)] +mod tests { + use crate::assertion_submission::DappSubmitArgs; + + #[test] + fn test_provide_or_select_with_valid_input() { + let args = DappSubmitArgs { + dapp_url: "".to_string(), + project_name: Some("Project1".to_string()), + assertion_name: None, + }; + + let values = vec!["Project1".to_string(), "Project2".to_string()]; + let result = + args.provide_or_select(Some("Project1".to_string()), values, "Select:".to_string()); + + assert!(result.is_ok()); + assert_eq!(result.unwrap(), "Project1"); + } +} diff --git a/crates/core/src/error.rs b/crates/core/src/error.rs index 0e228f2..d251959 100644 --- a/crates/core/src/error.rs +++ b/crates/core/src/error.rs @@ -2,34 +2,55 @@ use pcl_phoundry::error::PhoundryError; use reqwest::Error as ReqwestError; use thiserror::Error; +/// Errors that can occur during assertion submission to the Data Availability (DA) layer #[derive(Error, Debug)] pub enum DaSubmitError { + /// Error when HTTP request to the DA layer fails #[error("HTTP request failed: {0}")] RequestFailed(#[from] ReqwestError), + + /// Error when the submission is rejected by the DA layer #[error("Submission failed: {0}")] SubmissionFailed(String), + + /// Error during the build process of the assertion #[error("Build failed: {0}")] BuildError(#[from] PhoundryError), } +/// Errors that can occur during assertion submission to the Credible Layer dApp #[derive(Error, Debug)] pub enum DappSubmitError { + /// Error when no authentication token is found in the config #[error("No auth token found")] NoAuthToken, + + /// Error when user cancels the project selection process #[error("Project selection cancelled")] ProjectSelectionCancelled, + + /// Error when connection to the dApp API fails #[error("Failed to connect to the dApp API")] ApiConnectionError(#[from] ReqwestError), + + /// Error when the submission is rejected by the dApp #[error("Submission failed: {0}")] SubmissionFailed(String), } +/// Errors that can occur during configuration operations #[derive(Error, Debug)] pub enum ConfigError { + /// Error when reading the config file from ~/.pcl/config.toml fails #[error("Failed to read config file: {0}")] ReadError(std::io::Error), + + /// Error when writing to the config file at ~/.pcl/config.toml fails #[error("Failed to write config file: {0}")] WriteError(std::io::Error), + + /// Error when attempting an operation that requires authentication + /// but no authentication token is present in the config #[error("No Authentication Token Found")] NotAuthenticated, } From de8a403b51db7ad1166d6a4e2e5ce45d62f01e12 Mon Sep 17 00:00:00 2001 From: "Odysseas.eth" Date: Fri, 14 Feb 2025 14:30:35 +0200 Subject: [PATCH 13/25] nit: add comment --- crates/core/src/assertion_submission.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/core/src/assertion_submission.rs b/crates/core/src/assertion_submission.rs index b3400ec..a17eea7 100644 --- a/crates/core/src/assertion_submission.rs +++ b/crates/core/src/assertion_submission.rs @@ -93,6 +93,7 @@ impl DappSubmitArgs { .collect(); self.submit_assertion(project, assertions).await?; + // TOOD: remove assertion from config Ok(()) } From d29ae62a25ce986a6633da325f39f0a17b4f26ce Mon Sep 17 00:00:00 2001 From: "Odysseas.eth" Date: Fri, 14 Feb 2025 14:37:22 +0200 Subject: [PATCH 14/25] feat: add ci --- .github/workflows/ci.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..2f7e202 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,10 @@ +name: Rust CI + +on: + push: + branches: [main] + pull_request: + +jobs: + rust-base: + uses: init4tech/actions/.github/workflows/rust-base.yml@main \ No newline at end of file From 585eb12119601c5e8749e2f6cd7336a895cfe708 Mon Sep 17 00:00:00 2001 From: "Odysseas.eth" Date: Fri, 14 Feb 2025 14:41:15 +0200 Subject: [PATCH 15/25] fix: gha --- .github/workflows/ci.yml | 116 ++++++++++++++++++++++++++++++++++++++- Makefile | 27 +++++++++ 2 files changed, 140 insertions(+), 3 deletions(-) create mode 100644 Makefile diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2f7e202..14d68d2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,5 @@ -name: Rust CI +# blatantly lifted from https://github.com/init4tech/actions/blob/main/.github/workflows/rust-base.yml +name: Rust on: push: @@ -6,5 +7,114 @@ on: pull_request: jobs: - rust-base: - uses: init4tech/actions/.github/workflows/rust-base.yml@main \ No newline at end of file + unit-test: + name: Unit Tests + runs-on: + group: big-bois + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup ssh-agent + uses: webfactory/ssh-agent@v0.9.0 + with: + ssh-private-key: | + ${{ secrets.SSH_PRIVATE_KEY }} + + - name: Initialize Git submodules + run: git submodule update --init --recursive + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@nightly + + - uses: Swatinem/rust-cache@v2 + + - uses: taiki-e/install-action@nextest + + - name: Run Unit Tests + run: make test + + - name: Run Unit Tests w/ Optimism + run: make test-optimism + + fmt: + name: Format + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup ssh-agent + uses: webfactory/ssh-agent@v0.9.0 + with: + ssh-private-key: | + ${{ secrets.SSH_PRIVATE_KEY }} + + - name: Initialize Git submodules + run: git submodule update --init --recursive + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@nightly + with: + components: rustfmt + + - uses: Swatinem/rust-cache@v2 + + - name: Check formatting + run: make format + + lint: + name: Lint + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup ssh-agent + uses: webfactory/ssh-agent@v0.9.0 + with: + ssh-private-key: | + ${{ secrets.SSH_PRIVATE_KEY }} + + - name: Initialize Git submodules + run: git submodule update --init --recursive + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@nightly + with: + components: rustfmt + + - uses: Swatinem/rust-cache@v2 + + - name: Install clippy + run: rustup component add clippy + + - name: Lint + run: make lint + + build: + name: Test Build + runs-on: + group: big-bois + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup ssh-agent + uses: webfactory/ssh-agent@v0.9.0 + with: + ssh-private-key: | + ${{ secrets.SSH_PRIVATE_KEY }} + + - name: Initialize Git submodules + run: git submodule update --init --recursive + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@nightly + + - uses: Swatinem/rust-cache@v2 + + - uses: taiki-e/install-action@nextest + + - name: Build + run: make build \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3fbcd48 --- /dev/null +++ b/Makefile @@ -0,0 +1,27 @@ +# Build the binary +build: + cargo build --verbose --release + +# Build the contract mocks and run the rust tests +test: + forge build --root contract-mocks && cargo test --verbose + +# Build the contract mocks and run the rust tests using the optimism feature flag +test-optimism: + forge build --root contract-mocks && cargo test --verbose --features optimism + +# Validate formatting +format: + cargo fmt --check + +# Errors if there is a warning with clippy +lint: + cargo clippy -- -D warnings + +# Run foundry tests against the contract mocks +test-mocks: + forge test --root contract-mocks -vvv + +# Can be used as a manual pre-commit check +pre-commit: + cargo fmt && make lint \ No newline at end of file From 204d5639c47854b05ecb839cc8a86d8189b2eb90 Mon Sep 17 00:00:00 2001 From: "Odysseas.eth" Date: Fri, 14 Feb 2025 14:44:43 +0200 Subject: [PATCH 16/25] fix: remove ssh keys from ci --- .github/workflows/ci.yml | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 14d68d2..4b67d8d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,12 +44,6 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - - name: Setup ssh-agent - uses: webfactory/ssh-agent@v0.9.0 - with: - ssh-private-key: | - ${{ secrets.SSH_PRIVATE_KEY }} - - name: Initialize Git submodules run: git submodule update --init --recursive @@ -70,15 +64,6 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - - name: Setup ssh-agent - uses: webfactory/ssh-agent@v0.9.0 - with: - ssh-private-key: | - ${{ secrets.SSH_PRIVATE_KEY }} - - - name: Initialize Git submodules - run: git submodule update --init --recursive - - name: Install Rust toolchain uses: dtolnay/rust-toolchain@nightly with: @@ -100,15 +85,6 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - - name: Setup ssh-agent - uses: webfactory/ssh-agent@v0.9.0 - with: - ssh-private-key: | - ${{ secrets.SSH_PRIVATE_KEY }} - - - name: Initialize Git submodules - run: git submodule update --init --recursive - - name: Install Rust toolchain uses: dtolnay/rust-toolchain@nightly From 391a9255f5197367c407413efd75da1412b9f347 Mon Sep 17 00:00:00 2001 From: "Odysseas.eth" Date: Fri, 14 Feb 2025 14:46:25 +0200 Subject: [PATCH 17/25] fix: remove ssh key from unit --- .github/workflows/ci.yml | 9 --------- 1 file changed, 9 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4b67d8d..709ce32 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,15 +15,6 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - - name: Setup ssh-agent - uses: webfactory/ssh-agent@v0.9.0 - with: - ssh-private-key: | - ${{ secrets.SSH_PRIVATE_KEY }} - - - name: Initialize Git submodules - run: git submodule update --init --recursive - - name: Install Rust toolchain uses: dtolnay/rust-toolchain@nightly From 6cec4a03e8f738fc438bbefb06a300d2a8c75d4f Mon Sep 17 00:00:00 2001 From: "Odysseas.eth" Date: Fri, 14 Feb 2025 14:47:32 +0200 Subject: [PATCH 18/25] fix: remove unused build ci --- .github/workflows/{ci.yml => rust.yml} | 29 ++------------------------ 1 file changed, 2 insertions(+), 27 deletions(-) rename .github/workflows/{ci.yml => rust.yml} (68%) diff --git a/.github/workflows/ci.yml b/.github/workflows/rust.yml similarity index 68% rename from .github/workflows/ci.yml rename to .github/workflows/rust.yml index 709ce32..f21c219 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/rust.yml @@ -10,7 +10,7 @@ jobs: unit-test: name: Unit Tests runs-on: - group: big-bois + group: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v4 @@ -24,10 +24,6 @@ jobs: - name: Run Unit Tests run: make test - - - name: Run Unit Tests w/ Optimism - run: make test-optimism - fmt: name: Format runs-on: ubuntu-latest @@ -35,9 +31,6 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 - - name: Initialize Git submodules - run: git submodule update --init --recursive - - name: Install Rust toolchain uses: dtolnay/rust-toolchain@nightly with: @@ -66,22 +59,4 @@ jobs: run: rustup component add clippy - name: Lint - run: make lint - - build: - name: Test Build - runs-on: - group: big-bois - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Install Rust toolchain - uses: dtolnay/rust-toolchain@nightly - - - uses: Swatinem/rust-cache@v2 - - - uses: taiki-e/install-action@nextest - - - name: Build - run: make build \ No newline at end of file + run: make lint \ No newline at end of file From 847f01ec6b5f2326abd414a9fc4fe18c1a1dc8e1 Mon Sep 17 00:00:00 2001 From: "Odysseas.eth" Date: Fri, 14 Feb 2025 14:51:46 +0200 Subject: [PATCH 19/25] fix: ci, makefile --- .github/workflows/rust.yml | 14 ++++---------- Makefile | 16 ++++++---------- 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index f21c219..1c05463 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -9,8 +9,7 @@ on: jobs: unit-test: name: Unit Tests - runs-on: - group: ubuntu-latest + runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v4 @@ -20,8 +19,6 @@ jobs: - uses: Swatinem/rust-cache@v2 - - uses: taiki-e/install-action@nextest - - name: Run Unit Tests run: make test fmt: @@ -39,7 +36,7 @@ jobs: - uses: Swatinem/rust-cache@v2 - name: Check formatting - run: make format + run: make format-check lint: name: Lint @@ -51,12 +48,9 @@ jobs: - name: Install Rust toolchain uses: dtolnay/rust-toolchain@nightly with: - components: rustfmt + components: clippy - uses: Swatinem/rust-cache@v2 - - name: Install clippy - run: rustup component add clippy - - name: Lint - run: make lint \ No newline at end of file + run: make lint-check \ No newline at end of file diff --git a/Makefile b/Makefile index 3fbcd48..ae08991 100644 --- a/Makefile +++ b/Makefile @@ -4,24 +4,20 @@ build: # Build the contract mocks and run the rust tests test: - forge build --root contract-mocks && cargo test --verbose - -# Build the contract mocks and run the rust tests using the optimism feature flag -test-optimism: - forge build --root contract-mocks && cargo test --verbose --features optimism + cargo test --verbose # Validate formatting +format-check: + cargo fmt --check + +# Format format: cargo fmt --check # Errors if there is a warning with clippy -lint: +lint-check: cargo clippy -- -D warnings -# Run foundry tests against the contract mocks -test-mocks: - forge test --root contract-mocks -vvv - # Can be used as a manual pre-commit check pre-commit: cargo fmt && make lint \ No newline at end of file From 0231c89e59a21c048b05a1330bbba4028803217a Mon Sep 17 00:00:00 2001 From: "Odysseas.eth" Date: Fri, 14 Feb 2025 15:26:36 +0200 Subject: [PATCH 20/25] fix: bring back ssh keys for building phoundry/executor --- .github/workflows/rust.yml | 51 +++++++++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 1c05463..c1a8640 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -9,11 +9,18 @@ on: jobs: unit-test: name: Unit Tests - runs-on: ubuntu-latest + runs-on: + group: big-bois steps: - name: Checkout repository uses: actions/checkout@v4 + - name: Setup ssh-agent + uses: webfactory/ssh-agent@v0.9.0 + with: + ssh-private-key: | + ${{ secrets.SSH_PRIVATE_KEY }} + - name: Install Rust toolchain uses: dtolnay/rust-toolchain@nightly @@ -21,6 +28,7 @@ jobs: - name: Run Unit Tests run: make test + fmt: name: Format runs-on: ubuntu-latest @@ -28,6 +36,12 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 + - name: Setup ssh-agent + uses: webfactory/ssh-agent@v0.9.0 + with: + ssh-private-key: | + ${{ secrets.SSH_PRIVATE_KEY }} + - name: Install Rust toolchain uses: dtolnay/rust-toolchain@nightly with: @@ -45,12 +59,43 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 + - name: Setup ssh-agent + uses: webfactory/ssh-agent@v0.9.0 + with: + ssh-private-key: | + ${{ secrets.SSH_PRIVATE_KEY }} + - name: Install Rust toolchain uses: dtolnay/rust-toolchain@nightly with: - components: clippy + components: clippy - uses: Swatinem/rust-cache@v2 - name: Lint - run: make lint-check \ No newline at end of file + run: make lint-check + + build: + name: Test Build + runs-on: + group: big-bois + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup ssh-agent + uses: webfactory/ssh-agent@v0.9.0 + with: + ssh-private-key: | + ${{ secrets.SSH_PRIVATE_KEY }} + + - name: Initialize Git submodules + run: git submodule update --init --recursive + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@nightly + + - uses: Swatinem/rust-cache@v2 + + - name: Build + run: make build \ No newline at end of file From 7eb4f498b2e4ca16e2ddaefe40411d5097e43511 Mon Sep 17 00:00:00 2001 From: "Odysseas.eth" Date: Fri, 14 Feb 2025 15:32:31 +0200 Subject: [PATCH 21/25] fix: run ci on ubuntu latest --- .github/workflows/rust.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index c1a8640..1892780 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -9,8 +9,7 @@ on: jobs: unit-test: name: Unit Tests - runs-on: - group: big-bois + runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v4 @@ -77,8 +76,7 @@ jobs: build: name: Test Build - runs-on: - group: big-bois + runs-on: ubuntu-latest steps: - name: Checkout repository uses: actions/checkout@v4 From a037a56c6da0df8580ce49741624ae527b7eafca Mon Sep 17 00:00:00 2001 From: "Odysseas.eth" Date: Fri, 14 Feb 2025 20:23:07 +0200 Subject: [PATCH 22/25] fix: tests; add config tests --- Cargo.lock | 1 + crates/core/Cargo.toml | 1 + crates/core/src/assertion_da.rs | 7 ++- crates/core/src/config.rs | 93 +++++++++++++++++++++++++++++++++ 4 files changed, 100 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 19b7d75..78f2c77 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2492,6 +2492,7 @@ dependencies = [ "reqwest", "serde", "serde_json", + "tempfile", "thiserror", "tokio", "toml", diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index 795f847..861dec1 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -25,4 +25,5 @@ dirs = "6.0.0" [dev-dependencies] mockito = "1.2" +tempfile = "3.6.0" tokio = { workspace = true } diff --git a/crates/core/src/assertion_da.rs b/crates/core/src/assertion_da.rs index d2bbb85..e6e2e1f 100644 --- a/crates/core/src/assertion_da.rs +++ b/crates/core/src/assertion_da.rs @@ -7,8 +7,10 @@ use serde::{Deserialize, Serialize}; #[derive(Deserialize)] struct JsonRpcResponse { + #[serde(rename = "jsonrpc")] _json_rpc: String, result: SubmissionResponse, + #[serde(rename = "id")] _id: u64, } @@ -20,6 +22,7 @@ struct SubmissionResponse { #[derive(Serialize)] struct JsonRpcRequest { + #[serde(rename = "jsonrpc")] json_rpc: String, method: String, params: Vec, @@ -134,7 +137,7 @@ mod tests { #[tokio::test] async fn test_submit_request() { - let mut server = Server::new(); + let mut server = Server::new_async().await; let mock = server .mock("POST", "/") .match_body(r#"{"jsonrpc":"2.0","method":"da_submit_assertion","params":["0xtest_id","0xtest_bytecode"],"id":1}"#) @@ -158,7 +161,7 @@ mod tests { #[tokio::test] async fn test_submit_request_failure() { - let mut server = Server::new(); + let mut server = Server::new_async().await; let mock = server .mock("POST", "/") .with_status(400) diff --git a/crates/core/src/config.rs b/crates/core/src/config.rs index 2b24aaa..3ba8795 100644 --- a/crates/core/src/config.rs +++ b/crates/core/src/config.rs @@ -52,3 +52,96 @@ pub struct AssertionForSubmission { pub assertion_id: String, pub signature: String, } + +#[cfg(test)] +mod tests { + use super::*; + use std::fs; + use tempfile::TempDir; + use std::env; + + // Helper function to set up a temporary config directory + fn setup_temp_config() -> TempDir { + let temp_dir = TempDir::new().unwrap(); + env::set_var("HOME", temp_dir.path()); + + // Create config directory + fs::create_dir_all(temp_dir.path().join(CONFIG_DIR)).unwrap(); + temp_dir + } + + #[test] + fn test_write_and_read_config() { + let temp_dir = setup_temp_config(); + + let config = CliConfig { + auth: Some(UserAuth { + access_token: "test_access".to_string(), + refresh_token: "test_refresh".to_string(), + user_address: "test_address".to_string(), + }), + assertions_for_submission: vec![ + AssertionForSubmission { + assertion_contract: "contract1".to_string(), + assertion_id: "id1".to_string(), + signature: "sig1".to_string(), + } + ], + }; + + // Test writing + assert!(config.write_to_file().is_ok()); + + // Test reading + let read_config = CliConfig::read_from_file().unwrap(); + assert_eq!(read_config.auth.as_ref().unwrap().access_token, "test_access"); + assert_eq!(read_config.auth.as_ref().unwrap().refresh_token, "test_refresh"); + assert_eq!(read_config.auth.as_ref().unwrap().user_address, "test_address"); + assert_eq!(read_config.assertions_for_submission.len(), 1); + assert_eq!(read_config.assertions_for_submission[0].assertion_contract, "contract1"); + + temp_dir.close().unwrap(); + } + + #[test] + fn test_read_nonexistent_config() { + let temp_dir = setup_temp_config(); + + // Try reading without creating a file + let result = CliConfig::read_from_file(); + assert!(result.is_err()); + assert!(matches!(result.unwrap_err(), ConfigError::ReadError(_))); + + temp_dir.close().unwrap(); + } + + #[test] + fn test_read_or_default() { + let temp_dir = setup_temp_config(); + + // Should return default when no file exists + let config = CliConfig::read_or_default(); + assert!(config.auth.is_none()); + assert!(config.assertions_for_submission.is_empty()); + + temp_dir.close().unwrap(); + } + + #[test] + fn test_authentication_check() { + let config = CliConfig::default(); + assert!(config.must_be_authenticated().is_err()); + + let config = CliConfig { + auth: Some(UserAuth { + access_token: "test".to_string(), + refresh_token: "test".to_string(), + user_address: "test".to_string(), + }), + assertions_for_submission: vec![], + }; + assert!(config.must_be_authenticated().is_ok()); + } +} + + From fd9ac962184ffbb760f4e123f428b3f2a3d485e3 Mon Sep 17 00:00:00 2001 From: "Odysseas.eth" Date: Fri, 14 Feb 2025 20:25:13 +0200 Subject: [PATCH 23/25] fix: create file if it doesn't exist --- crates/core/src/config.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/crates/core/src/config.rs b/crates/core/src/config.rs index 3ba8795..9e444fe 100644 --- a/crates/core/src/config.rs +++ b/crates/core/src/config.rs @@ -14,6 +14,7 @@ pub struct CliConfig { impl CliConfig { pub fn write_to_file(&self) -> Result<(), ConfigError> { let config_dir = home_dir().unwrap().join(CONFIG_DIR); + std::fs::create_dir_all(&config_dir).map_err(ConfigError::WriteError)?; let config_file = config_dir.join(CONFIG_FILE); let config_str = toml::to_string(self).unwrap(); std::fs::write(config_file, config_str).map_err(ConfigError::WriteError)?; @@ -56,7 +57,6 @@ pub struct AssertionForSubmission { #[cfg(test)] mod tests { use super::*; - use std::fs; use tempfile::TempDir; use std::env; @@ -64,9 +64,6 @@ mod tests { fn setup_temp_config() -> TempDir { let temp_dir = TempDir::new().unwrap(); env::set_var("HOME", temp_dir.path()); - - // Create config directory - fs::create_dir_all(temp_dir.path().join(CONFIG_DIR)).unwrap(); temp_dir } From 1ecb0f8e2d9f214e78b89b26aa943a7c8633895f Mon Sep 17 00:00:00 2001 From: "Odysseas.eth" Date: Fri, 14 Feb 2025 20:51:19 +0200 Subject: [PATCH 24/25] chore: fmt --- crates/core/src/config.rs | 42 +++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/crates/core/src/config.rs b/crates/core/src/config.rs index 9e444fe..9dc8bea 100644 --- a/crates/core/src/config.rs +++ b/crates/core/src/config.rs @@ -57,8 +57,8 @@ pub struct AssertionForSubmission { #[cfg(test)] mod tests { use super::*; - use tempfile::TempDir; use std::env; + use tempfile::TempDir; // Helper function to set up a temporary config directory fn setup_temp_config() -> TempDir { @@ -70,20 +70,18 @@ mod tests { #[test] fn test_write_and_read_config() { let temp_dir = setup_temp_config(); - + let config = CliConfig { auth: Some(UserAuth { access_token: "test_access".to_string(), refresh_token: "test_refresh".to_string(), user_address: "test_address".to_string(), }), - assertions_for_submission: vec![ - AssertionForSubmission { - assertion_contract: "contract1".to_string(), - assertion_id: "id1".to_string(), - signature: "sig1".to_string(), - } - ], + assertions_for_submission: vec![AssertionForSubmission { + assertion_contract: "contract1".to_string(), + assertion_id: "id1".to_string(), + signature: "sig1".to_string(), + }], }; // Test writing @@ -91,11 +89,23 @@ mod tests { // Test reading let read_config = CliConfig::read_from_file().unwrap(); - assert_eq!(read_config.auth.as_ref().unwrap().access_token, "test_access"); - assert_eq!(read_config.auth.as_ref().unwrap().refresh_token, "test_refresh"); - assert_eq!(read_config.auth.as_ref().unwrap().user_address, "test_address"); + assert_eq!( + read_config.auth.as_ref().unwrap().access_token, + "test_access" + ); + assert_eq!( + read_config.auth.as_ref().unwrap().refresh_token, + "test_refresh" + ); + assert_eq!( + read_config.auth.as_ref().unwrap().user_address, + "test_address" + ); assert_eq!(read_config.assertions_for_submission.len(), 1); - assert_eq!(read_config.assertions_for_submission[0].assertion_contract, "contract1"); + assert_eq!( + read_config.assertions_for_submission[0].assertion_contract, + "contract1" + ); temp_dir.close().unwrap(); } @@ -103,7 +113,7 @@ mod tests { #[test] fn test_read_nonexistent_config() { let temp_dir = setup_temp_config(); - + // Try reading without creating a file let result = CliConfig::read_from_file(); assert!(result.is_err()); @@ -115,7 +125,7 @@ mod tests { #[test] fn test_read_or_default() { let temp_dir = setup_temp_config(); - + // Should return default when no file exists let config = CliConfig::read_or_default(); assert!(config.auth.is_none()); @@ -140,5 +150,3 @@ mod tests { assert!(config.must_be_authenticated().is_ok()); } } - - From fc2c02ed68efddace479a2f67613c875f80ff87a Mon Sep 17 00:00:00 2001 From: "Odysseas.eth" Date: Fri, 14 Feb 2025 21:14:40 +0200 Subject: [PATCH 25/25] nit: pass array not vector --- crates/core/src/assertion_submission.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/core/src/assertion_submission.rs b/crates/core/src/assertion_submission.rs index a17eea7..8bc3933 100644 --- a/crates/core/src/assertion_submission.rs +++ b/crates/core/src/assertion_submission.rs @@ -81,7 +81,7 @@ impl DappSubmitArgs { "Select an assertion to submit:".to_string(), )?; - let assertions = assertion_names + let assertions: Vec<&AssertionForSubmission> = assertion_names .iter() .map(|n| { config @@ -92,7 +92,7 @@ impl DappSubmitArgs { }) .collect(); - self.submit_assertion(project, assertions).await?; + self.submit_assertion(project, &assertions).await?; // TOOD: remove assertion from config Ok(()) @@ -109,7 +109,7 @@ impl DappSubmitArgs { async fn submit_assertion( &self, project: &Project, - assertions: Vec<&AssertionForSubmission>, + assertions: &[&AssertionForSubmission], ) -> Result<(), DappSubmitError> { let client = reqwest::Client::new(); // TODO: Update payload structure once API spec is finalized