Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Port C++ merkle tests to Rust #243

Merged
merged 33 commits into from
Aug 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
5a0c7a7
WIP Port merkle tests
tzerrell Aug 16, 2022
f401397
WIP Port merkle tests
tzerrell Aug 16, 2022
fc1265f
WIP Separate bad row access test
tzerrell Aug 16, 2022
0622802
WIP make verify::merkle public
tzerrell Aug 17, 2022
095842a
WIP test for valid/invalid Merkle queries
tzerrell Aug 17, 2022
c081bf1
Test querying incorrectly
tzerrell Aug 18, 2022
e82b1c5
Test manipulated proofs
tzerrell Aug 18, 2022
5c7c899
Test at more sizes
tzerrell Aug 18, 2022
ca35d9e
Lint
tzerrell Aug 18, 2022
1a4e0c3
Merge branch 'main' into tzerrell-port-merkle-tests
tzerrell Aug 18, 2022
090e0f9
Lint
tzerrell Aug 18, 2022
4b3ad3d
Add a circuit to ZKP for use in testing
tzerrell Aug 19, 2022
8b735f2
Use test_circuit for merkle tests
tzerrell Aug 19, 2022
afa312d
Lint
tzerrell Aug 19, 2022
eb17545
Clean warnings
tzerrell Aug 19, 2022
63a5912
Sort deps
tzerrell Aug 19, 2022
b057a90
Update lock files
tzerrell Aug 19, 2022
ef170dd
Condense `use`
tzerrell Aug 19, 2022
4aa188c
Merge branch 'main' into tzerrell-port-merkle-tests
tzerrell Aug 19, 2022
da113ce
Merge branch 'main' into tzerrell-port-merkle-tests
tzerrell Aug 22, 2022
47271fa
Cleanup from review comments
tzerrell Aug 22, 2022
b10b188
Update lock files
tzerrell Aug 23, 2022
7b14863
WIP Make separate risc0-circuit-fib crate
tzerrell Aug 23, 2022
0428522
Lint
tzerrell Aug 23, 2022
bf1d43e
Revert "WIP Make separate risc0-circuit-fib crate"
tzerrell Aug 23, 2022
b2365ff
Lint
tzerrell Aug 23, 2022
a463516
Merge branch 'main' into tzerrell-port-merkle-tests
tzerrell Aug 23, 2022
96e1eb1
Upgrade merkle tests for new error handling
tzerrell Aug 24, 2022
209bc83
Lint
tzerrell Aug 24, 2022
1530171
Merge branch 'main' into tzerrell-port-merkle-tests
tzerrell Aug 24, 2022
479b0d8
Update to new HAL API
tzerrell Aug 24, 2022
ec9abe2
Remove now-unused test circuit
tzerrell Aug 24, 2022
889b5ac
Update lock file
tzerrell Aug 24, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 6 additions & 6 deletions Cargo-guest.lock
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"

[[package]]
name = "cpufeatures"
version = "0.2.2"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b"
checksum = "dc948ebb96241bb40ab73effeb80d9f93afaad49359d159a5e61be51619fe813"
dependencies = [
"libc",
]
Expand Down Expand Up @@ -160,18 +160,18 @@ checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"

[[package]]
name = "serde"
version = "1.0.143"
version = "1.0.144"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53e8e5d5b70924f74ff5c6d64d9a5acd91422117c60f48c4e07855238a254553"
checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860"
dependencies = [
"serde_derive",
]

[[package]]
name = "serde_derive"
version = "1.0.143"
version = "1.0.144"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3d8e8de557aee63c26b85b947f5e59b690d0454c753f3adeb5cd7835ab88391"
checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00"
dependencies = [
"proc-macro2",
"quote",
Expand Down
32 changes: 16 additions & 16 deletions Cargo-host.lock
Original file line number Diff line number Diff line change
Expand Up @@ -335,9 +335,9 @@ checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc"

[[package]]
name = "cpufeatures"
version = "0.2.2"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b"
checksum = "dc948ebb96241bb40ab73effeb80d9f93afaad49359d159a5e61be51619fe813"
dependencies = [
"libc",
]
Expand Down Expand Up @@ -997,9 +997,9 @@ checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5"

[[package]]
name = "link-cplusplus"
version = "1.0.6"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8cae2cd7ba2f3f63938b9c724475dfb7b9861b545a90324476324ed21dbc8c8"
checksum = "9272ab7b96c9046fbc5bc56c06c117cb639fe2d509df0c421cad82d2915cf369"
dependencies = [
"cc",
]
Expand Down Expand Up @@ -1273,9 +1273,9 @@ checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae"

[[package]]
name = "plotters"
version = "0.3.2"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9428003b84df1496fb9d6eeee9c5f8145cb41ca375eb0dad204328888832811f"
checksum = "716b4eeb6c4a1d3ecc956f75b43ec2e8e8ba80026413e70a3f41fd3313d3492b"
dependencies = [
"num-traits",
"plotters-backend",
Expand All @@ -1292,9 +1292,9 @@ checksum = "193228616381fecdc1224c62e96946dfbc73ff4384fba576e052ff8c1bea8142"

[[package]]
name = "plotters-svg"
version = "0.3.2"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0918736323d1baff32ee0eade54984f6f201ad7e97d5cfb5d6ab4a358529615"
checksum = "f9a81d2759aae1dae668f783c308bc5c8ebd191ff4184aaa1b37f65a6ae5a56f"
dependencies = [
"plotters-backend",
]
Expand Down Expand Up @@ -1773,9 +1773,9 @@ dependencies = [

[[package]]
name = "security-framework"
version = "2.6.1"
version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dc14f172faf8a0194a3aded622712b0de276821addc574fa54fc0a1167e10dc"
checksum = "2bc1bb97804af6631813c55739f771071e0f2ed33ee20b68c86ec505d906356c"
dependencies = [
"bitflags",
"core-foundation",
Expand Down Expand Up @@ -1805,9 +1805,9 @@ dependencies = [

[[package]]
name = "serde"
version = "1.0.143"
version = "1.0.144"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53e8e5d5b70924f74ff5c6d64d9a5acd91422117c60f48c4e07855238a254553"
checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860"
dependencies = [
"serde_derive",
]
Expand All @@ -1824,9 +1824,9 @@ dependencies = [

[[package]]
name = "serde_derive"
version = "1.0.143"
version = "1.0.144"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3d8e8de557aee63c26b85b947f5e59b690d0454c753f3adeb5cd7835ab88391"
checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00"
dependencies = [
"proc-macro2",
"quote",
Expand All @@ -1835,9 +1835,9 @@ dependencies = [

[[package]]
name = "serde_json"
version = "1.0.83"
version = "1.0.85"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38dd04e3c8279e75b31ef29dbdceebfe5ad89f4d0937213c53f7d49d01b3d5a7"
checksum = "e55a28e3aaef9d5ce0506d0a14dbba8054ddc7e499ef522dd8b26859ec9d4a44"
dependencies = [
"itoa 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"ryu",
Expand Down
237 changes: 237 additions & 0 deletions risc0/zkp/rust/src/prove/merkle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,3 +125,240 @@ impl<H: Hal> MerkleTreeProver<H> {
out
}
}

#[cfg(test)]
mod tests {
use crate::{
core::sha_cpu,
hal::cpu::CpuHal,
verify::{merkle::MerkleTreeVerifier, read_iop::ReadIOP, VerificationError},
};
use rand::{Rng, RngCore};

use super::*;

fn init_prover<H: Hal>(
hal: &H,
rows: usize,
cols: usize,
queries: usize,
) -> MerkleTreeProver<H> {
// Initialize a prover with leaves 0..size
let size: u32 = (rows * cols) as u32;
let mut data = Vec::<Fp>::new();
for val in 0..size {
data.push(Fp::from(val));
}
let matrix = hal.copy_fp_from(data.as_slice());

MerkleTreeProver::new(hal, &matrix, rows, cols, queries)
}

fn bad_row_access<H: Hal, S: Sha>(sha: &S, hal: &H, rows: usize, cols: usize, queries: usize) {
let prover = init_prover(hal, rows, cols, queries);
let mut iop = WriteIOP::new(sha);
prover.prove(&mut iop, rows);
}

fn possibly_bad_verify<H: Hal, S: Sha>(
sha: &S,
hal: &H,
rows: usize,
cols: usize,
queries: usize,
bad_query: usize,
manipulate_proof: bool,
) {
let prover = init_prover(hal, rows, cols, queries);

let mut iop: WriteIOP<S> = WriteIOP::new(sha);
prover.commit(hal, &mut iop);
for _query in 0..queries {
let r_idx = (iop.rng.next_u32() as usize) % rows;
let col = prover.prove(&mut iop, r_idx);
for c_idx in 0..cols {
assert_eq!(col[c_idx], Fp::from((r_idx + c_idx * rows) as u32));
}
}
{
if manipulate_proof {
let mut rng = rand::thread_rng();
let manip_idx = rng.gen::<usize>() % iop.proof.len();
iop.proof[manip_idx] ^= 1;
}
let mut r_iop = ReadIOP::new(sha, &iop.proof);
let verifier = MerkleTreeVerifier::new(&mut r_iop, rows, cols, queries);
assert_eq!(verifier.root(), prover.root());
let mut err = false;
for query in 0..queries {
let r_idx = (r_iop.next_u32() as usize) % rows;
if query == bad_query {
if rows == 1 {
assert!(false, "Cannot test for bad query if there is only one row");
}
let r_idx = (r_idx + 1) % rows;
let verification = verifier.verify(&mut r_iop, r_idx);
match verification {
Ok(_) => assert!(
false,
"Merkle tree wrongly passed verify when tested on the wrong row"
),
Err(VerificationError::InvalidProof) => {}
Err(_) => assert!(
false,
"Merkle tree failed validation for an unexpected reason"
),
}
err = true;
break;
}
let col = verifier.verify(&mut r_iop, r_idx).unwrap();
for c_idx in 0..cols {
assert_eq!(col[c_idx], Fp::from((r_idx + c_idx * rows) as u32));
}
}
if !err {
r_iop.verify_complete();
}
}
}

fn randomize_sizes() -> (usize, usize, usize) {
// Chooses random values of `rows`, `cols`, and `queries` such that:
// `rows` is a power of 2
// `cols` & `queries` have a wide distribution but tend to take small values
let mut rng = rand::thread_rng();
let rows = 1 << (rng.gen::<u8>() % 10);
let cols = (rng.gen::<usize>() % (1 << (rng.gen::<u8>() % 10))) + 1;
let queries = (rng.gen::<usize>() % (1 << (rng.gen::<u8>() % 10))) + 1;
(rows, cols, queries)
}

#[test]
#[should_panic(expected = "assertion failed: idx < self.params.row_size")]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is probably fine for now but I think in the future we should avoid using panics and instead use Result to do error reporting. Panic won't work well in some environments since a panic causes the entire thread to be terminated.

Copy link
Member Author

@tzerrell tzerrell Aug 22, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests would be cleaner if this was Result-based error reporting than panics anyway. I'm tracking down an intermittent failure in this test the randomized version of this test anyway -- perhaps the most straightforward approach will be to rewrite the tested code to use Results.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fyi I switched to Result-based errors on the verifier side, but not here on the prover side. We should definitely do it eventually, but there is more to be done on the prover side and IMO it's less important on the prover side.

fn merkle_cpu_1_1_1_bad_row_access() {
let sha = sha_cpu::Impl {};
let hal = CpuHal::new();
bad_row_access(&sha, &hal, 1, 1, 1);
}

#[test]
#[should_panic(expected = "assertion failed: idx < self.params.row_size")]
fn merkle_cpu_4_4_2_bad_row_access() {
let sha = sha_cpu::Impl {};
let hal = CpuHal::new();
bad_row_access(&sha, &hal, 4, 4, 2);
}

#[test]
#[should_panic(expected = "assertion failed: idx < self.params.row_size")]
fn merkle_cpu_randomized_bad_row_access() {
let sha = sha_cpu::Impl {};
let hal = CpuHal::new();
let (rows, cols, queries) = randomize_sizes();
bad_row_access(&sha, &hal, rows, cols, queries);
}

#[test]
fn merkle_cpu_1_1_1_verify() {
let sha = sha_cpu::Impl {};
let hal = CpuHal::new();
// Test a complete verification with no bad queries (by setting bad_query out of
// range)
possibly_bad_verify(&sha, &hal, 1, 1, 1, 4, false);
}

#[test]
fn merkle_cpu_4_4_2_verify() {
let sha = sha_cpu::Impl {};
let hal = CpuHal::new();
// Test a complete verification with no bad queries (by setting bad_query out of
// range)
possibly_bad_verify(&sha, &hal, 4, 4, 2, 4, false);
}

#[test]
fn merkle_cpu_randomized_verify() {
let sha = sha_cpu::Impl {};
let hal = CpuHal::new();
for _rep in 0..100 {
let (rows, cols, queries) = randomize_sizes();
// Test a complete verification with no bad queries (by setting bad_query out of
// range)
possibly_bad_verify(&sha, &hal, rows, cols, queries, queries + 1, false);
}
}

#[test]
fn merkle_cpu_2_1_1_bad_query() {
// n.b. since we test bad queries by incrementing the row, we can't test for a
// bad query with rows == 1
let sha = sha_cpu::Impl {};
let hal = CpuHal::new();
possibly_bad_verify(&sha, &hal, 2, 1, 1, 0, false);
}

#[test]
fn merkle_cpu_4_4_2_bad_query() {
let mut rng = rand::thread_rng();
let sha = sha_cpu::Impl {};
let hal = CpuHal::new();
let queries = 2;
// Test a complete verification with a bad query
let bad_query = rng.gen::<usize>() % queries;
possibly_bad_verify(&sha, &hal, 4, 4, queries, bad_query, false);
}

#[test]
fn merkle_cpu_randomized_bad_query() {
let mut rng = rand::thread_rng();
let sha = sha_cpu::Impl {};
let hal = CpuHal::new();
let (rows, cols, queries) = randomize_sizes();
// At least two rows are required to test querying an incorrect row
let rows = if rows == 1 { 2 } else { rows };
// Test a complete verification with a bad query
let bad_query = rng.gen::<usize>() % queries;
possibly_bad_verify(&sha, &hal, rows, cols, queries, bad_query, false);
}

#[test]
#[should_panic]
fn merkle_cpu_1_1_1_verify_manipulated() {
let sha = sha_cpu::Impl {};
let hal = CpuHal::new();
for _rep in 0..50 {
// Test a verification with a manipulated proof but no bad queries (by setting
// bad_query out of range) Do this multiple times as the
// manipulation location is random
possibly_bad_verify(&sha, &hal, 1, 1, 1, 2, true);
}
}

#[test]
#[should_panic]
fn merkle_cpu_4_4_2_verify_manipulated() {
let sha = sha_cpu::Impl {};
let hal = CpuHal::new();
for _rep in 0..50 {
// Test a verification with a manipulated proof but no bad queries (by setting
// bad_query out of range) Do this multiple times as the
// manipulation location is random
possibly_bad_verify(&sha, &hal, 4, 4, 2, 4, true);
}
}

#[test]
#[should_panic]
fn merkle_cpu_randomized_verify_manipulated() {
let sha = sha_cpu::Impl {};
let hal = CpuHal::new();
for _rep in 0..50 {
let (rows, cols, queries) = randomize_sizes();
// Test a verification with a manipulated proof but no bad queries (by setting
// bad_query out of range) Do this multiple times as the
// manipulation location is random
possibly_bad_verify(&sha, &hal, rows, cols, queries, queries + 1, true);
}
}
}
2 changes: 1 addition & 1 deletion risc0/zkp/rust/src/verify/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

pub mod adapter;
mod fri;
mod merkle;
pub(crate) mod merkle;
pub mod read_iop;

use alloc::vec;
Expand Down