diff --git a/.github/workflows/lint-and-test.yml b/.github/workflows/lint-and-test.yml index 80a30d729..ae2699609 100644 --- a/.github/workflows/lint-and-test.yml +++ b/.github/workflows/lint-and-test.yml @@ -88,6 +88,10 @@ jobs: sudo apt-get install -y clang lld - name: Run cargo test run: cargo test --all-features + - name: Install Foundry/forge for solidity tests + uses: foundry-rs/foundry-toolchain@v1 + - name: Run solidity tests (ignored by default) + run: cargo test --all-features --package proof-of-sql --lib -- tests::sol_test --show-output --ignored - name: Run cargo test without rayon run: cargo test --no-default-features --features="arrow blitzar" - name: Dry run cargo test (proof-of-sql) (test feature only) diff --git a/Cargo.toml b/Cargo.toml index bcf02605f..2324bda84 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,8 +11,7 @@ license-file = "LICENSE" [workspace.dependencies] ahash = { version = "0.8.11", default-features = false } -# alloy-primitives = { version = "0.8.1" } -# alloy-sol-types = { version = "0.8.1" } +alloy-sol-types = { version = "0.8.5" } ark-bls12-381 = { version = "0.4.0" } ark-curve25519 = { version = "0.4.0" } ark-ec = { version = "0.4.0" } @@ -36,7 +35,6 @@ chrono = { version = "0.4.38", default-features = false } curve25519-dalek = { version = "4", features = ["rand_core"] } derive_more = { version = "0.99" } flexbuffers = { version = "2.0.0" } -# forge-script = { git = "https://github.com/foundry-rs/foundry", tag = "nightly-bf1a39980532f76cd76fd87ee32661180f606435" } indexmap = { version = "2.1", default-features = false } itertools = { version = "0.13.0", default-features = false, features = ["use_alloc"] } lalrpop = { version = "0.21.0" } @@ -56,7 +54,6 @@ serde = { version = "1", default-features = false } serde_json = { version = "1", default-features = false, features = ["alloc"] } snafu = { version = "0.8.4", default-features = false } tiny-keccak = { version = "2.0.2", features = [ "keccak" ] } -# tokio = { version = "1.39.3" } tracing = { version = "0.1.36", default-features = false } tracing-opentelemetry = { version = "0.22.0" } tracing-subscriber = { version = "0.3.0" } diff --git a/crates/proof-of-sql/Cargo.toml b/crates/proof-of-sql/Cargo.toml index eb13d6a6c..f6d1e5804 100644 --- a/crates/proof-of-sql/Cargo.toml +++ b/crates/proof-of-sql/Cargo.toml @@ -50,20 +50,17 @@ tracing = { workspace = true, features = ["attributes"] } zerocopy = { workspace = true } [dev-dependencies] -# alloy-primitives = { workspace = true } -# alloy-sol-types = { workspace = true } +alloy-sol-types = { workspace = true } arrow-csv = { workspace = true } blitzar = { workspace = true } clap = { workspace = true, features = ["derive"] } criterion = { workspace = true, features = ["html_reports"] } -# forge-script = { workspace = true } merlin = { workspace = true } opentelemetry = { workspace = true } opentelemetry-jaeger = { workspace = true } rand = { workspace = true, default-features = false } rand_core = { workspace = true, default-features = false } serde_json = { workspace = true } -# tokio = { workspace = true } tracing = { workspace = true } tracing-opentelemetry = { workspace = true } tracing-subscriber = { workspace = true } diff --git a/crates/proof-of-sql/src/tests/mod.rs b/crates/proof-of-sql/src/tests/mod.rs index 13880d03a..595f2d4a0 100644 --- a/crates/proof-of-sql/src/tests/mod.rs +++ b/crates/proof-of-sql/src/tests/mod.rs @@ -1 +1,3 @@ -// mod sol_test; +mod sol_test; +mod sol_test_util; +pub(crate) use sol_test_util::{ForgeScript, ForgeScriptError}; diff --git a/crates/proof-of-sql/src/tests/sol_test.rs b/crates/proof-of-sql/src/tests/sol_test.rs index 312d971f0..1e00ffe2f 100644 --- a/crates/proof-of-sql/src/tests/sol_test.rs +++ b/crates/proof-of-sql/src/tests/sol_test.rs @@ -1,42 +1,39 @@ -use alloy_primitives::{Bytes, U256}; -use alloy_sol_types::{sol, SolValue}; -use forge_script::ScriptArgs; +use crate::tests::{ForgeScript, ForgeScriptError}; +use alloy_sol_types::{private::primitives::U256, sol}; -#[tokio::test(flavor = "multi_thread")] -async fn we_can_run_solidity_script_from_rust() { - ScriptArgs { - path: "./sol_src/tests/TestScript.t.sol".to_string(), - sig: "rustTestWeCanThrowErrorDependingOnParameter".to_string(), - args: vec![U256::from(1234).to_string()], - ..Default::default() - } - .run_script() - .await +#[test] +#[ignore = "Because forge needs to be installed, we are ignoring this test by default. They will still be run from within the ci."] +fn we_can_run_solidity_script_from_rust() { + ForgeScript::new( + "./sol_src/tests/TestScript.t.sol", + "rustTestWeCanThrowErrorDependingOnParameter", + ) + .arg(U256::from(1234)) + .execute() .unwrap(); - assert!(ScriptArgs { - path: "./sol_src/tests/TestScript.t.sol".to_string(), - sig: "rustTestWeCanThrowErrorDependingOnParameter".to_string(), - args: vec![U256::from(0).to_string()], - ..Default::default() - } - .run_script() - .await - .is_err()); + assert!(matches!( + ForgeScript::new( + "./sol_src/tests/TestScript.t.sol", + "rustTestWeCanThrowErrorDependingOnParameter", + ) + .arg(U256::from(0)) + .execute(), + Err(ForgeScriptError::SolidityError { .. }) + )); } -#[tokio::test(flavor = "multi_thread")] -async fn we_can_pass_custom_struct_into_solidity_from_rust() { +#[test] +#[ignore = "Because forge needs to be installed, we are ignoring this test by default. They will still be run from within the ci."] +fn we_can_pass_custom_struct_into_solidity_from_rust() { sol!("./sol_src/tests/TestScript.t.sol"); let arg = TestScript::CustomStruct { value: U256::from(1234), }; - ScriptArgs { - path: "./sol_src/tests/TestScript.t.sol".to_string(), - sig: "rustTestWeCanAcceptCustomStructAsEncodedBytes".to_string(), - args: vec![Bytes::from(arg.abi_encode()).to_string()], - ..Default::default() - } - .run_script() - .await + ForgeScript::new( + "./sol_src/tests/TestScript.t.sol", + "rustTestWeCanAcceptCustomStructAsEncodedBytes", + ) + .arg(arg) + .execute() .unwrap(); } diff --git a/crates/proof-of-sql/src/tests/sol_test_util.rs b/crates/proof-of-sql/src/tests/sol_test_util.rs new file mode 100644 index 000000000..9ae41b09d --- /dev/null +++ b/crates/proof-of-sql/src/tests/sol_test_util.rs @@ -0,0 +1,45 @@ +use alloy_sol_types::{private::primitives::Bytes, SolValue}; +use snafu::Snafu; +use std::{ffi::OsStr, io, process::Command}; + +/// Error type returned by [ForgeScript] functions. +#[derive(Debug, Snafu)] +pub enum ForgeScriptError<'a> { + /// The script failed to run. This is usually because `forge` is not installed. + #[snafu(transparent)] + ExecutionFailed { source: io::Error }, + #[snafu(display("Script threw and error. Underlying command: {underlying_command:?}."))] + /// The script threw an error. This could be expected behavior. + SolidityError { underlying_command: &'a Command }, +} + +/// [ForgeScript] enables running solidity from within rust. Ultimately this type calls `forge script`. +/// As a result, `forge` must be installed. +/// See for instructions. +pub struct ForgeScript { + command: Command, +} + +impl ForgeScript { + /// Constructs a new `ForgeScript` for running a solidity function in `path` file, where the function is named `signature`. + pub fn new(path: impl AsRef, signature: impl AsRef) -> Self { + let mut command = Command::new("forge"); + command.arg("script").arg(path).arg("--sig").arg(signature); + Self { command } + } + /// Adds an argument to pass to the script. Only one argument can be passed per use. + pub fn arg(&mut self, arg: impl SolValue) -> &mut Self { + self.command.arg(Bytes::from(arg.abi_encode()).to_string()); + self + } + /// Executes the script as a child process, waiting for it to finish and collecting its status. + pub fn execute(&mut self) -> Result<(), ForgeScriptError> { + self.command + .status()? + .success() + .then_some(()) + .ok_or(ForgeScriptError::SolidityError { + underlying_command: &self.command, + }) + } +}