diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 0e23e1d..1a671e9 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -1,11 +1,8 @@ name: cargo build, fmt & clippy -on: - push: - branches: [ master ] - pull_request: - branches: [ master ] -env: +on: [push, pull_request] + +env: CARGO_TERM_COLOR: always jobs: @@ -24,3 +21,4 @@ jobs: - run: cargo hack build --verbose --each-feature --exclude-features --all-features - run: cargo fmt --all -- --check - run: cargo hack clippy --all-targets --each-feature --exclude-features --all-features -- -D warnings + - run: cargo test --release --verbose -- --test-threads=1 diff --git a/.gitignore b/.gitignore index faab9fa..a6777c2 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,7 @@ pkg/ *.tr *.pk *.vk -**/Verifier.toml \ No newline at end of file +**/Verifier.toml + +*.sol +**/target \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 665821e..dbbd666 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -800,7 +800,7 @@ dependencies = [ [[package]] name = "halo2-base" version = "0.3.0" -source = "git+https://github.com/axiom-crypto/halo2-lib?branch=release-0.3.0#8b9bdc2ba0d1f6f44e6b313847d2f0268e523c36" +source = "git+https://github.com/axiom-crypto/halo2-lib?branch=release-0.3.0#020346b19bd816b5ab5fc4a96b3605b8dcf6ffac" dependencies = [ "ff 0.12.1", "halo2_proofs 0.2.0 (git+https://github.com/axiom-crypto/halo2.git?branch=axiom/dev)", @@ -820,7 +820,7 @@ dependencies = [ [[package]] name = "halo2-ecc" version = "0.3.0" -source = "git+https://github.com/axiom-crypto/halo2-lib?branch=release-0.3.0#8b9bdc2ba0d1f6f44e6b313847d2f0268e523c36" +source = "git+https://github.com/axiom-crypto/halo2-lib?branch=release-0.3.0#020346b19bd816b5ab5fc4a96b3605b8dcf6ffac" dependencies = [ "ff 0.12.1", "group 0.12.1", @@ -2118,9 +2118,9 @@ dependencies = [ [[package]] name = "subtle" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" +checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" [[package]] name = "syn" diff --git a/examples/bit_and/Nargo.toml b/tests/test_programs/1_mul/Nargo.toml similarity index 59% rename from examples/bit_and/Nargo.toml rename to tests/test_programs/1_mul/Nargo.toml index 670888e..e0b467c 100644 --- a/examples/bit_and/Nargo.toml +++ b/tests/test_programs/1_mul/Nargo.toml @@ -1,5 +1,5 @@ [package] authors = [""] -compiler_version = "0.6.0" +compiler_version = "0.1" [dependencies] \ No newline at end of file diff --git a/tests/test_programs/1_mul/Prover.toml b/tests/test_programs/1_mul/Prover.toml new file mode 100644 index 0000000..9bff601 --- /dev/null +++ b/tests/test_programs/1_mul/Prover.toml @@ -0,0 +1,3 @@ +x = "3" +y = "4" +z = "429981696" diff --git a/tests/test_programs/1_mul/src/main.nr b/tests/test_programs/1_mul/src/main.nr new file mode 100644 index 0000000..4587b4b --- /dev/null +++ b/tests/test_programs/1_mul/src/main.nr @@ -0,0 +1,9 @@ +// Test unsafe integer multiplication with overflow: 12^8 = 429 981 696 +// The circuit should handle properly the growth of the bit size +fn main(mut x: u32, y: u32, z: u32) { + x *= y; + x *= x; //144 + x *= x; //20736 + x *= x; //429 981 696 + assert(x == z); +} diff --git a/tests/test_programs/2_div/Nargo.toml b/tests/test_programs/2_div/Nargo.toml new file mode 100644 index 0000000..e0b467c --- /dev/null +++ b/tests/test_programs/2_div/Nargo.toml @@ -0,0 +1,5 @@ +[package] +authors = [""] +compiler_version = "0.1" + +[dependencies] \ No newline at end of file diff --git a/tests/test_programs/2_div/Prover.toml b/tests/test_programs/2_div/Prover.toml new file mode 100644 index 0000000..ee6f0ef --- /dev/null +++ b/tests/test_programs/2_div/Prover.toml @@ -0,0 +1,3 @@ +x = "7" +y = "3" +z = "2" \ No newline at end of file diff --git a/tests/test_programs/2_div/src/main.nr b/tests/test_programs/2_div/src/main.nr new file mode 100644 index 0000000..ff0dee7 --- /dev/null +++ b/tests/test_programs/2_div/src/main.nr @@ -0,0 +1,7 @@ +// Testing integer division: 7/3 = 2 +fn main(mut x: u32, y: u32, z: u32) { + let a = x % y; + assert(x / y == z); + assert(a == x - z*y); + assert((50 as u64) % (9 as u64) == 5); +} diff --git a/tests/test_programs/3_add/Nargo.toml b/tests/test_programs/3_add/Nargo.toml new file mode 100644 index 0000000..e0b467c --- /dev/null +++ b/tests/test_programs/3_add/Nargo.toml @@ -0,0 +1,5 @@ +[package] +authors = [""] +compiler_version = "0.1" + +[dependencies] \ No newline at end of file diff --git a/examples/add/Prover.toml b/tests/test_programs/3_add/Prover.toml similarity index 100% rename from examples/add/Prover.toml rename to tests/test_programs/3_add/Prover.toml diff --git a/examples/add/src/main.nr b/tests/test_programs/3_add/src/main.nr similarity index 70% rename from examples/add/src/main.nr rename to tests/test_programs/3_add/src/main.nr index 46bbbae..2884415 100644 --- a/examples/add/src/main.nr +++ b/tests/test_programs/3_add/src/main.nr @@ -1,5 +1,5 @@ // Test integer addition: 3 + 4 = 7 -fn main(mut x: u32, y: u32, z: pub u32) { +fn main(mut x: u32, y: u32, z: u32) { x += y; assert(x == z); diff --git a/tests/test_programs/4_sub/Nargo.toml b/tests/test_programs/4_sub/Nargo.toml new file mode 100644 index 0000000..e0b467c --- /dev/null +++ b/tests/test_programs/4_sub/Nargo.toml @@ -0,0 +1,5 @@ +[package] +authors = [""] +compiler_version = "0.1" + +[dependencies] \ No newline at end of file diff --git a/tests/test_programs/4_sub/Prover.toml b/tests/test_programs/4_sub/Prover.toml new file mode 100644 index 0000000..1240475 --- /dev/null +++ b/tests/test_programs/4_sub/Prover.toml @@ -0,0 +1,3 @@ +x = "12" +y = "2418266113" +z = "1876701195" \ No newline at end of file diff --git a/tests/test_programs/4_sub/src/main.nr b/tests/test_programs/4_sub/src/main.nr new file mode 100644 index 0000000..80fc017 --- /dev/null +++ b/tests/test_programs/4_sub/src/main.nr @@ -0,0 +1,5 @@ +// Test unsafe integer subtraction with underflow: 12 - 2418266113 = 1876701195 modulo 2^32 +fn main(mut x: u32, y: u32, z: u32) { + x -= y; + assert(x == z); +} diff --git a/tests/test_programs/5_over/Nargo.toml b/tests/test_programs/5_over/Nargo.toml new file mode 100644 index 0000000..e0b467c --- /dev/null +++ b/tests/test_programs/5_over/Nargo.toml @@ -0,0 +1,5 @@ +[package] +authors = [""] +compiler_version = "0.1" + +[dependencies] \ No newline at end of file diff --git a/tests/test_programs/5_over/Prover.toml b/tests/test_programs/5_over/Prover.toml new file mode 100644 index 0000000..9a19863 --- /dev/null +++ b/tests/test_programs/5_over/Prover.toml @@ -0,0 +1,2 @@ +x = "43046721" +y = "3793632897" diff --git a/tests/test_programs/5_over/src/main.nr b/tests/test_programs/5_over/src/main.nr new file mode 100644 index 0000000..4fdff16 --- /dev/null +++ b/tests/test_programs/5_over/src/main.nr @@ -0,0 +1,9 @@ +// Test unsafe integer arithmetic +// Test odd bits integer +fn main(mut x: u32, y: u32) { + x = x * x; + assert(y == x); + + let c:u3 = 2; + assert(c > x as u3); +} diff --git a/tests/test_programs/6_array/Nargo.toml b/tests/test_programs/6_array/Nargo.toml new file mode 100644 index 0000000..e0b467c --- /dev/null +++ b/tests/test_programs/6_array/Nargo.toml @@ -0,0 +1,5 @@ +[package] +authors = [""] +compiler_version = "0.1" + +[dependencies] \ No newline at end of file diff --git a/tests/test_programs/6_array/Prover.toml b/tests/test_programs/6_array/Prover.toml new file mode 100644 index 0000000..c61cd3b --- /dev/null +++ b/tests/test_programs/6_array/Prover.toml @@ -0,0 +1,9 @@ +x = [104, 101, 108, 108, 111] +y = [10, 81, 18, 48, 0] +z = "59" +t = "10" + +#7128 +#15309 +#16349 + diff --git a/tests/test_programs/6_array/src/main.nr b/tests/test_programs/6_array/src/main.nr new file mode 100644 index 0000000..9593c56 --- /dev/null +++ b/tests/test_programs/6_array/src/main.nr @@ -0,0 +1,54 @@ +//Basic tests for arrays +fn main(x: [u32; 5], y: [u32; 5], mut z: u32, t: u32) { + let mut c = 2301; + z = y[4]; + //Test 1: + for i in 0..5 { + c = z*z*y[i]; + z -= c; + } + assert(z==0); //y[4]=0, so c and z are always 0 + + //Test 2: + c = 2301 as u32; + for i in 0..5 { + c = t+2 as u32; + c = z*z*x[i]; + z += x[i]*y[i] - c; + } + assert(z==3814912846); + + //Test 3: + c = 2300001 as u32; + z = y[4]; + for i in 0..5 { + z = z + x[i]*y[i]; + for _i in 0..3 { + c = i as u32 - 2 as u32; + z *= c; + } + } + assert(z==41472); + + //Test 4: + z = y[4]; + for i in 0..3 { + z += x[i] * y[i]; + for j in 0..2 { + z += x[i+j] - y[i+j]; + } + } + assert(z ==11539); + + //Test 5: + let cc = if z < 1 { x } else { y }; + assert(cc[0] == y[0]); + + // Test 6: for-each loops + for y_elem in y { + for x_elem in x { + assert(x_elem != y_elem); + } + } +} + diff --git a/tests/test_programs/7_function/Nargo.toml b/tests/test_programs/7_function/Nargo.toml new file mode 100644 index 0000000..e0b467c --- /dev/null +++ b/tests/test_programs/7_function/Nargo.toml @@ -0,0 +1,5 @@ +[package] +authors = [""] +compiler_version = "0.1" + +[dependencies] \ No newline at end of file diff --git a/tests/test_programs/7_function/Prover.toml b/tests/test_programs/7_function/Prover.toml new file mode 100644 index 0000000..9140e7f --- /dev/null +++ b/tests/test_programs/7_function/Prover.toml @@ -0,0 +1,6 @@ +x = "59" +y = "5" +a = "1" + +arr1=[3320379920, 1938147428, 1942509796, 1795943184, 24853, 0, 0, 0, 0] +arr2=[2912727897, 3590519536, 1687587470, 3896107618, 1092831095, 0, 0, 0, 0] \ No newline at end of file diff --git a/tests/test_programs/7_function/src/main.nr b/tests/test_programs/7_function/src/main.nr new file mode 100644 index 0000000..5a23b49 --- /dev/null +++ b/tests/test_programs/7_function/src/main.nr @@ -0,0 +1,149 @@ +//Tests for function calling +use dep::std; + +fn f1(mut x: Field) -> Field { + x = x + 1; + x = f2(x); + x +} + +fn f2(mut x: Field) -> Field{ + x += 2; + x +} + +// Simple example +fn test0(mut a: Field) { + a = f2(a); + assert(a == 3); +} + +// Nested call +fn test1(mut a: Field) { + a = f1(a); + assert(a == 4); +} + +fn test2(z: Field, t: u32 ) { + let a = z + t as Field; + assert(a == 64); + let e = pow(z, t as Field); + assert(e == 714924299); +} + +fn pow(base: Field, exponent: Field) -> Field { + let mut r = 1 as Field; + let b = exponent.to_le_bits(32 as u32); + for i in 1..33 { + r = r*r; + r = (b[32-i] as Field) * (r * base) + (1 - b[32-i] as Field) * r; + } + r +} + +fn test3(x: [u8; 3]) -> [u8; 3] { + let mut buffer = [0 as u8; 3]; + for i in 0..3 { + buffer[i] = x[i]; + } + assert(buffer == x); + buffer +} + +fn test_multiple(x: u32, y: u32) -> (u32, u32) { + (y,x) +} + +fn test_multiple2() -> my_struct { + my_struct { a: 5 as u32, b: 7 as u32 } +} + +fn test_multiple3(x: u32, y: u32) { + assert(x == y); +} + +struct my_struct { + a: u32, + b: u32, +} + +struct my2 { + aa: my_struct, + bb: my_struct, +} + +fn test_multiple4(s: my_struct) { + assert(s.a == s.b+2); +} + +fn test_multiple5(a: (u32, u32)) { + assert(a.0 == a.1+2); +} + + +fn test_multiple6(a: my2, b: my_struct, c: (my2, my_struct)) { + test_multiple4(a.aa); + test_multiple5((b.a, b.b)); + assert(c.0.aa.a == c.1.a); +} + + +fn foo(a: [Field]) -> [Field] { + a +} +fn bar() -> [Field] { + foo([0]) +} +fn main(x: u32 , y: u32 , a: Field, arr1: [u32; 9], arr2: [u32; 9]) { + let mut ss: my_struct = my_struct { b: x, a: x+2, }; + test_multiple4(ss); + test_multiple5((ss.a,ss.b)); + let my = my2 { + aa: ss, + bb: ss, + }; + ss.a = 61; + test_multiple6(my, ss, (my,ss)); + + let my_block = { + let mut ab = f2(a); + ab = ab + a; + (x,ab) + }; + assert(my_block.1 == 4); + + test0(a); + test1(a); + test2(x as Field, y); + assert(bar()[0] == 0); + + let mut b = [0 as u8, 5 as u8, 2 as u8]; + let c = test3(b); + assert(b == c); + b[0] = 1 as u8; + let cc = test3(b); + assert(c != cc); + let e = test_multiple(x, y); + assert(e.1 == e.0 + 54 as u32); + let d = test_multiple2(); + assert(d.b == d.a + 2 as u32); + test_multiple3(y, y); + + //Regression test for issue #628: + let result = first(arr_to_field(arr1), arr_to_field(arr2)); + assert(result[0] == arr1[0] as Field); +} + + +// Issue #628 +fn arr_to_field(arr: [u32; 9]) -> [Field; 9] { + let mut as_field: [Field; 9] = [0 as Field; 9]; + for i in 0..9 { + as_field[i] = arr[i] as Field; + } + as_field +} + +fn first(a: [Field; 9], _b: [Field; 9]) -> [Field; 9] { + a +} diff --git a/examples/add/Nargo.toml b/tests/test_programs/bit_and/Nargo.toml similarity index 100% rename from examples/add/Nargo.toml rename to tests/test_programs/bit_and/Nargo.toml diff --git a/examples/bit_and/Prover.toml b/tests/test_programs/bit_and/Prover.toml similarity index 100% rename from examples/bit_and/Prover.toml rename to tests/test_programs/bit_and/Prover.toml diff --git a/examples/bit_and/src/main.nr b/tests/test_programs/bit_and/src/main.nr similarity index 100% rename from examples/bit_and/src/main.nr rename to tests/test_programs/bit_and/src/main.nr diff --git a/tests/tests.rs b/tests/tests.rs new file mode 100644 index 0000000..c677b90 --- /dev/null +++ b/tests/tests.rs @@ -0,0 +1,176 @@ +use std::{fs, process::Command}; + +fn configure_test_dirs() -> Vec { + let test_dirs_names = vec![ + "1_mul", + "2_div", + "3_add", + "4_sub", + "5_over", + "6_array", + "7_function", + "bit_and", + ]; + test_dirs_names + .into_iter() + .map(test_program_dir_path) + .collect() +} + +fn nargo_cmd() -> std::process::Command { + Command::new("nargo") +} + +fn nargo_execute(test_program_dir: &std::path::PathBuf) -> std::io::Result { + nargo_cmd() + .current_dir(test_program_dir) + .arg("execute") + .spawn() + .unwrap() + .wait_with_output() +} + +fn nargo_test(test_program_dir: &std::path::PathBuf) -> std::io::Result { + nargo_cmd() + .current_dir(test_program_dir) + .arg("test") + .spawn() + .unwrap() + .wait_with_output() +} + +fn nargo_check(test_program_dir: &std::path::PathBuf) -> std::io::Result { + nargo_cmd() + .current_dir(test_program_dir) + .arg("check") + .spawn() + .unwrap() + .wait_with_output() +} + +fn nargo_gates(test_program_dir: &std::path::PathBuf) -> std::io::Result { + nargo_cmd() + .current_dir(test_program_dir) + .arg("gates") + .spawn() + .unwrap() + .wait_with_output() +} + +fn nargo_compile(test_program_dir: &std::path::PathBuf) -> std::io::Result { + nargo_cmd() + .current_dir(test_program_dir) + .arg("compile") + .arg("my_test_circuit") + .spawn() + .unwrap() + .wait_with_output() +} + +fn nargo_prove(test_program_dir: &std::path::PathBuf) -> std::io::Result { + nargo_cmd() + .current_dir(test_program_dir) + .arg("prove") + .arg("my_test_proof") + .arg("my_test_circuit") + .spawn() + .unwrap() + .wait_with_output() +} + +fn nargo_verify(test_program_dir: &std::path::PathBuf) -> std::io::Result { + nargo_cmd() + .current_dir(test_program_dir) + .arg("verify") + .arg("my_test_proof") + .arg("my_test_circuit") + .spawn() + .unwrap() + .wait_with_output() +} + +fn test_program_dir_path(dir_name: &str) -> std::path::PathBuf { + fs::canonicalize(std::path::PathBuf::from(format!( + "./tests/test_programs/{dir_name}" + ))) + .unwrap() +} + +fn assert_nargo_cmd_works(cmd_name: &str, test_test_program_dir: &std::path::PathBuf) { + let cmd_output = match cmd_name { + "check" => nargo_check(test_test_program_dir), + "contract" => todo!(), + "compile" => nargo_compile(test_test_program_dir), + "new" => panic!("This cmd doesn't depend on the backend"), + "execute" => nargo_execute(test_test_program_dir), + "prove" => nargo_prove(test_test_program_dir), + "verify" => nargo_verify(test_test_program_dir), + "test" => nargo_test(test_test_program_dir), + "gates" => nargo_gates(test_test_program_dir), + e => panic!("{e} is not a valid nargo cmd"), + } + .unwrap(); + + assert!( + cmd_output.status.success(), + "stderr(nargo {cmd_name}) in {}: {}", + test_test_program_dir.display(), + String::from_utf8(cmd_output.stderr).unwrap() + ); +} + +fn install_nargo(backend: &'static str) { + // Clone noir into repo + Command::new("git") + .arg("clone") + .arg("https://github.com/Ethan-000/noir") + .arg("--branch") + .arg("add_halo2_backend") + .spawn() + .unwrap() + .wait() + .unwrap(); + format!("\nInstalling {backend}. This may take a few moments.",); + // Install specified backend into noir + Command::new("cargo") + .current_dir(fs::canonicalize("./noir/crates/nargo_cli").unwrap()) + .arg("install") + .arg("--path") + .arg(".") + .arg("--locked") + .arg("--features") + .arg(backend) + .arg("--no-default-features") + .spawn() + .unwrap() + .wait() + .unwrap(); +} + +fn run_nargo_tests(test_program_dirs: Vec) { + for test_program in test_program_dirs { + assert_nargo_cmd_works("check", &test_program); + assert_nargo_cmd_works("compile", &test_program); + assert_nargo_cmd_works("execute", &test_program); + assert_nargo_cmd_works("prove", &test_program); + assert_nargo_cmd_works("verify", &test_program); + assert_nargo_cmd_works("test", &test_program); + assert_nargo_cmd_works("gates", &test_program); + } +} + +#[test] +fn test_axiom_backend() { + let test_program_dirs = configure_test_dirs(); + // Pass in Axiom Halo2 Backend as argument + install_nargo("axiom_halo2_backend"); + run_nargo_tests(test_program_dirs); +} + +#[test] +fn test_pse_backend() { + let test_program_dirs = configure_test_dirs(); + // Pass in PSE Halo2 Backend as argument + install_nargo("pse_halo2_backend"); + run_nargo_tests(test_program_dirs); +}