From b252474d3161c8c57342aff164241a9bd7862830 Mon Sep 17 00:00:00 2001 From: Noah Gundotra Date: Mon, 11 Nov 2024 15:34:50 -0500 Subject: [PATCH 1/3] add local build test --- README.md | 4 ++-- src/main.rs | 62 +++++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 53 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 9177c98..2ba9684 100644 --- a/README.md +++ b/README.md @@ -149,8 +149,8 @@ solana-verify verify-from-repo -um --program-id MFv2hWf31Z9kbCa1snEPYctwafyhdvnV Final Output: ``` -Executable Program Hash from repo: 7b37482dd6b2159932b5c2595bc6ce62cf6e587ae67f237c8152b802bf7d7bb8 -On-chain Program Hash: 7b37482dd6b2159932b5c2595bc6ce62cf6e587ae67f237c8152b802bf7d7bb8 +Executable Program Hash from repo: 890d68f48f96991016222b1fcbc2cc81b8ef2dcbf280c44fe378c523c108fad5 +On-chain Program Hash: 890d68f48f96991016222b1fcbc2cc81b8ef2dcbf280c44fe378c523c108fad5 Program hash matches ✅ ``` diff --git a/src/main.rs b/src/main.rs index e099cbf..2092017 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1002,6 +1002,7 @@ pub fn get_pkg_name_from_cargo_toml(cargo_toml_file: &str) -> Option { Some(pkg.name) } +#[cfg(test)] fn test_verify_program_hash_helper(expected_hash: &str, args: &[&str]) -> anyhow::Result<()> { let mut child = std::process::Command::new("./target/debug/solana-verify") .args(args) @@ -1052,16 +1053,9 @@ fn test_verify_program_hash_helper(expected_hash: &str, args: &[&str]) -> anyhow #[test] fn test_phoenix_v1() -> anyhow::Result<()> { const EXPECTED_HASH: &str = "6877a5b732b3494b828a324ec846d526d962223959534dbaf4209e0da3b2d6a9"; - - let args = &[ - "verify-from-repo", - "-um", - "--program-id", - "PhoeNiXZ8ByJGLkxNfZRnkUfjvmuYqLR89jjFHGqdXY", - "https://github.com/Ellipsis-Labs/phoenix-v1", - ]; - - test_verify_program_hash_helper(EXPECTED_HASH, args)?; + let args: Vec<&str> = + "verify-from-repo -um --program-id PhoeNiXZ8ByJGLkxNfZRnkUfjvmuYqLR89jjFHGqdXY https://github.com/Ellipsis-Labs/phoenix-v1".split(" ").collect(); + test_verify_program_hash_helper(EXPECTED_HASH, &args)?; Ok(()) } @@ -1083,8 +1077,54 @@ fn test_drift_v2() -> anyhow::Result<()> { #[test] fn test_marginfi_v2() -> anyhow::Result<()> { - const EXPECTED_HASH: &str = "7b37482dd6b2159932b5c2595bc6ce62cf6e587ae67f237c8152b802bf7d7bb8"; + const EXPECTED_HASH: &str = "890d68f48f96991016222b1fcbc2cc81b8ef2dcbf280c44fe378c523c108fad5"; let args: Vec<&str> = "verify-from-repo -um --program-id MFv2hWf31Z9kbCa1snEPYctwafyhdvnV7FZnsebVacA https://github.com/mrgnlabs/marginfi-v2 --commit-hash d33e649e415c354cc2a1e3c49131725552d69ba0 --library-name marginfi".split(" ").collect(); test_verify_program_hash_helper(EXPECTED_HASH, &args)?; Ok(()) } + +#[test] +fn test_local_example() -> anyhow::Result<()> { + const EXPECTED_HASH: &str = "08d91368d349c2b56c712422f6d274a1e8f1946ff2ecd1dc3efc3ebace52a760"; + + let args: Vec<&str> = "build ./examples/hello_world".split(" ").collect(); + let child = std::process::Command::new("./target/debug/solana-verify") + .args(args) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .context("Failed to execute solana-verify command")?; + + let output = child + .wait_with_output() + .context("Failed to wait for solana-verify command")?; + + if !output.status.success() { + let error = String::from_utf8_lossy(&output.stderr); + anyhow::bail!("Command failed: {}", error); + } + + + let args: Vec<&str> = "get-executable-hash ./examples/hello_world/target/deploy/hello_world.so".split(" ").collect(); + let child = std::process::Command::new("./target/debug/solana-verify") + .args(&args) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .context("Failed to execute solana-verify command")?; + + let output = child + .wait_with_output() + .context("Failed to wait for solana-verify command")?; + + if !output.status.success() { + let error = String::from_utf8_lossy(&output.stderr).trim().to_string(); + anyhow::bail!("Command failed: {}", error); + } + + let hash = String::from_utf8_lossy(&output.stdout).trim().to_string(); + assert_eq!(hash, EXPECTED_HASH, "Program hash {} does not match expected value {}", hash, EXPECTED_HASH); + Ok(()) +} From 28d60bdac687d679a808cf88c76e3d3a2c433061 Mon Sep 17 00:00:00 2001 From: Noah Gundotra Date: Mon, 11 Nov 2024 15:49:13 -0500 Subject: [PATCH 2/3] add verify_from_image --- src/main.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/main.rs b/src/main.rs index 2092017..f59d264 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1128,3 +1128,26 @@ fn test_local_example() -> anyhow::Result<()> { assert_eq!(hash, EXPECTED_HASH, "Program hash {} does not match expected value {}", hash, EXPECTED_HASH); Ok(()) } + + +#[test] +fn test_verify_from_image() -> anyhow::Result<()> { + let args: Vec<&str> = "verify-from-image -e examples/hello_world/target/deploy/hello_world.so -i ellipsislabs/hello_world_verifiable_build:latest -p 2ZrriTQSVekoj414Ynysd48jyn4AX6ZF4TTJRqHfbJfn".split(" ").collect(); + let child = std::process::Command::new("./target/debug/solana-verify") + .args(args) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .context("Failed to execute solana-verify command")?; + + let output = child + .wait_with_output() + .context("Failed to wait for solana-verify command")?; + + if !output.status.success() { + let error = String::from_utf8_lossy(&output.stderr); + anyhow::bail!("Command failed: {}", error); + } + Ok(()) +} \ No newline at end of file From 3a75a2a5aa820bdf5940e9aaffb1abb486e2886f Mon Sep 17 00:00:00 2001 From: Noah Gundotra Date: Mon, 11 Nov 2024 15:52:42 -0500 Subject: [PATCH 3/3] separate out tests --- src/main.rs | 157 ++-------------------------------------------------- src/test.rs | 155 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 160 insertions(+), 152 deletions(-) create mode 100644 src/test.rs diff --git a/src/main.rs b/src/main.rs index f59d264..320e7be 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -use anyhow::{anyhow, Context}; +use anyhow::anyhow; use cargo_lock::Lockfile; use cargo_toml::Manifest; use clap::{App, Arg, SubCommand}; @@ -13,7 +13,7 @@ use solana_sdk::{ pubkey::Pubkey, }; use std::{ - io::{Read, Write}, + io::Read, path::PathBuf, process::{exit, Stdio}, sync::{ @@ -28,6 +28,9 @@ pub mod image_config; pub mod solana_program; use image_config::IMAGE_MAP; +#[cfg(test)] +mod test; + use crate::{ api::send_job_to_remote, solana_program::{process_close, upload_program}, @@ -1001,153 +1004,3 @@ pub fn get_pkg_name_from_cargo_toml(cargo_toml_file: &str) -> Option { let pkg = manifest.package?; Some(pkg.name) } - -#[cfg(test)] -fn test_verify_program_hash_helper(expected_hash: &str, args: &[&str]) -> anyhow::Result<()> { - let mut child = std::process::Command::new("./target/debug/solana-verify") - .args(args) - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .spawn() - .context("Failed to execute solana-verify command")?; - - if let Some(mut stdin) = child.stdin.take() { - stdin.write_all(b"n")?; - } - - let output = child - .wait_with_output() - .context("Failed to wait for solana-verify command")?; - - if !output.status.success() { - let error = String::from_utf8_lossy(&output.stderr); - anyhow::bail!("Command failed: {}", error); - } - - // Print the last 10 lines of the output - let output_str = String::from_utf8_lossy(&output.stdout); - let lines: Vec<&str> = output_str.split('\n').collect(); - let last_10_lines: Vec = lines.iter().rev().take(10).map(|s| s.to_string()).collect(); - println!("Last 10 lines of output:\n{}", last_10_lines.join("\n")); - - let re = regex::Regex::new(r"Executable Program Hash from repo: ([a-f0-9]{64})") - .context("Failed to compile regex")?; - - let program_hash = re - .captures(&output_str) - .context("Could not find program hash in output")? - .get(1) - .context("Invalid capture group")? - .as_str(); - - assert_eq!( - program_hash, expected_hash, - "Program hash {} does not match expected value {}", - program_hash, expected_hash - ); - - Ok(()) -} - -#[test] -fn test_phoenix_v1() -> anyhow::Result<()> { - const EXPECTED_HASH: &str = "6877a5b732b3494b828a324ec846d526d962223959534dbaf4209e0da3b2d6a9"; - let args: Vec<&str> = - "verify-from-repo -um --program-id PhoeNiXZ8ByJGLkxNfZRnkUfjvmuYqLR89jjFHGqdXY https://github.com/Ellipsis-Labs/phoenix-v1".split(" ").collect(); - test_verify_program_hash_helper(EXPECTED_HASH, &args)?; - Ok(()) -} - -#[test] -fn test_squads_v3() -> anyhow::Result<()> { - const EXPECTED_HASH: &str = "72da599d9ee14b2a03a23ccfa6f06d53eea4a00825ad2191929cbd78fb69205c"; - let args: Vec<&str> = "verify-from-repo https://github.com/Squads-Protocol/squads-mpl --commit-hash c95b7673d616c377a349ca424261872dfcf8b19d --program-id SMPLecH534NA9acpos4G6x7uf3LWbCAwZQE9e8ZekMu -um --library-name squads_mpl --bpf".split(" ").collect(); - test_verify_program_hash_helper(EXPECTED_HASH, &args)?; - Ok(()) -} - -#[test] -fn test_drift_v2() -> anyhow::Result<()> { - const EXPECTED_HASH: &str = "e31d58edeabc3c30bf6f2aa60bfaa5e492b41ec203e9006404b463e5adee5828"; - let args: Vec<&str> = "verify-from-repo -um --program-id dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH https://github.com/drift-labs/protocol-v2 --commit-hash 110d3ff4f8ba07c178d69f9bfc7b30194fac56d6 --library-name drift".split(" ").collect(); - test_verify_program_hash_helper(EXPECTED_HASH, &args)?; - Ok(()) -} - -#[test] -fn test_marginfi_v2() -> anyhow::Result<()> { - const EXPECTED_HASH: &str = "890d68f48f96991016222b1fcbc2cc81b8ef2dcbf280c44fe378c523c108fad5"; - let args: Vec<&str> = "verify-from-repo -um --program-id MFv2hWf31Z9kbCa1snEPYctwafyhdvnV7FZnsebVacA https://github.com/mrgnlabs/marginfi-v2 --commit-hash d33e649e415c354cc2a1e3c49131725552d69ba0 --library-name marginfi".split(" ").collect(); - test_verify_program_hash_helper(EXPECTED_HASH, &args)?; - Ok(()) -} - -#[test] -fn test_local_example() -> anyhow::Result<()> { - const EXPECTED_HASH: &str = "08d91368d349c2b56c712422f6d274a1e8f1946ff2ecd1dc3efc3ebace52a760"; - - let args: Vec<&str> = "build ./examples/hello_world".split(" ").collect(); - let child = std::process::Command::new("./target/debug/solana-verify") - .args(args) - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .spawn() - .context("Failed to execute solana-verify command")?; - - let output = child - .wait_with_output() - .context("Failed to wait for solana-verify command")?; - - if !output.status.success() { - let error = String::from_utf8_lossy(&output.stderr); - anyhow::bail!("Command failed: {}", error); - } - - - let args: Vec<&str> = "get-executable-hash ./examples/hello_world/target/deploy/hello_world.so".split(" ").collect(); - let child = std::process::Command::new("./target/debug/solana-verify") - .args(&args) - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .spawn() - .context("Failed to execute solana-verify command")?; - - let output = child - .wait_with_output() - .context("Failed to wait for solana-verify command")?; - - if !output.status.success() { - let error = String::from_utf8_lossy(&output.stderr).trim().to_string(); - anyhow::bail!("Command failed: {}", error); - } - - let hash = String::from_utf8_lossy(&output.stdout).trim().to_string(); - assert_eq!(hash, EXPECTED_HASH, "Program hash {} does not match expected value {}", hash, EXPECTED_HASH); - Ok(()) -} - - -#[test] -fn test_verify_from_image() -> anyhow::Result<()> { - let args: Vec<&str> = "verify-from-image -e examples/hello_world/target/deploy/hello_world.so -i ellipsislabs/hello_world_verifiable_build:latest -p 2ZrriTQSVekoj414Ynysd48jyn4AX6ZF4TTJRqHfbJfn".split(" ").collect(); - let child = std::process::Command::new("./target/debug/solana-verify") - .args(args) - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .spawn() - .context("Failed to execute solana-verify command")?; - - let output = child - .wait_with_output() - .context("Failed to wait for solana-verify command")?; - - if !output.status.success() { - let error = String::from_utf8_lossy(&output.stderr); - anyhow::bail!("Command failed: {}", error); - } - Ok(()) -} \ No newline at end of file diff --git a/src/test.rs b/src/test.rs new file mode 100644 index 0000000..9e5074e --- /dev/null +++ b/src/test.rs @@ -0,0 +1,155 @@ +mod tests { + use anyhow::Context; + use regex::Regex; + use std::io::Write; + use std::process::Stdio; + + fn test_verify_program_hash_helper(expected_hash: &str, args: &[&str]) -> anyhow::Result<()> { + let mut child = std::process::Command::new("./target/debug/solana-verify") + .args(args) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .context("Failed to execute solana-verify command")?; + + if let Some(mut stdin) = child.stdin.take() { + stdin.write_all(b"n")?; + } + + let output = child + .wait_with_output() + .context("Failed to wait for solana-verify command")?; + + if !output.status.success() { + let error = String::from_utf8_lossy(&output.stderr); + anyhow::bail!("Command failed: {}", error); + } + + // Print the last 10 lines of the output + let output_str = String::from_utf8_lossy(&output.stdout); + let lines: Vec<&str> = output_str.split('\n').collect(); + let last_10_lines: Vec = lines.iter().rev().take(10).map(|s| s.to_string()).collect(); + println!("Last 10 lines of output:\n{}", last_10_lines.join("\n")); + + let re = Regex::new(r"Executable Program Hash from repo: ([a-f0-9]{64})") + .context("Failed to compile regex")?; + + let program_hash = re + .captures(&output_str) + .context("Could not find program hash in output")? + .get(1) + .context("Invalid capture group")? + .as_str(); + + assert_eq!( + program_hash, expected_hash, + "Program hash {} does not match expected value {}", + program_hash, expected_hash + ); + + Ok(()) + } + + #[test] + fn test_phoenix_v1() -> anyhow::Result<()> { + const EXPECTED_HASH: &str = "6877a5b732b3494b828a324ec846d526d962223959534dbaf4209e0da3b2d6a9"; + let args: Vec<&str> = + "verify-from-repo -um --program-id PhoeNiXZ8ByJGLkxNfZRnkUfjvmuYqLR89jjFHGqdXY https://github.com/Ellipsis-Labs/phoenix-v1".split(" ").collect(); + test_verify_program_hash_helper(EXPECTED_HASH, &args)?; + Ok(()) + } + + #[test] + fn test_squads_v3() -> anyhow::Result<()> { + const EXPECTED_HASH: &str = "72da599d9ee14b2a03a23ccfa6f06d53eea4a00825ad2191929cbd78fb69205c"; + let args: Vec<&str> = "verify-from-repo https://github.com/Squads-Protocol/squads-mpl --commit-hash c95b7673d616c377a349ca424261872dfcf8b19d --program-id SMPLecH534NA9acpos4G6x7uf3LWbCAwZQE9e8ZekMu -um --library-name squads_mpl --bpf".split(" ").collect(); + test_verify_program_hash_helper(EXPECTED_HASH, &args)?; + Ok(()) + } + + #[test] + fn test_drift_v2() -> anyhow::Result<()> { + const EXPECTED_HASH: &str = "e31d58edeabc3c30bf6f2aa60bfaa5e492b41ec203e9006404b463e5adee5828"; + let args: Vec<&str> = "verify-from-repo -um --program-id dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH https://github.com/drift-labs/protocol-v2 --commit-hash 110d3ff4f8ba07c178d69f9bfc7b30194fac56d6 --library-name drift".split(" ").collect(); + test_verify_program_hash_helper(EXPECTED_HASH, &args)?; + Ok(()) + } + + #[test] + fn test_marginfi_v2() -> anyhow::Result<()> { + const EXPECTED_HASH: &str = "890d68f48f96991016222b1fcbc2cc81b8ef2dcbf280c44fe378c523c108fad5"; + let args: Vec<&str> = "verify-from-repo -um --program-id MFv2hWf31Z9kbCa1snEPYctwafyhdvnV7FZnsebVacA https://github.com/mrgnlabs/marginfi-v2 --commit-hash d33e649e415c354cc2a1e3c49131725552d69ba0 --library-name marginfi".split(" ").collect(); + test_verify_program_hash_helper(EXPECTED_HASH, &args)?; + Ok(()) + } + + #[test] + fn test_local_example() -> anyhow::Result<()> { + const EXPECTED_HASH: &str = "08d91368d349c2b56c712422f6d274a1e8f1946ff2ecd1dc3efc3ebace52a760"; + + let args: Vec<&str> = "build ./examples/hello_world".split(" ").collect(); + let child = std::process::Command::new("./target/debug/solana-verify") + .args(args) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .context("Failed to execute solana-verify command")?; + + let output = child + .wait_with_output() + .context("Failed to wait for solana-verify command")?; + + if !output.status.success() { + let error = String::from_utf8_lossy(&output.stderr); + anyhow::bail!("Command failed: {}", error); + } + + + let args: Vec<&str> = "get-executable-hash ./examples/hello_world/target/deploy/hello_world.so".split(" ").collect(); + let child = std::process::Command::new("./target/debug/solana-verify") + .args(&args) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .context("Failed to execute solana-verify command")?; + + let output = child + .wait_with_output() + .context("Failed to wait for solana-verify command")?; + + if !output.status.success() { + let error = String::from_utf8_lossy(&output.stderr).trim().to_string(); + anyhow::bail!("Command failed: {}", error); + } + + let hash = String::from_utf8_lossy(&output.stdout).trim().to_string(); + assert_eq!(hash, EXPECTED_HASH, "Program hash {} does not match expected value {}", hash, EXPECTED_HASH); + Ok(()) + } + + + #[test] + fn test_verify_from_image() -> anyhow::Result<()> { + let args: Vec<&str> = "verify-from-image -e examples/hello_world/target/deploy/hello_world.so -i ellipsislabs/hello_world_verifiable_build:latest -p 2ZrriTQSVekoj414Ynysd48jyn4AX6ZF4TTJRqHfbJfn".split(" ").collect(); + let child = std::process::Command::new("./target/debug/solana-verify") + .args(args) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + .context("Failed to execute solana-verify command")?; + + let output = child + .wait_with_output() + .context("Failed to wait for solana-verify command")?; + + if !output.status.success() { + let error = String::from_utf8_lossy(&output.stderr); + anyhow::bail!("Command failed: {}", error); + } + Ok(()) + } +} \ No newline at end of file