diff --git a/Cargo.lock b/Cargo.lock index a6142dcbb..e345859d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -87,7 +87,7 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" dependencies = [ - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -97,7 +97,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" dependencies = [ "anstyle", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -318,8 +318,7 @@ checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] name = "clio" version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7fc6734af48458f72f5a3fa7b840903606427d98a710256e808f76a965047d9" +source = "git+https://github.com/0xmozak/clio.git#a5a47f19470ee6991cf7200751eae8d2e3ac07af" dependencies = [ "cfg-if", "clap", @@ -327,7 +326,7 @@ dependencies = [ "libc", "tempfile", "walkdir", - "windows-sys 0.42.0", + "windows-sys", ] [[package]] @@ -607,12 +606,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -790,7 +789,7 @@ checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -1005,7 +1004,7 @@ dependencies = [ "enumflags2", "iter_fixed", "itertools 0.12.1", - "lazy_static", + "once_cell", "plonky2", "tested-fixture", ] @@ -1059,9 +1058,9 @@ checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" [[package]] name = "num" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3135b08af27d103b0a51f2ae0f8632117b7b185ccf931445affa8df530576a41" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" dependencies = [ "num-bigint", "num-complex", @@ -1084,9 +1083,9 @@ dependencies = [ [[package]] name = "num-complex" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" dependencies = [ "num-traits", "rand", @@ -1120,11 +1119,10 @@ dependencies = [ [[package]] name = "num-rational" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" dependencies = [ - "autocfg", "num-bigint", "num-integer", "num-traits", @@ -1155,7 +1153,7 @@ checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" [[package]] name = "plonky2" version = "0.2.2" -source = "git+https://github.com/0xmozak/plonky2.git#b949c118202488e4c93fd0b1c6c99319b0ffacfc" +source = "git+https://github.com/0xmozak/plonky2.git#13cb36256626f53134758894897010c1506acd80" dependencies = [ "ahash", "anyhow", @@ -1193,7 +1191,7 @@ dependencies = [ [[package]] name = "plonky2_field" version = "0.2.2" -source = "git+https://github.com/0xmozak/plonky2.git#b949c118202488e4c93fd0b1c6c99319b0ffacfc" +source = "git+https://github.com/0xmozak/plonky2.git#13cb36256626f53134758894897010c1506acd80" dependencies = [ "anyhow", "itertools 0.12.1", @@ -1208,7 +1206,7 @@ dependencies = [ [[package]] name = "plonky2_maybe_rayon" version = "0.2.0" -source = "git+https://github.com/0xmozak/plonky2.git#b949c118202488e4c93fd0b1c6c99319b0ffacfc" +source = "git+https://github.com/0xmozak/plonky2.git#13cb36256626f53134758894897010c1506acd80" dependencies = [ "rayon", ] @@ -1216,7 +1214,7 @@ dependencies = [ [[package]] name = "plonky2_util" version = "0.2.0" -source = "git+https://github.com/0xmozak/plonky2.git#b949c118202488e4c93fd0b1c6c99319b0ffacfc" +source = "git+https://github.com/0xmozak/plonky2.git#13cb36256626f53134758894897010c1506acd80" [[package]] name = "plotters" @@ -1507,7 +1505,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -1539,9 +1537,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.200" +version = "1.0.201" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f" +checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c" dependencies = [ "serde_derive", ] @@ -1568,9 +1566,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.200" +version = "1.0.201" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb" +checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" dependencies = [ "proc-macro2", "quote", @@ -1579,9 +1577,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.116" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" dependencies = [ "itoa", "ryu", @@ -1687,7 +1685,7 @@ dependencies = [ [[package]] name = "starky" version = "0.4.0" -source = "git+https://github.com/0xmozak/plonky2.git#b949c118202488e4c93fd0b1c6c99319b0ffacfc" +source = "git+https://github.com/0xmozak/plonky2.git#13cb36256626f53134758894897010c1506acd80" dependencies = [ "ahash", "anyhow", @@ -1744,7 +1742,7 @@ dependencies = [ "cfg-if", "fastrand", "rustix", - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -2092,7 +2090,7 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d4cc384e1e73b93bafa6fb4f1df8c41695c8a91cf9c4c64358067d15a7b6c6b" dependencies = [ - "windows-sys 0.52.0", + "windows-sys", ] [[package]] @@ -2104,21 +2102,6 @@ dependencies = [ "windows-targets", ] -[[package]] -name = "windows-sys" -version = "0.42.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - [[package]] name = "windows-sys" version = "0.52.0" @@ -2134,46 +2117,28 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - [[package]] name = "windows_aarch64_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - [[package]] name = "windows_i686_gnu" version = "0.52.5" @@ -2186,48 +2151,24 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - [[package]] name = "windows_i686_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - [[package]] name = "windows_x86_64_gnu" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - [[package]] name = "windows_x86_64_msvc" version = "0.52.5" @@ -2254,18 +2195,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.33" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "087eca3c1eaf8c47b94d02790dd086cd594b912d2043d4de4bfdd466b3befb7c" +checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.33" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f4b6c273f496d8fd4eaf18853e6b448760225dc030ff2c485a786859aea6393" +checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 20151305e..c8a0014ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,6 +56,7 @@ plonky2_crypto = { git = "https://github.com/0xmozak/plonky2-crypto.git" } criterion = { version = "0.5", default-features = false, features = ["html_reports", "plotters", "cargo_bench_support"] } [patch.crates-io] +clio = { git = "https://github.com/0xmozak/clio.git" } plonky2 = { git = "https://github.com/0xmozak/plonky2.git" } plonky2_maybe_rayon = { git = "https://github.com/0xmozak/plonky2.git" } starky = { git = "https://github.com/0xmozak/plonky2.git" } diff --git a/circuits/src/cpu/columns.rs b/circuits/src/cpu/columns.rs index 90e7c9abb..a23aca7c4 100644 --- a/circuits/src/cpu/columns.rs +++ b/circuits/src/cpu/columns.rs @@ -180,6 +180,7 @@ pub struct CpuState { pub is_event_tape: T, pub is_events_commitment_tape: T, pub is_cast_list_commitment_tape: T, + pub is_self_prog_id_tape: T, pub is_halt: T, pub is_poseidon2: T, } @@ -336,6 +337,7 @@ pub fn lookup_for_storage_tables() -> TableWithTypedOutput TableWithTypedOutput( cb.always(lv.is_event_tape.is_binary()); cb.always(lv.is_events_commitment_tape.is_binary()); cb.always(lv.is_cast_list_commitment_tape.is_binary()); + cb.always(lv.is_self_prog_id_tape.is_binary()); cb.always( lv.inst.ops.ecall - (lv.is_halt @@ -30,6 +31,7 @@ pub(crate) fn constraints<'a, P: Copy>( + lv.is_event_tape + lv.is_events_commitment_tape + lv.is_cast_list_commitment_tape + + lv.is_self_prog_id_tape + lv.is_poseidon2), ); halt_constraints(lv, cb); @@ -70,6 +72,7 @@ pub(crate) fn storage_device_constraints<'a, P: Copy>( lv.is_cast_list_commitment_tape * (lv.op1_value - i64::from(ecall::CAST_LIST_COMMITMENT_TAPE)), ); + cb.always(lv.is_self_prog_id_tape * (lv.op1_value - i64::from(ecall::SELF_PROG_ID_TAPE))); } pub(crate) fn poseidon2_constraints<'a, P: Copy>( diff --git a/circuits/src/cross_table_lookup.rs b/circuits/src/cross_table_lookup.rs index 113c12e8f..53c7022b1 100644 --- a/circuits/src/cross_table_lookup.rs +++ b/circuits/src/cross_table_lookup.rs @@ -1,7 +1,7 @@ use core::ops::Neg; use anyhow::{ensure, Result}; -use itertools::{chain, iproduct, izip, zip_eq}; +use itertools::{chain, iproduct, izip, zip_eq, Itertools}; use plonky2::field::extension::{Extendable, FieldExtension}; use plonky2::field::packed::PackedField; use plonky2::field::polynomial::PolynomialValues; @@ -11,6 +11,8 @@ use plonky2::iop::ext_target::ExtensionTarget; use plonky2::iop::target::Target; use plonky2::plonk::circuit_builder::CircuitBuilder; use plonky2::plonk::config::GenericConfig; +#[allow(clippy::wildcard_imports)] +use plonky2_maybe_rayon::*; use starky::config::StarkConfig; use starky::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use starky::evaluation_frame::StarkEvaluationFrame; @@ -21,7 +23,7 @@ pub use crate::linear_combination::Column; use crate::linear_combination::ColumnSparse; pub use crate::linear_combination_typed::ColumnWithTypedInput; use crate::public_sub_table::PublicSubTable; -use crate::stark::mozak_stark::{all_kind, Table, TableKind, TableKindArray, TableWithTypedOutput}; +use crate::stark::mozak_stark::{all_kind, TableKind, TableKindArray, TableWithTypedOutput}; use crate::stark::permutation::challenge::{GrandProductChallenge, GrandProductChallengeSet}; use crate::stark::proof::{StarkProof, StarkProofTarget}; @@ -31,7 +33,7 @@ pub enum LookupError { InconsistentTableRows, } -#[derive(Clone, Default)] +#[derive(Clone, Debug, Default)] pub struct CtlData { pub(crate) zs_columns: Vec>, } @@ -53,7 +55,7 @@ impl CtlData { } /// Cross-table lookup data associated with one Z(x) polynomial. -#[derive(Clone)] +#[derive(Clone, Debug)] pub(crate) struct CtlZData { pub(crate) z: PolynomialValues, pub(crate) challenge: GrandProductChallenge, @@ -152,46 +154,33 @@ pub(crate) fn cross_table_lookup_data( cross_table_lookups: &[CrossTableLookup], ctl_challenges: &GrandProductChallengeSet, ) -> TableKindArray> { - let mut ctl_data_per_table = all_kind!(|_kind| CtlData::default()); - for &challenge in &ctl_challenges.challenges { - for CrossTableLookup { looking_tables } in cross_table_lookups { - log::debug!( - "Processing CTL for {:?}", - looking_tables - .iter() - .map(|table| table.kind) - .collect::>() - ); - - let make_z = |table: &Table| { - partial_sums( - &trace_poly_values[table.kind], - &table.columns, - &table.filter_column, - challenge, - ) - }; - let zs_looking = looking_tables.iter().map(make_z); - - debug_assert_eq!( - zs_looking - .clone() - .map(|z| *z.values.last().unwrap()) - .sum::(), - F::ZERO - ); - - for (table, z) in izip!(looking_tables, zs_looking) { - ctl_data_per_table[table.kind].zs_columns.push(CtlZData { - z, - challenge, - columns: table.columns.clone(), - filter_column: table.filter_column.clone(), - }); - } - } - } - ctl_data_per_table + let mut tables = iproduct!( + &ctl_challenges.challenges, + cross_table_lookups + .iter() + .flat_map(|CrossTableLookup { looking_tables }| looking_tables) + ) + .collect::>() + .into_par_iter() + .map(|(&challenge, table)| { + (table.kind, CtlZData { + z: partial_sums( + &trace_poly_values[table.kind], + &table.columns, + &table.filter_column, + challenge, + ), + challenge, + columns: table.columns.clone(), + filter_column: table.filter_column.clone(), + }) + }) + .collect::>() + .into_iter() + .into_group_map(); + all_kind!(|kind| CtlData { + zs_columns: tables.remove(&kind).unwrap(), + }) } /// Treat CTL and the challenge as a single entity. diff --git a/circuits/src/generation/cpu.rs b/circuits/src/generation/cpu.rs index 5f1dd3933..55115352b 100644 --- a/circuits/src/generation/cpu.rs +++ b/circuits/src/generation/cpu.rs @@ -132,6 +132,10 @@ pub fn generate_cpu_trace(record: &ExecutionRecord) -> Vec( @@ -193,6 +194,7 @@ mod tests { &event_tape_rows, &events_commitment_tape_rows, &cast_list_commitment_tape_rows, + &self_prog_id_tape_rows, &poseidon2_rows, &poseidon2_output_bytes, ); diff --git a/circuits/src/generation/halfword_memory.rs b/circuits/src/generation/halfword_memory.rs index db83dd72a..14b66e8e3 100644 --- a/circuits/src/generation/halfword_memory.rs +++ b/circuits/src/generation/halfword_memory.rs @@ -79,7 +79,7 @@ mod tests { use crate::generation::storage_device::{ generate_call_tape_trace, generate_cast_list_commitment_tape_trace, generate_event_tape_trace, generate_events_commitment_tape_trace, - generate_private_tape_trace, generate_public_tape_trace, + generate_private_tape_trace, generate_public_tape_trace, generate_self_prog_id_tape_trace, }; use crate::poseidon2_sponge::generation::generate_poseidon2_sponge_trace; use crate::test_utils::prep_table; @@ -170,6 +170,7 @@ mod tests { let events_commitment_tape_rows = generate_events_commitment_tape_trace(&record.executed); let cast_list_commitment_tape_rows = generate_cast_list_commitment_tape_trace(&record.executed); + let self_prog_id_tape_rows = generate_self_prog_id_tape_trace(&record.executed); let poseidon2_sponge_rows = generate_poseidon2_sponge_trace(&record.executed); let poseidon2_output_bytes = generate_poseidon2_output_bytes_trace(&poseidon2_sponge_rows); @@ -185,6 +186,7 @@ mod tests { &event_tape_rows, &events_commitment_tape_rows, &cast_list_commitment_tape_rows, + &self_prog_id_tape_rows, &poseidon2_sponge_rows, &poseidon2_output_bytes, ); diff --git a/circuits/src/generation/memory.rs b/circuits/src/generation/memory.rs index bb30b2de4..87576d3fd 100644 --- a/circuits/src/generation/memory.rs +++ b/circuits/src/generation/memory.rs @@ -160,6 +160,7 @@ pub fn generate_memory_trace( event_tape_rows: &[StorageDevice], events_commitment_tape_rows: &[StorageDevice], castlist_commitment_tape_rows: &[StorageDevice], + self_prog_id_tape_rows: &[StorageDevice], poseidon2_sponge_rows: &[Poseidon2Sponge], poseidon2_output_bytes_rows: &[Poseidon2OutputBytes], ) -> Vec> { @@ -178,6 +179,7 @@ pub fn generate_memory_trace( transform_storage(event_tape_rows), transform_storage(events_commitment_tape_rows), transform_storage(castlist_commitment_tape_rows), + transform_storage(self_prog_id_tape_rows), transform_poseidon2_sponge(poseidon2_sponge_rows), transform_poseidon2_output_bytes(poseidon2_output_bytes_rows,), ) @@ -220,7 +222,7 @@ mod tests { use crate::generation::storage_device::{ generate_call_tape_trace, generate_cast_list_commitment_tape_trace, generate_event_tape_trace, generate_events_commitment_tape_trace, - generate_private_tape_trace, generate_public_tape_trace, + generate_private_tape_trace, generate_public_tape_trace, generate_self_prog_id_tape_trace, }; use crate::memory::columns::Memory; use crate::memory::stark::MemoryStark; @@ -288,6 +290,7 @@ mod tests { let event_tape_rows = generate_event_tape_trace(&record.executed); let events_commitment_tape_rows = generate_events_commitment_tape_trace(&record.executed); let cast_list_commitment_tape_rows = generate_cast_list_commitment_tape_trace(&record.executed); + let self_prog_id_tape_rows = generate_self_prog_id_tape_trace(&record.executed); let poseidon2_sponge_trace = generate_poseidon2_sponge_trace(&record.executed); let poseidon2_output_bytes = generate_poseidon2_output_bytes_trace(&poseidon2_sponge_trace); @@ -303,6 +306,7 @@ mod tests { &event_tape_rows, &events_commitment_tape_rows, &cast_list_commitment_tape_rows, + &self_prog_id_tape_rows, &poseidon2_sponge_trace, &poseidon2_output_bytes, ); @@ -378,6 +382,7 @@ mod tests { let events_commitment_tape_rows = generate_events_commitment_tape_trace(&[]); let cast_list_commitment_tape_rows = generate_cast_list_commitment_tape_trace(&[]); + let self_prog_id_tape_rows = generate_self_prog_id_tape_trace(&[]); let poseidon2_trace = generate_poseidon2_sponge_trace(&[]); let poseidon2_output_bytes = generate_poseidon2_output_bytes_trace(&poseidon2_trace); let trace = super::generate_memory_trace::( @@ -392,6 +397,7 @@ mod tests { &event_tape_rows, &events_commitment_tape_rows, &cast_list_commitment_tape_rows, + &self_prog_id_tape_rows, &poseidon2_trace, &poseidon2_output_bytes, ); diff --git a/circuits/src/generation/memoryinit.rs b/circuits/src/generation/memoryinit.rs index 224377b79..0a3317e95 100644 --- a/circuits/src/generation/memoryinit.rs +++ b/circuits/src/generation/memoryinit.rs @@ -10,11 +10,6 @@ use crate::utils::pad_trace_with_default; pub fn generate_memory_init_trace(program: &Program) -> Vec> { let mut memory_inits: Vec> = chain! { elf_memory_init(program), - mozak_memory_init(program), - call_tape_init(program), - private_tape_init(program), - public_tape_init(program), - event_tape_init(program), } .collect(); @@ -37,101 +32,6 @@ where pad_trace_with_default(memory_inits) } -/// Generates a mozak memory init ROM trace -#[must_use] -pub fn generate_mozak_memory_init_trace(program: &Program) -> Vec> { - let trace = generate_init_trace(program, mozak_memory_init); - log::trace!("MozakMemoryInit trace {:?}", trace); - trace -} - -#[must_use] -pub fn mozak_memory_init(program: &Program) -> Vec> { - program - .mozak_ro_memory - .iter() - .flat_map(|mozak_ro_memory| { - chain! { - mozak_ro_memory.self_prog_id.data.clone(), - mozak_ro_memory.cast_list.data.clone(), - } - }) - .map(MemoryInit::new_readonly) - .collect_vec() -} - -#[must_use] -pub fn call_tape_init(program: &Program) -> Vec> { - program - .mozak_ro_memory - .iter() - .flat_map(|mozak_ro_memory| mozak_ro_memory.call_tape.data.clone()) - .map(MemoryInit::new_readonly) - .collect_vec() -} - -/// Generates a call tape memory init ROM trace -#[must_use] -pub fn generate_call_tape_init_trace(program: &Program) -> Vec> { - let trace = generate_init_trace(program, call_tape_init); - log::trace!("CallTapeInit trace {:?}", trace); - trace -} - -#[must_use] -pub fn private_tape_init(program: &Program) -> Vec> { - program - .mozak_ro_memory - .iter() - .flat_map(|mozak_ro_memory| mozak_ro_memory.private_tape.data.clone()) - .map(MemoryInit::new_readonly) - .collect_vec() -} - -/// Generates a private tape memory init ROM trace -#[must_use] -pub fn generate_private_tape_init_trace(program: &Program) -> Vec> { - let trace = generate_init_trace(program, private_tape_init); - log::trace!("PrivateTapeInit trace {:?}", trace); - trace -} - -#[must_use] -pub fn public_tape_init(program: &Program) -> Vec> { - program - .mozak_ro_memory - .iter() - .flat_map(|mozak_ro_memory| mozak_ro_memory.public_tape.data.clone()) - .map(MemoryInit::new_readonly) - .collect_vec() -} - -/// Generates a public tape memory init ROM trace -#[must_use] -pub fn generate_public_tape_init_trace(program: &Program) -> Vec> { - let trace = generate_init_trace(program, public_tape_init); - log::trace!("PublicTapeInit trace {:?}", trace); - trace -} - -#[must_use] -pub fn event_tape_init(program: &Program) -> Vec> { - program - .mozak_ro_memory - .iter() - .flat_map(|mozak_ro_memory| mozak_ro_memory.event_tape.data.clone()) - .map(MemoryInit::new_readonly) - .collect_vec() -} - -/// Generates a event tape memory init ROM trace -#[must_use] -pub fn generate_event_tape_init_trace(program: &Program) -> Vec> { - let trace = generate_init_trace(program, event_tape_init); - log::trace!("EventTapeInit trace {:?}", trace); - trace -} - #[must_use] pub fn elf_memory_init(program: &Program) -> Vec> { [(F::ZERO, &program.ro_memory), (F::ONE, &program.rw_memory)] diff --git a/circuits/src/generation/mod.rs b/circuits/src/generation/mod.rs index ffd0ccc34..4f0f440a8 100644 --- a/circuits/src/generation/mod.rs +++ b/circuits/src/generation/mod.rs @@ -34,22 +34,17 @@ use self::cpu::{generate_cpu_trace, generate_program_mult_trace}; use self::fullword_memory::generate_fullword_memory_trace; use self::halfword_memory::generate_halfword_memory_trace; use self::memory::generate_memory_trace; -use self::memoryinit::{ - generate_call_tape_init_trace, generate_event_tape_init_trace, generate_memory_init_trace, - generate_private_tape_init_trace, generate_public_tape_init_trace, -}; +use self::memoryinit::generate_memory_init_trace; use self::storage_device::{ generate_call_tape_trace, generate_cast_list_commitment_tape_trace, generate_event_tape_trace, - generate_events_commitment_tape_trace, + generate_events_commitment_tape_trace, generate_private_tape_trace, generate_public_tape_trace, + generate_self_prog_id_tape_trace, }; use self::xor::generate_xor_trace; use crate::columns_view::HasNamedColumns; use crate::cpu_skeleton::generation::generate_cpu_skeleton_trace; use crate::generation::memory_zeroinit::generate_memory_zero_init_trace; -use crate::generation::memoryinit::{ - generate_elf_memory_init_trace, generate_mozak_memory_init_trace, -}; -use crate::generation::storage_device::{generate_private_tape_trace, generate_public_tape_trace}; +use crate::generation::memoryinit::generate_elf_memory_init_trace; use crate::ops; use crate::poseidon2::generation::generate_poseidon2_trace; use crate::poseidon2_output_bytes::generation::generate_poseidon2_output_bytes_trace; @@ -91,11 +86,6 @@ pub fn generate_traces, const D: usize>( let memory_init = generate_memory_init_trace(program); let elf_memory_init_rows = generate_elf_memory_init_trace(program); - let mozak_memory_init_rows = generate_mozak_memory_init_trace(program); - let call_tape_init_rows = generate_call_tape_init_trace(program); - let private_tape_init_rows = generate_private_tape_init_trace(program); - let public_tape_init_rows = generate_public_tape_init_trace(program); - let event_tape_init_rows = generate_event_tape_init_trace(program); let memory_zeroinit_rows = generate_memory_zero_init_trace(&record.executed, program); @@ -107,6 +97,7 @@ pub fn generate_traces, const D: usize>( let event_tape_rows = generate_event_tape_trace(&record.executed); let events_commitment_tape_rows = generate_events_commitment_tape_trace(&record.executed); let cast_list_commitment_tape_rows = generate_cast_list_commitment_tape_trace(&record.executed); + let self_prog_id_tape_rows = generate_self_prog_id_tape_trace(&record.executed); let poseiden2_sponge_rows = generate_poseidon2_sponge_trace(&record.executed); let poseidon2_output_bytes_rows = generate_poseidon2_output_bytes_trace(&poseiden2_sponge_rows); let poseidon2_rows = generate_poseidon2_trace(&record.executed); @@ -123,6 +114,7 @@ pub fn generate_traces, const D: usize>( &event_tape_rows, &events_commitment_tape_rows, &cast_list_commitment_tape_rows, + &self_prog_id_tape_rows, &poseiden2_sponge_rows, &poseidon2_output_bytes_rows, ); @@ -140,6 +132,7 @@ pub fn generate_traces, const D: usize>( &event_tape_rows, &events_commitment_tape_rows, &cast_list_commitment_tape_rows, + &self_prog_id_tape_rows, ®ister_init_rows, ); // Generate rows for the looking values with their multiplicities. @@ -166,11 +159,6 @@ pub fn generate_traces, const D: usize>( program_mult_stark: trace_rows_to_poly_values(program_mult_rows), memory_stark: trace_rows_to_poly_values(memory_rows), elf_memory_init_stark: trace_rows_to_poly_values(elf_memory_init_rows), - mozak_memory_init_stark: trace_rows_to_poly_values(mozak_memory_init_rows), - call_tape_init_stark: trace_rows_to_poly_values(call_tape_init_rows), - private_tape_init_stark: trace_rows_to_poly_values(private_tape_init_rows), - public_tape_init_stark: trace_rows_to_poly_values(public_tape_init_rows), - event_tape_init_stark: trace_rows_to_poly_values(event_tape_init_rows), memory_zeroinit_stark: trace_rows_to_poly_values(memory_zeroinit_rows), rangecheck_u8_stark: trace_rows_to_poly_values(rangecheck_u8_rows), halfword_memory_stark: trace_rows_to_poly_values(halfword_memory_rows), @@ -181,6 +169,7 @@ pub fn generate_traces, const D: usize>( event_tape_stark: trace_rows_to_poly_values(event_tape_rows), events_commitment_tape_stark: trace_rows_to_poly_values(events_commitment_tape_rows), cast_list_commitment_tape_stark: trace_rows_to_poly_values(cast_list_commitment_tape_rows), + self_prog_id_tape_stark: trace_rows_to_poly_values(self_prog_id_tape_rows), register_init_stark: trace_rows_to_poly_values(register_init_rows), register_stark: trace_rows_to_poly_values(register_rows), register_zero_read_stark: trace_rows_to_poly_values(register_zero_read_rows), diff --git a/circuits/src/generation/storage_device.rs b/circuits/src/generation/storage_device.rs index e98e60eb7..cf7e001bc 100644 --- a/circuits/src/generation/storage_device.rs +++ b/circuits/src/generation/storage_device.rs @@ -37,6 +37,7 @@ fn is_storage_device_opcode(op: StorageDeviceOpcode) -> F { | StorageDeviceOpcode::StoreEventTape | StorageDeviceOpcode::StoreEventsCommitmentTape | StorageDeviceOpcode::StoreCastListCommitmentTape + | StorageDeviceOpcode::StoreSelfProgIdTape )) } @@ -119,3 +120,10 @@ pub fn generate_cast_list_commitment_tape_trace( ) -> Vec> { generate_storage_trace(step_rows, StorageDeviceOpcode::StoreCastListCommitmentTape) } + +#[must_use] +pub fn generate_self_prog_id_tape_trace( + step_rows: &[Row], +) -> Vec> { + generate_storage_trace(step_rows, StorageDeviceOpcode::StoreSelfProgIdTape) +} diff --git a/circuits/src/rangecheck/generation.rs b/circuits/src/rangecheck/generation.rs index 2e11ee306..b14aab11f 100644 --- a/circuits/src/rangecheck/generation.rs +++ b/circuits/src/rangecheck/generation.rs @@ -105,7 +105,7 @@ mod tests { use crate::generation::storage_device::{ generate_call_tape_trace, generate_cast_list_commitment_tape_trace, generate_event_tape_trace, generate_events_commitment_tape_trace, - generate_private_tape_trace, generate_public_tape_trace, + generate_private_tape_trace, generate_public_tape_trace, generate_self_prog_id_tape_trace, }; use crate::generation::MIN_TRACE_LENGTH; use crate::ops::{self, blt_taken}; @@ -146,6 +146,7 @@ mod tests { let events_commitment_tape_rows = generate_events_commitment_tape_trace(&record.executed); let cast_list_commitment_tape_rows = generate_cast_list_commitment_tape_trace(&record.executed); + let self_prog_id_tape_rows = generate_self_prog_id_tape_trace(&record.executed); let poseidon2_sponge_trace = generate_poseidon2_sponge_trace(&record.executed); let poseidon2_output_bytes = generate_poseidon2_output_bytes_trace(&poseidon2_sponge_trace); let memory_rows = generate_memory_trace::( @@ -160,6 +161,7 @@ mod tests { &event_tape_rows, &events_commitment_tape_rows, &cast_list_commitment_tape_rows, + &self_prog_id_tape_rows, &poseidon2_sponge_trace, &poseidon2_output_bytes, ); @@ -175,6 +177,7 @@ mod tests { &event_tape_rows, &events_commitment_tape_rows, &cast_list_commitment_tape_rows, + &self_prog_id_tape_rows, ®ister_init, ); let trace = generate_rangecheck_trace::( diff --git a/circuits/src/rangecheck_u8/generation.rs b/circuits/src/rangecheck_u8/generation.rs index 131b5783e..18f0f9fe1 100644 --- a/circuits/src/rangecheck_u8/generation.rs +++ b/circuits/src/rangecheck_u8/generation.rs @@ -53,7 +53,7 @@ mod tests { use crate::generation::storage_device::{ generate_call_tape_trace, generate_cast_list_commitment_tape_trace, generate_event_tape_trace, generate_events_commitment_tape_trace, - generate_private_tape_trace, generate_public_tape_trace, + generate_private_tape_trace, generate_public_tape_trace, generate_self_prog_id_tape_trace, }; use crate::ops; use crate::poseidon2_output_bytes::generation::generate_poseidon2_output_bytes_trace; @@ -94,6 +94,7 @@ mod tests { let events_commitment_tape_rows = generate_events_commitment_tape_trace(&record.executed); let cast_list_commitment_tape_rows = generate_cast_list_commitment_tape_trace(&record.executed); + let self_prog_id_tape_rows = generate_self_prog_id_tape_trace(&record.executed); let poseidon2_sponge_trace = generate_poseidon2_sponge_trace(&record.executed); let poseidon2_output_bytes = generate_poseidon2_output_bytes_trace(&poseidon2_sponge_trace); let memory_rows = generate_memory_trace::( @@ -108,6 +109,7 @@ mod tests { &event_tape_rows, &events_commitment_tape_rows, &cast_list_commitment_tape_rows, + &self_prog_id_tape_rows, &poseidon2_sponge_trace, &poseidon2_output_bytes, ); @@ -123,6 +125,7 @@ mod tests { &event_tape_rows, &events_commitment_tape_rows, &cast_list_commitment_tape_rows, + &self_prog_id_tape_rows, ®ister_init, ); let rangecheck_rows = generate_rangecheck_trace::( diff --git a/circuits/src/register/generation.rs b/circuits/src/register/generation.rs index d36284710..ce04ea458 100644 --- a/circuits/src/register/generation.rs +++ b/circuits/src/register/generation.rs @@ -95,6 +95,7 @@ pub fn generate_register_trace( mem_event_tape: &[StorageDevice], mem_events_commitment_tape: &[StorageDevice], mem_cast_list_commitment_tape: &[StorageDevice], + mem_self_prog_id_tape: &[StorageDevice], reg_init: &[RegisterInit], ) -> ( Vec>, @@ -116,6 +117,7 @@ pub fn generate_register_trace( TableKind::EventsCommitmentTape => extract(mem_events_commitment_tape, &looking_table), TableKind::CastListCommitmentTape => extract(mem_cast_list_commitment_tape, &looking_table), + TableKind::SelfProgIdTape => extract(mem_self_prog_id_tape, &looking_table), TableKind::RegisterInit => extract(reg_init, &looking_table), TableKind::Poseidon2Sponge => extract(poseidon2_sponge, &looking_table), // We are trying to build the Register tables, so we don't have the values to extract. @@ -181,7 +183,7 @@ mod tests { use crate::generation::storage_device::{ generate_call_tape_trace, generate_cast_list_commitment_tape_trace, generate_event_tape_trace, generate_events_commitment_tape_trace, - generate_private_tape_trace, generate_public_tape_trace, + generate_private_tape_trace, generate_public_tape_trace, generate_self_prog_id_tape_trace, }; use crate::poseidon2_sponge; use crate::test_utils::prep_table; @@ -229,6 +231,7 @@ mod tests { let events_commitment_tape_rows = generate_events_commitment_tape_trace(&record.executed); let cast_list_commitment_tape_rows = generate_cast_list_commitment_tape_trace(&record.executed); + let self_prog_id_tape_rows = generate_self_prog_id_tape_trace(&record.executed); let poseidon2_sponge_trace = poseidon2_sponge::generation::generate_poseidon2_sponge_trace(&record.executed); @@ -244,6 +247,7 @@ mod tests { &event_tape, &events_commitment_tape_rows, &cast_list_commitment_tape_rows, + &self_prog_id_tape_rows, ®ister_init, ); diff --git a/circuits/src/stark/batch_prover.rs b/circuits/src/stark/batch_prover.rs index 5805dc839..e7c497278 100644 --- a/circuits/src/stark/batch_prover.rs +++ b/circuits/src/stark/batch_prover.rs @@ -3,9 +3,9 @@ use std::collections::HashMap; use anyhow::{ensure, Result}; -use itertools::Itertools; +use itertools::{chain, Itertools}; use log::Level::Debug; -use log::{debug, info, log_enabled}; +use log::{debug, log_enabled}; use mozak_runner::elf::Program; use mozak_runner::vm::ExecutionRecord; use plonky2::field::extension::Extendable; @@ -39,6 +39,60 @@ use crate::stark::permutation::challenge::GrandProductChallengeTrait; use crate::stark::poly::compute_quotient_polys; use crate::stark::prover::prove_single_table; +#[derive(Debug)] +pub struct BatchFriOracleIndices { + poly_count: TableKindArray, + // start index in BatchFriOracle's field merkle tree leaves + fmt_start_indices: TableKindArray>, + // start index in BatchFriOracle's polynomial vector + poly_start_indices: TableKindArray>, + // degree bits (leaf layer) index in BatchFriOrable's field merkle tree + degree_bits_indices: TableKindArray>, +} + +#[allow(unused_assignments)] +impl BatchFriOracleIndices { + fn new( + public_table_kinds: &[TableKind], + poly_count: TableKindArray, + degree_bits: &TableKindArray, + ) -> Self { + let sorted_degree_bits = sort_degree_bits(public_table_kinds, degree_bits); + + let mut poly_start_indices = + all_kind!(|kind| (!public_table_kinds.contains(&kind)).then_some(0)); + let mut fmt_start_indices = + all_kind!(|kind| (!public_table_kinds.contains(&kind)).then_some(0)); + let mut poly_start_index = 0; + for deg in &sorted_degree_bits { + let mut fmt_start_index = 0; + all_kind!(|kind| { + if !public_table_kinds.contains(&kind) && degree_bits[kind] == *deg { + fmt_start_indices[kind] = Some(fmt_start_index); + poly_start_indices[kind] = Some(poly_start_index); + fmt_start_index += poly_count[kind]; + poly_start_index += poly_count[kind]; + } + }); + } + + let degree_bits_index_map: HashMap = sorted_degree_bits + .into_iter() + .enumerate() + .map(|(index, value)| (value, index)) + .collect(); + let degree_bits_indices = all_kind!(|kind| (!public_table_kinds.contains(&kind)) + .then_some(degree_bits_index_map[°ree_bits[kind]])); + + BatchFriOracleIndices { + poly_count, + fmt_start_indices, + poly_start_indices, + degree_bits_indices, + } + } +} + pub(crate) fn sort_degree_bits( public_table_kinds: &[TableKind], degree_bits: &TableKindArray, @@ -181,8 +235,10 @@ where } let rate_bits = config.fri_config.rate_bits; let cap_height = config.fri_config.cap_height; - let degree_bits = all_kind!(|kind| log2_strict(traces_poly_values[kind][0].len())); + let traces_poly_count = all_kind!(|kind| traces_poly_values[kind].len()); + let trace_indices = + BatchFriOracleIndices::new(public_table_kinds, traces_poly_count, °ree_bits); let batch_traces_poly_values = all_kind!(|kind| if public_table_kinds.contains(&kind) { None @@ -195,9 +251,11 @@ where .filter_map(|t| *t) .flat_map(std::clone::Clone::clone) .collect(); - batch_trace_polys.sort_by_key(|b| std::cmp::Reverse(b.len())); - let batch_trace_polys_len = batch_trace_polys.len(); + batch_trace_polys.sort_by_key(|p| std::cmp::Reverse(p.len())); + // This commitment is for all tables but public tables, in form of Field Merkle + // Tree (1st oracle) + let batch_trace_polys_len = batch_trace_polys.len(); let batch_trace_commitments: BatchFriOracle = timed!( timing, "Compute trace commitments for batch tables", @@ -211,37 +269,40 @@ where ) ); - // TODO: only need for public tables + // This commitment is for public tables, and would have separate oracle. let trace_commitments = timed!( timing, - "Compute trace commitments for each table", + "Compute trace commitments for public tables", traces_poly_values .clone() .with_kind() .map(|(trace, table)| { - timed!( - timing, - &format!("compute trace commitment for {table:?}"), - PolynomialBatch::::from_values( - trace.clone(), - rate_bits, - false, - cap_height, + public_table_kinds.contains(&table).then(|| { + timed!( timing, - None, + &format!("compute trace commitment for {table:?}"), + PolynomialBatch::::from_values( + trace.clone(), + rate_bits, + false, + cap_height, + timing, + None, + ) ) - ) + }) }) ); - let trace_caps = trace_commitments - .each_ref() - .map(|c| c.merkle_tree.cap.clone()); + let trace_caps = all_kind!(|kind| trace_commitments[kind] + .as_ref() + .map(|c| c.merkle_tree.cap.clone())); + // Add trace commitments to the challenger entropy pool. let mut challenger = Challenger::::new(); all_kind!(|kind| { - if public_table_kinds.contains(&kind) { - challenger.observe_cap(&trace_caps[kind]); + if let Some(c) = trace_caps[kind].clone() { + challenger.observe_cap(&c); } }); let fmt_trace_cap = batch_trace_commitments.field_merkle_tree.cap.clone(); @@ -273,6 +334,7 @@ where °ree_bits, &traces_poly_values, &trace_commitments, + &trace_indices, &batch_trace_commitments, &ctl_data_per_table, &public_sub_table_data_per_table, @@ -280,8 +342,8 @@ where timing, )?; - let program_rom_trace_cap = trace_caps[TableKind::Program].clone(); - let elf_memory_init_trace_cap = trace_caps[TableKind::ElfMemoryInit].clone(); + let program_rom_trace_cap = trace_caps[TableKind::Program].clone().unwrap(); + let elf_memory_init_trace_cap = trace_caps[TableKind::ElfMemoryInit].clone().unwrap(); if log_enabled!(Debug) { timing.print(); } @@ -310,7 +372,8 @@ pub fn batch_prove_with_commitments( public_inputs: &PublicInputs, degree_bits: &TableKindArray, traces_poly_values: &TableKindArray>>, - trace_commitments: &TableKindArray>, + trace_commitments: &TableKindArray>>, + trace_indices: &BatchFriOracleIndices, batch_trace_commitments: &BatchFriOracle, ctl_data_per_table: &TableKindArray>, public_sub_data_per_table: &TableKindArray>, @@ -331,14 +394,15 @@ where } .build(); - let separate_proofs = all_starks!(mozak_stark, |stark, kind| if public_table_kinds - .contains(&kind) + // Computes separate proofs for each public table. + let separate_proofs = all_starks!(mozak_stark, |stark, kind| if let Some(trace_commitment) = + &trace_commitments[kind] { Some(prove_single_table( stark, config, &traces_poly_values[kind], - &trace_commitments[kind], + trace_commitment, public_inputs[kind], &ctl_data_per_table[kind], &public_sub_data_per_table[kind], @@ -349,6 +413,8 @@ where None }); + // Computing ctl zs polynomials for all but those for public tables + let mut ctl_zs_poly_count = all_kind!(|_kind| 0); let all_ctl_z_polys = all_kind!(|kind| { if public_table_kinds.contains(&kind) { None @@ -369,17 +435,16 @@ where assert!(!z_polys.is_empty()); - info!( - "ctl_data_per_table len {}", - ctl_data_per_table[kind].zs_columns.len() - ); - info!("z_poly len {}", z_polys.len()); + ctl_zs_poly_count[kind] = z_polys.len(); z_polys }) } }); + let ctl_zs_indices = + BatchFriOracleIndices::new(public_table_kinds, ctl_zs_poly_count, degree_bits); + // TODO: can we remove duplicates in the ctl polynomials? let mut batch_ctl_z_polys: Vec<_> = all_ctl_z_polys .iter() @@ -389,6 +454,9 @@ where batch_ctl_z_polys.sort_by_key(|b| std::cmp::Reverse(b.len())); let batch_ctl_zs_polys_len = batch_ctl_z_polys.len(); + // Commitment to all ctl_zs polynomials, except for those of public tables. + // Field Merkle tree is used as oracle here, same as we did for batched traces. + // (2nd FMT Oracle) let batch_ctl_zs_commitments: BatchFriOracle = timed!( timing, "compute batch Zs commitment", @@ -402,39 +470,51 @@ where ) ); - // TODO: remove it - let ctl_zs_commitments = all_starks!(mozak_stark, |stark, kind| timed!( - timing, - format!("{stark}: compute Zs commitment").as_str(), - all_ctl_z_polys[kind] - .as_ref() - .map(|poly| PolynomialBatch::::from_values( - poly.clone(), - rate_bits, - false, - config.fri_config.cap_height, - timing, - None, - )) - )); - let ctl_zs_cap = batch_ctl_zs_commitments.field_merkle_tree.cap.clone(); challenger.observe_cap(&ctl_zs_cap); let alphas = challenger.get_n_challenges(config.num_challenges); + let sorted_degree_bits = sort_degree_bits(public_table_kinds, degree_bits); - // TODO: we should be able to compute `quotient_polys` from - // `batch_trace_commitments` and `batch_ctl_zs_commitments`. + let mut quotient_poly_count = all_kind!(|_kind| 0); let quotient_chunks = all_starks!(mozak_stark, |stark, kind| { - if let Some(ctl_zs_commitment) = ctl_zs_commitments[kind].as_ref() { + if public_table_kinds.contains(&kind) { + None + } else { let degree = 1 << degree_bits[kind]; + + let degree_bits_index = trace_indices.degree_bits_indices[kind].unwrap(); + let trace_slice_start = trace_indices.fmt_start_indices[kind].unwrap(); + let trace_slice_len = trace_indices.poly_count[kind]; + let get_trace_values_packed = |i_start, step| -> Vec<::Packing> { + batch_trace_commitments.get_lde_values_packed( + degree_bits_index, + i_start, + step, + trace_slice_start, + trace_slice_len, + ) + }; + + let ctl_zs_slice_start = ctl_zs_indices.fmt_start_indices[kind].unwrap(); + let ctl_zs_slice_len = ctl_zs_indices.poly_count[kind]; + let get_ctl_zs_values_packed = |i_start, step| -> Vec<::Packing> { + batch_ctl_zs_commitments.get_lde_values_packed( + degree_bits_index, + i_start, + step, + ctl_zs_slice_start, + ctl_zs_slice_len, + ) + }; + let quotient_polys = timed!( timing, format!("{stark}: compute quotient polynomial").as_str(), compute_quotient_polys::::Packing, C, _, D>( stark, - &trace_commitments[kind], - ctl_zs_commitment, + &get_trace_values_packed, + &get_ctl_zs_values_packed, public_inputs[kind], &ctl_data_per_table[kind], &public_sub_data_per_table[kind], @@ -461,12 +541,14 @@ where }) .collect() ); + quotient_poly_count[kind] = quotient_chunks.len(); Some(quotient_chunks) - } else { - None } }); + let quotient_indices = + BatchFriOracleIndices::new(public_table_kinds, quotient_poly_count, degree_bits); + let mut batch_quotient_chunks: Vec<_> = quotient_chunks .iter() .filter_map(|t| t.as_ref()) @@ -475,22 +557,8 @@ where batch_quotient_chunks.sort_by_key(|b| std::cmp::Reverse(b.len())); let batch_quotient_chunks_len = batch_quotient_chunks.len(); - // TODO: remove it - let quotient_commitments = all_starks!(mozak_stark, |stark, kind| timed!( - timing, - format!("{stark}: compute quotient commitment").as_str(), - quotient_chunks[kind] - .as_ref() - .map(|poly| PolynomialBatch::::from_coeffs( - poly.clone(), - rate_bits, - false, - config.fri_config.cap_height, - timing, - None, - )) - )); - + // Commitment to quotient polynomials for all except those of public tables. + // Stored as Field merkle tree (3rd and final FMT oracle) let batch_quotient_commitments: BatchFriOracle = timed!( timing, "compute batch Zs commitment", @@ -509,40 +577,44 @@ where let zeta = challenger.get_extension_challenge::(); - // TODO: compute `openings` from `batch_trace_commitments` and - // `batch_ctl_zs_commitments`. - let batch_openings = all_starks!(mozak_stark, |_stark, kind| if let Some(ctl_zs_commitment) = - ctl_zs_commitments[kind].as_ref() + // Sets up batched fri instance for all tables but the public tables. + let batch_openings = all_starks!(mozak_stark, |_stark, kind| if public_table_kinds + .contains(&kind) { - if let Some(quotient_commitment) = quotient_commitments[kind].as_ref() { - // To avoid leaking witness data, we want to ensure that our opening locations, - // `zeta` and `g * zeta`, are not in our subgroup `H`. It suffices to check - // `zeta` only, since `(g * zeta)^n = zeta^n`, where `n` is the order of - // `g`. - let g = F::primitive_root_of_unity(degree_bits[kind]); - ensure!( - zeta.exp_power_of_2(degree_bits[kind]) != F::Extension::ONE, - "Opening point is in the subgroup." - ); - let openings = StarkOpeningSet::new( - zeta, - g, - &trace_commitments[kind], - ctl_zs_commitment, - quotient_commitment, - degree_bits[kind], - ); - - challenger.observe_openings(&openings.to_fri_openings()); - Some(openings) - } else { - None - } - } else { None - }); + } else { + // To avoid leaking witness data, we want to ensure that our opening locations, + // `zeta` and `g * zeta`, are not in our subgroup `H`. It suffices to check + // `zeta` only, since `(g * zeta)^n = zeta^n`, where `n` is the order of + // `g`. + let g = F::primitive_root_of_unity(degree_bits[kind]); + ensure!( + zeta.exp_power_of_2(degree_bits[kind]) != F::Extension::ONE, + "Opening point is in the subgroup." + ); - let sorted_degree_bits = sort_degree_bits(public_table_kinds, degree_bits); + let openings = StarkOpeningSet::batch_new( + zeta, + g, + [ + trace_indices.poly_start_indices[kind].unwrap(), + ctl_zs_indices.poly_start_indices[kind].unwrap(), + quotient_indices.poly_start_indices[kind].unwrap(), + ], + [ + trace_indices.poly_count[kind], + ctl_zs_indices.poly_count[kind], + quotient_indices.poly_count[kind], + ], + batch_trace_commitments, + &batch_ctl_zs_commitments, + &batch_quotient_commitments, + degree_bits[kind], + ); + + challenger.observe_openings(&openings.to_fri_openings()); + Some(openings) + }); let num_ctl_zs_per_table = all_kind!(|kind| ctl_data_per_table[kind].len() + public_sub_data_per_table[kind].len()); @@ -638,22 +710,31 @@ pub(crate) fn batch_reduction_arity_bits( rate_bits: usize, cap_height: usize, ) -> Vec { - let mut result = Vec::new(); - let arity_bits = 3; - let mut cur_index = 0; - let mut cur_degree_bits = degree_bits[cur_index]; - while cur_degree_bits + rate_bits >= cap_height + arity_bits { - let mut cur_arity_bits = arity_bits; - let target_degree_bits = cur_degree_bits - arity_bits; - if cur_index < degree_bits.len() - 1 && target_degree_bits < degree_bits[cur_index + 1] { - cur_arity_bits = cur_degree_bits - degree_bits[cur_index + 1]; - cur_index += 1; - } - result.push(cur_arity_bits); - assert!(cur_degree_bits >= cur_arity_bits); - cur_degree_bits -= cur_arity_bits; - } - result + let default_arity_bits = 3; + let final_poly_bits = 5; + let lowest_degree_bits = degree_bits.last().unwrap(); + assert!(lowest_degree_bits + rate_bits >= cap_height); + // First, let's figure out our intermediate degree bits. + let intermediate_degree_bits = + degree_bits + .iter() + .tuple_windows() + .flat_map(|(°ree_bit, &next_degree_bit)| { + (next_degree_bit + 1..=degree_bit) + .rev() + .step_by(default_arity_bits) + }); + // Next, deal with the last part. + let last_degree_bits = + (lowest_degree_bits + rate_bits).min(cap_height.max(final_poly_bits)) - rate_bits; + let final_degree_bits = (last_degree_bits..=*lowest_degree_bits) + .rev() + .step_by(default_arity_bits); + // Finally, the reduction arity bits are just the differences: + chain!(intermediate_degree_bits, final_degree_bits) + .tuple_windows() + .map(|(degree_bit, next_degree_bit)| degree_bit - next_degree_bit) + .collect() } #[cfg(test)] @@ -663,13 +744,42 @@ mod tests { use plonky2::plonk::config::{GenericConfig, PoseidonGoldilocksConfig}; use plonky2::util::timing::TimingTree; - use crate::stark::batch_prover::batch_prove; + use crate::stark::batch_prover::{batch_prove, batch_reduction_arity_bits}; use crate::stark::batch_verifier::batch_verify_proof; use crate::stark::mozak_stark::{MozakStark, PublicInputs, TableKind}; use crate::stark::proof::BatchProof; use crate::test_utils::fast_test_config; use crate::utils::from_u32; + #[test] + fn reduction_arity_bits_in_batch_proving() { + let degree_bits = vec![15, 8, 6, 5, 3]; + let rate_bits = 2; + let cap_height = 0; + let expected_res = vec![3, 3, 1, 2, 1, 2]; + assert_eq!( + expected_res, + batch_reduction_arity_bits(°ree_bits, rate_bits, cap_height) + ); + + let rate_bits = 1; + let cap_height = 4; + let expected_res = vec![3, 3, 1, 2, 1, 2]; + assert_eq!( + expected_res, + batch_reduction_arity_bits(°ree_bits, rate_bits, cap_height) + ); + } + + #[test] + #[should_panic(expected = "assertion failed")] + fn bad_reduction_arity_bits_in_batch_proving() { + let degree_bits = vec![8, 6, 5, 3]; + let rate_bits = 2; + let cap_height = 6; + batch_reduction_arity_bits(°ree_bits, rate_bits, cap_height); + } + #[test] fn batch_prove_add() { const D: usize = 2; diff --git a/circuits/src/stark/batch_verifier.rs b/circuits/src/stark/batch_verifier.rs index 4912218b5..12b182e4f 100644 --- a/circuits/src/stark/batch_verifier.rs +++ b/circuits/src/stark/batch_verifier.rs @@ -127,6 +127,7 @@ where all_starks!(mozak_stark, |stark, kind| { if public_table_kinds.contains(&kind) { if let Some(challenges) = &stark_challenges[kind] { + // Verifying public tables proof, including individual FRI proof verify_stark_proof_with_challenges( stark, &all_proof.proofs[kind], @@ -139,6 +140,8 @@ where ensure!(false); } } else { + // Verifying quotient polynomials of the batched stark proof (for all starks but + // public starks). Batched FRI proof for the openings to be done later. verify_quotient_polynomials( stark, degree_bits[kind], diff --git a/circuits/src/stark/mozak_stark.rs b/circuits/src/stark/mozak_stark.rs index fa0898402..5125ffc95 100644 --- a/circuits/src/stark/mozak_stark.rs +++ b/circuits/src/stark/mozak_stark.rs @@ -101,16 +101,6 @@ pub struct MozakStark, const D: usize> { pub memory_stark: MemoryStark, #[StarkSet(stark_kind = "ElfMemoryInit")] pub elf_memory_init_stark: MemoryInitStark, - #[StarkSet(stark_kind = "CallTapeInit")] - pub call_tape_init_stark: MemoryInitStark, - #[StarkSet(stark_kind = "PrivateTapeInit")] - pub private_tape_init_stark: MemoryInitStark, - #[StarkSet(stark_kind = "PublicTapeInit")] - pub public_tape_init_stark: MemoryInitStark, - #[StarkSet(stark_kind = "EventTapeInit")] - pub event_tape_init_stark: MemoryInitStark, - #[StarkSet(stark_kind = "MozakMemoryInit")] - pub mozak_memory_init_stark: MemoryInitStark, // TODO(Bing): find a way to natively constrain zero initializations within // the `MemoryStark`, instead of relying on a CTL between this and the // `MemoryStark`. @@ -138,6 +128,8 @@ pub struct MozakStark, const D: usize> { // a fixed size version of this STARK. #[StarkSet(stark_kind = "CastListCommitmentTape")] pub cast_list_commitment_tape_stark: StorageDeviceStark, + #[StarkSet(stark_kind = "SelfProgIdTape")] + pub self_prog_id_tape_stark: StorageDeviceStark, #[StarkSet(stark_kind = "RegisterInit")] pub register_init_stark: RegisterInitStark, #[StarkSet(stark_kind = "Register")] @@ -431,11 +423,6 @@ impl, const D: usize> Default for MozakStark program_mult_stark: ProgramMultStark::default(), memory_stark: MemoryStark::default(), elf_memory_init_stark: MemoryInitStark::default(), - call_tape_init_stark: MemoryInitStark::default(), - private_tape_init_stark: MemoryInitStark::default(), - public_tape_init_stark: MemoryInitStark::default(), - event_tape_init_stark: MemoryInitStark::default(), - mozak_memory_init_stark: MemoryInitStark::default(), memory_zeroinit_stark: MemoryZeroInitStark::default(), rangecheck_u8_stark: RangeCheckU8Stark::default(), halfword_memory_stark: HalfWordMemoryStark::default(), @@ -450,6 +437,7 @@ impl, const D: usize> Default for MozakStark event_tape_stark: StorageDeviceStark::default(), events_commitment_tape_stark: StorageDeviceStark::default(), cast_list_commitment_tape_stark: StorageDeviceStark::default(), + self_prog_id_tape_stark: StorageDeviceStark::default(), poseidon2_sponge_stark: Poseidon2SpongeStark::default(), poseidon2_stark: Poseidon2_12Stark::default(), poseidon2_output_bytes_stark: Poseidon2OutputBytesStark::default(), @@ -606,11 +594,6 @@ table_impl!(ProgramTable, TableKind::Program, ProgramRom); table_impl!(ProgramMultTable, TableKind::ProgramMult, ProgramMult); table_impl!(MemoryTable, TableKind::Memory, Memory); table_impl!(ElfMemoryInitTable, TableKind::ElfMemoryInit, MemoryInit); -table_impl!(CallTapeInitTable, TableKind::CallTapeInit, MemoryInit); -table_impl!(PrivateTapeInitTable, TableKind::PrivateTapeInit, MemoryInit); -table_impl!(PublicTapeInitTable, TableKind::PublicTapeInit, MemoryInit); -table_impl!(EventTapeInitTable, TableKind::EventTapeInit, MemoryInit); -table_impl!(MozakMemoryInitTable, TableKind::MozakMemoryInit, MemoryInit); table_impl!( MemoryZeroInitTable, TableKind::MemoryZeroInit, @@ -661,6 +644,11 @@ table_impl!( TableKind::CastListCommitmentTape, StorageDevice ); +table_impl!( + SelfProgIdTapeTable, + TableKind::SelfProgIdTape, + StorageDevice +); table_impl!( Poseidon2SpongeTable, TableKind::Poseidon2Sponge, @@ -751,7 +739,8 @@ impl Lookups for IntoMemoryTable { TableKind::CallTape, TableKind::EventTape, TableKind::EventsCommitmentTape, - TableKind::CastListCommitmentTape + TableKind::CastListCommitmentTape, + TableKind::SelfProgIdTape, ] .map(storage_device::columns::lookup_for_memory), memory_fullword::columns::lookup_for_memory_limb(), @@ -773,11 +762,6 @@ impl Lookups for MemoryInitMemoryTable { CrossTableLookupWithTypedOutput::new( vec![ memoryinit::columns::lookup_for_memory(ElfMemoryInitTable::new), - memoryinit::columns::lookup_for_memory(MozakMemoryInitTable::new), - memoryinit::columns::lookup_for_memory(CallTapeInitTable::new), - memoryinit::columns::lookup_for_memory(PrivateTapeInitTable::new), - memoryinit::columns::lookup_for_memory(PublicTapeInitTable::new), - memoryinit::columns::lookup_for_memory(EventTapeInitTable::new), memory_zeroinit::columns::lookup_for_memory(), ], vec![memory::columns::lookup_for_memoryinit()], @@ -907,6 +891,7 @@ impl Lookups for StorageDeviceToCpuTable { TableKind::EventTape, TableKind::EventsCommitmentTape, TableKind::CastListCommitmentTape, + TableKind::SelfProgIdTape, ], 0.. ) diff --git a/circuits/src/stark/poly.rs b/circuits/src/stark/poly.rs index e62e73322..c7c33d9cd 100644 --- a/circuits/src/stark/poly.rs +++ b/circuits/src/stark/poly.rs @@ -4,7 +4,6 @@ use plonky2::field::extension::{Extendable, FieldExtension}; use plonky2::field::packed::PackedField; use plonky2::field::polynomial::{PolynomialCoeffs, PolynomialValues}; use plonky2::field::zero_poly_coset::ZeroPolyOnCoset; -use plonky2::fri::oracle::PolynomialBatch; use plonky2::hash::hash_types::RichField; use plonky2::plonk::circuit_builder::CircuitBuilder; use plonky2::plonk::config::GenericConfig; @@ -23,10 +22,10 @@ use crate::cross_table_lookup::{ /// Computes the quotient polynomials `(sum alpha^i C_i(x)) / Z_H(x)` for /// `alpha` in `alphas`, where the `C_i`s are the Stark constraints. -pub fn compute_quotient_polys<'a, F, P, C, S, const D: usize>( +pub fn compute_quotient_polys( stark: &S, - trace_commitment: &'a PolynomialBatch, - ctl_zs_commitment: &'a PolynomialBatch, + get_trace_values_packed: &(dyn Fn(usize, usize) -> Vec

+ Sync + Send), + get_ctl_zs_values_packed: &(dyn Fn(usize, usize) -> Vec

+ Sync + Send), public_inputs: &[F], ctl_data: &CtlData, public_sub_table_data: &CtlData, @@ -60,10 +59,6 @@ where let z_h_on_coset = ZeroPolyOnCoset::::new(degree_bits, quotient_degree_bits); - // Retrieve the LDE values at index `i`. - let get_trace_values_packed = - |i_start| -> Vec

{ trace_commitment.get_lde_values_packed(i_start, step) }; - // Last element of the subgroup. let last = F::primitive_root_of_unity(degree_bits).inverse(); let size = degree << quotient_degree_bits; @@ -94,8 +89,8 @@ where lagrange_basis_last, ); let vars = StarkEvaluationFrame::from_values( - &get_trace_values_packed(i_start), - &get_trace_values_packed(i_next_start), + &get_trace_values_packed(i_start, step), + &get_trace_values_packed(i_next_start, step), public_inputs, ); let public_sub_table_data_chain = public_sub_table_data.zs_columns.as_slice(); @@ -105,8 +100,8 @@ where .chain(public_sub_table_data_chain.iter()) .enumerate() .map(|(i, zs_columns)| CtlCheckVars:: { - local_z: ctl_zs_commitment.get_lde_values_packed(i_start, step)[i], - next_z: ctl_zs_commitment.get_lde_values_packed(i_next_start, step)[i], + local_z: get_ctl_zs_values_packed(i_start, step)[i], + next_z: get_ctl_zs_values_packed(i_next_start, step)[i], challenges: zs_columns.challenge, columns: &zs_columns.columns, filter_column: &zs_columns.filter_column, diff --git a/circuits/src/stark/proof.rs b/circuits/src/stark/proof.rs index 8c010e76e..7ff53e1f7 100644 --- a/circuits/src/stark/proof.rs +++ b/circuits/src/stark/proof.rs @@ -1,5 +1,6 @@ use itertools::{chain, Itertools}; use plonky2::field::extension::{Extendable, FieldExtension}; +use plonky2::fri::batch_oracle::BatchFriOracle; use plonky2::fri::oracle::PolynomialBatch; use plonky2::fri::proof::{FriChallenges, FriChallengesTarget, FriProof, FriProofTarget}; use plonky2::fri::structure::{ @@ -251,6 +252,44 @@ impl, const D: usize> StarkOpeningSet { } } + #[allow(clippy::too_many_arguments)] + pub fn batch_new>( + zeta: F::Extension, + g: F, + poly_start: [usize; 3], + poly_count: [usize; 3], + trace_commitment: &BatchFriOracle, + ctl_zs_commitment: &BatchFriOracle, + quotient_commitment: &BatchFriOracle, + degree_bits: usize, + ) -> Self { + let eval_commitment = |z: F::Extension, c: &BatchFriOracle, index| { + c.polynomials[poly_start[index]..poly_start[index] + poly_count[index]] + .par_iter() + .map(|p| p.to_extension().eval(z)) + .collect::>() + }; + let eval_commitment_base = |z: F, c: &BatchFriOracle, index| { + c.polynomials[poly_start[index]..poly_start[index] + poly_count[index]] + .par_iter() + .map(|p| p.eval(z)) + .collect::>() + }; + let zeta_next = zeta.scalar_mul(g); + Self { + local_values: eval_commitment(zeta, trace_commitment, 0), + next_values: eval_commitment(zeta_next, trace_commitment, 0), + ctl_zs: eval_commitment(zeta, ctl_zs_commitment, 1), + ctl_zs_next: eval_commitment(zeta_next, ctl_zs_commitment, 1), + ctl_zs_last: eval_commitment_base( + F::primitive_root_of_unity(degree_bits).inverse(), + ctl_zs_commitment, + 1, + ), + quotient_polys: eval_commitment(zeta, quotient_commitment, 2), + } + } + pub(crate) fn to_fri_openings(&self) -> FriOpenings { let zeta_batch = FriOpeningBatch { values: chain!(&self.local_values, &self.ctl_zs, &self.quotient_polys) diff --git a/circuits/src/stark/prover.rs b/circuits/src/stark/prover.rs index 4ad3ec026..76df820d5 100644 --- a/circuits/src/stark/prover.rs +++ b/circuits/src/stark/prover.rs @@ -55,16 +55,23 @@ where F: RichField + Extendable, C: GenericConfig, { debug!("Starting Prove"); - let traces_poly_values = timed!( timing, - "Generate Traces", + "Generate traces", generate_traces(program, record, timing) ); debug!("Done with Trace Generation"); if mozak_stark.debug || std::env::var("MOZAK_STARK_DEBUG").is_ok() { - debug_traces(&traces_poly_values, mozak_stark, &public_inputs); - debug_ctl(&traces_poly_values, mozak_stark); + timed!( + timing, + "Mozak stark debug", + debug_traces(&traces_poly_values, mozak_stark, &public_inputs) + ); + timed!( + timing, + "Mozak CTL debug", + debug_ctl(&traces_poly_values, mozak_stark) + ); } timed!( timing, @@ -233,13 +240,22 @@ where challenger.observe_cap(&ctl_zs_cap); let alphas = challenger.get_n_challenges(config.num_challenges); + + let get_trace_values_packed = |i_start, step| -> Vec<::Packing> { + trace_commitment.get_lde_values_packed(i_start, step) + }; + + let get_ctl_zs_values_packed = |i_start, step| -> Vec<::Packing> { + ctl_zs_commitment.get_lde_values_packed(i_start, step) + }; + let quotient_polys = timed!( timing, format!("{stark}: compute quotient polynomial").as_str(), compute_quotient_polys::::Packing, C, S, D>( stark, - trace_commitment, - &ctl_zs_commitment, + &get_trace_values_packed, + &get_ctl_zs_values_packed, public_inputs, ctl_data, public_sub_table_data, diff --git a/circuits/src/stark/recursive_verifier.rs b/circuits/src/stark/recursive_verifier.rs index 801c0ac8d..eaf5b4997 100644 --- a/circuits/src/stark/recursive_verifier.rs +++ b/circuits/src/stark/recursive_verifier.rs @@ -6,7 +6,7 @@ use std::marker::PhantomData; use anyhow::Result; use itertools::{zip_eq, Itertools}; use log::info; -use mozak_sdk::core::ecall::COMMITMENT_SIZE; +use mozak_sdk::core::constants::DIGEST_BYTES; use plonky2::field::extension::Extendable; use plonky2::field::types::Field; use plonky2::fri::witness_util::set_fri_proof_target; @@ -63,8 +63,8 @@ pub struct VMRecursiveProofPublicInputs { pub entry_point: T, pub program_trace_cap: [[T; NUM_HASH_OUT_ELTS]; VM_RECURSION_CONFIG_NUM_CAPS], pub elf_memory_init_trace_cap: [[T; NUM_HASH_OUT_ELTS]; VM_RECURSION_CONFIG_NUM_CAPS], - pub event_commitment_tape: [T; COMMITMENT_SIZE], - pub castlist_commitment_tape: [T; COMMITMENT_SIZE], + pub event_commitment_tape: [T; DIGEST_BYTES], + pub castlist_commitment_tape: [T; DIGEST_BYTES], } columns_view_impl!(VMRecursiveProofPublicInputs); @@ -652,7 +652,7 @@ mod tests { use log::info; use mozak_runner::code; use mozak_runner::instruction::{Args, Instruction, Op}; - use mozak_sdk::core::ecall::COMMITMENT_SIZE; + use mozak_sdk::core::constants::DIGEST_BYTES; use plonky2::field::goldilocks_field::GoldilocksField; use plonky2::iop::witness::{PartialWitness, WitnessWrite}; use plonky2::plonk::circuit_builder::CircuitBuilder; @@ -718,8 +718,8 @@ mod tests { let recursive_proof = mozak_stark_circuit.prove(&mozak_proof)?; let public_input_slice: [F; VM_PUBLIC_INPUT_SIZE] = recursive_proof.public_inputs.as_slice().try_into().unwrap(); - let expected_event_commitment_tape = [F::ZERO; COMMITMENT_SIZE]; - let expected_castlist_commitment_tape = [F::ZERO; COMMITMENT_SIZE]; + let expected_event_commitment_tape = [F::ZERO; DIGEST_BYTES]; + let expected_castlist_commitment_tape = [F::ZERO; DIGEST_BYTES]; let recursive_proof_public_inputs: &VMRecursiveProofPublicInputs = &public_input_slice.into(); assert_eq!( diff --git a/circuits/src/storage_device/columns.rs b/circuits/src/storage_device/columns.rs index c4c55a7a3..281531b11 100644 --- a/circuits/src/storage_device/columns.rs +++ b/circuits/src/storage_device/columns.rs @@ -1,6 +1,6 @@ use core::ops::Add; -use mozak_sdk::core::ecall::COMMITMENT_SIZE; +use mozak_sdk::core::constants::DIGEST_BYTES; use mozak_sdk::core::reg_abi::REG_A1; use crate::columns_view::{columns_view_impl, make_col_map, NumberOfColumns}; @@ -8,7 +8,7 @@ use crate::cross_table_lookup::{Column, ColumnWithTypedInput}; use crate::memory::columns::MemoryCtl; use crate::register::RegisterCtl; use crate::stark::mozak_stark::{ - CallTapeTable, CastListCommitmentTapeTable, EventsCommitmentTapeTable, + CallTapeTable, CastListCommitmentTapeTable, EventsCommitmentTapeTable, SelfProgIdTapeTable, StorageDevicePrivateTable, StorageDevicePublicTable, TableKind, TableWithTypedOutput, }; use crate::tape_commitments::columns::TapeCommitmentCTL; @@ -111,6 +111,7 @@ pub fn register_looking() -> Vec>> { CallTapeTable::new(data, COL_MAP.ops.is_storage_device), EventsCommitmentTapeTable::new(data, COL_MAP.ops.is_storage_device), CastListCommitmentTapeTable::new(data, COL_MAP.ops.is_storage_device), + SelfProgIdTapeTable::new(data, COL_MAP.ops.is_storage_device), ] } @@ -119,7 +120,7 @@ pub fn event_commitment_lookup_in_tape_commitments( ) -> TableWithTypedOutput> { let data = TapeCommitmentCTL { byte: COL_MAP.value, - index: i64::try_from(COMMITMENT_SIZE - 1).unwrap() - COL_MAP.size, + index: i64::try_from(DIGEST_BYTES - 1).unwrap() - COL_MAP.size, }; EventsCommitmentTapeTable::new(data, COL_MAP.ops.is_memory_store) } @@ -129,7 +130,7 @@ pub fn castlist_commitment_lookup_in_tape_commitments( ) -> TableWithTypedOutput> { let data = TapeCommitmentCTL { byte: COL_MAP.value, - index: i64::try_from(COMMITMENT_SIZE - 1).unwrap() - COL_MAP.size, + index: i64::try_from(DIGEST_BYTES - 1).unwrap() - COL_MAP.size, }; CastListCommitmentTapeTable::new(data, COL_MAP.ops.is_memory_store) } diff --git a/circuits/src/storage_device/stark.rs b/circuits/src/storage_device/stark.rs index f8a5ccddc..6ed9de221 100644 --- a/circuits/src/storage_device/stark.rs +++ b/circuits/src/storage_device/stark.rs @@ -117,14 +117,12 @@ mod tests { use itertools::Itertools; use mozak_runner::code::execute_code_with_ro_memory; use mozak_runner::decode::ECALL; - use mozak_runner::elf::{Program, RuntimeArguments}; use mozak_runner::instruction::{Args, Instruction, Op}; - use mozak_runner::state::State; - use mozak_runner::test_utils::{u32_extra_except_mozak_ro_memory, u8_extra}; - use mozak_runner::vm::ExecutionRecord; - use mozak_sdk::core::ecall::{self, COMMITMENT_SIZE}; + use mozak_runner::state::RawTapes; + use mozak_runner::test_utils::{u32_extra, u8_extra}; + use mozak_sdk::core::constants::DIGEST_BYTES; + use mozak_sdk::core::ecall::{self}; use mozak_sdk::core::reg_abi::{REG_A0, REG_A1, REG_A2}; - use plonky2::field::goldilocks_field::GoldilocksField; use plonky2::plonk::config::Poseidon2GoldilocksConfig; use proptest::prelude::ProptestConfig; use proptest::proptest; @@ -134,142 +132,139 @@ mod tests { use crate::storage_device::stark::StorageDeviceStark; use crate::test_utils::{ProveAndVerify, D, F}; - #[must_use] - fn execute_code_with_runtime_args( - code: impl IntoIterator, - rw_mem: &[(u32, u8)], - regs: &[(u8, u32)], - runtime_args: RuntimeArguments, - ) -> (Program, ExecutionRecord) { - execute_code_with_ro_memory(code, &[], rw_mem, regs, runtime_args) - } - pub fn prove_read_private_zero_size(address: u32) { - let (program, record) = execute_code_with_runtime_args( + let (program, record) = execute_code_with_ro_memory( // set sys-call IO_READ in x10(or a0) [ECALL], + &[], &[(address, 0)], &[ (REG_A0, ecall::PRIVATE_TAPE), (REG_A1, address), // A1 - address (REG_A2, 0), // A2 - size ], - RuntimeArguments::default(), + RawTapes::default(), ); Stark::prove_and_verify(&program, &record).unwrap(); } pub fn prove_read_public_zero_size(address: u32) { - let (program, record) = execute_code_with_runtime_args( + let (program, record) = execute_code_with_ro_memory( // set sys-call IO_READ in x10(or a0) [ECALL], + &[], &[(address, 0)], &[ (REG_A0, ecall::PUBLIC_TAPE), (REG_A1, address), // A1 - address (REG_A2, 0), // A2 - size ], - RuntimeArguments::default(), + RawTapes::default(), ); Stark::prove_and_verify(&program, &record).unwrap(); } pub fn prove_read_call_tape_zero_size(address: u32) { - let (program, record) = execute_code_with_runtime_args( + let (program, record) = execute_code_with_ro_memory( // set sys-call IO_READ in x10(or a0) [ECALL], + &[], &[(address, 0)], &[ (REG_A0, ecall::CALL_TAPE), (REG_A1, address), // A1 - address (REG_A2, 0), // A2 - size ], - RuntimeArguments::default(), + RawTapes::default(), ); Stark::prove_and_verify(&program, &record).unwrap(); } pub fn prove_read_event_tape_zero_size(address: u32) { - let (program, record) = execute_code_with_runtime_args( + let (program, record) = execute_code_with_ro_memory( // set sys-call IO_READ in x10(or a0) [ECALL], + &[], &[(address, 0)], &[ (REG_A0, ecall::EVENT_TAPE), (REG_A1, address), // A1 - address (REG_A2, 0), // A2 - size ], - RuntimeArguments::default(), + RawTapes::default(), ); Stark::prove_and_verify(&program, &record).unwrap(); } pub fn prove_read_private(address: u32, private_tape: Vec) { - let (program, record) = execute_code_with_runtime_args( + let (program, record) = execute_code_with_ro_memory( // set sys-call IO_READ in x10(or a0) [ECALL], + &[], &[(address, 0)], &[ (REG_A0, ecall::PRIVATE_TAPE), (REG_A1, address), // A1 - address (REG_A2, 1), // A2 - size ], - RuntimeArguments { + RawTapes { private_tape, ..Default::default() }, ); - let state: State = State::from(program.clone()); assert_ne!( - state.private_tape.data.len(), + record.last_state.private_tape.data.len(), 0, "Proving an execution with an empty tape might make our tests pass, even if things are wrong" ); + Stark::prove_and_verify(&program, &record).unwrap(); } pub fn prove_read_public(address: u32, public_tape: Vec) { - let (program, record) = execute_code_with_runtime_args( + let (program, record) = execute_code_with_ro_memory( // set sys-call IO_READ in x10(or a0) [ECALL], + &[], &[(address, 0)], &[ (REG_A0, ecall::PUBLIC_TAPE), (REG_A1, address), // A1 - address (REG_A2, 1), // A2 - size ], - RuntimeArguments { + RawTapes { public_tape, ..Default::default() }, ); - let state: State = State::from(program.clone()); + assert_ne!( - state.public_tape.data.len(), + record.last_state.public_tape.data.len(), 0, "Proving an execution with an empty tape might make our tests pass, even if things are wrong" ); + Stark::prove_and_verify(&program, &record).unwrap(); } pub fn prove_read_call_tape(address: u32, call_tape: Vec) { - let (program, record) = execute_code_with_runtime_args( + let (program, record) = execute_code_with_ro_memory( // set sys-call IO_READ in x10(or a0) [ECALL], + &[], &[(address, 0)], &[ (REG_A0, ecall::CALL_TAPE), (REG_A1, address), // A1 - address (REG_A2, 1), // A2 - size ], - RuntimeArguments { + RawTapes { call_tape, ..Default::default() }, ); - let state: State = State::from(program.clone()); assert_ne!( - state.call_tape.data.len(), + record.last_state.call_tape.data.len(), 0, "Proving an execution with an empty tape might make our tests pass, even if things are wrong" ); @@ -277,23 +272,23 @@ mod tests { } pub fn prove_read_event_tape(address: u32, event_tape: Vec) { - let (program, record) = execute_code_with_runtime_args( + let (program, record) = execute_code_with_ro_memory( // set sys-call IO_READ in x10(or a0) [ECALL], + &[], &[(address, 0)], &[ (REG_A0, ecall::EVENT_TAPE), (REG_A1, address), // A1 - address (REG_A2, 1), // A2 - size ], - RuntimeArguments { + RawTapes { event_tape, ..Default::default() }, ); - let state: State = State::from(program.clone()); assert_ne!( - state.event_tape.data.len(), + record.last_state.event_tape.data.len(), 0, "Proving an execution with an empty tape might make our tests pass, even if things are wrong" ); @@ -304,25 +299,26 @@ mod tests { address: u32, events_commitment_tape: [u8; 32], ) { - let (program, record) = execute_code_with_runtime_args( + let (program, record) = execute_code_with_ro_memory( // set sys-call IO_READ in x10(or a0) [ECALL], - &(0..COMMITMENT_SIZE) + &[], + &(0..DIGEST_BYTES) .map(|i| (address.wrapping_add(u32::try_from(i).unwrap()), 0_u8)) .collect_vec(), &[ (REG_A0, ecall::EVENTS_COMMITMENT_TAPE), - (REG_A1, address), // A1 - address - (REG_A2, u32::try_from(COMMITMENT_SIZE).unwrap()), // A2 - size + (REG_A1, address), // A1 - address + (REG_A2, u32::try_from(DIGEST_BYTES).unwrap()), // A2 - size ], - RuntimeArguments { + RawTapes { events_commitment_tape, ..Default::default() }, ); - let state: State = State::from(program.clone()); + assert_ne!( - state.events_commitment_tape.0.len(), + record.last_state.events_commitment_tape.len(), 0, "Proving an execution with an empty tape might make our tests pass, even if things are wrong" ); @@ -334,35 +330,36 @@ mod tests { address: u32, cast_list_commitment_tape: [u8; 32], ) { - let (program, record) = execute_code_with_runtime_args( + let (program, record) = execute_code_with_ro_memory( // set sys-call IO_READ in x10(or a0) [ECALL], - &(0..COMMITMENT_SIZE) + &[], + &(0..DIGEST_BYTES) .map(|i| (address.wrapping_add(u32::try_from(i).unwrap()), 0_u8)) .collect_vec(), &[ (REG_A0, ecall::CAST_LIST_COMMITMENT_TAPE), - (REG_A1, address), // A1 - address - (REG_A2, u32::try_from(COMMITMENT_SIZE).unwrap()), // A2 - size + (REG_A1, address), // A1 - address + (REG_A2, u32::try_from(DIGEST_BYTES).unwrap()), // A2 - size ], - RuntimeArguments { + RawTapes { cast_list_commitment_tape, ..Default::default() }, ); Stark::prove_and_verify(&program, &record).unwrap(); - let state: State = State::from(program.clone()); assert_ne!( - state.cast_list_commitment_tape.0.len(), + record.last_state.cast_list_commitment_tape.len(), 0, "Proving an execution with an empty tape might make our tests pass, even if things are wrong" ); + Stark::prove_and_verify(&program, &record).unwrap(); } pub fn prove_read_explicit(address: u32, content: u8) { - let (program, record) = execute_code_with_runtime_args( + let (program, record) = execute_code_with_ro_memory( [ Instruction { op: Op::ADD, @@ -416,6 +413,7 @@ mod tests { }, }, ], + &[], &[ (address, 0), (address.wrapping_add(1), 0), @@ -423,7 +421,7 @@ mod tests { (address.wrapping_add(3), 0), ], &[], - RuntimeArguments { + RawTapes { private_tape: vec![content, content, content, content], ..Default::default() }, @@ -434,52 +432,51 @@ mod tests { proptest! { #![proptest_config(ProptestConfig::with_cases(1))] #[test] - fn prove_read_private_zero_size_mozak(address in u32_extra_except_mozak_ro_memory()) { + fn prove_read_private_zero_size_mozak(address in u32_extra()) { prove_read_private_zero_size::>(address); } #[test] - fn prove_read_private_mozak(address in u32_extra_except_mozak_ro_memory(), content in u8_extra()) { + fn prove_read_private_mozak(address in u32_extra(), content in u8_extra()) { prove_read_private::>(address, vec![content]); } #[test] - fn prove_read_public_zero_size_mozak(address in u32_extra_except_mozak_ro_memory()) { + fn prove_read_public_zero_size_mozak(address in u32_extra()) { prove_read_public_zero_size::>(address); } #[test] - fn prove_read_public_mozak(address in u32_extra_except_mozak_ro_memory(), content in u8_extra()) { + fn prove_read_public_mozak(address in u32_extra(), content in u8_extra()) { prove_read_public::>(address, vec![content]); } #[test] - fn prove_read_call_tape_zero_size_mozak(address in u32_extra_except_mozak_ro_memory()) { + fn prove_read_call_tape_zero_size_mozak(address in u32_extra()) { prove_read_call_tape_zero_size::>(address); } #[test] - fn prove_read_call_tape_mozak(address in u32_extra_except_mozak_ro_memory(), content in u8_extra()) { + fn prove_read_call_tape_mozak(address in u32_extra(), content in u8_extra()) { prove_read_call_tape::>(address, vec![content]); } #[test] - fn prove_read_event_tape_zero_size_mozak(address in u32_extra_except_mozak_ro_memory()) { + fn prove_read_event_tape_zero_size_mozak(address in u32_extra()) { prove_read_event_tape_zero_size::>(address); } #[test] - fn prove_read_event_tape_mozak(address in u32_extra_except_mozak_ro_memory(), content in u8_extra()) { + fn prove_read_event_tape_mozak(address in u32_extra(), content in u8_extra()) { prove_read_event_tape::>(address, vec![content]); } - #[test] - fn prove_events_commitment_tape_mozak(address in u32_extra_except_mozak_ro_memory(), content in u8_extra()) { + fn prove_events_commitment_tape_mozak(address in u32_extra(), content in u8_extra()) { prove_events_commitment_tape::>(address, [content; 32]); } #[test] - fn prove_cast_list_commitment_tape_mozak(address in u32_extra_except_mozak_ro_memory(), content in u8_extra()) { + fn prove_cast_list_commitment_tape_mozak(address in u32_extra(), content in u8_extra()) { prove_cast_list_commitment_tape::>(address, [content; 32]); } #[test] - fn prove_read_mozak_explicit(address in u32_extra_except_mozak_ro_memory(), content in u8_extra()) { + fn prove_read_mozak_explicit(address in u32_extra(), content in u8_extra()) { prove_read_explicit::>(address, content); } } diff --git a/circuits/src/tape_commitments/columns.rs b/circuits/src/tape_commitments/columns.rs index 46daed6c1..0b63820e8 100644 --- a/circuits/src/tape_commitments/columns.rs +++ b/circuits/src/tape_commitments/columns.rs @@ -1,4 +1,4 @@ -use mozak_sdk::core::ecall::COMMITMENT_SIZE; +use mozak_sdk::core::constants::DIGEST_BYTES; use crate::columns_view::{columns_view_impl, make_col_map}; use crate::linear_combination::Column; @@ -69,7 +69,7 @@ pub fn make_event_commitment_tape_public() -> PublicSubTable { vec![TAPE_COMMITMENTS.commitment_byte_row.byte], TAPE_COMMITMENTS.is_event_commitment_tape_row, ), - num_rows: COMMITMENT_SIZE, + num_rows: DIGEST_BYTES, } } @@ -80,6 +80,6 @@ pub fn make_castlist_commitment_tape_public() -> PublicSubTable { vec![TAPE_COMMITMENTS.commitment_byte_row.byte], TAPE_COMMITMENTS.is_castlist_commitment_tape_row, ), - num_rows: COMMITMENT_SIZE, + num_rows: DIGEST_BYTES, } } diff --git a/circuits/src/tape_commitments/stark.rs b/circuits/src/tape_commitments/stark.rs index fd0d59aca..3c55b7581 100644 --- a/circuits/src/tape_commitments/stark.rs +++ b/circuits/src/tape_commitments/stark.rs @@ -85,9 +85,10 @@ mod tests { use itertools::chain; use mozak_runner::code; use mozak_runner::decode::ECALL; - use mozak_runner::elf::RuntimeArguments; use mozak_runner::instruction::{Args, Instruction, Op}; - use mozak_sdk::core::ecall::{self, COMMITMENT_SIZE}; + use mozak_runner::state::RawTapes; + use mozak_sdk::core::constants::DIGEST_BYTES; + use mozak_sdk::core::ecall::{self}; use mozak_sdk::core::reg_abi::{REG_A0, REG_A1, REG_A2}; use plonky2::field::types::Field; use plonky2::plonk::circuit_data::CircuitConfig; @@ -148,12 +149,12 @@ mod tests { let code_ecall_cast_list_commitment_tape = read_ecall_code( ecall::CAST_LIST_COMMITMENT_TAPE, CAST_LIST_COMMITMENT_ADDRESS, - COMMITMENT_SIZE, + DIGEST_BYTES, ); let code_ecall_events_commitment_tape = read_ecall_code( ecall::EVENTS_COMMITMENT_TAPE, EVENTS_COMMITMENT_ADDRESS, - COMMITMENT_SIZE, + DIGEST_BYTES, ); chain!( code_ecall_cast_list_commitment_tape, @@ -166,30 +167,28 @@ mod tests { fn test_tape_commitment_stark() -> Result<(), anyhow::Error> { let mut rng = rand::thread_rng(); // generate tapes with random bytes - let cast_list_commitment_tape: [u8; COMMITMENT_SIZE] = rng.gen(); - let events_commitment_tape: [u8; COMMITMENT_SIZE] = rng.gen(); + let cast_list_commitment_tape: [u8; DIGEST_BYTES] = rng.gen(); + let events_commitment_tape: [u8; DIGEST_BYTES] = rng.gen(); let code = read_tape_commitments_code(); - let (program, record) = - code::execute_code_with_ro_memory(code, &[], &[], &[], RuntimeArguments { - events_commitment_tape, - cast_list_commitment_tape, - ..Default::default() - }); + let (program, record) = code::execute_code_with_ro_memory(code, &[], &[], &[], RawTapes { + events_commitment_tape, + cast_list_commitment_tape, + ..Default::default() + }); TapeCommitmentsStark::prove_and_verify(&program, &record) } #[test] fn test_tape_commitment_mozak_stark() -> Result<(), anyhow::Error> { let mut rng = rand::thread_rng(); // generate tapes with random bytes - let cast_list_commitment_tape: [u8; COMMITMENT_SIZE] = rng.gen(); - let events_commitment_tape: [u8; COMMITMENT_SIZE] = rng.gen(); + let cast_list_commitment_tape: [u8; DIGEST_BYTES] = rng.gen(); + let events_commitment_tape: [u8; DIGEST_BYTES] = rng.gen(); let code = read_tape_commitments_code(); - let (program, record) = - code::execute_code_with_ro_memory(code, &[], &[], &[], RuntimeArguments { - events_commitment_tape, - cast_list_commitment_tape, - ..Default::default() - }); + let (program, record) = code::execute_code_with_ro_memory(code, &[], &[], &[], RawTapes { + events_commitment_tape, + cast_list_commitment_tape, + ..Default::default() + }); MozakStark::prove_and_verify(&program, &record) } @@ -197,15 +196,14 @@ mod tests { fn test_tape_commitment_recursive_prover() -> Result<(), anyhow::Error> { let mut rng = rand::thread_rng(); // generate tapes with random bytes - let cast_list_commitment_tape: [u8; COMMITMENT_SIZE] = rng.gen(); - let events_commitment_tape: [u8; COMMITMENT_SIZE] = rng.gen(); + let cast_list_commitment_tape: [u8; DIGEST_BYTES] = rng.gen(); + let events_commitment_tape: [u8; DIGEST_BYTES] = rng.gen(); let code = read_tape_commitments_code(); - let (program, record) = - code::execute_code_with_ro_memory(code, &[], &[], &[], RuntimeArguments { - events_commitment_tape, - cast_list_commitment_tape, - ..Default::default() - }); + let (program, record) = code::execute_code_with_ro_memory(code, &[], &[], &[], RawTapes { + events_commitment_tape, + cast_list_commitment_tape, + ..Default::default() + }); let stark = MozakStark::::default(); let config = StarkConfig::standard_fast_config(); let public_inputs = PublicInputs { diff --git a/circuits/src/test_utils.rs b/circuits/src/test_utils.rs index a7b88e8a8..bc982e544 100644 --- a/circuits/src/test_utils.rs +++ b/circuits/src/test_utils.rs @@ -33,6 +33,7 @@ use crate::generation::memoryinit::generate_memory_init_trace; use crate::generation::storage_device::{ generate_call_tape_trace, generate_cast_list_commitment_tape_trace, generate_event_tape_trace, generate_events_commitment_tape_trace, generate_private_tape_trace, generate_public_tape_trace, + generate_self_prog_id_tape_trace, }; use crate::generation::xor::generate_xor_trace; use crate::memory::stark::MemoryStark; @@ -161,6 +162,7 @@ impl ProveAndVerify for RangeCheckStark { let events_commitment_tape_rows = generate_events_commitment_tape_trace(&record.executed); let cast_list_commitment_tape_rows = generate_cast_list_commitment_tape_trace(&record.executed); + let self_prog_id_tape_rows = generate_self_prog_id_tape_trace(&record.executed); let poseidon2_sponge_trace = generate_poseidon2_sponge_trace(&record.executed); let poseidon2_output_bytes = generate_poseidon2_output_bytes_trace(&poseidon2_sponge_trace); let memory_trace = generate_memory_trace::( @@ -175,6 +177,7 @@ impl ProveAndVerify for RangeCheckStark { &event_tape_rows, &events_commitment_tape_rows, &cast_list_commitment_tape_rows, + &self_prog_id_tape_rows, &poseidon2_sponge_trace, &poseidon2_output_bytes, ); @@ -190,6 +193,7 @@ impl ProveAndVerify for RangeCheckStark { &event_tape_rows, &events_commitment_tape_rows, &cast_list_commitment_tape_rows, + &self_prog_id_tape_rows, ®ister_init, ); let trace_poly_values = trace_rows_to_poly_values(generate_rangecheck_trace( @@ -251,6 +255,7 @@ impl ProveAndVerify for MemoryStark { let events_commitment_tape_rows = generate_events_commitment_tape_trace(&record.executed); let cast_list_commitment_tape_rows = generate_cast_list_commitment_tape_trace(&record.executed); + let self_prog_id_tape_rows = generate_self_prog_id_tape_trace(&record.executed); let poseidon2_sponge_trace = generate_poseidon2_sponge_trace(&record.executed); let poseidon2_output_bytes = generate_poseidon2_output_bytes_trace(&poseidon2_sponge_trace); let trace_poly_values = trace_rows_to_poly_values(generate_memory_trace( @@ -265,6 +270,7 @@ impl ProveAndVerify for MemoryStark { &event_tape_rows, &events_commitment_tape_rows, &cast_list_commitment_tape_rows, + &self_prog_id_tape_rows, &poseidon2_sponge_trace, &poseidon2_output_bytes, )); @@ -397,6 +403,7 @@ impl ProveAndVerify for RegisterStark { let events_commitment_tape_rows = generate_events_commitment_tape_trace(&record.executed); let cast_list_commitment_tape_rows = generate_cast_list_commitment_tape_trace(&record.executed); + let self_prog_id_tape_rows = generate_self_prog_id_tape_trace(&record.executed); let poseidon2_sponge_rows = generate_poseidon2_sponge_trace(&record.executed); let register_init = generate_register_init_trace(record); @@ -411,6 +418,7 @@ impl ProveAndVerify for RegisterStark { &event_tape, &events_commitment_tape_rows, &cast_list_commitment_tape_rows, + &self_prog_id_tape_rows, ®ister_init, ); let trace_poly_values = trace_rows_to_poly_values(trace); diff --git a/cli/src/main.rs b/cli/src/main.rs index 03b6ac9fd..548ea2bd5 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -1,8 +1,6 @@ #![deny(clippy::pedantic)] #![deny(clippy::cargo)] -// TODO(bing): `clio` uses an older `windows-sys` vs other dependencies. -// Remove when `clio` updates, or if `clio` is no longer needed. -#![allow(clippy::multiple_crate_versions)] + use std::collections::HashMap; use std::io::{Read, Write}; use std::path::PathBuf; @@ -28,9 +26,8 @@ use mozak_circuits::stark::utils::trace_rows_to_poly_values; use mozak_circuits::stark::verifier::verify_proof; use mozak_circuits::test_utils::{prove_and_verify_mozak_stark, C, D, F, S}; use mozak_cli::cli_benches::benches::BenchArgs; -use mozak_cli::runner::{deserialize_system_tape, load_program, tapes_to_runtime_arguments}; +use mozak_cli::runner::{deserialize_system_tape, load_program, raw_tapes_from_system_tape}; use mozak_node::types::{Attestation, Transaction}; -use mozak_runner::elf::RuntimeArguments; use mozak_runner::state::{RawTapes, State}; use mozak_runner::vm::step; use mozak_sdk::common::types::{CrossProgramCall, ProgramIdentifier, SystemTape}; @@ -118,7 +115,7 @@ fn main() -> Result<()> { .init(); match cli.command { Command::Decode { elf } => { - let program = load_program(elf, &RuntimeArguments::default())?; + let program = load_program(elf)?; debug!("{program:?}"); } Command::Run(RunArgs { @@ -126,11 +123,9 @@ fn main() -> Result<()> { system_tape, self_prog_id, }) => { - let args = system_tape - .map(|s| tapes_to_runtime_arguments(s, self_prog_id)) - .unwrap_or_default(); - let program = load_program(elf, &args).unwrap(); - let state: State = State::new(program.clone(), RawTapes::default()); + let raw_tapes = raw_tapes_from_system_tape(system_tape, self_prog_id.unwrap().into()); + let program = load_program(elf).unwrap(); + let state: State = State::new(program.clone(), raw_tapes); step(&program, state)?; } Command::ProveAndVerify(RunArgs { @@ -138,13 +133,11 @@ fn main() -> Result<()> { system_tape, self_prog_id, }) => { - let args = system_tape - .map(|s| tapes_to_runtime_arguments(s, self_prog_id.clone())) - .unwrap_or_default(); + let program = load_program(elf).unwrap(); - let program = load_program(elf, &args).unwrap(); - let state = State::new(program.clone(), args.into()); + let raw_tapes = raw_tapes_from_system_tape(system_tape, self_prog_id.unwrap().into()); + let state = State::new(program.clone(), raw_tapes); let record = step(&program, state)?; prove_and_verify_mozak_stark(&program, &record, &config)?; } @@ -155,11 +148,10 @@ fn main() -> Result<()> { mut proof, recursive_proof, }) => { - let args = system_tape - .map(|s| tapes_to_runtime_arguments(s, self_prog_id)) - .unwrap_or_default(); - let program = load_program(elf, &args).unwrap(); - let state = State::new(program.clone(), RawTapes::default()); + let program = load_program(elf).unwrap(); + let raw_tapes = raw_tapes_from_system_tape(system_tape, self_prog_id.unwrap().into()); + + let state = State::new(program.clone(), raw_tapes); let record = step(&program, state)?; let stark = if cli.debug { MozakStark::default_debug() @@ -278,14 +270,6 @@ fn main() -> Result<()> { let ids_and_paths = ids_and_paths_from_cast_list(entrypoint_program_id, &cast_list); - // This does nothing - we rely entirely on ecalls. - // TODO(bing): Refactor `load_program` to not take this as a param, in a - // separate PR. - let args = tapes_to_runtime_arguments( - system_tape_path, - Some(format!("{entrypoint_program_id:?}")), - ); - let mut attestations: Vec = vec![]; let mut call_tape_hash = None; @@ -293,7 +277,6 @@ fn main() -> Result<()> { let program = load_program( Input::try_from(elf) .unwrap_or_else(|_| panic!("Elf filepath {elf:?} not found")), - &args, )?; let raw_call_tape: Vec = @@ -397,7 +380,7 @@ fn main() -> Result<()> { println!("Recursive VM proof verified successfully!"); } Command::ProgramRomHash { elf } => { - let program = load_program(elf, &RuntimeArguments::default())?; + let program = load_program(elf)?; let trace = generate_program_rom_trace(&program); let trace_poly_values = trace_rows_to_poly_values(trace); let rate_bits = config.fri_config.rate_bits; @@ -414,7 +397,7 @@ fn main() -> Result<()> { println!("{trace_cap:?}"); } Command::MemoryInitHash { elf } => { - let program = load_program(elf, &RuntimeArguments::default())?; + let program = load_program(elf)?; let trace = generate_elf_memory_init_trace(&program); let trace_poly_values = trace_rows_to_poly_values(trace); let rate_bits = config.fri_config.rate_bits; diff --git a/cli/src/runner.rs b/cli/src/runner.rs index 1fa005a51..a05a31bc0 100644 --- a/cli/src/runner.rs +++ b/cli/src/runner.rs @@ -7,7 +7,8 @@ use anyhow::Result; use clio::Input; use itertools::{izip, Itertools}; use log::debug; -use mozak_runner::elf::{Program, RuntimeArguments}; +use mozak_runner::elf::Program; +use mozak_runner::state::RawTapes; use mozak_sdk::common::merkle::merkleize; use mozak_sdk::common::types::{ CanonicalOrderedTemporalHints, Poseidon2Hash, ProgramIdentifier, SystemTape, @@ -15,14 +16,15 @@ use mozak_sdk::common::types::{ use rkyv::rancor::{Panic, Strategy}; use rkyv::ser::AllocSerializer; -pub fn load_program(mut elf: Input, args: &RuntimeArguments) -> Result { +pub fn load_program(mut elf: Input) -> Result { let mut elf_bytes = Vec::new(); let bytes_read = elf.read_to_end(&mut elf_bytes)?; debug!("Read {bytes_read} of ELF data."); - Program::mozak_load_program(&elf_bytes, args) + Program::mozak_load_program(&elf_bytes) } -/// Deserializes an rkyv-serialized system tape binary file into `SystemTape`. +/// Deserializes a serde JSON serialized system tape binary file into a +/// [`SystemTape`]. /// /// # Errors /// @@ -53,22 +55,14 @@ fn length_prefixed_bytes(data: Vec, dgb_string: &str) -> Vec { len_prefix_bytes } -/// Deserializes an rkyv-serialized system tape binary file into -/// [`SystemTapes`](mozak_sdk::sys::SystemTapes). -/// -/// # Panics -/// -/// Panics if conversion from rkyv-serialized system tape to -/// [`RuntimeArguments`](mozak_runner::elf::RuntimeArguments) -/// fails. -pub fn tapes_to_runtime_arguments( - tape_bin: Input, - self_prog_id: Option, -) -> RuntimeArguments { - let sys_tapes: SystemTape = deserialize_system_tape(tape_bin).unwrap(); - let self_prog_id: ProgramIdentifier = self_prog_id.unwrap_or_default().into(); +pub fn raw_tapes_from_system_tape(sys: Option, self_prog_id: ProgramIdentifier) -> RawTapes { + if sys.is_none() { + return RawTapes::default(); + } - let cast_list = sys_tapes + let sys = &deserialize_system_tape(sys.unwrap()).unwrap(); + + let cast_list = sys .call_tape .writer .iter() @@ -77,7 +71,7 @@ pub fn tapes_to_runtime_arguments( .into_iter() .collect_vec(); - let canonical_order_temporal_hints: Vec = sys_tapes + let canonical_order_temporal_hints: Vec = sys .event_tape .writer .get(&self_prog_id) @@ -115,33 +109,30 @@ pub fn tapes_to_runtime_arguments( length_prefixed_bytes(tape_bytes, dgb_string) } - RuntimeArguments { - self_prog_id: self_prog_id.inner().to_vec(), - events_commitment_tape, - cast_list_commitment_tape, - cast_list: serialise(&cast_list, "CAST_LIST"), - public_tape: length_prefixed_bytes( - sys_tapes - .public_input_tape + RawTapes { + private_tape: length_prefixed_bytes( + sys.private_input_tape .writer .get(&self_prog_id) .cloned() .unwrap_or_default() .0, - "INPUT_PUBLIC", + "PRIVATE_TAPE", ), - private_tape: length_prefixed_bytes( - sys_tapes - .private_input_tape + public_tape: length_prefixed_bytes( + sys.public_input_tape .writer .get(&self_prog_id) .cloned() .unwrap_or_default() .0, - "INPUT_PRIVATE", + "PUBLIC_TAPE", ), - call_tape: serialise(&sys_tapes.call_tape.writer, "CALL_TAPE"), + call_tape: serialise(&sys.call_tape.writer, "CALL_TAPE"), event_tape: serialise(&canonical_order_temporal_hints, "EVENT_TAPE"), + self_prog_id_tape: self_prog_id.0 .0, + events_commitment_tape, + cast_list_commitment_tape, } } } diff --git a/examples/Cargo.lock b/examples/Cargo.lock index 7bd4c3534..e27964b7e 100644 --- a/examples/Cargo.lock +++ b/examples/Cargo.lock @@ -23,9 +23,9 @@ checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "anyhow" -version = "1.0.82" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" +checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3" [[package]] name = "array-init" @@ -38,9 +38,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "bitvec" @@ -175,9 +175,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "js-sys", @@ -188,9 +188,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ "ahash", "allocator-api2", @@ -205,22 +205,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "hex-literal" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d70693199b3cf4552f3fa720b54163927a3ebed2aef240efaf556033ab336a11" -dependencies = [ - "hex-literal-impl", - "proc-macro-hack", -] - -[[package]] -name = "hex-literal-impl" -version = "0.2.3" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59448fc2f82a5fb6907f78c3d69d843e82ff5b051923313cc4438cb0c7b745a8" -dependencies = [ - "proc-macro-hack", -] +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" [[package]] name = "inputtape" @@ -258,9 +245,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.153" +version = "0.2.154" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" [[package]] name = "log" @@ -322,9 +309,9 @@ checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" [[package]] name = "num" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3135b08af27d103b0a51f2ae0f8632117b7b185ccf931445affa8df530576a41" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" dependencies = [ "num-bigint", "num-complex", @@ -336,11 +323,10 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7" dependencies = [ - "autocfg", "num-integer", "num-traits", "rand", @@ -348,9 +334,9 @@ dependencies = [ [[package]] name = "num-complex" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" dependencies = [ "num-traits", "rand", @@ -367,9 +353,9 @@ dependencies = [ [[package]] name = "num-iter" -version = "0.1.44" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" dependencies = [ "autocfg", "num-integer", @@ -378,11 +364,10 @@ dependencies = [ [[package]] name = "num-rational" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" dependencies = [ - "autocfg", "num-bigint", "num-integer", "num-traits", @@ -390,9 +375,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] @@ -413,7 +398,7 @@ dependencies = [ [[package]] name = "plonky2" version = "0.2.2" -source = "git+https://github.com/0xmozak/plonky2.git#29187d339279da1b6ed3f8508bd1a356e224e60e" +source = "git+https://github.com/0xmozak/plonky2.git#13cb36256626f53134758894897010c1506acd80" dependencies = [ "ahash", "anyhow", @@ -435,7 +420,7 @@ dependencies = [ [[package]] name = "plonky2_field" version = "0.2.2" -source = "git+https://github.com/0xmozak/plonky2.git#29187d339279da1b6ed3f8508bd1a356e224e60e" +source = "git+https://github.com/0xmozak/plonky2.git#13cb36256626f53134758894897010c1506acd80" dependencies = [ "anyhow", "itertools", @@ -450,12 +435,12 @@ dependencies = [ [[package]] name = "plonky2_maybe_rayon" version = "0.2.0" -source = "git+https://github.com/0xmozak/plonky2.git#29187d339279da1b6ed3f8508bd1a356e224e60e" +source = "git+https://github.com/0xmozak/plonky2.git#13cb36256626f53134758894897010c1506acd80" [[package]] name = "plonky2_util" version = "0.2.0" -source = "git+https://github.com/0xmozak/plonky2.git#29187d339279da1b6ed3f8508bd1a356e224e60e" +source = "git+https://github.com/0xmozak/plonky2.git#13cb36256626f53134758894897010c1506acd80" [[package]] name = "ppv-lite86" @@ -463,17 +448,11 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" -[[package]] -name = "proc-macro-hack" -version = "0.5.20+deprecated" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" - [[package]] name = "proc-macro2" -version = "1.0.81" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" +checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" dependencies = [ "unicode-ident", ] @@ -595,15 +574,15 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "serde" -version = "1.0.198" +version = "1.0.201" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc" +checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c" dependencies = [ "serde_derive", ] @@ -621,9 +600,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.198" +version = "1.0.201" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" +checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" dependencies = [ "proc-macro2", "quote", @@ -632,9 +611,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.116" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" dependencies = [ "itoa", "ryu", @@ -691,9 +670,9 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "syn" -version = "2.0.59" +version = "2.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a6531ffc7b071655e4ce2e04bd464c4830bb585a61cabb96cf808f05172615a" +checksum = "c993ed8ccba56ae856363b1845da7266a7cb78e1d146c8a32d54b45a8b831fc9" dependencies = [ "proc-macro2", "quote", @@ -756,7 +735,7 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unroll" version = "0.2.0" -source = "git+https://github.com/0xmozak/unroll.git#1602aa5cd0b9babacb883ae369389444071f0402" +source = "git+https://github.com/0xmozak/unroll.git#ad890f01b927ff5650937f5edaa3a03c582d1369" dependencies = [ "quote", "syn", @@ -851,18 +830,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.32" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.32" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", diff --git a/examples/sha2/Cargo.toml b/examples/sha2/Cargo.toml index 877e16076..ab5a76ecd 100644 --- a/examples/sha2/Cargo.toml +++ b/examples/sha2/Cargo.toml @@ -4,7 +4,7 @@ name = "sha2" version = "0.1.0" [dependencies] -hex-literal = "0.2" +hex-literal = "0.4" mozak-sdk = { path = "../../sdk", default-features = false } sha2 = { version = "0.10", default-features = false } diff --git a/node/src/block_proposer/state.rs b/node/src/block_proposer/state.rs index 93291a080..931a9eefc 100644 --- a/node/src/block_proposer/state.rs +++ b/node/src/block_proposer/state.rs @@ -2,7 +2,7 @@ use std::mem; use std::ops::Add; use itertools::{chain, Itertools}; -use mozak_recproofs::circuits::state_update::{self, AddressPresent}; +use mozak_recproofs::circuits::state_update; use plonky2::field::types::Field; use plonky2::hash::poseidon2::Poseidon2Hash; use plonky2::plonk::circuit_data::CircuitConfig; @@ -155,7 +155,7 @@ struct SparseMerkleBranch { proof: ProofWithPublicInputs, old_hash: [F; 4], new_hash: [F; 4], - summary_hash: [F; 4], + summary_hash: Option<[F; 4]>, left: Option>, right: Option>, } @@ -223,7 +223,7 @@ impl Add for FinalizeOutcome { struct LeafHashes<'a> { old_hash: &'a [F; 4], new_hash: &'a [F; 4], - summary_hash: &'a [F; 4], + summary_hash: Option<&'a [F; 4]>, } pub fn hash_branch(l: &[F; 4], r: &[F; 4]) -> [F; 4] { @@ -233,8 +233,6 @@ pub fn hash_branch(l: &[F; 4], r: &[F; 4]) -> [F; 4] { pub struct AuxStateData { max_tree_depth: usize, - empty_summary_hash: [F; 4], - empty_leaf_hash: [F; 4], empty_branch_hashes: Vec<[F; 4]>, @@ -247,7 +245,6 @@ pub struct AuxStateData { impl AuxStateData { pub fn new(config: &CircuitConfig, max_tree_depth: usize) -> Self { - let empty_summary_hash = [F::ZERO; 4]; let empty_leaf_hash = [F::ZERO; 4]; let mut curr = empty_leaf_hash; @@ -268,28 +265,19 @@ impl AuxStateData { .collect_vec(); let empty_leaf_proof = leaf_circuit - .prove( - empty_leaf_hash.into(), - empty_leaf_hash.into(), - empty_summary_hash.into(), - None, - ) + .prove(empty_leaf_hash.into(), empty_leaf_hash.into(), None) .unwrap(); let mut init = empty_leaf_proof.clone(); let empty_branch_proofs = branch_circuits .iter() .zip(&empty_branch_hashes) .map(|(circuit, hash)| { - let hash = (*hash).into(); - init = circuit - .prove(hash, hash, empty_summary_hash.into(), (), &init, &init) - .unwrap(); + init = circuit.prove(&init, &init).unwrap(); init.clone() }) .collect_vec(); Self { max_tree_depth, - empty_summary_hash, empty_leaf_hash, empty_branch_hashes, leaf_circuit, @@ -453,28 +441,24 @@ impl AuxStateData { let ((left_hash, left_proof), (right_hash, right_proof), summary) = match (&branch.left, &branch.right) { - (None, None) => ( - (empty_hash, empty_proof), - (empty_hash, empty_proof), - self.empty_summary_hash, - ), + (None, None) => ((empty_hash, empty_proof), (empty_hash, empty_proof), None), (None, Some(right)) => ( (empty_hash, empty_proof), self.get_node_new_helper(right), - *self.get_summary_helper(right), + self.get_summary_helper(right).copied(), ), (Some(left), None) => ( self.get_node_new_helper(left), (empty_hash, empty_proof), - *self.get_summary_helper(left), + self.get_summary_helper(left).copied(), ), (Some(left), Some(right)) => ( self.get_node_new_helper(left), self.get_node_new_helper(right), - hash_branch( - self.get_summary_helper(left), - self.get_summary_helper(right), - ), + Some(hash_branch( + self.get_summary_helper(left).unwrap(), + self.get_summary_helper(right).unwrap(), + )), ), }; @@ -484,16 +468,7 @@ impl AuxStateData { } branch.summary_hash = summary; branch.proof = self.branch_circuits[branch.height] - .prove( - branch.old_hash.into(), - branch.new_hash.into(), - branch.summary_hash.into(), - addr.map_or(AddressPresent::Implicit, |v| { - AddressPresent::Present(v.addr.0) - }), - left_proof, - right_proof, - ) + .prove(left_proof, right_proof) .unwrap(); } @@ -510,9 +485,10 @@ impl AuxStateData { } } - fn get_summary_helper<'a>(&'a self, node: &'a SparseMerkleNode) -> &'a [F; 4] { + fn get_summary_helper<'a>(&'a self, node: &'a SparseMerkleNode) -> Option<&'a [F; 4]> { match node { - SparseMerkleNode::Branch(SparseMerkleBranch { summary_hash, .. }) => summary_hash, + SparseMerkleNode::Branch(SparseMerkleBranch { summary_hash, .. }) => + summary_hash.as_ref(), SparseMerkleNode::Leaf(SparseMerkleLeaf { kind, .. }) => self.get_leaf_hashes(kind).summary_hash, } @@ -541,9 +517,10 @@ impl AuxStateData { let leaf = self.create_leaf_helper(addr, new); let LeafHashes { new_hash: leaf_new, - summary_hash: &leaf_summary, + summary_hash: leaf_summary, .. } = self.get_leaf_hashes(&leaf.kind); + let leaf_summary = leaf_summary.copied(); let ((left_leaf, left_proof), (right_leaf, right_proof)) = if dir == Dir::Left { ( (leaf_new, &leaf.proof), @@ -559,14 +536,7 @@ impl AuxStateData { let new_hash = hash_branch(left_leaf, right_leaf); let proof = self.branch_circuits[0] - .prove( - old_hash.into(), - new_hash.into(), - leaf_summary.into(), - (), - left_proof, - right_proof, - ) + .prove(left_proof, right_proof) .unwrap(); let leaf = Some(Box::new(SparseMerkleNode::Leaf(leaf))); @@ -608,14 +578,7 @@ impl AuxStateData { let new_hash = hash_branch(left_child, right_child); let proof = self.branch_circuits[height] - .prove( - old_hash.into(), - new_hash.into(), - child_summary.into(), - (), - left_proof, - right_proof, - ) + .prove(left_proof, right_proof) .unwrap(); let child = Some(Box::new(SparseMerkleNode::Branch(child))); @@ -644,7 +607,7 @@ impl AuxStateData { | LeafKind::ReadEmptyLeaf { summary_hash } => LeafHashes { old_hash: &self.empty_leaf_hash, new_hash: &self.empty_leaf_hash, - summary_hash, + summary_hash: Some(summary_hash), }, LeafKind::BeingCreated { new_hash, @@ -653,12 +616,12 @@ impl AuxStateData { } => LeafHashes { old_hash: &self.empty_leaf_hash, new_hash, - summary_hash, + summary_hash: Some(summary_hash), }, LeafKind::Unused { old_hash, .. } => LeafHashes { old_hash, new_hash: old_hash, - summary_hash: &self.empty_summary_hash, + summary_hash: None, }, LeafKind::BeingDeleted { old_hash, @@ -667,7 +630,7 @@ impl AuxStateData { } => LeafHashes { old_hash, new_hash: &self.empty_leaf_hash, - summary_hash, + summary_hash: Some(summary_hash), }, LeafKind::BeingRead { old_hash, @@ -676,7 +639,7 @@ impl AuxStateData { } => LeafHashes { old_hash, new_hash: old_hash, - summary_hash, + summary_hash: Some(summary_hash), }, LeafKind::BeingUpdated { old_hash, @@ -686,7 +649,7 @@ impl AuxStateData { } => LeafHashes { old_hash, new_hash, - summary_hash, + summary_hash: Some(summary_hash), }, } } @@ -698,7 +661,6 @@ impl AuxStateData { .prove( self.empty_leaf_hash.into(), self.empty_leaf_hash.into(), - summary_hash.into(), Some(addr.0), ) .unwrap(); @@ -726,12 +688,7 @@ impl AuxStateData { let summary_hash = addr.summary_hash(self.empty_leaf_hash, new_hash); let proof = self .leaf_circuit - .prove( - self.empty_leaf_hash.into(), - new_hash.into(), - summary_hash.into(), - Some(addr.0), - ) + .prove(self.empty_leaf_hash.into(), new_hash.into(), Some(addr.0)) .unwrap(); SparseMerkleLeaf { proof, @@ -748,12 +705,7 @@ impl AuxStateData { let summary_hash = addr.summary_hash(old_hash, new_hash); let proof = self .leaf_circuit - .prove( - old_hash.into(), - new_hash.into(), - summary_hash.into(), - Some(addr.0), - ) + .prove(old_hash.into(), new_hash.into(), Some(addr.0)) .unwrap(); SparseMerkleLeaf { @@ -770,12 +722,7 @@ impl AuxStateData { let summary_hash = addr.summary_hash(old_hash, old_hash); let proof = self .leaf_circuit - .prove( - old_hash.into(), - old_hash.into(), - summary_hash.into(), - Some(addr.0), - ) + .prove(old_hash.into(), old_hash.into(), Some(addr.0)) .unwrap(); SparseMerkleLeaf { @@ -799,12 +746,7 @@ impl AuxStateData { let summary_hash = addr.summary_hash(old_hash, new_hash); let proof = self .leaf_circuit - .prove( - old_hash.into(), - new_hash.into(), - summary_hash.into(), - Some(addr.0), - ) + .prove(old_hash.into(), new_hash.into(), Some(addr.0)) .unwrap(); SparseMerkleLeaf { proof, @@ -900,12 +842,7 @@ impl AuxStateData { leaf.proof = self .leaf_circuit - .prove( - old_hash.into(), - old_hash.into(), - self.empty_summary_hash.into(), - None, - ) + .prove(old_hash.into(), old_hash.into(), None) .unwrap(); FinalizeOutcome::Recalc @@ -925,7 +862,7 @@ impl<'a> State<'a> { proof: aux.empty_branch_proofs[tree_depth].clone(), old_hash: aux.empty_branch_hashes[tree_depth], new_hash: aux.empty_branch_hashes[tree_depth], - summary_hash: aux.empty_summary_hash, + summary_hash: None, left: None, right: None, }; diff --git a/recproofs/Cargo.toml b/recproofs/Cargo.toml index 3e73a794d..41e795695 100644 --- a/recproofs/Cargo.toml +++ b/recproofs/Cargo.toml @@ -19,7 +19,7 @@ plonky2 = { workspace = true, default-features = false } [dev-dependencies] array-util = "1" criterion = { workspace = true, default-features = false } -lazy_static = "1.4" +once_cell = "1" tested-fixture = "1" [features] diff --git a/recproofs/benches/recproof.rs b/recproofs/benches/recproof.rs index 1498ad689..8bdacb8fe 100644 --- a/recproofs/benches/recproof.rs +++ b/recproofs/benches/recproof.rs @@ -3,20 +3,18 @@ use std::time::Duration; use anyhow::Result; use criterion::{black_box, criterion_group, criterion_main, Criterion}; use mozak_recproofs::circuits::state_update::{BranchCircuit, LeafCircuit}; -use mozak_recproofs::subcircuits::{make_tree, unbounded}; -use mozak_recproofs::test_utils::{hash_branch, hash_str, C, D, F}; +use mozak_recproofs::subcircuits::{propagate, unbounded}; +use mozak_recproofs::test_utils::{hash_str, C, D, F}; use plonky2::field::types::Field; -use plonky2::hash::hash_types::{HashOut, RichField}; -use plonky2::hash::poseidon2::Poseidon2Hash; +use plonky2::hash::hash_types::HashOut; use plonky2::iop::witness::PartialWitness; use plonky2::plonk::circuit_builder::CircuitBuilder; use plonky2::plonk::circuit_data::{CircuitConfig, CircuitData}; -use plonky2::plonk::config::Hasher; use plonky2::plonk::proof::ProofWithPublicInputs; pub struct DummyLeafCircuit { pub unbounded: unbounded::LeafSubCircuit, - pub make_tree: make_tree::LeafSubCircuit, + pub propagate: propagate::LeafSubCircuit<4>, pub circuit: CircuitData, } @@ -26,19 +24,19 @@ impl DummyLeafCircuit { let mut builder = CircuitBuilder::::new(circuit_config.clone()); let unbounded_inputs = unbounded::SubCircuitInputs::default(&mut builder); - let make_tree_inputs = make_tree::SubCircuitInputs::default(&mut builder); + let propagate_inputs = propagate::SubCircuitInputs::default(&mut builder); let unbounded_targets = unbounded_inputs.build_leaf::(&mut builder); - let make_tree_targets = make_tree_inputs.build_leaf(&mut builder); + let propagate_targets = propagate_inputs.build_leaf(&mut builder); let circuit = builder.build(); let public_inputs = &circuit.prover_only.public_inputs; let unbounded = unbounded_targets.build(public_inputs); - let make_tree = make_tree_targets.build(public_inputs); + let propagate = propagate_targets.build(public_inputs); Self { - make_tree, + propagate, unbounded, circuit, } @@ -46,12 +44,11 @@ impl DummyLeafCircuit { pub fn prove( &self, - present: bool, leaf_value: HashOut, branch: &DummyBranchCircuit, ) -> Result> { let mut inputs = PartialWitness::new(); - self.make_tree.set_witness(&mut inputs, present, leaf_value); + self.propagate.set_witness(&mut inputs, leaf_value.elements); self.unbounded.set_witness(&mut inputs, &branch.circuit); self.circuit.prove(inputs) } @@ -59,7 +56,7 @@ impl DummyLeafCircuit { pub struct DummyBranchCircuit { pub unbounded: unbounded::BranchSubCircuit, - pub make_tree: make_tree::BranchSubCircuit, + pub propagate: propagate::BranchSubCircuit<4>, pub circuit: CircuitData, } @@ -69,34 +66,25 @@ impl DummyBranchCircuit { let mut builder = CircuitBuilder::::new(circuit_config.clone()); let unbounded_inputs = unbounded::SubCircuitInputs::default(&mut builder); - let make_tree_inputs = make_tree::SubCircuitInputs::default(&mut builder); + let propagate_inputs = propagate::SubCircuitInputs::default(&mut builder); let unbounded_targets = unbounded_inputs.build_branch(&mut builder, &leaf.unbounded, &leaf.circuit); - let make_tree_targets = make_tree_inputs.build_branch( + let propagate_targets = propagate_inputs.build_branch( &mut builder, - &leaf.make_tree.indices, + &leaf.propagate.indices, &unbounded_targets.left_proof, &unbounded_targets.right_proof, ); - builder.connect( - unbounded_targets.left_is_leaf.target, - make_tree_targets.left_is_leaf.target, - ); - builder.connect( - unbounded_targets.right_is_leaf.target, - make_tree_targets.right_is_leaf.target, - ); - let circuit = builder.build(); let public_inputs = &circuit.prover_only.public_inputs; let unbounded = unbounded_targets.build(&leaf.unbounded, public_inputs); - let make_tree = make_tree_targets.build(&leaf.make_tree.indices, public_inputs); + let propagate = propagate_targets.build(&leaf.propagate.indices, public_inputs); Self { - make_tree, + propagate, unbounded, circuit, } @@ -104,26 +92,23 @@ impl DummyBranchCircuit { pub fn prove( &self, - hash: HashOut, - leaf_value: HashOut, + left_is_leaf: bool, left_proof: &ProofWithPublicInputs, + right_is_leaf: bool, right_proof: &ProofWithPublicInputs, ) -> Result> { let mut inputs = PartialWitness::new(); - self.unbounded - .set_partial_witness(&mut inputs, left_proof, right_proof); - self.make_tree.set_witness(&mut inputs, hash, leaf_value); + self.unbounded.set_witness( + &mut inputs, + left_is_leaf, + left_proof, + right_is_leaf, + right_proof, + ); self.circuit.prove(inputs) } } -fn hash_write(address: u64, left: &HashOut, right: &HashOut) -> HashOut { - let address = F::from_canonical_u64(address); - let [l0, l1, l2, l3] = left.elements; - let [r0, r1, r2, r3] = right.elements; - Poseidon2Hash::hash_no_pad(&[address, l0, l1, l2, l3, r0, r1, r2, r3]) -} - fn bench_prove_verify_recproof(c: &mut Criterion) { let mut group = c.benchmark_group("prove_verify_recproof"); group.measurement_time(Duration::new(10, 0)); @@ -135,24 +120,13 @@ fn bench_prove_verify_recproof(c: &mut Criterion) { let zero_hash = black_box(HashOut::from([F::ZERO; 4])); let non_zero_hash_1 = black_box(hash_str("Non-Zero Hash 1")); - let hash_0_and_0 = black_box(hash_branch(&zero_hash, &zero_hash)); - let hash_0_and_1 = black_box(hash_branch(&zero_hash, &non_zero_hash_1)); - let hash_1_and_0 = hash_branch(&non_zero_hash_1, &zero_hash); - let hash_00_and_00 = hash_branch(&hash_0_and_0, &hash_0_and_0); - let hash_01_and_10 = hash_branch(&hash_0_and_1, &hash_1_and_0); - - let slot_3_r0w1 = hash_write(3, &zero_hash, &non_zero_hash_1); - let slot_4_r0w1 = hash_write(4, &zero_hash, &non_zero_hash_1); - let slot_3_and_4 = hash_branch(&slot_3_r0w1, &slot_4_r0w1); // Leaf proofs - let zero_proof = leaf_circuit - .prove(zero_hash, zero_hash, zero_hash, None) - .unwrap(); + let zero_proof = leaf_circuit.prove(zero_hash, zero_hash, None).unwrap(); leaf_circuit.circuit.verify(zero_proof.clone()).unwrap(); let proof_0_to_1_id_3 = leaf_circuit - .prove(zero_hash, non_zero_hash_1, slot_3_r0w1, Some(3)) + .prove(zero_hash, non_zero_hash_1, Some(3)) .unwrap(); leaf_circuit .circuit @@ -160,7 +134,7 @@ fn bench_prove_verify_recproof(c: &mut Criterion) { .unwrap(); let proof_0_to_1_id_4 = leaf_circuit - .prove(zero_hash, non_zero_hash_1, slot_4_r0w1, Some(4)) + .prove(zero_hash, non_zero_hash_1, Some(4)) .unwrap(); leaf_circuit .circuit @@ -169,32 +143,18 @@ fn bench_prove_verify_recproof(c: &mut Criterion) { // Branch proofs let branch_00_and_01_proof = branch_circuit_1 - .prove( - hash_0_and_0, - hash_0_and_1, - slot_3_r0w1, - (), - &zero_proof, - &proof_0_to_1_id_3, - ) + .prove(&zero_proof, &proof_0_to_1_id_3) .unwrap(); let branch_01_and_00_proof = branch_circuit_1 - .prove( - hash_0_and_0, - hash_1_and_0, - slot_4_r0w1, - (), - &proof_0_to_1_id_4, - &zero_proof, - ) + .prove(&proof_0_to_1_id_4, &zero_proof) .unwrap(); // Benches group.bench_function("recproof_leaf_prove", |b| { b.iter(|| { leaf_circuit - .prove(zero_hash, non_zero_hash_1, slot_3_r0w1, Some(3)) + .prove(zero_hash, non_zero_hash_1, Some(3)) .unwrap() }) }); @@ -210,14 +170,7 @@ fn bench_prove_verify_recproof(c: &mut Criterion) { group.bench_function("recproof_branch_prove_1", |b| { b.iter(|| { branch_circuit_1 - .prove( - hash_0_and_0, - hash_0_and_1, - slot_3_r0w1, - (), - &zero_proof, - &proof_0_to_1_id_3, - ) + .prove(&zero_proof, &proof_0_to_1_id_3) .unwrap() }) }); @@ -233,14 +186,7 @@ fn bench_prove_verify_recproof(c: &mut Criterion) { group.bench_function("recproof_branch_prove_2", |b| { b.iter(|| { branch_circuit_2 - .prove( - hash_00_and_00, - hash_01_and_10, - slot_3_and_4, - (), - &branch_00_and_01_proof, - &branch_01_and_00_proof, - ) + .prove(&branch_00_and_01_proof, &branch_01_and_00_proof) .unwrap() }) }); @@ -257,34 +203,32 @@ fn bench_prove_verify_unbounded(c: &mut Criterion) { let branch = DummyBranchCircuit::new(&circuit_config, &leaf); let non_zero_hash = black_box(hash_str("Non-Zero Hash")); - let branch_hash = hash_branch(&non_zero_hash, &non_zero_hash); - let branch_hash_1 = hash_branch(&non_zero_hash, &branch_hash); - let leaf_1_proof = leaf.prove(false, non_zero_hash, &branch).unwrap(); + let leaf_1_proof = leaf.prove(non_zero_hash, &branch).unwrap(); leaf.circuit.verify(leaf_1_proof.clone()).unwrap(); - let leaf_2_proof = leaf.prove(true, non_zero_hash, &branch).unwrap(); + let leaf_2_proof = leaf.prove(non_zero_hash, &branch).unwrap(); leaf.circuit.verify(leaf_2_proof.clone()).unwrap(); let branch_proof_1 = branch - .prove(non_zero_hash, non_zero_hash, &leaf_1_proof, &leaf_2_proof) + .prove(true, &leaf_1_proof, true, &leaf_2_proof) .unwrap(); branch.circuit.verify(branch_proof_1.clone()).unwrap(); let branch_proof_2 = branch - .prove(branch_hash, non_zero_hash, &leaf_2_proof, &leaf_2_proof) + .prove(true, &leaf_2_proof, true, &leaf_2_proof) .unwrap(); branch.circuit.verify(branch_proof_2.clone()).unwrap(); let double_branch_proof = branch - .prove(branch_hash_1, non_zero_hash, &leaf_2_proof, &branch_proof_2) + .prove(true, &leaf_2_proof, false, &branch_proof_2) .unwrap(); branch.circuit.verify(double_branch_proof.clone()).unwrap(); group.bench_function("branch_prove_1", |b| { b.iter(|| { branch - .prove(non_zero_hash, non_zero_hash, &leaf_1_proof, &leaf_2_proof) + .prove(true, &leaf_1_proof, true, &leaf_2_proof) .unwrap() }) }); @@ -295,7 +239,7 @@ fn bench_prove_verify_unbounded(c: &mut Criterion) { group.bench_function("branch_prove_2", |b| { b.iter(|| { branch - .prove(branch_hash_1, non_zero_hash, &leaf_2_proof, &branch_proof_2) + .prove(true, &leaf_2_proof, false, &branch_proof_2) .unwrap() }) }); diff --git a/recproofs/src/circuits/accumulate_delta.rs b/recproofs/src/circuits/accumulate_delta.rs index e8122f87b..205b40a43 100644 --- a/recproofs/src/circuits/accumulate_delta.rs +++ b/recproofs/src/circuits/accumulate_delta.rs @@ -193,138 +193,547 @@ where #[cfg(test)] pub mod test { - use lazy_static::lazy_static; + use enumflags2::BitFlags; use plonky2::field::types::Field; use super::*; + use crate::circuits::test_data::{ + ADDRESS_A, ADDRESS_B, ADDRESS_C, ADDRESS_D, EVENT_T0_P0_A_CREDIT, EVENT_T0_P0_A_WRITE, + EVENT_T0_P2_A_ENSURE, EVENT_T0_P2_A_READ, EVENT_T0_P2_C_TAKE, EVENT_T0_PM_C_CREDIT, + EVENT_T0_PM_C_GIVE, EVENT_T0_PM_C_WRITE, EVENT_T1_P1_B_CREDIT, EVENT_T1_P1_B_GIVE, + EVENT_T1_P1_B_WRITE, EVENT_T1_P2_A_READ, EVENT_T1_P2_D_READ, EVENT_T1_PM_B_ENSURE, + EVENT_T1_PM_B_TAKE, STATE_0, STATE_1, + }; use crate::test_utils::{C, CONFIG, D, F}; + use crate::EventFlags; - lazy_static! { - pub static ref LEAF: LeafCircuit = LeafCircuit::new(&CONFIG); - pub static ref BRANCH: BranchCircuit = BranchCircuit::new(&CONFIG, &LEAF); + #[tested_fixture::tested_fixture(pub LEAF)] + fn build_leaf() -> LeafCircuit { LeafCircuit::new(&CONFIG) } + + #[tested_fixture::tested_fixture(pub BRANCH)] + fn build_branch() -> BranchCircuit { BranchCircuit::new(&CONFIG, &LEAF) } + + #[allow(clippy::too_many_arguments)] + fn assert_proof( + proof: &ProofWithPublicInputs, + address: u64, + flags: impl Into>, + old_owner: impl Into>, + new_owner: impl Into>, + old_data: impl Into>, + new_data: impl Into>, + credit_delta: impl Into>, + ) { + let indices = &LEAF.partial_state.indices; + assert_eq!(*indices, BRANCH.partial_state.indices); + + let p_address = indices.address.get(&proof.public_inputs); + assert_eq!(p_address, F::from_canonical_u64(address)); + let p_flags = indices.object_flags.get(&proof.public_inputs); + assert_eq!(p_flags, F::from_canonical_u8(flags.into().bits())); + let p_old_owner = indices.old_owner.get_any(&proof.public_inputs); + assert_eq!(p_old_owner, old_owner.into().unwrap_or_default()); + let p_new_owner = indices.new_owner.get_any(&proof.public_inputs); + assert_eq!(p_new_owner, new_owner.into().unwrap_or_default()); + let p_old_data = indices.old_data.get_any(&proof.public_inputs); + assert_eq!(p_old_data, old_data.into().unwrap_or_default()); + let p_new_data = indices.new_data.get_any(&proof.public_inputs); + assert_eq!(p_new_data, new_data.into().unwrap_or_default()); + let p_credit_delta = indices.credit_delta.get(&proof.public_inputs); + assert_eq!(p_credit_delta, credit_delta.into().unwrap_or_default()); } - #[test] - fn verify_leaf() -> Result<()> { - let program_hash_1 = [4, 8, 15, 16].map(F::from_canonical_u64); - let program_hash_2 = [2, 3, 4, 2].map(F::from_canonical_u64); - - let non_zero_val_1 = [3, 1, 4, 15].map(F::from_canonical_u64); - let non_zero_val_2 = [42, 0, 0, 0].map(F::from_canonical_u64); - let non_zero_val_3 = [42, 0, 0, 1].map(F::from_canonical_u64); - - let proof = LEAF.prove( - &BRANCH, - 200, - program_hash_1, - EventType::Write, - non_zero_val_1, - )?; - LEAF.circuit.verify(proof)?; - - let proof = LEAF.prove( - &BRANCH, - 200, - program_hash_1, - EventType::Read, - non_zero_val_1, - )?; - LEAF.circuit.verify(proof)?; - - let proof = LEAF.prove( - &BRANCH, - 200, - program_hash_1, - EventType::Ensure, - non_zero_val_1, - )?; - LEAF.circuit.verify(proof)?; - - let proof = LEAF.prove( - &BRANCH, - 200, - program_hash_1, - EventType::GiveOwner, - program_hash_2, - )?; - LEAF.circuit.verify(proof)?; - - let proof = LEAF.prove( - &BRANCH, - 200, - program_hash_2, - EventType::TakeOwner, - program_hash_1, - )?; - LEAF.circuit.verify(proof)?; - - let proof = LEAF.prove( - &BRANCH, - 200, - program_hash_1, - EventType::CreditDelta, - non_zero_val_2, + #[tested_fixture::tested_fixture(T0_PM_C_CREDIT_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_t0_pm_c_credit_leaf() -> Result> { + let event = EVENT_T0_PM_C_CREDIT; + let proof = LEAF.prove(&BRANCH, event.address, event.owner, event.ty, event.value)?; + assert_proof( + &proof, + event.address, + BitFlags::empty(), + event.owner, + None, + None, + None, + event.value[0], + ); + LEAF.circuit.verify(proof.clone())?; + Ok(proof) + } + + #[tested_fixture::tested_fixture(T0_PM_C_GIVE_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_t0_pm_c_give_leaf() -> Result> { + let event = EVENT_T0_PM_C_GIVE; + let proof = LEAF.prove(&BRANCH, event.address, event.owner, event.ty, event.value)?; + assert_proof( + &proof, + event.address, + EventFlags::GiveOwnerFlag, + event.owner, + event.value, + None, + None, + None, + ); + LEAF.circuit.verify(proof.clone())?; + Ok(proof) + } + + #[tested_fixture::tested_fixture(T0_PM_C_WRITE_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_t0_pm_c_write_leaf() -> Result> { + let event = EVENT_T0_PM_C_WRITE; + let proof = LEAF.prove(&BRANCH, event.address, event.owner, event.ty, event.value)?; + assert_proof( + &proof, + event.address, + EventFlags::WriteFlag, + event.owner, + None, + None, + event.value, + None, + ); + LEAF.circuit.verify(proof.clone())?; + Ok(proof) + } + + #[tested_fixture::tested_fixture(T0_P0_A_WRITE_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_t0_p0_a_write_leaf() -> Result> { + let event = EVENT_T0_P0_A_WRITE; + let proof = LEAF.prove(&BRANCH, event.address, event.owner, event.ty, event.value)?; + assert_proof( + &proof, + event.address, + EventFlags::WriteFlag, + event.owner, + None, + None, + event.value, + None, + ); + LEAF.circuit.verify(proof.clone())?; + Ok(proof) + } + + #[tested_fixture::tested_fixture(T0_P0_A_CREDIT_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_t0_p0_a_credit_leaf() -> Result> { + let event = EVENT_T0_P0_A_CREDIT; + let proof = LEAF.prove(&BRANCH, event.address, event.owner, event.ty, event.value)?; + assert_proof( + &proof, + event.address, + BitFlags::empty(), + event.owner, + None, + None, + None, + -event.value[0], + ); + LEAF.circuit.verify(proof.clone())?; + Ok(proof) + } + + #[tested_fixture::tested_fixture(T0_P2_A_READ_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_t0_p2_a_read_leaf() -> Result> { + let event = EVENT_T0_P2_A_READ; + let proof = LEAF.prove(&BRANCH, event.address, event.owner, event.ty, event.value)?; + assert_proof( + &proof, + event.address, + EventFlags::ReadFlag, + None, + None, + event.value, + None, + None, + ); + LEAF.circuit.verify(proof.clone())?; + Ok(proof) + } + + #[tested_fixture::tested_fixture(T0_P2_A_ENSURE_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_t0_p2_a_ensure_leaf() -> Result> { + let event = EVENT_T0_P2_A_ENSURE; + let proof = LEAF.prove(&BRANCH, event.address, event.owner, event.ty, event.value)?; + assert_proof( + &proof, + event.address, + EventFlags::EnsureFlag, + None, + None, + None, + event.value, + None, + ); + LEAF.circuit.verify(proof.clone())?; + Ok(proof) + } + + #[tested_fixture::tested_fixture(T0_P2_C_TAKE_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_t0_p2_c_take_leaf() -> Result> { + let event = EVENT_T0_P2_C_TAKE; + let proof = LEAF.prove(&BRANCH, event.address, event.owner, event.ty, event.value)?; + assert_proof( + &proof, + event.address, + EventFlags::TakeOwnerFlag, + event.value, + event.owner, + None, + None, + None, + ); + LEAF.circuit.verify(proof.clone())?; + Ok(proof) + } + + #[tested_fixture::tested_fixture(T1_PM_B_TAKE_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_t1_pm_b_take_leaf() -> Result> { + let event = EVENT_T1_PM_B_TAKE; + let proof = LEAF.prove(&BRANCH, event.address, event.owner, event.ty, event.value)?; + assert_proof( + &proof, + event.address, + EventFlags::TakeOwnerFlag, + event.value, + event.owner, + None, + None, + None, + ); + LEAF.circuit.verify(proof.clone())?; + Ok(proof) + } + + #[tested_fixture::tested_fixture(T1_PM_B_ENSURE_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_t1_pm_b_ensure_leaf() -> Result> { + let event = EVENT_T1_PM_B_ENSURE; + let proof = LEAF.prove(&BRANCH, event.address, event.owner, event.ty, event.value)?; + assert_proof( + &proof, + event.address, + EventFlags::EnsureFlag, + None, + None, + None, + event.value, + None, + ); + LEAF.circuit.verify(proof.clone())?; + Ok(proof) + } + + #[tested_fixture::tested_fixture(T1_P1_B_WRITE_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_t1_p1_b_write_leaf() -> Result> { + let event = EVENT_T1_P1_B_WRITE; + let proof = LEAF.prove(&BRANCH, event.address, event.owner, event.ty, event.value)?; + assert_proof( + &proof, + event.address, + EventFlags::WriteFlag, + event.owner, + None, + None, + event.value, + None, + ); + LEAF.circuit.verify(proof.clone())?; + Ok(proof) + } + + #[tested_fixture::tested_fixture(T1_P1_B_GIVE_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_t1_p1_b_give_leaf() -> Result> { + let event = EVENT_T1_P1_B_GIVE; + let proof = LEAF.prove(&BRANCH, event.address, event.owner, event.ty, event.value)?; + assert_proof( + &proof, + event.address, + EventFlags::GiveOwnerFlag, + event.owner, + event.value, + None, + None, + None, + ); + LEAF.circuit.verify(proof.clone())?; + Ok(proof) + } + + #[tested_fixture::tested_fixture(T1_P1_B_CREDIT_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_t1_p1_b_credit_leaf() -> Result> { + let event = EVENT_T1_P1_B_CREDIT; + let proof = LEAF.prove(&BRANCH, event.address, event.owner, event.ty, event.value)?; + assert_proof( + &proof, + event.address, + BitFlags::empty(), + event.owner, + None, + None, + None, + -event.value[0], + ); + LEAF.circuit.verify(proof.clone())?; + Ok(proof) + } + + #[tested_fixture::tested_fixture(T1_P2_A_READ_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_t1_p2_a_read_leaf() -> Result> { + let event = EVENT_T1_P2_A_READ; + let proof = LEAF.prove(&BRANCH, event.address, event.owner, event.ty, event.value)?; + assert_proof( + &proof, + event.address, + EventFlags::ReadFlag, + None, + None, + event.value, + None, + None, + ); + LEAF.circuit.verify(proof.clone())?; + Ok(proof) + } + + #[tested_fixture::tested_fixture(T1_P2_D_READ_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_t1_p2_d_read_leaf() -> Result> { + let event = EVENT_T1_P2_D_READ; + let proof = LEAF.prove(&BRANCH, event.address, event.owner, event.ty, event.value)?; + assert_proof( + &proof, + event.address, + EventFlags::ReadFlag, + None, + None, + event.value, + None, + None, + ); + LEAF.circuit.verify(proof.clone())?; + Ok(proof) + } + + #[tested_fixture::tested_fixture(pub A_BRANCH_PROOF: ProofWithPublicInputs)] + fn verify_a_branch() -> Result> { + let address = ADDRESS_A; + let old = &STATE_0[address]; + let new = &STATE_1[address]; + let address = address as u64; + let credit_delta = -EVENT_T0_P0_A_CREDIT.value[0]; + + let p0_proof = BRANCH.prove( + true, + &T0_P0_A_WRITE_LEAF_PROOF, + Some((true, &T0_P0_A_CREDIT_LEAF_PROOF)), )?; - LEAF.circuit.verify(proof)?; - - let proof = LEAF.prove( - &BRANCH, - 200, - program_hash_1, - EventType::CreditDelta, - non_zero_val_3, + assert_proof( + &p0_proof, + address, + EventFlags::WriteFlag, + old.constraint_owner, + None, + None, + new.data, + credit_delta, + ); + BRANCH.circuit.verify(p0_proof.clone())?; + + let p2_proof = BRANCH.prove( + true, + &T0_P2_A_READ_LEAF_PROOF, + Some((true, &T0_P2_A_ENSURE_LEAF_PROOF)), )?; - LEAF.circuit.verify(proof)?; + assert_proof( + &p2_proof, + address, + EventFlags::ReadFlag | EventFlags::EnsureFlag, + None, + None, + old.data, + new.data, + None, + ); + BRANCH.circuit.verify(p2_proof.clone())?; - Ok(()) - } + let t0_proof = BRANCH.prove(false, &p0_proof, Some((false, &p2_proof)))?; + assert_proof( + &t0_proof, + address, + EventFlags::WriteFlag | EventFlags::ReadFlag | EventFlags::EnsureFlag, + old.constraint_owner, + None, + old.data, + new.data, + credit_delta, + ); + BRANCH.circuit.verify(t0_proof.clone())?; - #[test] - fn verify_branch() -> Result<()> { - let program_hash_1 = [4, 8, 15, 16].map(F::from_canonical_u64); + let root_proof = BRANCH.prove(false, &t0_proof, Some((true, &T1_P2_A_READ_LEAF_PROOF)))?; + assert_proof( + &root_proof, + address, + EventFlags::WriteFlag | EventFlags::ReadFlag | EventFlags::EnsureFlag, + old.constraint_owner, + None, + old.data, + new.data, + credit_delta, + ); + BRANCH.circuit.verify(root_proof.clone())?; - let non_zero_val_1 = [3, 1, 4, 15].map(F::from_canonical_u64); - let non_zero_val_2 = [1, 6, 180, 33].map(F::from_canonical_u64); + Ok(root_proof) + } - let read_proof = LEAF.prove( - &BRANCH, - 200, - program_hash_1, - EventType::Read, - non_zero_val_1, + #[tested_fixture::tested_fixture(pub B_BRANCH_PROOF: ProofWithPublicInputs)] + fn verify_b_branch() -> Result> { + let address = ADDRESS_B; + let old = &STATE_0[address]; + let new = &STATE_1[address]; + let address = address as u64; + let credit_delta = -EVENT_T1_P1_B_CREDIT.value[0]; + + let pm_proof = BRANCH.prove( + true, + &T1_PM_B_TAKE_LEAF_PROOF, + Some((true, &T1_PM_B_ENSURE_LEAF_PROOF)), )?; - LEAF.circuit.verify(read_proof.clone())?; - - let write_proof = LEAF.prove( - &BRANCH, - 200, - program_hash_1, - EventType::Write, - non_zero_val_2, + assert_proof( + &pm_proof, + address, + EventFlags::TakeOwnerFlag | EventFlags::EnsureFlag, + old.constraint_owner, + new.constraint_owner, + None, + new.data, + None, + ); + BRANCH.circuit.verify(pm_proof.clone())?; + + let p1_proof_1 = BRANCH.prove( + true, + &T1_P1_B_WRITE_LEAF_PROOF, + Some((true, &T1_P1_B_GIVE_LEAF_PROOF)), )?; - LEAF.circuit.verify(write_proof.clone())?; - - let ensure_proof = LEAF.prove( - &BRANCH, - 200, - program_hash_1, - EventType::Ensure, - non_zero_val_2, + assert_proof( + &p1_proof_1, + address, + EventFlags::GiveOwnerFlag | EventFlags::WriteFlag, + old.constraint_owner, + new.constraint_owner, + None, + new.data, + None, + ); + BRANCH.circuit.verify(p1_proof_1.clone())?; + + let p1_proof_2 = + BRANCH.prove(false, &p1_proof_1, Some((true, &T1_P1_B_CREDIT_LEAF_PROOF)))?; + assert_proof( + &p1_proof_2, + address, + EventFlags::GiveOwnerFlag | EventFlags::WriteFlag, + old.constraint_owner, + new.constraint_owner, + None, + new.data, + credit_delta, + ); + BRANCH.circuit.verify(p1_proof_2.clone())?; + + let root_proof = BRANCH.prove(false, &pm_proof, Some((false, &p1_proof_2)))?; + assert_proof( + &root_proof, + address, + EventFlags::TakeOwnerFlag + | EventFlags::EnsureFlag + | EventFlags::GiveOwnerFlag + | EventFlags::WriteFlag, + old.constraint_owner, + new.constraint_owner, + None, + new.data, + credit_delta, + ); + BRANCH.circuit.verify(root_proof.clone())?; + + Ok(root_proof) + } + + #[tested_fixture::tested_fixture(pub C_BRANCH_PROOF: ProofWithPublicInputs)] + fn verify_c_branch() -> Result> { + let address = ADDRESS_C; + let old = &STATE_0[address]; + let new = &STATE_1[address]; + let address = address as u64; + let credit_delta = EVENT_T0_PM_C_CREDIT.value[0]; + + let pm_proof_1 = BRANCH.prove( + true, + &T0_PM_C_CREDIT_LEAF_PROOF, + Some((true, &T0_PM_C_GIVE_LEAF_PROOF)), )?; - LEAF.circuit.verify(ensure_proof.clone())?; + assert_proof( + &pm_proof_1, + address, + EventFlags::GiveOwnerFlag, + old.constraint_owner, + new.constraint_owner, + None, + None, + credit_delta, + ); + BRANCH.circuit.verify(pm_proof_1.clone())?; - let branch_proof_1 = BRANCH.prove(true, &read_proof, Some((true, &write_proof)))?; - BRANCH.circuit.verify(branch_proof_1.clone())?; + let pm_proof_2 = + BRANCH.prove(false, &pm_proof_1, Some((true, &T0_PM_C_WRITE_LEAF_PROOF)))?; + assert_proof( + &pm_proof_2, + address, + EventFlags::GiveOwnerFlag | EventFlags::WriteFlag, + old.constraint_owner, + new.constraint_owner, + None, + new.data, + credit_delta, + ); + BRANCH.circuit.verify(pm_proof_2.clone())?; - let branch_proof_2 = BRANCH.prove(false, &branch_proof_1, Some((true, &ensure_proof)))?; - BRANCH.circuit.verify(branch_proof_2.clone())?; + let root_proof = + BRANCH.prove(false, &pm_proof_2, Some((true, &T0_P2_C_TAKE_LEAF_PROOF)))?; + assert_proof( + &root_proof, + address, + EventFlags::GiveOwnerFlag | EventFlags::WriteFlag | EventFlags::TakeOwnerFlag, + old.constraint_owner, + new.constraint_owner, + None, + new.data, + credit_delta, + ); + BRANCH.circuit.verify(root_proof.clone())?; + + Ok(root_proof) + } - let branch_proof_3 = BRANCH.prove(false, &branch_proof_2, None)?; - BRANCH.circuit.verify(branch_proof_3)?; + #[tested_fixture::tested_fixture(pub D_BRANCH_PROOF: ProofWithPublicInputs)] + fn verify_d_branch() -> Result> { + let address = ADDRESS_D; + let old = &STATE_0[address]; + let address = address as u64; - let branch_proof_4 = BRANCH.prove(true, &read_proof, None)?; - BRANCH.circuit.verify(branch_proof_4)?; + let proof = BRANCH.prove(true, &T1_P2_D_READ_LEAF_PROOF, None)?; + assert_proof( + &proof, + address, + EventFlags::ReadFlag, + None, + None, + old.data, + None, + None, + ); + BRANCH.circuit.verify(proof.clone())?; - Ok(()) + Ok(proof) } } diff --git a/recproofs/src/circuits/accumulate_delta/core.rs b/recproofs/src/circuits/accumulate_delta/core.rs index 4c9bdbda9..22aa804a4 100644 --- a/recproofs/src/circuits/accumulate_delta/core.rs +++ b/recproofs/src/circuits/accumulate_delta/core.rs @@ -712,12 +712,10 @@ impl BranchSubCircuit { witness.set_target_arr(&targets.new_data, &new_data); // Handle the credits - #[allow(clippy::cast_possible_wrap)] - let left_credits = indices.credit_delta.get(left_inputs).to_canonical_u64() as i64; - #[allow(clippy::cast_possible_wrap)] - let right_credits = indices.credit_delta.get(right_inputs).to_canonical_u64() as i64; + let left_credits = indices.credit_delta.get(left_inputs); + let right_credits = indices.credit_delta.get(right_inputs); let credits = left_credits + right_credits; - witness.set_target(targets.credit_delta, F::from_noncanonical_i64(credits)); + witness.set_target(targets.credit_delta, credits); // Both sides, so not partial witness.set_bool_target(self.targets.partial, false); @@ -730,14 +728,18 @@ mod test { use std::panic::{catch_unwind, UnwindSafe}; use anyhow::Result; - use lazy_static::lazy_static; use plonky2::field::types::Field; use plonky2::plonk::circuit_data::{CircuitConfig, CircuitData}; use plonky2::plonk::proof::ProofWithPublicInputs; use super::*; + use crate::circuits::test_data::{ + ADDRESS_A, EVENT_T0_P0_A_CREDIT, EVENT_T0_P0_A_WRITE, EVENT_T0_P2_A_ENSURE, + EVENT_T0_P2_A_READ, EVENT_T0_P2_C_TAKE, EVENT_T0_PM_C_CREDIT, EVENT_T0_PM_C_GIVE, + PROGRAM_HASHES, STATE_0, STATE_1, + }; use crate::subcircuits::bounded; - use crate::test_utils::{C, CONFIG, D, F}; + use crate::test_utils::{C, CONFIG, D, F, ZERO_VAL}; pub struct DummyLeafCircuit { pub bounded: bounded::LeafSubCircuit, @@ -881,88 +883,174 @@ mod test { } } - lazy_static! { - static ref LEAF: DummyLeafCircuit = DummyLeafCircuit::new(&CONFIG); - static ref BRANCHES: [DummyBranchCircuit; 2] = { - let b0 = DummyBranchCircuit::from_leaf(&CONFIG, &LEAF); - let b1 = DummyBranchCircuit::from_branch(&CONFIG, &b0); - [b0, b1] - }; + #[tested_fixture::tested_fixture(LEAF)] + fn build_leaf() -> DummyLeafCircuit { DummyLeafCircuit::new(&CONFIG) } + + #[tested_fixture::tested_fixture(BRANCHES)] + fn build_branches() -> [DummyBranchCircuit; 2] { + let b0 = DummyBranchCircuit::from_leaf(&CONFIG, &LEAF); + let b1 = DummyBranchCircuit::from_branch(&CONFIG, &b0); + [b0, b1] } - #[test] - fn verify_leaf() -> Result<()> { - let program_hash_1 = [4, 8, 15, 16].map(F::from_canonical_u64); - let program_hash_2 = [2, 3, 4, 2].map(F::from_canonical_u64); - - let non_zero_val_1 = [3, 1, 4, 15].map(F::from_canonical_u64); - let non_zero_val_2 = [42, 0, 0, 0].map(F::from_canonical_u64); - let non_zero_val_3 = [42, 0, 0, 1].map(F::from_canonical_u64); - - let proof = LEAF.prove(Event { - owner: program_hash_1, - ty: EventType::Write, - address: 200, - value: non_zero_val_1, - })?; - LEAF.circuit.verify(proof)?; - - let proof = LEAF.prove(Event { - owner: program_hash_1, - ty: EventType::Read, - address: 200, - value: non_zero_val_1, - })?; - LEAF.circuit.verify(proof)?; - - let proof = LEAF.prove(Event { - owner: program_hash_1, - ty: EventType::Ensure, - address: 200, - value: non_zero_val_1, - })?; - LEAF.circuit.verify(proof)?; - - let proof = LEAF.prove(Event { - owner: program_hash_1, - ty: EventType::GiveOwner, - address: 200, - value: program_hash_2, - })?; - LEAF.circuit.verify(proof)?; - - let proof = LEAF.prove(Event { - owner: program_hash_2, - ty: EventType::TakeOwner, - address: 200, - value: program_hash_1, - })?; - LEAF.circuit.verify(proof)?; - - let proof = LEAF.prove(Event { - owner: program_hash_1, - ty: EventType::CreditDelta, - address: 200, - value: non_zero_val_2, - })?; - LEAF.circuit.verify(proof)?; - - let proof = LEAF.prove(Event { - owner: program_hash_1, - ty: EventType::CreditDelta, - address: 200, - value: non_zero_val_3, - })?; - LEAF.circuit.verify(proof)?; + #[allow(clippy::too_many_arguments)] + fn assert_leaf( + proof: &ProofWithPublicInputs, + address: u64, + flags: impl Into>, + old_owner: impl Into>, + new_owner: impl Into>, + old_data: impl Into>, + new_data: impl Into>, + credit_delta: impl Into>, + ) { + let indices = &LEAF.state_from_events.indices; + let p_address = indices.address.get(&proof.public_inputs); + assert_eq!(p_address, F::from_canonical_u64(address)); + let p_flags = indices.object_flags.get(&proof.public_inputs); + assert_eq!(p_flags, F::from_canonical_u8(flags.into().bits())); + let p_old_owner = indices.old_owner.get_any(&proof.public_inputs); + assert_eq!(p_old_owner, old_owner.into().unwrap_or_default()); + let p_new_owner = indices.new_owner.get_any(&proof.public_inputs); + assert_eq!(p_new_owner, new_owner.into().unwrap_or_default()); + let p_old_data = indices.old_data.get_any(&proof.public_inputs); + assert_eq!(p_old_data, old_data.into().unwrap_or_default()); + let p_new_data = indices.new_data.get_any(&proof.public_inputs); + assert_eq!(p_new_data, new_data.into().unwrap_or_default()); + let p_credit_delta = indices.credit_delta.get(&proof.public_inputs); + assert_eq!(p_credit_delta, credit_delta.into().unwrap_or_default()); + } - Ok(()) + #[tested_fixture::tested_fixture(WRITE_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_write_leaf() -> Result> { + let event = EVENT_T0_P0_A_WRITE; + let proof = LEAF.prove(event)?; + assert_leaf( + &proof, + event.address, + EventFlags::WriteFlag, + event.owner, + None, + None, + event.value, + None, + ); + LEAF.circuit.verify(proof.clone())?; + Ok(proof) + } + + #[tested_fixture::tested_fixture(READ_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_read_leaf() -> Result> { + let event = EVENT_T0_P2_A_READ; + let proof = LEAF.prove(event)?; + assert_leaf( + &proof, + event.address, + EventFlags::ReadFlag, + None, + None, + event.value, + None, + None, + ); + LEAF.circuit.verify(proof.clone())?; + Ok(proof) + } + + #[tested_fixture::tested_fixture(ENSURE_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_ensure_leaf() -> Result> { + let event = EVENT_T0_P2_A_ENSURE; + let proof = LEAF.prove(event)?; + assert_leaf( + &proof, + event.address, + EventFlags::EnsureFlag, + None, + None, + None, + event.value, + None, + ); + LEAF.circuit.verify(proof.clone())?; + Ok(proof) + } + + #[tested_fixture::tested_fixture(GIVE_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_give_leaf() -> Result> { + let event = EVENT_T0_PM_C_GIVE; + let proof = LEAF.prove(event)?; + assert_leaf( + &proof, + event.address, + EventFlags::GiveOwnerFlag, + event.owner, + event.value, + None, + None, + None, + ); + LEAF.circuit.verify(proof.clone())?; + Ok(proof) + } + + #[tested_fixture::tested_fixture(TAKE_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_take_leaf() -> Result> { + let event = EVENT_T0_P2_C_TAKE; + let proof = LEAF.prove(event)?; + assert_leaf( + &proof, + event.address, + EventFlags::TakeOwnerFlag, + event.value, + event.owner, + None, + None, + None, + ); + LEAF.circuit.verify(proof.clone())?; + Ok(proof) + } + + #[tested_fixture::tested_fixture(CREDIT_PLUS_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_credit_plus_leaf() -> Result> { + let event = EVENT_T0_PM_C_CREDIT; + let proof = LEAF.prove(event)?; + assert_leaf( + &proof, + event.address, + BitFlags::empty(), + event.owner, + None, + None, + None, + event.value[0], + ); + LEAF.circuit.verify(proof.clone())?; + Ok(proof) + } + + #[tested_fixture::tested_fixture(CREDIT_MINUS_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_credit_minus_leaf() -> Result> { + let event = EVENT_T0_P0_A_CREDIT; + let proof = LEAF.prove(event)?; + assert_leaf( + &proof, + event.address, + BitFlags::empty(), + event.owner, + None, + None, + None, + -event.value[0], + ); + LEAF.circuit.verify(proof.clone())?; + Ok(proof) } - fn leaf_test_helper(owner: [u64; 4], ty: EventType, value: [u64; 4], f: Fn) + fn leaf_test_helper(owner: [F; 4], ty: EventType, value: [u64; 4], f: Fn) where Fn: FnOnce(&mut LeafWitnessValue, [F; 4], [F; 4]) + UnwindSafe, { let event = catch_unwind(|| { - let owner = owner.map(F::from_canonical_u64); let value = value.map(F::from_canonical_u64); let mut event = LeafWitnessValue::from_event(Event { @@ -985,7 +1073,7 @@ mod test { #[should_panic(expected = "was set twice with different values")] fn bad_write_leaf_1() { leaf_test_helper( - [4, 8, 15, 16], + PROGRAM_HASHES[0], EventType::Write, [3, 1, 4, 15], |event, _, _| { @@ -998,7 +1086,7 @@ mod test { #[should_panic(expected = "was set twice with different values")] fn bad_write_leaf_2() { leaf_test_helper( - [4, 8, 15, 16], + PROGRAM_HASHES[0], EventType::Write, [3, 1, 4, 15], |event, _, _| { @@ -1011,7 +1099,7 @@ mod test { #[should_panic(expected = "was set twice with different values")] fn bad_write_leaf_3() { leaf_test_helper( - [4, 8, 15, 16], + PROGRAM_HASHES[0], EventType::Write, [3, 1, 4, 15], |event, _, _| { @@ -1024,7 +1112,7 @@ mod test { #[should_panic(expected = "was set twice with different values")] fn bad_write_leaf_4() { leaf_test_helper( - [4, 8, 15, 16], + PROGRAM_HASHES[0], EventType::Write, [3, 1, 4, 15], |event, _, _| { @@ -1037,7 +1125,7 @@ mod test { #[should_panic(expected = "was set twice with different values")] fn bad_ensure_leaf_1() { leaf_test_helper( - [4, 8, 15, 16], + PROGRAM_HASHES[0], EventType::Ensure, [3, 1, 4, 15], |event, _, _| { @@ -1050,7 +1138,7 @@ mod test { #[should_panic(expected = "was set twice with different values")] fn bad_give_leaf_1() { leaf_test_helper( - [4, 8, 15, 16], + PROGRAM_HASHES[0], EventType::GiveOwner, [3, 1, 4, 15], |event, owner, _| { @@ -1063,7 +1151,7 @@ mod test { #[should_panic(expected = "was set twice with different values")] fn bad_give_leaf_2() { leaf_test_helper( - [4, 8, 15, 16], + PROGRAM_HASHES[0], EventType::GiveOwner, [3, 1, 4, 15], |event, _, value| { @@ -1076,7 +1164,7 @@ mod test { #[should_panic(expected = "was set twice with different values")] fn bad_credit_leaf_1() { leaf_test_helper( - [4, 8, 15, 16], + PROGRAM_HASHES[0], EventType::CreditDelta, [13, 0, 0, 0], |event, _, _| { @@ -1089,7 +1177,7 @@ mod test { #[should_panic(expected = "was set twice with different values")] fn bad_credit_leaf_sign() { leaf_test_helper( - [4, 8, 15, 16], + PROGRAM_HASHES[0], EventType::CreditDelta, [13, 0, 0, 1], |event, _, _| { @@ -1334,106 +1422,105 @@ mod test { ); } + #[tested_fixture::tested_fixture(READ_WRITE_BRANCH_PROOF: ProofWithPublicInputs)] + fn verify_read_write_branch() -> Result> { + let witness = BranchWitnessValue { + address: ADDRESS_A as u64, + object_flags: EventFlags::ReadFlag | EventFlags::WriteFlag, + old_owner: PROGRAM_HASHES[0], + new_owner: ZERO_VAL, + old_data: STATE_0[ADDRESS_A].data, + new_data: STATE_1[ADDRESS_A].data, + credit_delta: 0, + }; + let proof = BRANCHES[0].prove(witness, &READ_LEAF_PROOF, Some(&WRITE_LEAF_PROOF))?; + BRANCHES[0].circuit.verify(proof)?; + let proof = BRANCHES[0].prove(witness, &WRITE_LEAF_PROOF, Some(&READ_LEAF_PROOF))?; + BRANCHES[0].circuit.verify(proof)?; + let proof = BRANCHES[0].prove_implicit(&READ_LEAF_PROOF, Some(&WRITE_LEAF_PROOF))?; + BRANCHES[0].circuit.verify(proof)?; + let proof = BRANCHES[0].prove_implicit(&WRITE_LEAF_PROOF, Some(&READ_LEAF_PROOF))?; + BRANCHES[0].circuit.verify(proof.clone())?; + Ok(proof) + } + + #[tested_fixture::tested_fixture(ENSURE_BRANCH_PROOF: ProofWithPublicInputs)] + fn verify_ensure_branch() -> Result> { + let witness = BranchWitnessValue { + address: ADDRESS_A as u64, + object_flags: EventFlags::EnsureFlag.into(), + old_owner: ZERO_VAL, + new_owner: ZERO_VAL, + old_data: ZERO_VAL, + new_data: STATE_1[ADDRESS_A].data, + credit_delta: 0, + }; + let proof = BRANCHES[0].prove(witness, &ENSURE_LEAF_PROOF, Some(&ENSURE_LEAF_PROOF))?; + BRANCHES[0].circuit.verify(proof)?; + let proof = BRANCHES[0].prove_implicit(&ENSURE_LEAF_PROOF, Some(&ENSURE_LEAF_PROOF))?; + BRANCHES[0].circuit.verify(proof.clone())?; + Ok(proof) + } + #[test] - fn verify_branch() -> Result<()> { - let program_hash_1 = [4, 8, 15, 16].map(F::from_canonical_u64); - - let zero_val = [F::ZERO; 4]; - let non_zero_val_1 = [3, 1, 4, 15].map(F::from_canonical_u64); - let non_zero_val_2 = [1, 6, 180, 33].map(F::from_canonical_u64); - - let read_proof = LEAF.prove(Event { - owner: program_hash_1, - ty: EventType::Read, - address: 200, - value: non_zero_val_1, - })?; - LEAF.circuit.verify(read_proof.clone())?; - - let write_proof = LEAF.prove(Event { - owner: program_hash_1, - ty: EventType::Write, - address: 200, - value: non_zero_val_2, - })?; - LEAF.circuit.verify(write_proof.clone())?; - - let ensure_proof = LEAF.prove(Event { - owner: program_hash_1, - ty: EventType::Ensure, - address: 200, - value: non_zero_val_2, - })?; - LEAF.circuit.verify(ensure_proof.clone())?; - - let branch_proof_1 = BRANCHES[0].prove( - BranchWitnessValue { - address: 200, - object_flags: EventFlags::ReadFlag | EventFlags::WriteFlag, - old_owner: program_hash_1, - new_owner: zero_val, - old_data: non_zero_val_1, - new_data: non_zero_val_2, - credit_delta: 0, - }, - &read_proof, - Some(&write_proof), - )?; - BRANCHES[0].circuit.verify(branch_proof_1.clone())?; - let branch_proof_1 = BRANCHES[0].prove_implicit(&read_proof, Some(&write_proof))?; - BRANCHES[0].circuit.verify(branch_proof_1.clone())?; + fn verify_left_branches() -> Result<()> { + let leafs = [ + &WRITE_LEAF_PROOF, + &READ_LEAF_PROOF, + &ENSURE_LEAF_PROOF, + &GIVE_LEAF_PROOF, + &TAKE_LEAF_PROOF, + &CREDIT_PLUS_LEAF_PROOF, + &CREDIT_MINUS_LEAF_PROOF, + ]; + for leaf in leafs { + let proof = BRANCHES[0].prove_implicit(leaf, None)?; + BRANCHES[0].circuit.verify(proof)?; + } + Ok(()) + } - let branch_proof_2 = BRANCHES[0].prove( - BranchWitnessValue { - address: 200, - object_flags: EventFlags::EnsureFlag.into(), - old_owner: zero_val, - new_owner: zero_val, - old_data: zero_val, - new_data: non_zero_val_2, - credit_delta: 0, - }, - &ensure_proof, - Some(&ensure_proof), - )?; - BRANCHES[0].circuit.verify(branch_proof_2.clone())?; - let branch_proof_2 = BRANCHES[0].prove_implicit(&ensure_proof, Some(&ensure_proof))?; - BRANCHES[0].circuit.verify(branch_proof_2.clone())?; + #[test] + fn verify_left_double_branch() -> Result<()> { + let branches = [&READ_WRITE_BRANCH_PROOF, &ENSURE_BRANCH_PROOF]; + for branch in branches { + let proof = BRANCHES[1].prove_implicit(branch, None)?; + BRANCHES[1].circuit.verify(proof)?; + } + Ok(()) + } - let double_branch_proof = BRANCHES[1].prove( - BranchWitnessValue { - address: 200, - object_flags: EventFlags::ReadFlag | EventFlags::WriteFlag | EventFlags::EnsureFlag, - old_owner: program_hash_1, - new_owner: zero_val, - old_data: non_zero_val_1, - new_data: non_zero_val_2, - credit_delta: 0, - }, - &branch_proof_1, - Some(&branch_proof_2), - )?; - BRANCHES[1].circuit.verify(double_branch_proof)?; - let double_branch_proof = - BRANCHES[1].prove_implicit(&branch_proof_1, Some(&branch_proof_2))?; - BRANCHES[1].circuit.verify(double_branch_proof)?; + #[test] + fn verify_double_branch() -> Result<()> { + let witness = BranchWitnessValue { + address: ADDRESS_A as u64, + object_flags: EventFlags::ReadFlag | EventFlags::WriteFlag | EventFlags::EnsureFlag, + old_owner: PROGRAM_HASHES[0], + new_owner: ZERO_VAL, + old_data: STATE_0[ADDRESS_A].data, + new_data: STATE_1[ADDRESS_A].data, + credit_delta: 0, + }; - let double_branch_proof = BRANCHES[1].prove( - BranchWitnessValue { - address: 200, - object_flags: EventFlags::ReadFlag | EventFlags::WriteFlag, - old_owner: program_hash_1, - new_owner: zero_val, - old_data: non_zero_val_1, - new_data: non_zero_val_2, - credit_delta: 0, - }, - &branch_proof_1, - None, + let proof = BRANCHES[1].prove( + witness, + &READ_WRITE_BRANCH_PROOF, + Some(&ENSURE_BRANCH_PROOF), + )?; + BRANCHES[1].circuit.verify(proof)?; + let proof = BRANCHES[1].prove( + witness, + &ENSURE_BRANCH_PROOF, + Some(&READ_WRITE_BRANCH_PROOF), )?; - BRANCHES[1].circuit.verify(double_branch_proof)?; - let double_branch_proof = BRANCHES[1].prove_implicit(&branch_proof_1, None)?; - BRANCHES[1].circuit.verify(double_branch_proof)?; + BRANCHES[1].circuit.verify(proof)?; + + let proof = + BRANCHES[1].prove_implicit(&READ_WRITE_BRANCH_PROOF, Some(&ENSURE_BRANCH_PROOF))?; + BRANCHES[1].circuit.verify(proof)?; + let proof = + BRANCHES[1].prove_implicit(&ENSURE_BRANCH_PROOF, Some(&READ_WRITE_BRANCH_PROOF))?; + BRANCHES[1].circuit.verify(proof)?; Ok(()) } diff --git a/recproofs/src/circuits/build_event_root.rs b/recproofs/src/circuits/build_event_root.rs index a2cb96d5a..e731884e3 100644 --- a/recproofs/src/circuits/build_event_root.rs +++ b/recproofs/src/circuits/build_event_root.rs @@ -108,9 +108,24 @@ where } } - /// `hash` only needs to be provided to check externally, otherwise it will - /// be calculated pub fn prove( + &self, + event: Event, + branch: &BranchCircuit, + ) -> Result> { + let mut inputs = PartialWitness::new(); + self.unbounded.set_witness(&mut inputs, &branch.circuit); + self.event_owner.set_witness(&mut inputs, event.owner); + inputs.set_target(self.targets.event_ty, F::from_canonical_u8(event.ty as u8)); + inputs.set_target( + self.targets.event_address, + F::from_canonical_u64(event.address), + ); + inputs.set_target_arr(&self.targets.event_value, &event.value); + self.circuit.prove(inputs) + } + + pub fn prove_unsafe( &self, event: Event, hash: Option>, @@ -214,10 +229,35 @@ where } } + pub fn prove( + &self, + left_is_leaf: bool, + left_proof: &ProofWithPublicInputs, + right_proof: Option<(bool, &ProofWithPublicInputs)>, + ) -> Result> { + let mut inputs = PartialWitness::new(); + let partial = right_proof.is_none(); + let (right_is_leaf, right_proof) = if let Some(right_proof) = right_proof { + right_proof + } else { + (left_is_leaf, left_proof) + }; + self.unbounded.set_witness( + &mut inputs, + left_is_leaf, + left_proof, + right_is_leaf, + right_proof, + ); + self.hash.set_witness(&mut inputs, None, partial); + self.vm_hash.set_witness(&mut inputs, None, partial); + self.circuit.prove(inputs) + } + /// `hash` `vm_hash` and `event_owner` only need to be provided to check /// externally, otherwise they will be calculated #[allow(clippy::too_many_arguments)] - pub fn prove( + pub fn prove_unsafe( &self, hash: Option>, vm_hash: Option>, @@ -253,185 +293,160 @@ where pub mod test { use std::panic::catch_unwind; - use lazy_static::lazy_static; use plonky2::field::types::Field; - use plonky2::hash::hash_types::NUM_HASH_OUT_ELTS; use super::*; + use crate::circuits::test_data::{ + EVENT_T0_P0_A_CREDIT, EVENT_T0_P0_A_WRITE, EVENT_T0_P2_A_ENSURE, EVENT_T0_P2_A_READ, + EVENT_T0_P2_C_TAKE, EVENT_T0_PM_C_CREDIT, EVENT_T0_PM_C_GIVE, EVENT_T0_PM_C_WRITE, + EVENT_T1_P1_B_CREDIT, EVENT_T1_P1_B_GIVE, EVENT_T1_P1_B_WRITE, EVENT_T1_P2_A_READ, + EVENT_T1_P2_D_READ, EVENT_T1_PM_B_ENSURE, EVENT_T1_PM_B_TAKE, + }; use crate::test_utils::{hash_branch, hash_branch_bytes, C, CONFIG, D, F}; use crate::EventType; - lazy_static! { - pub static ref LEAF: LeafCircuit = LeafCircuit::new(&CONFIG); - pub static ref BRANCH: BranchCircuit = BranchCircuit::new(&CONFIG, &LEAF); - } + #[tested_fixture::tested_fixture(pub LEAF)] + fn build_leaf() -> LeafCircuit { LeafCircuit::new(&CONFIG) } + + #[tested_fixture::tested_fixture(pub BRANCH)] + fn build_branch() -> BranchCircuit { BranchCircuit::new(&CONFIG, &LEAF) } - fn assert_hash(h: HashOut, v: [u64; NUM_HASH_OUT_ELTS]) { - assert_eq!(h.elements, v.map(F::from_canonical_u64)); + fn get_owner(proof: &ProofWithPublicInputs) -> [F; 4] { + let indices = &LEAF.event_owner.indices; + assert_eq!(*indices, BRANCH.event_owner.indices); + indices.values.get_any(&proof.public_inputs) } - #[allow(clippy::unreadable_literal)] - fn verify_simple_hashes( - read_0_byte_hash: HashOut, - write_1_byte_hash: HashOut, - write_2_byte_hash: HashOut, - branch_1_bytes_hash: HashOut, - branch_2_bytes_hash: HashOut, + fn assert_value( + proof: &ProofWithPublicInputs, + hash: HashOut, + vm_hash: HashOut, + owner: [F; 4], ) { - const READ_0_HASH: [u64; NUM_HASH_OUT_ELTS] = [ - 5023514523864295108, - 4051561141343640122, - 3758781677193965394, - 7012892372712682540, - ]; - const WRITE_1_HASH: [u64; NUM_HASH_OUT_ELTS] = [ - 11854432413139190920, - 14575118316579458705, - 14584234533325208490, - 11181033945500676901, - ]; - const WRITE_2_HASH: [u64; NUM_HASH_OUT_ELTS] = [ - 8452768134152568687, - 10692713461445353847, - 5855010291180688462, - 2467885008139505172, - ]; - const BRANCH_1_HASH: [u64; NUM_HASH_OUT_ELTS] = [ - 4894617691362441836, - 4942438599282550417, - 7714632204656605723, - 13787008141052350745, - ]; - const BRANCH_2_HASH: [u64; NUM_HASH_OUT_ELTS] = [ - 6728532228735213252, - 8351611441116195138, - 2838146134331855767, - 13548305524571022409, - ]; - - assert_hash(read_0_byte_hash, READ_0_HASH); - assert_hash(write_1_byte_hash, WRITE_1_HASH); - assert_hash(write_2_byte_hash, WRITE_2_HASH); - assert_hash(branch_1_bytes_hash, BRANCH_1_HASH); - assert_hash(branch_2_bytes_hash, BRANCH_2_HASH); + let indices = &LEAF.hash.indices; + assert_eq!(*indices, BRANCH.hash.indices); + let p_hash = indices.unpruned_hash.get_any(&proof.public_inputs); + assert_eq!(p_hash, hash.elements); + + let indices = &LEAF.vm_hash.indices; + assert_eq!(*indices, BRANCH.vm_hash.indices); + let p_vm_hash = indices.unpruned_hash.get_any(&proof.public_inputs); + assert_eq!(p_vm_hash, vm_hash.elements); + + let p_owner = get_owner(proof); + assert_eq!(p_owner, owner); } - #[test] - fn verify_simple() -> Result<()> { - let program_hash_1 = [4, 8, 15, 16].map(F::from_canonical_u64); - - let zero_val = [F::ZERO; 4]; - let non_zero_val_1 = [3, 1, 4, 15].map(F::from_canonical_u64); - let non_zero_val_2 = [1, 6, 180, 33].map(F::from_canonical_u64); - - // Duplicate or conflicting events are actually fine as far as this circuit - // cares - let read_0 = Event { - address: 42, - owner: program_hash_1, - ty: EventType::Read, - value: zero_val, - }; - let write_1 = Event { - address: 42, - owner: program_hash_1, - ty: EventType::Write, - value: non_zero_val_1, - }; - let write_2 = Event { - address: 42, - owner: program_hash_1, - ty: EventType::Write, - value: non_zero_val_2, - }; - let read_0_hash = read_0.hash(); - let write_1_hash = write_1.hash(); - let write_2_hash = write_2.hash(); - let read_0_byte_hash = read_0.byte_wise_hash(); - let write_1_byte_hash = write_1.byte_wise_hash(); - let write_2_byte_hash = write_2.byte_wise_hash(); - - // Read zero - let read_proof = LEAF.prove(read_0, Some(read_0_hash), Some(read_0_byte_hash), &BRANCH)?; - LEAF.circuit.verify(read_proof.clone())?; - - // Write pi - let write_proof_1 = LEAF.prove( - write_1, - Some(write_1_hash), - Some(write_1_byte_hash), - &BRANCH, - )?; - LEAF.circuit.verify(write_proof_1.clone())?; - - // Write phi - // This illegal at the protocol level as a double write, but legal for this - // stage - let write_proof_2 = LEAF.prove( - write_2, - Some(write_2_hash), - Some(write_2_byte_hash), - &BRANCH, - )?; - LEAF.circuit.verify(write_proof_2.clone())?; - - let branch_1_hash = hash_branch(&write_1_hash, &write_2_hash); - let branch_2_hash = hash_branch(&read_0_hash, &branch_1_hash); - let branch_1_bytes_hash = hash_branch_bytes(&write_1_byte_hash, &write_2_byte_hash); - let branch_2_bytes_hash = hash_branch_bytes(&read_0_byte_hash, &branch_1_bytes_hash); - - // Combine writes - let branch_proof_1 = BRANCH.prove( - Some(branch_1_hash), - Some(branch_1_bytes_hash), - Some(program_hash_1), - true, - &write_proof_1, - Some((true, &write_proof_2)), - )?; - BRANCH.circuit.verify(branch_proof_1.clone())?; - - // Combine with reads - let branch_proof_2 = BRANCH.prove( - Some(branch_2_hash), - Some(branch_2_bytes_hash), - Some(program_hash_1), - true, - &read_proof, - Some((false, &branch_proof_1)), - )?; - BRANCH.circuit.verify(branch_proof_2.clone())?; - - verify_simple_hashes( - read_0_byte_hash, - write_1_byte_hash, - write_2_byte_hash, - branch_1_bytes_hash, - branch_2_bytes_hash, + fn test_leaf(event: Event) -> Result<(Event, ProofWithPublicInputs)> { + let proof = LEAF.prove(event, &BRANCH)?; + assert_value(&proof, event.hash(), event.byte_wise_hash(), event.owner); + LEAF.circuit.verify(proof.clone())?; + Ok((event, proof)) + } + + #[allow(clippy::type_complexity)] + fn test_branch_0( + left: &(Event, ProofWithPublicInputs), + right: &(Event, ProofWithPublicInputs), + ) -> Result<(ProofWithPublicInputs, HashOut, HashOut)> { + assert_eq!( + left.0.owner, right.0.owner, + "Test bug: tried to combine different event owners" ); - // Single-sided proof - let branch_proof_1 = BRANCH.prove( - Some(write_1_hash), - Some(write_1_byte_hash), - Some(program_hash_1), - true, - &write_proof_1, - None, - )?; - BRANCH.circuit.verify(branch_proof_1.clone())?; - - let branch_proof_3 = BRANCH.prove( - Some(branch_2_hash), - Some(branch_2_bytes_hash), - Some(program_hash_1), - false, - &branch_proof_2, - None, - )?; - BRANCH.circuit.verify(branch_proof_3)?; + let proof = BRANCH.prove(true, &left.1, Some((true, &right.1)))?; + let hash = hash_branch(&left.0.hash(), &right.0.hash()); + let vm_hash = hash_branch_bytes(&left.0.byte_wise_hash(), &right.0.byte_wise_hash()); + assert_value(&proof, hash, vm_hash, left.0.owner); + BRANCH.circuit.verify(proof.clone())?; + + Ok((proof, hash, vm_hash)) + } + + #[allow(clippy::type_complexity)] + fn test_branch_1( + left: (&ProofWithPublicInputs, HashOut, HashOut), + right: &(Event, ProofWithPublicInputs), + ) -> Result<(ProofWithPublicInputs, HashOut, HashOut)> { + let owner = get_owner(left.0); + assert_eq!( + owner, right.0.owner, + "Test bug: tried to combine different event owners" + ); + + let proof = BRANCH.prove(false, left.0, Some((true, &right.1)))?; + let hash = hash_branch(&left.1, &right.0.hash()); + let vm_hash = hash_branch_bytes(&left.2, &right.0.byte_wise_hash()); + assert_value(&proof, hash, vm_hash, owner); + BRANCH.circuit.verify(proof.clone())?; + + Ok((proof, hash, vm_hash)) + } + + macro_rules! make_leaf_tests { + ($($($name:ident | $proof:ident = $event:ident),+ $(,)?)?) => {$($( + #[tested_fixture::tested_fixture($proof: (Event, ProofWithPublicInputs))] + fn $name() -> Result<(Event, ProofWithPublicInputs)> { + test_leaf($event) + } + )+)?}; + } + + make_leaf_tests! { + verify_t0_pm_c_credit_leaf | T0_PM_C_CREDIT_LEAF_PROOF = EVENT_T0_PM_C_CREDIT, + verify_t0_pm_c_give_leaf | T0_PM_C_GIVE_LEAF_PROOF = EVENT_T0_PM_C_GIVE, + verify_t0_pm_c_write_leaf | T0_PM_C_WRITE_LEAF_PROOF = EVENT_T0_PM_C_WRITE, + verify_t0_p0_a_write_leaf | T0_P0_A_WRITE_LEAF_PROOF = EVENT_T0_P0_A_WRITE, + verify_t0_p0_a_credit_leaf | T0_P0_A_CREDIT_LEAF_PROOF = EVENT_T0_P0_A_CREDIT, + verify_t0_p2_a_read_leaf | T0_P2_A_READ_LEAF_PROOF = EVENT_T0_P2_A_READ, + verify_t0_p2_a_ensure_leaf | T0_P2_A_ENSURE_LEAF_PROOF = EVENT_T0_P2_A_ENSURE, + verify_t0_p2_c_take_leaf | T0_P2_C_TAKE_LEAF_PROOF = EVENT_T0_P2_C_TAKE, + verify_t1_pm_b_take_leaf | T1_PM_B_TAKE_LEAF_PROOF = EVENT_T1_PM_B_TAKE, + verify_t1_pm_b_ensure_leaf | T1_PM_B_ENSURE_LEAF_PROOF = EVENT_T1_PM_B_ENSURE, + verify_t1_p1_b_write_leaf | T1_P1_B_WRITE_LEAF_PROOF = EVENT_T1_P1_B_WRITE, + verify_t1_p1_b_give_leaf | T1_P1_B_GIVE_LEAF_PROOF = EVENT_T1_P1_B_GIVE, + verify_t1_p1_b_credit_leaf | T1_P1_B_CREDIT_LEAF_PROOF = EVENT_T1_P1_B_CREDIT, + verify_t1_p2_a_read_leaf | T1_P2_A_READ_LEAF_PROOF = EVENT_T1_P2_A_READ, + verify_t1_p2_d_read_leaf | T1_P2_D_READ_LEAF_PROOF = EVENT_T1_P2_D_READ, + } + + #[tested_fixture::tested_fixture(pub T0_PM_BRANCH_PROOF: (ProofWithPublicInputs, HashOut, HashOut))] + fn verify_t0_pm_branch() -> Result<(ProofWithPublicInputs, HashOut, HashOut)> { + let (proof, hash, vm_hash) = + test_branch_0(&T0_PM_C_CREDIT_LEAF_PROOF, &T0_PM_C_GIVE_LEAF_PROOF)?; + + test_branch_1((&proof, hash, vm_hash), &T0_PM_C_WRITE_LEAF_PROOF) + } + + #[tested_fixture::tested_fixture(pub T0_P0_BRANCH_PROOF: (ProofWithPublicInputs, HashOut, HashOut))] + fn verify_t0_p0_branch() -> Result<(ProofWithPublicInputs, HashOut, HashOut)> { + test_branch_0(&T0_P0_A_WRITE_LEAF_PROOF, &T0_P0_A_CREDIT_LEAF_PROOF) + } + + #[tested_fixture::tested_fixture(pub T0_P2_BRANCH_PROOF: (ProofWithPublicInputs, HashOut, HashOut))] + fn verify_t0_p2_branch() -> Result<(ProofWithPublicInputs, HashOut, HashOut)> { + let (proof, hash, vm_hash) = + test_branch_0(&T0_P2_A_READ_LEAF_PROOF, &T0_P2_A_ENSURE_LEAF_PROOF)?; + + test_branch_1((&proof, hash, vm_hash), &T0_P2_C_TAKE_LEAF_PROOF) + } + + #[tested_fixture::tested_fixture(pub T1_PM_BRANCH_PROOF: (ProofWithPublicInputs, HashOut, HashOut))] + fn verify_t1_pm_branch() -> Result<(ProofWithPublicInputs, HashOut, HashOut)> { + test_branch_0(&T1_PM_B_TAKE_LEAF_PROOF, &T1_PM_B_ENSURE_LEAF_PROOF) + } + + #[tested_fixture::tested_fixture(pub T1_P1_BRANCH_PROOF: (ProofWithPublicInputs, HashOut, HashOut))] + fn verify_t1_p1_branch() -> Result<(ProofWithPublicInputs, HashOut, HashOut)> { + let (proof, hash, vm_hash) = + test_branch_0(&T1_P1_B_WRITE_LEAF_PROOF, &T1_P1_B_GIVE_LEAF_PROOF)?; + + test_branch_1((&proof, hash, vm_hash), &T1_P1_B_CREDIT_LEAF_PROOF) + } - Ok(()) + #[tested_fixture::tested_fixture(pub T1_P2_BRANCH_PROOF: (ProofWithPublicInputs, HashOut, HashOut))] + fn verify_t1_p2_branch() -> Result<(ProofWithPublicInputs, HashOut, HashOut)> { + test_branch_0(&T1_P2_A_READ_LEAF_PROOF, &T1_P2_D_READ_LEAF_PROOF) } #[test] @@ -463,7 +478,7 @@ pub mod test { .expect("shouldn't fail"); // Fail to prove with mismatched hashes - LEAF.prove(read_1, Some(read_0_hash), Some(read_0_byte_hash), &BRANCH) + LEAF.prove_unsafe(read_1, Some(read_0_hash), Some(read_0_byte_hash), &BRANCH) .unwrap(); } @@ -497,14 +512,10 @@ pub mod test { let read_1_byte_hash = read_1.byte_wise_hash(); // Read zero - let read_proof_1 = LEAF - .prove(read_0, Some(read_0_hash), Some(read_0_byte_hash), &BRANCH) - .unwrap(); + let read_proof_1 = LEAF.prove(read_0, &BRANCH).unwrap(); LEAF.circuit.verify(read_proof_1.clone()).unwrap(); - let read_proof_2 = LEAF - .prove(read_1, Some(read_1_hash), Some(read_1_byte_hash), &BRANCH) - .unwrap(); + let read_proof_2 = LEAF.prove(read_1, &BRANCH).unwrap(); LEAF.circuit.verify(read_proof_2.clone()).unwrap(); // Combine reads @@ -523,7 +534,7 @@ pub mod test { // Fail to prove with mismatched program hashes between branches // This tree requires all events are from the same program BRANCH - .prove( + .prove_unsafe( Some(branch_1_hash), Some(branch_1_bytes_hash), Some(program_hash_1), diff --git a/recproofs/src/circuits/match_delta.rs b/recproofs/src/circuits/match_delta.rs index 579af2561..5300a741b 100644 --- a/recproofs/src/circuits/match_delta.rs +++ b/recproofs/src/circuits/match_delta.rs @@ -229,7 +229,7 @@ where pub fn prove( &self, branch: &BranchCircuit, - accumulate_event_proof: ProofWithPublicInputs, + accumulate_event_proof: &ProofWithPublicInputs, v: LeafWitnessValue, ) -> Result> { let mut inputs = PartialWitness::new(); @@ -254,7 +254,7 @@ where inputs.set_target_arr(&self.compare_delta.targets.new_owner, &v.new_owner); inputs.set_target_arr(&self.compare_delta.targets.old_data, &v.old_data); inputs.set_target_arr(&self.compare_delta.targets.new_data, &v.new_data); - inputs.set_proof_with_pis_target(&self.targets.accumulate_event, &accumulate_event_proof); + inputs.set_proof_with_pis_target(&self.targets.accumulate_event, accumulate_event_proof); self.circuit.prove(inputs) } } @@ -375,18 +375,23 @@ where #[cfg(test)] pub mod test { - use lazy_static::lazy_static; - use plonky2::field::types::Field; + use plonky2::field::types::{Field, PrimeField64}; use super::*; - use crate::circuits::accumulate_delta::test::{BRANCH as ACC_BRANCH, LEAF as ACC_LEAF}; + use crate::circuits::accumulate_delta::test::{ + self as acc, BRANCH as ACC_BRANCH, LEAF as ACC_LEAF, + }; + use crate::circuits::test_data::{ + ADDRESS_A, ADDRESS_B, ADDRESS_C, ADDRESS_D, STATE_0, STATE_1, + }; use crate::test_utils::{C, CONFIG, D, F}; use crate::EventType; - lazy_static! { - pub static ref LEAF: LeafCircuit = LeafCircuit::new(&CONFIG, &ACC_BRANCH); - pub static ref BRANCH: BranchCircuit = BranchCircuit::new(&CONFIG, &LEAF); - } + #[tested_fixture::tested_fixture(pub LEAF)] + fn build_leaf() -> LeafCircuit { LeafCircuit::new(&CONFIG, &ACC_BRANCH) } + + #[tested_fixture::tested_fixture(pub BRANCH)] + fn build_branch() -> BranchCircuit { BranchCircuit::new(&CONFIG, &LEAF) } fn make_acc_proof( vals: impl IntoIterator, @@ -415,32 +420,81 @@ pub mod test { Ok(acc_left_proof) } - #[test] - fn verify_leaf() -> Result<()> { - let program_hash_1 = [4, 8, 15, 16].map(F::from_canonical_u64); - - let non_zero_val_1 = [3, 1, 4, 15].map(F::from_canonical_u64); - let non_zero_val_2 = [1, 6, 180, 33].map(F::from_canonical_u64); + #[tested_fixture::tested_fixture(pub A_LEAF_PROOF: ProofWithPublicInputs)] + + fn verify_a_leaf() -> Result> { + let address = ADDRESS_A; + let (old, new) = (&STATE_0[address], &STATE_1[address]); + let witness = LeafWitnessValue { + block_height: 1, + last_updated: 0, + old_owner: old.constraint_owner, + new_owner: new.constraint_owner, + old_data: old.data, + new_data: new.data, + old_credits: old.credits.to_canonical_u64(), + new_credits: new.credits.to_canonical_u64(), + }; + let proof = LEAF.prove(&BRANCH, *acc::A_BRANCH_PROOF, witness)?; + LEAF.circuit.verify(proof.clone())?; + Ok(proof) + } - let acc_branch_proof_1 = make_acc_proof([ - (200, program_hash_1, EventType::Read, non_zero_val_1), - (200, program_hash_1, EventType::Write, non_zero_val_2), - (200, program_hash_1, EventType::Ensure, non_zero_val_2), - ])?; + #[tested_fixture::tested_fixture(pub B_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_b_leaf() -> Result> { + let address = ADDRESS_B; + let (old, new) = (&STATE_0[address], &STATE_1[address]); + let witness = LeafWitnessValue { + block_height: 1, + last_updated: 0, + old_owner: old.constraint_owner, + new_owner: new.constraint_owner, + old_data: old.data, + new_data: new.data, + old_credits: old.credits.to_canonical_u64(), + new_credits: new.credits.to_canonical_u64(), + }; + let proof = LEAF.prove(&BRANCH, *acc::B_BRANCH_PROOF, witness)?; + LEAF.circuit.verify(proof.clone())?; + Ok(proof) + } - let leaf_proof = LEAF.prove(&BRANCH, acc_branch_proof_1, LeafWitnessValue { - block_height: 10, - last_updated: 9, - old_owner: program_hash_1, - new_owner: program_hash_1, - old_data: non_zero_val_1, - new_data: non_zero_val_2, - old_credits: 50, - new_credits: 50, - })?; - LEAF.circuit.verify(leaf_proof)?; + #[tested_fixture::tested_fixture(pub C_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_c_leaf() -> Result> { + let address = ADDRESS_C; + let (old, new) = (&STATE_0[address], &STATE_1[address]); + let witness = LeafWitnessValue { + block_height: 1, + last_updated: 0, + old_owner: old.constraint_owner, + new_owner: new.constraint_owner, + old_data: old.data, + new_data: new.data, + old_credits: old.credits.to_canonical_u64(), + new_credits: new.credits.to_canonical_u64(), + }; + let proof = LEAF.prove(&BRANCH, *acc::C_BRANCH_PROOF, witness)?; + LEAF.circuit.verify(proof.clone())?; + Ok(proof) + } - Ok(()) + #[tested_fixture::tested_fixture(pub D_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_d_leaf() -> Result> { + let address = ADDRESS_D; + let (old, new) = (&STATE_0[address], &STATE_1[address]); + let witness = LeafWitnessValue { + block_height: 1, + last_updated: 0, + old_owner: old.constraint_owner, + new_owner: new.constraint_owner, + old_data: old.data, + new_data: new.data, + old_credits: old.credits.to_canonical_u64(), + new_credits: new.credits.to_canonical_u64(), + }; + let proof = LEAF.prove(&BRANCH, *acc::D_BRANCH_PROOF, witness)?; + LEAF.circuit.verify(proof.clone())?; + Ok(proof) } #[test] @@ -462,7 +516,7 @@ pub mod test { (400, program_hash_1, EventType::Ensure, non_zero_val_2), ])?; - let leaf_proof_200 = LEAF.prove(&BRANCH, acc_branch_proof_200, LeafWitnessValue { + let leaf_proof_200 = LEAF.prove(&BRANCH, &acc_branch_proof_200, LeafWitnessValue { block_height: 10, last_updated: 9, old_owner: program_hash_1, @@ -474,7 +528,7 @@ pub mod test { })?; LEAF.circuit.verify(leaf_proof_200.clone())?; - let leaf_proof_400 = LEAF.prove(&BRANCH, acc_branch_proof_400, LeafWitnessValue { + let leaf_proof_400 = LEAF.prove(&BRANCH, &acc_branch_proof_400, LeafWitnessValue { block_height: 10, last_updated: 9, old_owner: program_hash_1, diff --git a/recproofs/src/circuits/match_delta/core.rs b/recproofs/src/circuits/match_delta/core.rs index a4835343c..7bd4f225a 100644 --- a/recproofs/src/circuits/match_delta/core.rs +++ b/recproofs/src/circuits/match_delta/core.rs @@ -324,7 +324,6 @@ impl BranchSubCircuit { #[cfg(test)] mod test { use anyhow::Result; - use lazy_static::lazy_static; use plonky2::plonk::circuit_data::{CircuitConfig, CircuitData}; use plonky2::plonk::config::Hasher; use plonky2::plonk::proof::ProofWithPublicInputs; @@ -450,11 +449,15 @@ mod test { } } - lazy_static! { - static ref LEAF: DummyLeafCircuit = DummyLeafCircuit::new(&CONFIG); - static ref BRANCH_1: DummyBranchCircuit = DummyBranchCircuit::from_leaf(&CONFIG, &LEAF); - static ref BRANCH_2: DummyBranchCircuit = - DummyBranchCircuit::from_branch(&CONFIG, &BRANCH_1); + #[tested_fixture::tested_fixture(LEAF)] + fn build_leaf() -> DummyLeafCircuit { DummyLeafCircuit::new(&CONFIG) } + + #[tested_fixture::tested_fixture(BRANCH_1)] + fn build_branch_1() -> DummyBranchCircuit { DummyBranchCircuit::from_leaf(&CONFIG, &LEAF) } + + #[tested_fixture::tested_fixture(BRANCH_2)] + fn build_branch_2() -> DummyBranchCircuit { + DummyBranchCircuit::from_branch(&CONFIG, &BRANCH_1) } #[test] diff --git a/recproofs/src/circuits/merge.rs b/recproofs/src/circuits/merge.rs index d231e108d..937c3979d 100644 --- a/recproofs/src/circuits/merge.rs +++ b/recproofs/src/circuits/merge.rs @@ -67,12 +67,31 @@ where branch: &BranchCircuit, a_hash: Option>, b_hash: Option>, + ) -> Result> { + let mut inputs = PartialWitness::new(); + self.unbounded.set_witness(&mut inputs, &branch.circuit); + self.merge.set_witness(&mut inputs, a_hash, b_hash); + self.circuit.prove(inputs) + } + + pub fn prove_unsafe( + &self, + branch: &BranchCircuit, + a_hash: Option>, + b_hash: Option>, merged_hash: Option>, ) -> Result> { let mut inputs = PartialWitness::new(); self.unbounded.set_witness(&mut inputs, &branch.circuit); - self.merge - .set_witness(&mut inputs, a_hash, b_hash, merged_hash); + self.merge.set_witness_unsafe( + &mut inputs, + a_hash.is_some(), + a_hash.unwrap_or_default(), + b_hash.is_some(), + b_hash.unwrap_or_default(), + merged_hash.is_some(), + merged_hash, + ); self.circuit.prove(inputs) } } @@ -140,111 +159,419 @@ where right_is_leaf, right_proof, ); + self.merge.set_witness(&mut inputs); self.circuit.prove(inputs) } } #[cfg(test)] pub mod test { - use lazy_static::lazy_static; + use anyhow::Ok; + use once_cell::sync::Lazy; use plonky2::field::types::Field; - use plonky2::hash::hash_types::NUM_HASH_OUT_ELTS; + pub use super::BranchCircuit; use super::*; - use crate::test_utils::{hash_branch, hash_str, C, CONFIG, D, F}; + use crate::circuits::test_data::{ + EVENT_T0_P0_A_CREDIT, EVENT_T0_P0_A_WRITE, EVENT_T0_P2_A_ENSURE, EVENT_T0_P2_A_READ, + EVENT_T0_P2_C_TAKE, EVENT_T0_PM_C_CREDIT, EVENT_T0_PM_C_GIVE, EVENT_T0_PM_C_WRITE, + EVENT_T1_P1_B_CREDIT, EVENT_T1_P1_B_GIVE, EVENT_T1_P1_B_WRITE, EVENT_T1_P2_A_READ, + EVENT_T1_P2_D_READ, EVENT_T1_PM_B_ENSURE, EVENT_T1_PM_B_TAKE, + }; + use crate::test_utils::{hash_branch, C, CONFIG, D, F, NON_ZERO_HASHES, ZERO_HASH}; + + fn assert_leaf(proof: &ProofWithPublicInputs, merged: Option>) { + let indices = &LEAF.merge.indices; + + let p_present = indices.merged_present.get_any(&proof.public_inputs); + assert_eq!(p_present, F::from_bool(merged.is_some())); + + let p_merged = indices.merged_hash.get_any(&proof.public_inputs); + assert_eq!(p_merged, merged.unwrap_or_default().elements); + } + + fn assert_branch( + proof: &ProofWithPublicInputs, + a_hash: Option>, + b_hash: Option>, + merged: Option>, + ) { + let indices = &BRANCH.merge.indices; + + let p_a_present = indices.a_present.get_any(&proof.public_inputs); + assert_eq!(p_a_present, F::from_bool(a_hash.is_some())); + + let p_a_hash = indices.a_hash.get_any(&proof.public_inputs); + assert_eq!(p_a_hash, a_hash.unwrap_or_default().elements); - lazy_static! { - pub static ref LEAF: LeafCircuit = LeafCircuit::new(&CONFIG); - pub static ref BRANCH: BranchCircuit = BranchCircuit::new(&CONFIG, &LEAF); + let p_b_present = indices.b_present.get_any(&proof.public_inputs); + assert_eq!(p_b_present, F::from_bool(b_hash.is_some())); + + let p_b_hash = indices.b_hash.get_any(&proof.public_inputs); + assert_eq!(p_b_hash, b_hash.unwrap_or_default().elements); + + let p_merged_present = indices.merged_present.get_any(&proof.public_inputs); + assert_eq!(p_merged_present, F::from_bool(merged.is_some())); + + let p_merged = indices.merged_hash.get_any(&proof.public_inputs); + assert_eq!(p_merged, merged.unwrap_or_default().elements); } - #[test] - fn verify_leaf_empty() -> Result<()> { - let zero_hash = HashOut::from([F::ZERO; NUM_HASH_OUT_ELTS]); - let a_val = hash_str("Value Alpha"); - let b_val = hash_str("Value Beta"); + #[tested_fixture::tested_fixture(pub LEAF)] + fn build_leaf() -> LeafCircuit { LeafCircuit::new(&CONFIG) } - let proof = LEAF.prove(&BRANCH, None, None, Some(zero_hash))?; - LEAF.circuit.verify(proof)?; + #[tested_fixture::tested_fixture(pub BRANCH)] + fn build_branch() -> BranchCircuit { BranchCircuit::new(&CONFIG, &LEAF) } - let proof = LEAF.prove(&BRANCH, Some(a_val), None, Some(a_val))?; - LEAF.circuit.verify(proof)?; + #[tested_fixture::tested_fixture(EMPTY_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_empty_leaf() -> Result> { + let proof = LEAF.prove(&BRANCH, None, None)?; + assert_leaf(&proof, None); + LEAF.circuit.verify(proof.clone())?; + Ok(proof) + } - let proof = LEAF.prove(&BRANCH, None, Some(b_val), Some(b_val))?; - LEAF.circuit.verify(proof)?; + #[tested_fixture::tested_fixture(LEFT_ZERO_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_left_zero_leaf() -> Result> { + let proof = LEAF.prove(&BRANCH, Some(ZERO_HASH), None)?; + assert_leaf(&proof, Some(ZERO_HASH)); + LEAF.circuit.verify(proof.clone())?; + Ok(proof) + } + + #[tested_fixture::tested_fixture(RIGHT_ZERO_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_right_zero_leaf() -> Result> { + let proof = LEAF.prove(&BRANCH, None, Some(ZERO_HASH))?; + assert_leaf(&proof, Some(ZERO_HASH)); + LEAF.circuit.verify(proof.clone())?; + Ok(proof) + } - let proof = LEAF.prove(&BRANCH, Some(zero_hash), None, Some(zero_hash))?; - LEAF.circuit.verify(proof)?; + #[tested_fixture::tested_fixture(LEFT_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_left_leaf() -> Result> { + let proof = LEAF.prove(&BRANCH, Some(NON_ZERO_HASHES[0]), None)?; + assert_leaf(&proof, Some(NON_ZERO_HASHES[0])); + LEAF.circuit.verify(proof.clone())?; + Ok(proof) + } + + #[tested_fixture::tested_fixture(RIGHT_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_right_leaf() -> Result> { + let proof = LEAF.prove(&BRANCH, None, Some(NON_ZERO_HASHES[1]))?; + assert_leaf(&proof, Some(NON_ZERO_HASHES[1])); + LEAF.circuit.verify(proof.clone())?; + Ok(proof) + } - let proof = LEAF.prove(&BRANCH, None, Some(zero_hash), Some(zero_hash))?; - LEAF.circuit.verify(proof)?; + #[tested_fixture::tested_fixture(pub EMPTY_BRANCH_PROOF: ProofWithPublicInputs)] + fn verify_empty_branch() -> Result> { + let proof = BRANCH.prove(true, *EMPTY_LEAF_PROOF, true, *EMPTY_LEAF_PROOF)?; + assert_branch(&proof, None, None, None); + BRANCH.circuit.verify(proof.clone())?; + Ok(proof) + } + + #[tested_fixture::tested_fixture(pub LEFT_ZERO_BRANCH_PROOF: ProofWithPublicInputs)] + fn verify_left_zero_branch_1() -> Result> { + let proof = BRANCH.prove(true, *LEFT_ZERO_LEAF_PROOF, true, *EMPTY_LEAF_PROOF)?; + assert_branch(&proof, Some(ZERO_HASH), None, Some(ZERO_HASH)); + BRANCH.circuit.verify(proof.clone())?; + Ok(proof) + } + #[test] + fn verify_left_zero_branch_2() -> Result<()> { + let proof = BRANCH.prove(true, *EMPTY_LEAF_PROOF, true, *LEFT_ZERO_LEAF_PROOF)?; + assert_branch(&proof, Some(ZERO_HASH), None, Some(ZERO_HASH)); + BRANCH.circuit.verify(proof)?; Ok(()) } + #[tested_fixture::tested_fixture(pub RIGHT_ZERO_BRANCH_PROOF: ProofWithPublicInputs)] + fn verify_right_zero_branch_1() -> Result> { + let proof = BRANCH.prove(true, *RIGHT_ZERO_LEAF_PROOF, true, *EMPTY_LEAF_PROOF)?; + assert_branch(&proof, None, Some(ZERO_HASH), Some(ZERO_HASH)); + BRANCH.circuit.verify(proof.clone())?; + Ok(proof) + } + #[test] - fn verify_leaf() -> Result<()> { - let zero_hash = HashOut::from([F::ZERO; NUM_HASH_OUT_ELTS]); - let a_val = hash_str("Value Alpha"); - let b_val = hash_str("Value Beta"); - let ab_hash = hash_branch(&a_val, &b_val); - let zero_zero_hash = hash_branch(&zero_hash, &zero_hash); - let a_zero_hash = hash_branch(&a_val, &zero_hash); - let zero_b_hash = hash_branch(&zero_hash, &b_val); - - let proof = LEAF.prove(&BRANCH, Some(a_val), Some(b_val), Some(ab_hash))?; - LEAF.circuit.verify(proof)?; - - let proof = LEAF.prove( - &BRANCH, - Some(zero_hash), - Some(zero_hash), - Some(zero_zero_hash), - )?; - LEAF.circuit.verify(proof)?; - - let proof = LEAF.prove(&BRANCH, Some(a_val), Some(zero_hash), Some(a_zero_hash))?; - LEAF.circuit.verify(proof)?; - - let proof = LEAF.prove(&BRANCH, Some(zero_hash), Some(b_val), Some(zero_b_hash))?; - LEAF.circuit.verify(proof)?; + fn verify_right_zero_branch_2() -> Result<()> { + let proof = BRANCH.prove(true, *EMPTY_LEAF_PROOF, true, *RIGHT_ZERO_LEAF_PROOF)?; + assert_branch(&proof, None, Some(ZERO_HASH), Some(ZERO_HASH)); + BRANCH.circuit.verify(proof)?; + Ok(()) + } + #[tested_fixture::tested_fixture(pub LEFT_BRANCH_PROOF: ProofWithPublicInputs)] + fn verify_left_branch_1() -> Result> { + let proof = BRANCH.prove(true, *LEFT_LEAF_PROOF, true, *EMPTY_LEAF_PROOF)?; + assert_branch( + &proof, + Some(NON_ZERO_HASHES[0]), + None, + Some(NON_ZERO_HASHES[0]), + ); + BRANCH.circuit.verify(proof.clone())?; + Ok(proof) + } + + #[test] + fn verify_left_branch_2() -> Result<()> { + let proof = BRANCH.prove(true, *EMPTY_LEAF_PROOF, true, *LEFT_LEAF_PROOF)?; + assert_branch( + &proof, + Some(NON_ZERO_HASHES[0]), + None, + Some(NON_ZERO_HASHES[0]), + ); + BRANCH.circuit.verify(proof)?; Ok(()) } + #[tested_fixture::tested_fixture(pub RIGHT_BRANCH_PROOF: ProofWithPublicInputs)] + fn verify_right_branch() -> Result> { + let proof = BRANCH.prove(true, *RIGHT_LEAF_PROOF, true, *EMPTY_LEAF_PROOF)?; + assert_branch( + &proof, + None, + Some(NON_ZERO_HASHES[1]), + Some(NON_ZERO_HASHES[1]), + ); + BRANCH.circuit.verify(proof.clone())?; + Ok(proof) + } + #[test] - fn verify_branch_empty() -> Result<()> { - let zero_hash = HashOut::from([F::ZERO; NUM_HASH_OUT_ELTS]); + fn verify_right_branch_2() -> Result<()> { + let proof = BRANCH.prove(true, *EMPTY_LEAF_PROOF, true, *RIGHT_LEAF_PROOF)?; + assert_branch( + &proof, + None, + Some(NON_ZERO_HASHES[1]), + Some(NON_ZERO_HASHES[1]), + ); + BRANCH.circuit.verify(proof.clone())?; + Ok(()) + } + + #[tested_fixture::tested_fixture(pub BOTH_BRANCH_PROOF: ProofWithPublicInputs)] + fn verify_both_branch() -> Result> { + let merged = hash_branch(&NON_ZERO_HASHES[0], &NON_ZERO_HASHES[1]); + let proof = BRANCH.prove(true, *LEFT_LEAF_PROOF, true, *RIGHT_LEAF_PROOF)?; + assert_branch( + &proof, + Some(NON_ZERO_HASHES[0]), + Some(NON_ZERO_HASHES[1]), + Some(merged), + ); + BRANCH.circuit.verify(proof.clone())?; + Ok(proof) + } - let empty_proof = LEAF.prove(&BRANCH, None, None, Some(zero_hash))?; - LEAF.circuit.verify(empty_proof.clone())?; + // T1 merges + static T0_PM_HASH: Lazy> = Lazy::new(|| { + hash_branch( + &hash_branch(&EVENT_T0_PM_C_CREDIT.hash(), &EVENT_T0_PM_C_GIVE.hash()), + &EVENT_T0_PM_C_WRITE.hash(), + ) + }); + static T0_P0_HASH: Lazy> = + Lazy::new(|| hash_branch(&EVENT_T0_P0_A_WRITE.hash(), &EVENT_T0_P0_A_CREDIT.hash())); + static T0_P2_A_HASH: Lazy> = + Lazy::new(|| hash_branch(&EVENT_T0_P2_A_READ.hash(), &EVENT_T0_P2_A_ENSURE.hash())); + static T0_P2_C_HASH: Lazy> = Lazy::new(|| EVENT_T0_P2_C_TAKE.hash()); + static T0_P2_HASH: Lazy> = Lazy::new(|| hash_branch(&T0_P2_A_HASH, &T0_P2_C_HASH)); + static T0_A_HASH: Lazy> = Lazy::new(|| hash_branch(&T0_P0_HASH, &T0_P2_A_HASH)); + static T0_C_HASH: Lazy> = Lazy::new(|| hash_branch(&T0_PM_HASH, &T0_P2_C_HASH)); + static T0_HASH: Lazy> = Lazy::new(|| hash_branch(&T0_A_HASH, &T0_C_HASH)); + + #[tested_fixture::tested_fixture(T0_PM_LEFT_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_t0_pm_left_leaf() -> Result> { + let proof = LEAF.prove(&BRANCH, Some(*T0_PM_HASH), None)?; + assert_leaf(&proof, Some(*T0_PM_HASH)); + LEAF.circuit.verify(proof.clone())?; + Ok(proof) + } - let empty_branch = BRANCH.prove(true, &empty_proof, true, &empty_proof)?; - BRANCH.circuit.verify(empty_branch.clone())?; + #[tested_fixture::tested_fixture(T0_P0_RIGHT_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_t0_p0_right_leaf() -> Result> { + let proof = LEAF.prove(&BRANCH, None, Some(*T0_P0_HASH))?; + assert_leaf(&proof, Some(*T0_P0_HASH)); + LEAF.circuit.verify(proof.clone())?; + Ok(proof) + } - let empty_branch = BRANCH.prove(false, &empty_branch, true, &empty_proof)?; - BRANCH.circuit.verify(empty_branch.clone())?; + #[tested_fixture::tested_fixture(T0_A_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_t0_a_leaf() -> Result> { + let proof = LEAF.prove(&BRANCH, Some(*T0_P0_HASH), Some(*T0_P2_A_HASH))?; + assert_leaf(&proof, Some(*T0_A_HASH)); + LEAF.circuit.verify(proof.clone())?; + Ok(proof) + } - let empty_branch = BRANCH.prove(false, &empty_branch, false, &empty_branch)?; - BRANCH.circuit.verify(empty_branch.clone())?; + #[tested_fixture::tested_fixture(T0_C_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_t0_c_leaf() -> Result> { + let proof = LEAF.prove(&BRANCH, Some(*T0_PM_HASH), Some(*T0_P2_C_HASH))?; + assert_leaf(&proof, Some(*T0_C_HASH)); + LEAF.circuit.verify(proof.clone())?; + Ok(proof) + } - Ok(()) + #[tested_fixture::tested_fixture(pub T0_PM_P0_BRANCH_PROOF: (ProofWithPublicInputs, HashOut))] + fn verify_t0_pm_p0_branch() -> Result<(ProofWithPublicInputs, HashOut)> { + // This is a simple merge because: + // P0 contains only A and + // PM contains only C + // Also we put P0 to the left of PM because A < C + let merged = hash_branch(&T0_P0_HASH, &T0_PM_HASH); + let proof = BRANCH.prove(true, *T0_P0_RIGHT_LEAF_PROOF, true, *T0_PM_LEFT_LEAF_PROOF)?; + assert_branch(&proof, Some(*T0_PM_HASH), Some(*T0_P0_HASH), Some(merged)); + BRANCH.circuit.verify(proof.clone())?; + Ok((proof, merged)) } - #[test] - fn verify_branch_single() -> Result<()> { - let a_val = hash_str("Value Alpha"); - let b_val = hash_str("Value Beta"); + #[tested_fixture::tested_fixture(pub T0_BRANCH_PROOF: ProofWithPublicInputs)] + fn verify_t0_branch() -> Result> { + // Merge A to the left of C because A < C + let left_merged = hash_branch(&T0_P0_HASH, &T0_PM_HASH); + let proof = BRANCH.prove(true, *T0_A_LEAF_PROOF, true, *T0_C_LEAF_PROOF)?; + assert_branch(&proof, Some(left_merged), Some(*T0_P2_HASH), Some(*T0_HASH)); + Ok(proof) + } + + // T1 merges + static T1_PM_HASH: Lazy> = + Lazy::new(|| hash_branch(&EVENT_T1_PM_B_TAKE.hash(), &EVENT_T1_PM_B_ENSURE.hash())); + static T1_P1_HASH: Lazy> = Lazy::new(|| { + hash_branch( + &hash_branch(&EVENT_T1_P1_B_WRITE.hash(), &EVENT_T1_P1_B_GIVE.hash()), + &EVENT_T1_P1_B_CREDIT.hash(), + ) + }); + static T1_B_HASH: Lazy> = Lazy::new(|| hash_branch(&T1_PM_HASH, &T1_P1_HASH)); + static T1_P2_A_HASH: Lazy> = Lazy::new(|| EVENT_T1_P2_A_READ.hash()); + static T1_P2_D_HASH: Lazy> = Lazy::new(|| EVENT_T1_P2_D_READ.hash()); + static T1_P2_HASH: Lazy> = Lazy::new(|| hash_branch(&T1_P2_A_HASH, &T1_P2_D_HASH)); + static T1_AB_HASH: Lazy> = Lazy::new(|| hash_branch(&T1_P2_A_HASH, &T1_B_HASH)); + static T1_HASH: Lazy> = Lazy::new(|| hash_branch(&T1_AB_HASH, &T1_P2_D_HASH)); + + #[tested_fixture::tested_fixture(pub T1_PM_P1_BRANCH_PROOF: ProofWithPublicInputs)] + fn verify_t1_pm_p1_branch() -> Result> { + // This is a simple merge because: + // PM contains only B and + // P1 contains only B and + // We put PM to the left arbitrarily + // This means we can do this all in a single leaf proof + let proof = LEAF.prove(&BRANCH, Some(*T1_PM_HASH), Some(*T1_P1_HASH))?; + assert_leaf(&proof, Some(*T1_B_HASH)); + LEAF.circuit.verify(proof.clone())?; + + // But since the result must be a branch, just merge with an empty branch + let proof = BRANCH.prove(true, &proof, true, *EMPTY_LEAF_PROOF)?; + assert_branch( + &proof, + Some(*T1_PM_HASH), + Some(*T1_P1_HASH), + Some(*T1_B_HASH), + ); + BRANCH.circuit.verify(proof.clone())?; + Ok(proof) + } + + #[tested_fixture::tested_fixture(T1_B_LEFT_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_t1_b_left_leaf() -> Result> { + let proof = LEAF.prove(&BRANCH, Some(*T1_B_HASH), None)?; + assert_leaf(&proof, Some(*T1_B_HASH)); + LEAF.circuit.verify(proof.clone())?; + Ok(proof) + } + + #[tested_fixture::tested_fixture(T1_P2_A_RIGHT_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_t1_p2_a_right_leaf() -> Result> { + let proof = LEAF.prove(&BRANCH, None, Some(*T1_P2_A_HASH))?; + assert_leaf(&proof, Some(*T1_P2_A_HASH)); + LEAF.circuit.verify(proof.clone())?; + Ok(proof) + } + + #[tested_fixture::tested_fixture(T1_P2_D_RIGHT_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_t1_p2_d_right_leaf() -> Result> { + let proof = LEAF.prove(&BRANCH, None, Some(*T1_P2_D_HASH))?; + assert_leaf(&proof, Some(*T1_P2_D_HASH)); + LEAF.circuit.verify(proof.clone())?; + Ok(proof) + } + + #[tested_fixture::tested_fixture(T1_AB_BRANCH_PROOF: ProofWithPublicInputs)] + fn verify_t1_ab_branch() -> Result> { + // Merge A to the left of B because A < B + let proof = BRANCH.prove(true, *T1_P2_A_RIGHT_LEAF_PROOF, true, *T1_B_LEFT_LEAF_PROOF)?; + assert_branch( + &proof, + Some(*T1_B_HASH), + Some(*T1_P2_A_HASH), + Some(*T1_AB_HASH), + ); + Ok(proof) + } + + #[tested_fixture::tested_fixture(pub T1_BRANCH_PROOF: ProofWithPublicInputs)] + fn verify_t1_branch() -> Result> { + // Merge A to the left of B because A < B + let proof = BRANCH.prove(false, *T1_AB_BRANCH_PROOF, true, *T1_P2_D_RIGHT_LEAF_PROOF)?; + assert_branch(&proof, Some(*T1_B_HASH), Some(*T1_P2_HASH), Some(*T1_HASH)); + Ok(proof) + } - let a_proof = LEAF.prove(&BRANCH, Some(a_val), None, Some(a_val))?; - LEAF.circuit.verify(a_proof.clone())?; + // Merge transactions + static T0_T1_A_HASH: Lazy> = Lazy::new(|| hash_branch(&T0_A_HASH, &T1_P2_A_HASH)); + static T0_T1_AB_HASH: Lazy> = Lazy::new(|| hash_branch(&T0_T1_A_HASH, &T1_B_HASH)); + static T0_T1_CD_HASH: Lazy> = Lazy::new(|| hash_branch(&T0_C_HASH, &T1_P2_D_HASH)); + static T0_T1_HASH: Lazy> = Lazy::new(|| hash_branch(&T0_T1_AB_HASH, &T0_T1_CD_HASH)); + + #[tested_fixture::tested_fixture(T0_T1_A_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_t0_t1_a_leaf() -> Result> { + let proof = LEAF.prove(&BRANCH, Some(*T0_A_HASH), Some(*T1_P2_A_HASH))?; + assert_leaf(&proof, Some(*T0_T1_A_HASH)); + LEAF.circuit.verify(proof.clone())?; + Ok(proof) + } - let b_proof = LEAF.prove(&BRANCH, None, Some(b_val), Some(b_val))?; - LEAF.circuit.verify(b_proof.clone())?; + #[tested_fixture::tested_fixture(T0_T1_B_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_t0_t1_b_leaf() -> Result> { + let proof = LEAF.prove(&BRANCH, None, Some(*T1_B_HASH))?; + assert_leaf(&proof, Some(*T1_B_HASH)); + LEAF.circuit.verify(proof.clone())?; + Ok(proof) + } - let proof = BRANCH.prove(true, &a_proof, true, &b_proof)?; + #[tested_fixture::tested_fixture(T0_T1_CD_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_t0_t1_cd_leaf() -> Result> { + let proof = LEAF.prove(&BRANCH, Some(*T0_C_HASH), Some(*T1_P2_D_HASH))?; + assert_leaf(&proof, Some(*T0_T1_CD_HASH)); + LEAF.circuit.verify(proof.clone())?; + Ok(proof) + } + + #[tested_fixture::tested_fixture(T0_T1_AB_BRANCH_PROOF: ProofWithPublicInputs)] + fn verify_t0_t1_ab_branch() -> Result> { + let proof = BRANCH.prove(true, &T0_T1_A_LEAF_PROOF, true, *T0_T1_B_LEAF_PROOF)?; + assert_branch( + &proof, + Some(*T0_A_HASH), + Some(*T1_AB_HASH), + Some(*T0_T1_AB_HASH), + ); BRANCH.circuit.verify(proof.clone())?; + Ok(proof) + } - Ok(()) + #[tested_fixture::tested_fixture(pub T0_T1_BRANCH_PROOF: ProofWithPublicInputs)] + fn verify_t0_t1_branch() -> Result> { + let proof = BRANCH.prove(false, &T0_T1_AB_BRANCH_PROOF, true, *T0_T1_CD_LEAF_PROOF)?; + assert_branch(&proof, Some(*T0_HASH), Some(*T1_HASH), Some(*T0_T1_HASH)); + BRANCH.circuit.verify(proof.clone())?; + Ok(proof) } } diff --git a/recproofs/src/circuits/merge/core.rs b/recproofs/src/circuits/merge/core.rs index 45be906da..b2754e542 100644 --- a/recproofs/src/circuits/merge/core.rs +++ b/recproofs/src/circuits/merge/core.rs @@ -150,17 +150,11 @@ impl LeafSubCircuit { inputs: &mut PartialWitness, a_hash: Option>, b_hash: Option>, - merged_hash: Option>, ) { - self.set_witness_unsafe( - inputs, - a_hash.is_some(), - a_hash.unwrap_or_default(), - b_hash.is_some(), - b_hash.unwrap_or_default(), - a_hash.is_some() | b_hash.is_some(), - merged_hash, - ); + inputs.set_bool_target(self.targets.inputs.a_present, a_hash.is_some()); + inputs.set_bool_target(self.targets.inputs.b_present, b_hash.is_some()); + inputs.set_hash_target(self.targets.inputs.a_hash, a_hash.unwrap_or_default()); + inputs.set_hash_target(self.targets.inputs.b_hash, b_hash.unwrap_or_default()); } #[allow(clippy::too_many_arguments)] @@ -175,8 +169,8 @@ impl LeafSubCircuit { merged_hash: Option>, ) { inputs.set_bool_target(self.targets.inputs.a_present, a_present); - inputs.set_bool_target(self.targets.inputs.b_present, b_present); inputs.set_hash_target(self.targets.inputs.a_hash, a_hash); + inputs.set_bool_target(self.targets.inputs.b_present, b_present); inputs.set_hash_target(self.targets.inputs.b_hash, b_hash); inputs.set_bool_target(self.targets.inputs.merged_present, merged_present); if let Some(merged_hash) = merged_hash { @@ -276,39 +270,27 @@ impl BranchTargets { } impl BranchSubCircuit { - pub fn set_witness( - &self, - inputs: &mut PartialWitness, - a_hash: Option>, - b_hash: Option>, - merged_hash: Option>, - ) { - self.set_witness_unsafe( - inputs, - a_hash.is_some(), - a_hash.unwrap_or_default(), - b_hash.is_some(), - b_hash.unwrap_or_default(), - a_hash.is_some() | b_hash.is_some(), - merged_hash, - ); - } + pub fn set_witness(&self, _inputs: &mut PartialWitness) {} #[allow(clippy::too_many_arguments)] pub fn set_witness_unsafe( &self, inputs: &mut PartialWitness, a_present: bool, - a_hash: HashOut, + a_hash: Option>, b_present: bool, - b_hash: HashOut, + b_hash: Option>, merged_present: bool, merged_hash: Option>, ) { inputs.set_bool_target(self.targets.inputs.a_present, a_present); + if let Some(a_hash) = a_hash { + inputs.set_hash_target(self.targets.inputs.a_hash, a_hash); + } inputs.set_bool_target(self.targets.inputs.b_present, b_present); - inputs.set_hash_target(self.targets.inputs.a_hash, a_hash); - inputs.set_hash_target(self.targets.inputs.b_hash, b_hash); + if let Some(b_hash) = b_hash { + inputs.set_hash_target(self.targets.inputs.b_hash, b_hash); + } inputs.set_bool_target(self.targets.inputs.merged_present, merged_present); if let Some(merged_hash) = merged_hash { inputs.set_hash_target(self.targets.inputs.merged_hash, merged_hash); @@ -318,18 +300,14 @@ impl BranchSubCircuit { #[cfg(test)] mod test { - use std::panic::catch_unwind; - use anyhow::Result; - use lazy_static::lazy_static; use plonky2::field::types::Field; - use plonky2::hash::hash_types::NUM_HASH_OUT_ELTS; use plonky2::plonk::circuit_data::{CircuitConfig, CircuitData}; use plonky2::plonk::proof::ProofWithPublicInputs; use super::*; use crate::subcircuits::bounded; - use crate::test_utils::{hash_branch, hash_str, C, CONFIG, D, F}; + use crate::test_utils::{hash_branch, hash_str, C, CONFIG, D, F, NON_ZERO_HASHES, ZERO_HASH}; pub struct DummyLeafCircuit { pub bounded: bounded::LeafSubCircuit, @@ -365,12 +343,10 @@ mod test { &self, a_tree: Option>, b_tree: Option>, - merged_hash: Option>, ) -> Result> { let mut inputs = PartialWitness::new(); self.bounded.set_witness(&mut inputs); - self.merge - .set_witness(&mut inputs, a_tree, b_tree, merged_hash); + self.merge.set_witness(&mut inputs, a_tree, b_tree); self.circuit.prove(inputs) } @@ -449,17 +425,13 @@ mod test { pub fn prove( &self, - a_tree: Option>, - b_tree: Option>, - merged_hash: Option>, left_proof: &ProofWithPublicInputs, right_proof: &ProofWithPublicInputs, ) -> Result> { let mut inputs = PartialWitness::new(); self.bounded .set_witness(&mut inputs, left_proof, right_proof); - self.merge - .set_witness(&mut inputs, a_tree, b_tree, merged_hash); + self.merge.set_witness(&mut inputs); self.circuit.prove(inputs) } @@ -467,9 +439,9 @@ mod test { pub fn prove_unsafe( &self, a_present: bool, - a_hash: HashOut, + a_hash: Option>, b_present: bool, - b_hash: HashOut, + b_hash: Option>, merged_present: bool, merged_hash: Option>, left_proof: &ProofWithPublicInputs, @@ -491,58 +463,208 @@ mod test { } } - lazy_static! { - static ref LEAF: DummyLeafCircuit = DummyLeafCircuit::new(&CONFIG); - static ref BRANCH_1: DummyBranchCircuit = DummyBranchCircuit::from_leaf(&CONFIG, &LEAF); + #[tested_fixture::tested_fixture(LEAF)] + fn build_leaf() -> DummyLeafCircuit { DummyLeafCircuit::new(&CONFIG) } + + #[tested_fixture::tested_fixture(BRANCH_1)] + fn build_branch_1() -> DummyBranchCircuit { DummyBranchCircuit::from_leaf(&CONFIG, &LEAF) } + + fn assert_leaf(proof: &ProofWithPublicInputs, merged: Option>) { + let indices = &LEAF.merge.indices; + let p_present = indices.merged_present.get_any(&proof.public_inputs); + assert_eq!(p_present, F::from_bool(merged.is_some())); + + let p_merged = indices.merged_hash.get_any(&proof.public_inputs); + assert_eq!(p_merged, merged.unwrap_or_default().elements); + } + + fn assert_branch( + branch: &DummyBranchCircuit, + proof: &ProofWithPublicInputs, + a_hash: Option>, + b_hash: Option>, + merged: Option>, + ) { + let indices = &branch.merge.indices; + + let p_a_present = indices.a_present.get_any(&proof.public_inputs); + assert_eq!(p_a_present, F::from_bool(a_hash.is_some())); + + let p_a_hash = indices.a_hash.get_any(&proof.public_inputs); + assert_eq!(p_a_hash, a_hash.unwrap_or_default().elements); + + let p_b_present = indices.b_present.get_any(&proof.public_inputs); + assert_eq!(p_b_present, F::from_bool(b_hash.is_some())); + + let p_b_hash = indices.b_hash.get_any(&proof.public_inputs); + assert_eq!(p_b_hash, b_hash.unwrap_or_default().elements); + + let p_merged_present = indices.merged_present.get_any(&proof.public_inputs); + assert_eq!(p_merged_present, F::from_bool(merged.is_some())); + + let p_merged = indices.merged_hash.get_any(&proof.public_inputs); + assert_eq!(p_merged, merged.unwrap_or_default().elements); + } + + #[tested_fixture::tested_fixture(EMPTY_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_empty_leaf() -> Result> { + let proof = LEAF.prove(None, None)?; + assert_leaf(&proof, None); + LEAF.circuit.verify(proof.clone())?; + Ok(proof) + } + + #[tested_fixture::tested_fixture(LEFT_ZERO_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_left_zero_leaf() -> Result> { + let proof = LEAF.prove(Some(ZERO_HASH), None)?; + assert_leaf(&proof, Some(ZERO_HASH)); + LEAF.circuit.verify(proof.clone())?; + Ok(proof) + } + + #[tested_fixture::tested_fixture(RIGHT_ZERO_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_right_zero_leaf() -> Result> { + let proof = LEAF.prove(None, Some(ZERO_HASH))?; + assert_leaf(&proof, Some(ZERO_HASH)); + LEAF.circuit.verify(proof.clone())?; + Ok(proof) + } + + #[tested_fixture::tested_fixture(LEFT_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_left_leaf() -> Result> { + let proof = LEAF.prove(Some(NON_ZERO_HASHES[0]), None)?; + assert_leaf(&proof, Some(NON_ZERO_HASHES[0])); + LEAF.circuit.verify(proof.clone())?; + Ok(proof) + } + + #[tested_fixture::tested_fixture(RIGHT_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_right_leaf() -> Result> { + let proof = LEAF.prove(None, Some(NON_ZERO_HASHES[1]))?; + assert_leaf(&proof, Some(NON_ZERO_HASHES[1])); + LEAF.circuit.verify(proof.clone())?; + Ok(proof) + } + + #[tested_fixture::tested_fixture(BOTH_LEAF_PROOF_1: (ProofWithPublicInputs, HashOut))] + fn verify_both_leaf_1() -> Result<(ProofWithPublicInputs, HashOut)> { + let merged = hash_branch(&NON_ZERO_HASHES[0], &NON_ZERO_HASHES[1]); + let proof = LEAF.prove(Some(NON_ZERO_HASHES[0]), Some(NON_ZERO_HASHES[1]))?; + assert_leaf(&proof, Some(merged)); + LEAF.circuit.verify(proof.clone())?; + Ok((proof, merged)) + } + + #[tested_fixture::tested_fixture(BOTH_LEAF_PROOF_2: (ProofWithPublicInputs, HashOut))] + fn verify_both_leaf_2() -> Result<(ProofWithPublicInputs, HashOut)> { + let merged = hash_branch(&NON_ZERO_HASHES[2], &NON_ZERO_HASHES[3]); + let proof = LEAF.prove(Some(NON_ZERO_HASHES[2]), Some(NON_ZERO_HASHES[3]))?; + assert_leaf(&proof, Some(merged)); + LEAF.circuit.verify(proof.clone())?; + Ok((proof, merged)) } #[test] fn verify_leaf() -> Result<()> { - let zero_hash = HashOut::from([F::ZERO; NUM_HASH_OUT_ELTS]); let a_val = hash_str("Value Alpha"); let b_val = hash_str("Value Beta"); let ab_hash = hash_branch(&a_val, &b_val); - let zero_zero_hash = hash_branch(&zero_hash, &zero_hash); - let a_zero_hash = hash_branch(&a_val, &zero_hash); - let zero_b_hash = hash_branch(&zero_hash, &b_val); + let zero_zero_hash = hash_branch(&ZERO_HASH, &ZERO_HASH); + let a_zero_hash = hash_branch(&a_val, &ZERO_HASH); + let zero_b_hash = hash_branch(&ZERO_HASH, &b_val); - let proof = LEAF.prove(None, None, Some(zero_hash))?; + let proof = LEAF.prove(Some(a_val), Some(b_val))?; + assert_leaf(&proof, Some(ab_hash)); LEAF.circuit.verify(proof)?; - let proof = LEAF.prove(Some(a_val), None, Some(a_val))?; + let proof = LEAF.prove(Some(ZERO_HASH), Some(ZERO_HASH))?; + assert_leaf(&proof, Some(zero_zero_hash)); LEAF.circuit.verify(proof)?; - let proof = LEAF.prove(None, Some(b_val), Some(b_val))?; + let proof = LEAF.prove(Some(a_val), Some(ZERO_HASH))?; + assert_leaf(&proof, Some(a_zero_hash)); LEAF.circuit.verify(proof)?; - let proof = LEAF.prove(Some(a_val), Some(b_val), Some(ab_hash))?; + let proof = LEAF.prove(Some(ZERO_HASH), Some(b_val))?; + assert_leaf(&proof, Some(zero_b_hash)); LEAF.circuit.verify(proof)?; - let proof = LEAF.prove(Some(zero_hash), None, Some(zero_hash))?; - LEAF.circuit.verify(proof)?; + Ok(()) + } - let proof = LEAF.prove(None, Some(zero_hash), Some(zero_hash))?; - LEAF.circuit.verify(proof)?; + #[test] + #[should_panic(expected = "was set twice with different values")] + fn bad_leaf_empty_zero() { + let proof = LEAF + .prove_unsafe(false, ZERO_HASH, false, ZERO_HASH, true, None) + .unwrap(); + LEAF.circuit.verify(proof).unwrap(); + } - let proof = LEAF.prove(Some(zero_hash), Some(zero_hash), Some(zero_zero_hash))?; - LEAF.circuit.verify(proof)?; + #[test] + #[should_panic(expected = "was set twice with different values")] + fn bad_leaf_empty_left() { + let proof = LEAF + .prove_unsafe(false, NON_ZERO_HASHES[0], false, ZERO_HASH, true, None) + .unwrap(); + LEAF.circuit.verify(proof).unwrap(); + } - let proof = LEAF.prove(Some(a_val), Some(zero_hash), Some(a_zero_hash))?; - LEAF.circuit.verify(proof)?; + #[test] + #[should_panic(expected = "was set twice with different values")] + fn bad_leaf_empty_right() { + let proof = LEAF + .prove_unsafe(false, ZERO_HASH, false, NON_ZERO_HASHES[1], true, None) + .unwrap(); + LEAF.circuit.verify(proof).unwrap(); + } - let proof = LEAF.prove(Some(zero_hash), Some(b_val), Some(zero_b_hash))?; - LEAF.circuit.verify(proof)?; + #[test] + #[should_panic(expected = "was set twice with different values")] + fn bad_leaf_empty_both() { + let proof = LEAF + .prove_unsafe( + false, + NON_ZERO_HASHES[0], + false, + NON_ZERO_HASHES[1], + true, + None, + ) + .unwrap(); + LEAF.circuit.verify(proof).unwrap(); + } - Ok(()) + #[test] + #[should_panic(expected = "Tried to invert zero")] + fn bad_leaf_non_empty_left() { + let proof = LEAF + .prove_unsafe(false, NON_ZERO_HASHES[0], false, ZERO_HASH, false, None) + .unwrap(); + LEAF.circuit.verify(proof).unwrap(); } #[test] - #[should_panic(expected = "was set twice with different values")] - fn bad_leaf_empty() { - let zero_hash = HashOut::from([F::ZERO; NUM_HASH_OUT_ELTS]); + #[should_panic(expected = "Tried to invert zero")] + fn bad_leaf_non_empty_right() { + let proof = LEAF + .prove_unsafe(false, ZERO_HASH, false, NON_ZERO_HASHES[1], false, None) + .unwrap(); + LEAF.circuit.verify(proof).unwrap(); + } + #[test] + #[should_panic(expected = "Tried to invert zero")] + fn bad_leaf_non_empty_both() { let proof = LEAF - .prove_unsafe(false, zero_hash, false, zero_hash, true, None) + .prove_unsafe( + false, + NON_ZERO_HASHES[0], + false, + NON_ZERO_HASHES[1], + false, + None, + ) .unwrap(); LEAF.circuit.verify(proof).unwrap(); } @@ -550,11 +672,15 @@ mod test { #[test] #[should_panic(expected = "was set twice with different values")] fn bad_leaf_forward() { - let non_zero_hash_1 = hash_str("Non-Zero Hash 1"); - let non_zero_hash_2 = hash_str("Non-Zero Hash 2"); - let proof = LEAF - .prove(Some(non_zero_hash_1), None, Some(non_zero_hash_2)) + .prove_unsafe( + true, + NON_ZERO_HASHES[0], + false, + ZERO_HASH, + true, + Some(NON_ZERO_HASHES[1]), + ) .unwrap(); LEAF.circuit.verify(proof).unwrap(); } @@ -562,135 +688,148 @@ mod test { #[test] #[should_panic(expected = "was set twice with different values")] fn bad_leaf_hash() { - let non_zero_hash_1 = hash_str("Non-Zero Hash 1"); - let non_zero_hash_2 = hash_str("Non-Zero Hash 2"); - let non_zero_hash_3 = hash_str("Non-Zero Hash 3"); - let proof = LEAF - .prove( - Some(non_zero_hash_1), - Some(non_zero_hash_2), - Some(non_zero_hash_3), + .prove_unsafe( + true, + NON_ZERO_HASHES[0], + true, + NON_ZERO_HASHES[1], + true, + Some(NON_ZERO_HASHES[2]), ) .unwrap(); LEAF.circuit.verify(proof).unwrap(); } - #[test] - fn verify_branch_empty() -> Result<()> { - let zero_hash = HashOut::from([F::ZERO; NUM_HASH_OUT_ELTS]); - - let empty_proof = LEAF.prove(None, None, Some(zero_hash))?; - LEAF.circuit.verify(empty_proof.clone())?; - - let proof = BRANCH_1.prove(None, None, Some(zero_hash), &empty_proof, &empty_proof)?; + #[tested_fixture::tested_fixture(EMPTY_BRANCH_PROOF: ProofWithPublicInputs)] + fn verify_branch_empty() -> Result> { + let proof = BRANCH_1.prove(&EMPTY_LEAF_PROOF, &EMPTY_LEAF_PROOF)?; + assert_branch(*BRANCH_1, &proof, None, None, None); BRANCH_1.circuit.verify(proof.clone())?; + Ok(proof) + } + #[test] + fn verify_branch_single_leafs_1() -> Result<()> { + let merged = hash_branch(&NON_ZERO_HASHES[0], &NON_ZERO_HASHES[1]); + let proof = BRANCH_1.prove(*LEFT_LEAF_PROOF, *RIGHT_LEAF_PROOF)?; + assert_branch( + *BRANCH_1, + &proof, + Some(NON_ZERO_HASHES[0]), + Some(NON_ZERO_HASHES[1]), + Some(merged), + ); + BRANCH_1.circuit.verify(proof.clone())?; Ok(()) } #[test] - fn verify_branch_single() -> Result<()> { - let a_val = hash_str("Value Alpha"); - let b_val = hash_str("Value Beta"); - let ab_hash = hash_branch(&a_val, &b_val); - - let a_proof = LEAF.prove(Some(a_val), None, Some(a_val))?; - LEAF.circuit.verify(a_proof.clone())?; - - let b_proof = LEAF.prove(None, Some(b_val), Some(b_val))?; - LEAF.circuit.verify(b_proof.clone())?; - - let proof = BRANCH_1.prove(Some(a_val), Some(b_val), Some(ab_hash), &a_proof, &b_proof)?; + fn verify_branch_single_leafs_2() -> Result<()> { + let merged = hash_branch(&NON_ZERO_HASHES[1], &NON_ZERO_HASHES[0]); + let proof = BRANCH_1.prove(*RIGHT_LEAF_PROOF, *LEFT_LEAF_PROOF)?; + assert_branch( + *BRANCH_1, + &proof, + Some(NON_ZERO_HASHES[0]), + Some(NON_ZERO_HASHES[1]), + Some(merged), + ); BRANCH_1.circuit.verify(proof.clone())?; - Ok(()) } #[test] - fn verify_branch_multi() -> Result<()> { - let zero_hash = HashOut::from([F::ZERO; NUM_HASH_OUT_ELTS]); - let a_val = hash_str("Value Alpha"); - let b_val = hash_str("Value Beta"); - let ab_hash = hash_branch(&a_val, &b_val); - - let empty_proof = LEAF.prove(None, None, Some(zero_hash))?; - LEAF.circuit.verify(empty_proof.clone())?; - - let ab_proof = LEAF.prove(Some(a_val), Some(b_val), Some(ab_hash))?; - LEAF.circuit.verify(ab_proof.clone())?; - - let proof = BRANCH_1.prove( - Some(a_val), - Some(b_val), - Some(ab_hash), - &ab_proof, - &empty_proof, - )?; + fn verify_branch_right_empty() -> Result<()> { + // Both is (A, B) => AB + // Empty is (∅, ∅) => ∅ + // So merged results in (A∅, B∅) => AB + let proof = BRANCH_1.prove(&BOTH_LEAF_PROOF_1.0, *EMPTY_LEAF_PROOF)?; + assert_branch( + *BRANCH_1, + &proof, + Some(NON_ZERO_HASHES[0]), + Some(NON_ZERO_HASHES[1]), + Some(BOTH_LEAF_PROOF_1.1), + ); BRANCH_1.circuit.verify(proof)?; - Ok(()) } #[test] - #[allow(clippy::similar_names)] - fn verify_complex_branch() -> Result<()> { - let zero_hash = HashOut::from([F::ZERO; NUM_HASH_OUT_ELTS]); - let a_val = hash_str("Value Alpha"); - let b_val = hash_str("Value Beta"); - let c_val = hash_str("Value Gamma"); - let d_val = hash_str("Value Delta"); - let ab_hash = hash_branch(&a_val, &b_val); - let ac_hash = hash_branch(&a_val, &c_val); - let bd_hash = hash_branch(&b_val, &d_val); - let cd_hash = hash_branch(&c_val, &d_val); - let abcd_hash = hash_branch(&ab_hash, &cd_hash); - - // Imagine we want to merge tree AC with tree BD as follows: - // ABCD - // /\ /\ - // A B C D + fn verify_branch_left_empty() -> Result<()> { + // Empty is (∅, ∅) => ∅ + // Both is (A, B) => AB + // So merged results in (∅A, ∅B) => AB + let proof = BRANCH_1.prove(*EMPTY_LEAF_PROOF, &BOTH_LEAF_PROOF_1.0)?; + assert_branch( + *BRANCH_1, + &proof, + Some(NON_ZERO_HASHES[0]), + Some(NON_ZERO_HASHES[1]), + Some(BOTH_LEAF_PROOF_1.1), + ); + BRANCH_1.circuit.verify(proof)?; + Ok(()) + } - let empty_proof = LEAF.prove(None, None, Some(zero_hash))?; - LEAF.circuit.verify(empty_proof.clone())?; + #[test] + fn verify_complex_branch_1() -> Result<()> { + // P0 is (A, B) => AB + // P1 is (C, D) => CD + // So merged results should be (AC, BD) => (AB)(CD) + let alpha = hash_branch(&NON_ZERO_HASHES[0], &NON_ZERO_HASHES[2]); + let beta = hash_branch(&NON_ZERO_HASHES[1], &NON_ZERO_HASHES[3]); + let merged = hash_branch(&BOTH_LEAF_PROOF_1.1, &BOTH_LEAF_PROOF_2.1); + + let mut proof = BRANCH_1.prove(&BOTH_LEAF_PROOF_1.0, &BOTH_LEAF_PROOF_2.0)?; + assert_branch(*BRANCH_1, &proof, Some(alpha), Some(beta), Some(merged)); + BRANCH_1.circuit.verify(proof.clone())?; - let ab_proof = LEAF.prove(Some(a_val), Some(b_val), Some(ab_hash))?; - LEAF.circuit.verify(ab_proof.clone())?; + // Test that empty leafs have no effect + let mut branch_next = DummyBranchCircuit::from_branch(&CONFIG, &BRANCH_1); + let mut empty_proof = EMPTY_BRANCH_PROOF.clone(); + for _ in 0..4 { + proof = branch_next.prove(&proof, &empty_proof)?; + assert_branch(&branch_next, &proof, Some(alpha), Some(beta), Some(merged)); + branch_next.circuit.verify(proof.clone())?; - let cd_proof = LEAF.prove(Some(c_val), Some(d_val), Some(cd_hash))?; - LEAF.circuit.verify(cd_proof.clone())?; + empty_proof = branch_next.prove(&empty_proof, &empty_proof)?; + assert_branch(&branch_next, &empty_proof, None, None, None); + branch_next.circuit.verify(empty_proof.clone())?; - let mut empty_proof = - BRANCH_1.prove(None, None, Some(zero_hash), &empty_proof, &empty_proof)?; - BRANCH_1.circuit.verify(empty_proof.clone())?; + branch_next = DummyBranchCircuit::from_branch(&CONFIG, &branch_next); + } - let mut abcd_proof = BRANCH_1.prove( - Some(ac_hash), - Some(bd_hash), - Some(abcd_hash), - &ab_proof, - &cd_proof, - )?; - BRANCH_1.circuit.verify(abcd_proof.clone())?; + Ok(()) + } - let mut branch_2 = DummyBranchCircuit::from_branch(&CONFIG, &BRANCH_1); + #[test] + fn verify_complex_branch_2() -> Result<()> { + // P0 is (C, D) => CD + // P1 is (A, B) => AB + // So merged results should be (CA, BD) => (CD)(AB) + let alpha = hash_branch(&NON_ZERO_HASHES[2], &NON_ZERO_HASHES[0]); + let beta = hash_branch(&NON_ZERO_HASHES[3], &NON_ZERO_HASHES[1]); + let merged = hash_branch(&BOTH_LEAF_PROOF_2.1, &BOTH_LEAF_PROOF_1.1); + + let mut proof = BRANCH_1.prove(&BOTH_LEAF_PROOF_2.0, &BOTH_LEAF_PROOF_1.0)?; + assert_branch(*BRANCH_1, &proof, Some(alpha), Some(beta), Some(merged)); + BRANCH_1.circuit.verify(proof.clone())?; // Test that empty leafs have no effect + let mut branch_next = DummyBranchCircuit::from_branch(&CONFIG, &BRANCH_1); + let mut empty_proof = EMPTY_BRANCH_PROOF.clone(); for _ in 0..4 { - abcd_proof = branch_2.prove( - Some(ac_hash), - Some(bd_hash), - Some(abcd_hash), - &abcd_proof, - &empty_proof, - )?; - branch_2.circuit.verify(abcd_proof.clone())?; - - empty_proof = - branch_2.prove(None, None, Some(zero_hash), &empty_proof, &empty_proof)?; - branch_2.circuit.verify(empty_proof.clone())?; - - branch_2 = DummyBranchCircuit::from_branch(&CONFIG, &branch_2); + proof = branch_next.prove(&proof, &empty_proof)?; + assert_branch(&branch_next, &proof, Some(alpha), Some(beta), Some(merged)); + branch_next.circuit.verify(proof.clone())?; + + empty_proof = branch_next.prove(&empty_proof, &empty_proof)?; + assert_branch(&branch_next, &empty_proof, None, None, None); + branch_next.circuit.verify(empty_proof.clone())?; + + branch_next = DummyBranchCircuit::from_branch(&CONFIG, &branch_next); } Ok(()) @@ -699,26 +838,16 @@ mod test { #[test] #[should_panic(expected = "was set twice with different values")] fn bad_branch_empty() { - let (zero_hash, empty_proof) = catch_unwind(|| { - let zero_hash = HashOut::from([F::ZERO; NUM_HASH_OUT_ELTS]); - - let empty_proof = LEAF.prove(None, None, Some(zero_hash)).unwrap(); - LEAF.circuit.verify(empty_proof.clone()).unwrap(); - - (zero_hash, empty_proof) - }) - .expect("shouldn't fail"); - let proof = BRANCH_1 .prove_unsafe( false, - zero_hash, + None, false, - zero_hash, + None, true, - Some(zero_hash), - &empty_proof, - &empty_proof, + None, + &EMPTY_LEAF_PROOF, + &EMPTY_LEAF_PROOF, ) .unwrap(); BRANCH_1.circuit.verify(proof.clone()).unwrap(); @@ -726,31 +855,17 @@ mod test { #[test] #[should_panic(expected = "was set twice with different values")] - fn bad_branch_forward() { - let (non_zero_hash_1, non_zero_hash_2, empty_proof, a_proof) = catch_unwind(|| { - let zero_hash = HashOut::from([F::ZERO; NUM_HASH_OUT_ELTS]); - let non_zero_hash_1 = hash_str("Non-Zero Hash 1"); - let non_zero_hash_2 = hash_str("Non-Zero Hash 2"); - - let empty_proof = LEAF.prove(None, None, Some(zero_hash)).unwrap(); - LEAF.circuit.verify(empty_proof.clone()).unwrap(); - - let a_proof = LEAF - .prove(Some(non_zero_hash_1), None, Some(non_zero_hash_1)) - .unwrap(); - LEAF.circuit.verify(a_proof.clone()).unwrap(); - - (non_zero_hash_1, non_zero_hash_2, empty_proof, a_proof) - }) - .expect("shouldn't fail"); - + fn bad_branch_forward_left() { let proof = BRANCH_1 - .prove( - Some(non_zero_hash_1), + .prove_unsafe( + true, + Some(NON_ZERO_HASHES[0]), + false, None, - Some(non_zero_hash_2), - &a_proof, - &empty_proof, + true, + Some(NON_ZERO_HASHES[1]), + &LEFT_LEAF_PROOF, + *EMPTY_LEAF_PROOF, ) .unwrap(); BRANCH_1.circuit.verify(proof.clone()).unwrap(); diff --git a/recproofs/src/circuits/merge/embed.rs b/recproofs/src/circuits/merge/embed.rs index 301f52188..63ebc90af 100644 --- a/recproofs/src/circuits/merge/embed.rs +++ b/recproofs/src/circuits/merge/embed.rs @@ -219,20 +219,15 @@ impl BranchSubCircuit { #[cfg(test)] mod test { - use std::panic::catch_unwind; - use anyhow::Result; - use lazy_static::lazy_static; use plonky2::field::types::Field; use plonky2::hash::hash_types::NUM_HASH_OUT_ELTS; use plonky2::plonk::circuit_data::{CircuitConfig, CircuitData}; use super::*; - use crate::circuits::merge::{ - BranchCircuit as MergeBranchCircuit, LeafCircuit as MergeLeafCircuit, - }; + use crate::circuits::merge::test as merge; use crate::subcircuits::bounded; - use crate::test_utils::{hash_branch, hash_str, C, CONFIG, D, F}; + use crate::test_utils::{hash_branch, hash_str, C, CONFIG, D, F, NON_ZERO_HASHES, ZERO_HASH}; pub struct DummyLeafCircuit { pub bounded: bounded::LeafSubCircuit, @@ -294,7 +289,7 @@ mod test { #[must_use] pub fn new( circuit_config: &CircuitConfig, - mc: &MergeBranchCircuit, + mc: &merge::BranchCircuit, indices: &PublicIndices, child: &CircuitData, ) -> Self { @@ -327,7 +322,7 @@ mod test { #[must_use] pub fn from_leaf( circuit_config: &CircuitConfig, - mc: &MergeBranchCircuit, + mc: &merge::BranchCircuit, leaf: &DummyLeafCircuit, ) -> Self { Self::new(circuit_config, mc, &leaf.merge.indices, &leaf.circuit) @@ -336,7 +331,7 @@ mod test { #[must_use] pub fn from_branch( circuit_config: &CircuitConfig, - mc: &MergeBranchCircuit, + mc: &merge::BranchCircuit, branch: &Self, ) -> Self { Self::new(circuit_config, mc, &branch.merge.indices, &branch.circuit) @@ -373,15 +368,17 @@ mod test { } } - lazy_static! { - static ref MC_LEAF: MergeLeafCircuit = MergeLeafCircuit::new(&CONFIG); - static ref MC_BRANCH: MergeBranchCircuit = - MergeBranchCircuit::new(&CONFIG, &MC_LEAF); - static ref LEAF: DummyLeafCircuit = DummyLeafCircuit::new(&CONFIG); - static ref BRANCH_1: DummyBranchCircuit = - DummyBranchCircuit::from_leaf(&CONFIG, &MC_BRANCH, &LEAF); - static ref BRANCH_2: DummyBranchCircuit = - DummyBranchCircuit::from_branch(&CONFIG, &MC_BRANCH, &BRANCH_1); + #[tested_fixture::tested_fixture(pub LEAF)] + fn build_leaf() -> DummyLeafCircuit { DummyLeafCircuit::new(&CONFIG) } + + #[tested_fixture::tested_fixture(pub BRANCH_1)] + fn build_branch_1() -> DummyBranchCircuit { + DummyBranchCircuit::from_leaf(&CONFIG, &merge::BRANCH, &LEAF) + } + + #[tested_fixture::tested_fixture(pub BRANCH_2)] + fn build_branch_2() -> DummyBranchCircuit { + DummyBranchCircuit::from_branch(&CONFIG, &merge::BRANCH, &BRANCH_1) } #[test] @@ -405,233 +402,160 @@ mod test { Ok(()) } - #[test] - fn verify_branch_empty() -> Result<()> { - let zero_hash = HashOut::from([F::ZERO; NUM_HASH_OUT_ELTS]); - - let empty_merge_leaf = MC_LEAF.prove(&MC_BRANCH, None, None, Some(zero_hash))?; - MC_LEAF.circuit.verify(empty_merge_leaf.clone())?; - - let merge_branch = MC_BRANCH.prove(true, &empty_merge_leaf, true, &empty_merge_leaf)?; - MC_BRANCH.circuit.verify(merge_branch.clone())?; - - let empty_proof = LEAF.prove(None)?; - LEAF.circuit.verify(empty_proof.clone())?; - - let branch_proof = BRANCH_1.prove(&merge_branch, &empty_proof, &empty_proof)?; - BRANCH_1.circuit.verify(branch_proof)?; - - Ok(()) + #[tested_fixture::tested_fixture(EMPTY_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_empty_leaf() -> Result> { + let proof = LEAF.prove(None)?; + LEAF.circuit.verify(proof.clone())?; + Ok(proof) } - #[test] - fn verify_branch_single() -> Result<()> { - let zero_hash = HashOut::from([F::ZERO; NUM_HASH_OUT_ELTS]); - let a_val = hash_str("Value Alpha"); - let b_val = hash_str("Value Beta"); - - let empty_merge_leaf = MC_LEAF.prove(&MC_BRANCH, None, None, Some(zero_hash))?; - MC_LEAF.circuit.verify(empty_merge_leaf.clone())?; - - let a_merge_leaf = MC_LEAF.prove(&MC_BRANCH, Some(a_val), None, Some(a_val))?; - MC_LEAF.circuit.verify(a_merge_leaf.clone())?; - - let b_merge_leaf = MC_LEAF.prove(&MC_BRANCH, None, Some(b_val), Some(b_val))?; - MC_LEAF.circuit.verify(b_merge_leaf.clone())?; - - let merge_branch_a = MC_BRANCH.prove(true, &a_merge_leaf, true, &empty_merge_leaf)?; - MC_BRANCH.circuit.verify(merge_branch_a.clone())?; - - let merge_branch_b = MC_BRANCH.prove(true, &empty_merge_leaf, true, &b_merge_leaf)?; - MC_BRANCH.circuit.verify(merge_branch_b.clone())?; - - let merge_branch_ab = MC_BRANCH.prove(false, &merge_branch_a, false, &merge_branch_b)?; - MC_BRANCH.circuit.verify(merge_branch_b.clone())?; + #[tested_fixture::tested_fixture(BAD_EMPTY_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_bad_empty_leaf() -> Result> { + let proof = LEAF.prove_unsafe(false, NON_ZERO_HASHES[2])?; + LEAF.circuit.verify(proof.clone())?; + Ok(proof) + } - let empty_proof = LEAF.prove(None)?; - LEAF.circuit.verify(empty_proof.clone())?; + #[tested_fixture::tested_fixture(ZERO_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_zero_leaf() -> Result> { + let proof = LEAF.prove(Some(ZERO_HASH))?; + LEAF.circuit.verify(proof.clone())?; + Ok(proof) + } - let a_proof = LEAF.prove(Some(a_val))?; - LEAF.circuit.verify(a_proof.clone())?; + #[tested_fixture::tested_fixture(NON_ZERO_LEAF_PROOF_1: ProofWithPublicInputs)] + fn verify_non_zero_leaf_1() -> Result> { + let proof = LEAF.prove(Some(NON_ZERO_HASHES[0]))?; + LEAF.circuit.verify(proof.clone())?; + Ok(proof) + } - let b_proof = LEAF.prove(Some(b_val))?; - LEAF.circuit.verify(b_proof.clone())?; + #[tested_fixture::tested_fixture(NON_ZERO_LEAF_PROOF_2: ProofWithPublicInputs)] + fn verify_non_zero_leaf_2() -> Result> { + let proof = LEAF.prove(Some(NON_ZERO_HASHES[1]))?; + LEAF.circuit.verify(proof.clone())?; + Ok(proof) + } - let a_branch_proof = BRANCH_1.prove(&merge_branch_a, &a_proof, &empty_proof)?; - BRANCH_1.circuit.verify(a_branch_proof.clone())?; + #[tested_fixture::tested_fixture(EMPTY_BRANCH_PROOF: ProofWithPublicInputs)] + fn verify_empty_branch() -> Result> { + let proof = BRANCH_1.prove( + *merge::EMPTY_BRANCH_PROOF, + *EMPTY_LEAF_PROOF, + *EMPTY_LEAF_PROOF, + )?; + BRANCH_1.circuit.verify(proof.clone())?; + Ok(proof) + } - let b_branch_proof = BRANCH_1.prove(&merge_branch_b, &empty_proof, &b_proof)?; - BRANCH_1.circuit.verify(b_branch_proof.clone())?; + #[tested_fixture::tested_fixture(LEFT_BRANCH_PROOF: ProofWithPublicInputs)] + fn verify_left_branch() -> Result> { + let proof = BRANCH_1.prove( + *merge::LEFT_BRANCH_PROOF, + *NON_ZERO_LEAF_PROOF_1, + *EMPTY_LEAF_PROOF, + )?; + BRANCH_1.circuit.verify(proof.clone())?; + Ok(proof) + } - let ab_branch_proof = BRANCH_2.prove(&merge_branch_ab, &a_branch_proof, &b_branch_proof)?; - BRANCH_2.circuit.verify(ab_branch_proof.clone())?; + #[tested_fixture::tested_fixture(RIGHT_BRANCH_PROOF: ProofWithPublicInputs)] + fn verify_right_branch() -> Result> { + let proof = BRANCH_1.prove( + *merge::RIGHT_BRANCH_PROOF, + *EMPTY_LEAF_PROOF, + *NON_ZERO_LEAF_PROOF_2, + )?; + BRANCH_1.circuit.verify(proof.clone())?; + Ok(proof) + } + #[test] + fn verify_both_branch() -> Result<()> { + let proof = BRANCH_1.prove( + &merge::BOTH_BRANCH_PROOF, + *NON_ZERO_LEAF_PROOF_1, + *NON_ZERO_LEAF_PROOF_2, + )?; + BRANCH_1.circuit.verify(proof)?; Ok(()) } #[test] - fn verify_branch_pair() -> Result<()> { - let a_val = hash_str("Value Alpha"); - let b_val = hash_str("Value Beta"); - - let a_merge_leaf = MC_LEAF.prove(&MC_BRANCH, Some(a_val), None, Some(a_val))?; - MC_LEAF.circuit.verify(a_merge_leaf.clone())?; - - let b_merge_leaf = MC_LEAF.prove(&MC_BRANCH, None, Some(b_val), Some(b_val))?; - MC_LEAF.circuit.verify(b_merge_leaf.clone())?; - - let merge_branch = MC_BRANCH.prove(true, &a_merge_leaf, true, &b_merge_leaf)?; - MC_BRANCH.circuit.verify(merge_branch.clone())?; - - let a_proof = LEAF.prove(Some(a_val))?; - LEAF.circuit.verify(a_proof.clone())?; - - let b_proof = LEAF.prove(Some(b_val))?; - LEAF.circuit.verify(b_proof.clone())?; - - let branch_proof = BRANCH_1.prove(&merge_branch, &a_proof, &b_proof)?; - BRANCH_1.circuit.verify(branch_proof)?; - + fn verify_both_double_branch() -> Result<()> { + let proof = BRANCH_2.prove( + &merge::BOTH_BRANCH_PROOF, + *LEFT_BRANCH_PROOF, + *RIGHT_BRANCH_PROOF, + )?; + BRANCH_2.circuit.verify(proof)?; Ok(()) } #[test] #[should_panic(expected = "was set twice with different values")] fn bad_branch_empty_1() { - let (merge_branch, good_empty_proof, bad_empty_proof) = catch_unwind(|| { - let zero_hash = HashOut::from([F::ZERO; NUM_HASH_OUT_ELTS]); - - let empty_merge_leaf = MC_LEAF - .prove(&MC_BRANCH, None, None, Some(zero_hash)) - .unwrap(); - MC_LEAF.circuit.verify(empty_merge_leaf.clone()).unwrap(); - - let merge_branch = MC_BRANCH - .prove(true, &empty_merge_leaf, true, &empty_merge_leaf) - .unwrap(); - MC_BRANCH.circuit.verify(merge_branch.clone()).unwrap(); - - let good_empty_proof = LEAF.prove(None).unwrap(); - LEAF.circuit.verify(good_empty_proof.clone()).unwrap(); - - let bad_empty_proof = LEAF.prove(Some(zero_hash)).unwrap(); - LEAF.circuit.verify(bad_empty_proof.clone()).unwrap(); - - (merge_branch, good_empty_proof, bad_empty_proof) - }) - .expect("shouldn't fail"); - - let branch_proof = BRANCH_1 - .prove(&merge_branch, &good_empty_proof, &bad_empty_proof) + let proof = BRANCH_1 + .prove( + *merge::EMPTY_BRANCH_PROOF, + *EMPTY_LEAF_PROOF, + *ZERO_LEAF_PROOF, + ) .unwrap(); - BRANCH_1.circuit.verify(branch_proof).unwrap(); + BRANCH_1.circuit.verify(proof).unwrap(); } #[test] #[should_panic(expected = "was set twice with different values")] fn bad_branch_empty_2() { - let (merge_branch, good_empty_proof, bad_empty_proof) = catch_unwind(|| { - let zero_hash = HashOut::from([F::ZERO; NUM_HASH_OUT_ELTS]); - let non_zero_hash_1 = hash_str("Non-Zero Hash 1"); - - let empty_merge_leaf = MC_LEAF - .prove(&MC_BRANCH, None, None, Some(zero_hash)) - .unwrap(); - MC_LEAF.circuit.verify(empty_merge_leaf.clone()).unwrap(); - - let merge_branch = MC_BRANCH - .prove(true, &empty_merge_leaf, true, &empty_merge_leaf) - .unwrap(); - MC_BRANCH.circuit.verify(merge_branch.clone()).unwrap(); - - let good_empty_proof = LEAF.prove(None).unwrap(); - LEAF.circuit.verify(good_empty_proof.clone()).unwrap(); - - let bad_empty_proof = LEAF.prove_unsafe(false, non_zero_hash_1).unwrap(); - LEAF.circuit.verify(bad_empty_proof.clone()).unwrap(); - - (merge_branch, good_empty_proof, bad_empty_proof) - }) - .expect("shouldn't fail"); - - let branch_proof = BRANCH_1 - .prove(&merge_branch, &good_empty_proof, &bad_empty_proof) + let proof = BRANCH_1 + .prove( + *merge::EMPTY_BRANCH_PROOF, + *EMPTY_LEAF_PROOF, + *BAD_EMPTY_LEAF_PROOF, + ) .unwrap(); - BRANCH_1.circuit.verify(branch_proof).unwrap(); + BRANCH_1.circuit.verify(proof).unwrap(); } #[test] #[should_panic(expected = "was set twice with different values")] fn bad_branch_empty_3() { - let (merge_branch, empty_proof) = catch_unwind(|| { - let zero_hash = HashOut::from([F::ZERO; NUM_HASH_OUT_ELTS]); - - let empty_merge_leaf = MC_LEAF - .prove(&MC_BRANCH, None, None, Some(zero_hash)) - .unwrap(); - MC_LEAF.circuit.verify(empty_merge_leaf.clone()).unwrap(); - - let bad_empty_merge_leaf = MC_LEAF - .prove(&MC_BRANCH, Some(zero_hash), None, Some(zero_hash)) - .unwrap(); - MC_LEAF - .circuit - .verify(bad_empty_merge_leaf.clone()) - .unwrap(); - - let merge_branch = MC_BRANCH - .prove(true, &empty_merge_leaf, true, &bad_empty_merge_leaf) - .unwrap(); - MC_BRANCH.circuit.verify(merge_branch.clone()).unwrap(); - - let empty_proof = LEAF.prove(None).unwrap(); - LEAF.circuit.verify(empty_proof.clone()).unwrap(); - - (merge_branch, empty_proof) - }) - .expect("shouldn't fail"); - - let branch_proof = BRANCH_1 - .prove(&merge_branch, &empty_proof, &empty_proof) + let proof = BRANCH_1 + .prove( + *merge::LEFT_ZERO_BRANCH_PROOF, + *EMPTY_LEAF_PROOF, + *EMPTY_LEAF_PROOF, + ) + .unwrap(); + BRANCH_1.circuit.verify(proof).unwrap(); + } + + #[test] + #[should_panic(expected = "was set twice with different values")] + fn bad_branch_empty_4() { + let proof = BRANCH_1 + .prove( + *merge::RIGHT_ZERO_BRANCH_PROOF, + *EMPTY_LEAF_PROOF, + *EMPTY_LEAF_PROOF, + ) .unwrap(); - BRANCH_1.circuit.verify(branch_proof).unwrap(); + BRANCH_1.circuit.verify(proof).unwrap(); } #[test] #[should_panic(expected = "was set twice with different values")] fn bad_branch_pair() { - let (merge_branch, ba_hash, a_proof, b_proof) = catch_unwind(|| { - let a_val = hash_str("Value Alpha"); - let b_val = hash_str("Value Beta"); - let ba_hash = hash_branch(&b_val, &a_val); - - let a_merge_leaf = MC_LEAF - .prove(&MC_BRANCH, Some(a_val), None, Some(a_val)) - .unwrap(); - MC_LEAF.circuit.verify(a_merge_leaf.clone()).unwrap(); - - let b_merge_leaf = MC_LEAF - .prove(&MC_BRANCH, None, Some(b_val), Some(b_val)) - .unwrap(); - MC_LEAF.circuit.verify(b_merge_leaf.clone()).unwrap(); - - let merge_branch = MC_BRANCH - .prove(true, &a_merge_leaf, true, &b_merge_leaf) - .unwrap(); - MC_BRANCH.circuit.verify(merge_branch.clone()).unwrap(); - - let a_proof = LEAF.prove(Some(a_val)).unwrap(); - LEAF.circuit.verify(a_proof.clone()).unwrap(); - - let b_proof = LEAF.prove(Some(b_val)).unwrap(); - LEAF.circuit.verify(b_proof.clone()).unwrap(); - (merge_branch, ba_hash, a_proof, b_proof) - }) - .expect("shouldn't fail"); - - let branch_proof = BRANCH_1 - .prove_unsafe(&merge_branch, true, ba_hash, &a_proof, &b_proof) + let proof = BRANCH_1 + .prove_unsafe( + &merge::BOTH_BRANCH_PROOF, + true, + hash_branch(&NON_ZERO_HASHES[1], &NON_ZERO_HASHES[0]), + *NON_ZERO_LEAF_PROOF_1, + *NON_ZERO_LEAF_PROOF_2, + ) .unwrap(); - BRANCH_1.circuit.verify(branch_proof).unwrap(); + BRANCH_1.circuit.verify(proof).unwrap(); } } diff --git a/recproofs/src/circuits/mod.rs b/recproofs/src/circuits/mod.rs index eb1cc8ca3..90c79d6f8 100644 --- a/recproofs/src/circuits/mod.rs +++ b/recproofs/src/circuits/mod.rs @@ -5,3 +5,231 @@ pub mod merge; pub mod state_update; pub mod verify_program; pub mod verify_tx; + +/// A repository of testing data to allow unit tests to build on one another +/// and cross-reference by having them all draw from a consistent transaction +/// set. +/// +/// At present this consists of 2 transactions modifying a state-tree of size 8 +/// (only addresses 0..=7 are valid). +#[cfg(test)] +pub mod test_data { + use once_cell::sync::Lazy; + use plonky2::field::types::Field; + use plonky2::hash::hash_types::HashOut; + + use crate::test_utils::{hash_val_bytes, make_f, make_fs, F, NON_ZERO_VALUES, ZERO_VAL}; + use crate::{Event, EventType, Object}; + + /// The hashes of the programs used + pub const PROGRAM_HASHES: [[F; 4]; 3] = [ + make_fs([31, 41, 59, 26]), + make_fs([53, 58, 97, 93]), + make_fs([23, 84, 62, 64]), + ]; + + /// Each transaction has a call list + pub const CALL_LISTS: [[F; 4]; 2] = [make_fs([86, 7, 5, 309]), make_fs([8, 67, 530, 9])]; + + /// Each transaction has a call list + pub static CAST_ROOT: Lazy<[HashOut; 2]> = Lazy::new(|| { + [ + hash_val_bytes( + hash_val_bytes(ZERO_VAL, PROGRAM_HASHES[0]), + PROGRAM_HASHES[2], + ) + .into(), + hash_val_bytes( + hash_val_bytes(ZERO_VAL, PROGRAM_HASHES[1]), + PROGRAM_HASHES[2], + ) + .into(), + ] + }); + + // The addresses that will be used by events + /// An address being updated + pub const ADDRESS_A: usize = 2; + /// An address being deleted + pub const ADDRESS_B: usize = 4; + /// An address being created + pub const ADDRESS_C: usize = 5; + /// An address being read + pub const ADDRESS_D: usize = 6; + /// An address being ignored + pub const ADDRESS_E: usize = 7; + + /// The empty object + pub const ZERO_OBJ: Object = Object { + constraint_owner: ZERO_VAL, + last_updated: F::ZERO, + credits: F::ZERO, + data: ZERO_VAL, + }; + + /// The hash of the empty object + pub static ZERO_OBJ_HASH: Lazy> = Lazy::new(|| ZERO_OBJ.hash()); + + /// The initial state + pub const STATE_0: [Object; 8] = { + let mut state = [ZERO_OBJ; 8]; + + state[ADDRESS_A] = Object { + constraint_owner: PROGRAM_HASHES[0], + last_updated: F::ZERO, + credits: make_f(400), + data: ZERO_VAL, + }; + state[ADDRESS_B] = Object { + constraint_owner: PROGRAM_HASHES[1], + last_updated: F::ZERO, + credits: make_f(500), + data: NON_ZERO_VALUES[0], + }; + state[ADDRESS_D] = Object { + constraint_owner: PROGRAM_HASHES[1], + last_updated: F::ZERO, + credits: F::ZERO, + data: NON_ZERO_VALUES[1], + }; + state[ADDRESS_E] = Object { + constraint_owner: PROGRAM_HASHES[2], + last_updated: F::ZERO, + credits: F::ZERO, + data: NON_ZERO_VALUES[2], + }; + + state + }; + + /// The next state + pub const STATE_1: [Object; 8] = { + let mut state = [ZERO_OBJ; 8]; + + state[ADDRESS_A] = Object { + constraint_owner: PROGRAM_HASHES[0], + last_updated: make_f(1), + credits: make_f(100), + data: NON_ZERO_VALUES[3], + }; + state[ADDRESS_C] = Object { + constraint_owner: PROGRAM_HASHES[2], + last_updated: make_f(1), + credits: make_f(300), + data: NON_ZERO_VALUES[4], + }; + state[ADDRESS_D] = STATE_0[ADDRESS_D]; + state[ADDRESS_E] = STATE_0[ADDRESS_E]; + + state + }; + + // The events of the first transaction + + pub const EVENT_T0_PM_C_CREDIT: Event = Event { + address: ADDRESS_C as u64, + owner: [F::ZERO; 4], + ty: EventType::CreditDelta, + value: make_fs([300, 0, 0, 0]), + }; + + pub const EVENT_T0_PM_C_GIVE: Event = Event { + address: ADDRESS_C as u64, + owner: [F::ZERO; 4], + ty: EventType::GiveOwner, + value: PROGRAM_HASHES[2], + }; + + pub const EVENT_T0_PM_C_WRITE: Event = Event { + address: ADDRESS_C as u64, + owner: [F::ZERO; 4], + ty: EventType::Write, + value: NON_ZERO_VALUES[4], + }; + + pub const EVENT_T0_P0_A_WRITE: Event = Event { + address: ADDRESS_A as u64, + owner: PROGRAM_HASHES[0], + ty: EventType::Write, + value: NON_ZERO_VALUES[3], + }; + + pub const EVENT_T0_P0_A_CREDIT: Event = Event { + address: ADDRESS_A as u64, + owner: PROGRAM_HASHES[0], + ty: EventType::CreditDelta, + value: make_fs([300, 0, 0, 1]), + }; + + pub const EVENT_T0_P2_A_READ: Event = Event { + address: ADDRESS_A as u64, + owner: PROGRAM_HASHES[2], + ty: EventType::Read, + value: ZERO_VAL, + }; + + pub const EVENT_T0_P2_A_ENSURE: Event = Event { + address: ADDRESS_A as u64, + owner: PROGRAM_HASHES[2], + ty: EventType::Ensure, + value: NON_ZERO_VALUES[3], + }; + + pub const EVENT_T0_P2_C_TAKE: Event = Event { + address: ADDRESS_C as u64, + owner: PROGRAM_HASHES[2], + ty: EventType::TakeOwner, + value: [F::ZERO; 4], + }; + + // The events of the second transaction + + pub const EVENT_T1_PM_B_TAKE: Event = Event { + address: ADDRESS_B as u64, + owner: [F::ZERO; 4], + ty: EventType::TakeOwner, + value: PROGRAM_HASHES[1], + }; + + pub const EVENT_T1_PM_B_ENSURE: Event = Event { + address: ADDRESS_B as u64, + owner: [F::ZERO; 4], + ty: EventType::Ensure, + value: [F::ZERO; 4], + }; + + pub const EVENT_T1_P1_B_WRITE: Event = Event { + address: ADDRESS_B as u64, + owner: PROGRAM_HASHES[1], + ty: EventType::Write, + value: ZERO_VAL, + }; + + pub const EVENT_T1_P1_B_GIVE: Event = Event { + address: ADDRESS_B as u64, + owner: PROGRAM_HASHES[1], + ty: EventType::GiveOwner, + value: ZERO_VAL, + }; + + pub const EVENT_T1_P1_B_CREDIT: Event = Event { + address: ADDRESS_B as u64, + owner: PROGRAM_HASHES[1], + ty: EventType::CreditDelta, + value: make_fs([500, 0, 0, 1]), + }; + + pub const EVENT_T1_P2_A_READ: Event = Event { + address: ADDRESS_A as u64, + owner: PROGRAM_HASHES[2], + ty: EventType::Read, + value: ZERO_VAL, + }; + + pub const EVENT_T1_P2_D_READ: Event = Event { + address: ADDRESS_D as u64, + owner: PROGRAM_HASHES[2], + ty: EventType::Read, + value: NON_ZERO_VALUES[1], + }; +} diff --git a/recproofs/src/circuits/state_update.rs b/recproofs/src/circuits/state_update.rs index b3a0947fa..3f0b14805 100644 --- a/recproofs/src/circuits/state_update.rs +++ b/recproofs/src/circuits/state_update.rs @@ -101,12 +101,26 @@ where &self, old_hash: HashOut, new_hash: HashOut, - summary_hash: HashOut, address: Option, ) -> Result> { let mut inputs = PartialWitness::new(); self.bounded.set_witness(&mut inputs); - self.summarized.set_witness(&mut inputs, summary_hash); + self.old.set_witness(&mut inputs, old_hash); + self.new.set_witness(&mut inputs, new_hash); + self.address.set_witness(&mut inputs, address); + self.circuit.prove(inputs) + } + + pub fn prove_unsafe( + &self, + old_hash: HashOut, + new_hash: HashOut, + summarized: Option>, + address: Option, + ) -> Result> { + let mut inputs = PartialWitness::new(); + self.bounded.set_witness(&mut inputs); + self.summarized.set_witness(&mut inputs, summarized); self.old.set_witness(&mut inputs, old_hash); self.new.set_witness(&mut inputs, new_hash); self.address.set_witness(&mut inputs, address); @@ -226,9 +240,22 @@ where pub fn prove( &self, - old_hash: HashOut, - new_hash: HashOut, - summary_hash: HashOut, + left_proof: &ProofWithPublicInputs, + right_proof: &ProofWithPublicInputs, + ) -> Result> + where + >::Hasher: AlgebraicHasher, { + let mut inputs = PartialWitness::new(); + self.bounded + .set_witness(&mut inputs, left_proof, right_proof); + self.circuit.prove(inputs) + } + + pub fn prove_unsafe( + &self, + old_hash: Option>, + new_hash: Option>, + summary_hash: Option>>, address: impl Into, left_proof: &ProofWithPublicInputs, right_proof: &ProofWithPublicInputs, @@ -238,9 +265,15 @@ where let mut inputs = PartialWitness::new(); self.bounded .set_witness(&mut inputs, left_proof, right_proof); - self.summarized.set_witness(&mut inputs, summary_hash); - self.old.set_witness(&mut inputs, old_hash); - self.new.set_witness(&mut inputs, new_hash); + if let Some(summary_hash) = summary_hash { + self.summarized.set_witness(&mut inputs, summary_hash); + } + if let Some(old_hash) = old_hash { + self.old.set_witness(&mut inputs, old_hash); + } + if let Some(new_hash) = new_hash { + self.new.set_witness(&mut inputs, new_hash); + } match address.into() { AddressPresent::Present(a) => self.address.set_witness(&mut inputs, Some(a)), AddressPresent::Absent => self.address.set_witness(&mut inputs, None), @@ -269,81 +302,141 @@ impl From for AddressPresent { #[cfg(test)] mod test { - use lazy_static::lazy_static; use plonky2::field::types::Field; - use plonky2::plonk::config::Hasher; use super::*; - use crate::test_utils::{hash_branch, hash_str, C, CONFIG, D, F}; + use crate::circuits::test_data::{ + ADDRESS_A, ADDRESS_B, ADDRESS_C, ADDRESS_D, ADDRESS_E, STATE_0, STATE_1, ZERO_OBJ_HASH, + }; + use crate::summarize; + use crate::test_utils::{hash_branch, C, CONFIG, D, F, NON_ZERO_HASHES, ZERO_HASH}; + + #[tested_fixture::tested_fixture(LEAF)] + fn build_leaf() -> LeafCircuit { LeafCircuit::new(&CONFIG) } + + #[tested_fixture::tested_fixture(BRANCH_1)] + fn build_branch_1() -> BranchCircuit { BranchCircuit::from_leaf(&CONFIG, &LEAF) } - fn hash_write(address: u64, left: &HashOut, right: &HashOut) -> HashOut { - let address = F::from_canonical_u64(address); - let [l0, l1, l2, l3] = left.elements; - let [r0, r1, r2, r3] = right.elements; - Poseidon2Hash::hash_no_pad(&[address, l0, l1, l2, l3, r0, r1, r2, r3]) + #[tested_fixture::tested_fixture(BRANCH_2)] + fn build_branch_2() -> BranchCircuit { BranchCircuit::from_branch(&CONFIG, &BRANCH_1) } + + #[tested_fixture::tested_fixture(pub BRANCH_3)] + fn build_branch_3() -> BranchCircuit { BranchCircuit::from_branch(&CONFIG, &BRANCH_2) } + + fn assert_leaf(proof: &ProofWithPublicInputs, summary: Option>) { + let indices = &LEAF.summarized.indices; + let p_summary_present = indices.summary_hash_present.get_any(&proof.public_inputs); + assert_eq!(p_summary_present, F::from_bool(summary.is_some())); + + let p_summary = indices.summary_hash.get_any(&proof.public_inputs); + assert_eq!(p_summary, summary.unwrap_or_default().elements); } - lazy_static! { - static ref LEAF: LeafCircuit = LeafCircuit::new(&CONFIG); - static ref BRANCH_0: BranchCircuit = BranchCircuit::from_leaf(&CONFIG, &LEAF); - static ref BRANCH_1: BranchCircuit = - BranchCircuit::from_branch(&CONFIG, &BRANCH_0); + fn assert_branch( + proof: &ProofWithPublicInputs, + summary_address: Option<(HashOut, u64)>, + ) { + let indices = &LEAF.summarized.indices; + let p_summary_present = indices.summary_hash_present.get_any(&proof.public_inputs); + assert_eq!(p_summary_present, F::from_bool(summary_address.is_some())); + + let p_summary = indices.summary_hash.get_any(&proof.public_inputs); + assert_eq!(p_summary, summary_address.unwrap_or_default().0.elements); + + let indices = &LEAF.address.indices; + let p_address = indices.node_address.get(&proof.public_inputs); + assert_eq!( + p_address, + summary_address.map_or(F::NEG_ONE, |x| F::from_canonical_u64(x.1)) + ); } - #[test] - fn verify_leaf() -> Result<()> { - let zero_hash = HashOut::from([F::ZERO; 4]); - let non_zero_hash_1 = hash_str("Non-Zero Hash 1"); - let non_zero_hash_2 = hash_str("Non-Zero Hash 2"); - let slot_42_r0w1 = hash_write(42, &zero_hash, &non_zero_hash_1); - let slot_42_r1w2 = hash_write(42, &non_zero_hash_1, &non_zero_hash_2); - let slot_42_r2w0 = hash_write(42, &non_zero_hash_2, &zero_hash); - - // Create - let proof = LEAF.prove(zero_hash, non_zero_hash_1, slot_42_r0w1, Some(42))?; - LEAF.circuit.verify(proof)?; - - // Update - let proof = LEAF.prove(non_zero_hash_1, non_zero_hash_2, slot_42_r1w2, Some(42))?; - LEAF.circuit.verify(proof)?; - - // Non-Update - let proof = LEAF.prove(non_zero_hash_2, non_zero_hash_2, zero_hash, None)?; - LEAF.circuit.verify(proof)?; - - // Destroy - let proof = LEAF.prove(non_zero_hash_2, zero_hash, slot_42_r2w0, Some(42))?; - LEAF.circuit.verify(proof)?; - - // Non-Update - let proof = LEAF.prove(zero_hash, zero_hash, zero_hash, None)?; - LEAF.circuit.verify(proof)?; - - Ok(()) + #[tested_fixture::tested_fixture(EMPTY_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_empty_leaf() -> Result> { + let proof = LEAF.prove(*ZERO_OBJ_HASH, *ZERO_OBJ_HASH, None)?; + assert_leaf(&proof, None); + LEAF.circuit.verify(proof.clone())?; + Ok(proof) + } + + #[tested_fixture::tested_fixture(UPDATE_LEAF_PROOF: (ProofWithPublicInputs, HashOut))] + fn verify_update_leaf() -> Result<(ProofWithPublicInputs, HashOut)> { + let old = STATE_0[ADDRESS_A].hash(); + let new = STATE_1[ADDRESS_A].hash(); + let a = ADDRESS_A as u64; + let summary = summarize(a, old, new); + let proof = LEAF.prove(old, new, Some(a))?; + assert_leaf(&proof, Some(summary)); + LEAF.circuit.verify(proof.clone())?; + Ok((proof, summary)) + } + + #[tested_fixture::tested_fixture(DELETE_LEAF_PROOF: (ProofWithPublicInputs, HashOut))] + fn verify_delete_leaf() -> Result<(ProofWithPublicInputs, HashOut)> { + let old = STATE_0[ADDRESS_B].hash(); + let new = STATE_1[ADDRESS_B].hash(); + let a = ADDRESS_B as u64; + let summary = summarize(a, old, new); + let proof = LEAF.prove(old, new, Some(a))?; + assert_leaf(&proof, Some(summary)); + LEAF.circuit.verify(proof.clone())?; + Ok((proof, summary)) + } + + #[tested_fixture::tested_fixture(CREATE_LEAF_PROOF: (ProofWithPublicInputs, HashOut))] + fn verify_create_leaf() -> Result<(ProofWithPublicInputs, HashOut)> { + let old = STATE_0[ADDRESS_C].hash(); + let new = STATE_1[ADDRESS_C].hash(); + let a = ADDRESS_C as u64; + let summary = summarize(a, old, new); + let proof = LEAF.prove(old, new, Some(a))?; + assert_leaf(&proof, Some(summary)); + LEAF.circuit.verify(proof.clone())?; + Ok((proof, summary)) + } + + #[tested_fixture::tested_fixture(READ_LEAF_PROOF: (ProofWithPublicInputs, HashOut))] + fn verify_read_leaf() -> Result<(ProofWithPublicInputs, HashOut)> { + let old = STATE_0[ADDRESS_D].hash(); + let new = STATE_1[ADDRESS_D].hash(); + assert_eq!(old, new); + let a = ADDRESS_D as u64; + let summary = summarize(a, old, new); + let proof = LEAF.prove(old, new, Some(a))?; + assert_leaf(&proof, Some(summary)); + LEAF.circuit.verify(proof.clone())?; + Ok((proof, summary)) + } + + #[tested_fixture::tested_fixture(IGNORED_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_ignored_leaf() -> Result> { + let old = STATE_0[ADDRESS_E].hash(); + let new = STATE_1[ADDRESS_E].hash(); + assert_eq!(old, new); + let proof = LEAF.prove(old, new, None)?; + assert_leaf(&proof, None); + LEAF.circuit.verify(proof.clone())?; + Ok(proof) } #[test] #[should_panic(expected = "Tried to invert zero")] fn bad_leaf_create() { - let zero_hash = HashOut::from([F::ZERO; 4]); - let non_zero_hash_1 = hash_str("Non-Zero Hash 1"); - - let proof = LEAF - .prove(zero_hash, non_zero_hash_1, zero_hash, None) - .unwrap(); + let proof = LEAF.prove(ZERO_HASH, NON_ZERO_HASHES[0], None).unwrap(); LEAF.circuit.verify(proof).unwrap(); } #[test] #[should_panic(expected = "was set twice with different values")] fn bad_leaf_update() { - let zero_hash = HashOut::from([F::ZERO; 4]); - let non_zero_hash_1 = hash_str("Non-Zero Hash 1"); - let non_zero_hash_2 = hash_str("Non-Zero Hash 2"); - let hash_0_to_1 = hash_branch(&zero_hash, &non_zero_hash_1); - + let summary = summarize(42, ZERO_HASH, NON_ZERO_HASHES[1]); let proof = LEAF - .prove(non_zero_hash_1, non_zero_hash_2, hash_0_to_1, Some(42)) + .prove_unsafe( + NON_ZERO_HASHES[0], + NON_ZERO_HASHES[1], + Some(summary), + Some(42), + ) .unwrap(); LEAF.circuit.verify(proof).unwrap(); } @@ -351,98 +444,104 @@ mod test { #[test] #[should_panic(expected = "was set twice with different values")] fn bad_leaf_non_update() { - let non_zero_hash_2 = hash_str("Non-Zero Hash 2"); + let summary = summarize(42, ZERO_HASH, NON_ZERO_HASHES[0]); + let proof = LEAF + .prove_unsafe( + NON_ZERO_HASHES[0], + NON_ZERO_HASHES[0], + Some(summary), + Some(42), + ) + .unwrap(); + LEAF.circuit.verify(proof).unwrap(); + } + #[test] + #[should_panic(expected = "was set twice with different values")] + fn bad_leaf_address() { + let summary = summarize(41, ZERO_HASH, NON_ZERO_HASHES[0]); let proof = LEAF - .prove(non_zero_hash_2, non_zero_hash_2, non_zero_hash_2, Some(42)) + .prove_unsafe(ZERO_HASH, NON_ZERO_HASHES[0], Some(summary), Some(42)) .unwrap(); LEAF.circuit.verify(proof).unwrap(); } + #[tested_fixture::tested_fixture(EMPTY_BRANCH_PROOF: ProofWithPublicInputs)] + fn verify_empty_branch() -> Result> { + let proof = BRANCH_1.prove(&EMPTY_LEAF_PROOF, &EMPTY_LEAF_PROOF)?; + assert_branch(&proof, None); + BRANCH_1.circuit.verify(proof.clone())?; + Ok(proof) + } + + #[tested_fixture::tested_fixture(UPDATE_BRANCH_PROOF: (ProofWithPublicInputs, HashOut))] + fn verify_update_branch() -> Result<(ProofWithPublicInputs, HashOut)> { + let (left_proof, left_hash) = *UPDATE_LEAF_PROOF; + let proof = BRANCH_1.prove(left_proof, &EMPTY_LEAF_PROOF)?; + assert_branch(&proof, Some((*left_hash, ADDRESS_A as u64 / 2))); + BRANCH_1.circuit.verify(proof.clone())?; + Ok((proof, *left_hash)) + } + + #[tested_fixture::tested_fixture(DELETE_CREATE_BRANCH_PROOF: (ProofWithPublicInputs, HashOut))] + fn verify_delete_create_branch() -> Result<(ProofWithPublicInputs, HashOut)> { + let (left_proof, left_hash) = *DELETE_LEAF_PROOF; + let (right_proof, right_hash) = *CREATE_LEAF_PROOF; + let summary = hash_branch(left_hash, right_hash); + let proof = BRANCH_1.prove(left_proof, right_proof)?; + assert_branch(&proof, Some((summary, ADDRESS_B as u64 / 2))); + BRANCH_1.circuit.verify(proof.clone())?; + Ok((proof, summary)) + } + + #[tested_fixture::tested_fixture(READ_IGNORED_BRANCH_PROOF: (ProofWithPublicInputs, HashOut))] + fn verify_read_ignored_branch() -> Result<(ProofWithPublicInputs, HashOut)> { + let (left_proof, left_hash) = *READ_LEAF_PROOF; + let right_proof = *IGNORED_LEAF_PROOF; + let proof = BRANCH_1.prove(left_proof, right_proof)?; + assert_branch(&proof, Some((*left_hash, ADDRESS_D as u64 / 2))); + BRANCH_1.circuit.verify(proof.clone())?; + Ok((proof, *left_hash)) + } + + #[tested_fixture::tested_fixture(LEFT_DOUBLE_BRANCH_PROOF: (ProofWithPublicInputs, HashOut))] + fn verify_left_double_branch() -> Result<(ProofWithPublicInputs, HashOut)> { + let left_proof = *EMPTY_BRANCH_PROOF; + let (right_proof, right_hash) = *UPDATE_BRANCH_PROOF; + let proof = BRANCH_2.prove(left_proof, right_proof)?; + assert_branch(&proof, Some((*right_hash, 0))); + BRANCH_2.circuit.verify(proof.clone())?; + Ok((proof, *right_hash)) + } + + #[tested_fixture::tested_fixture(RIGHT_DOUBLE_BRANCH_PROOF: (ProofWithPublicInputs, HashOut))] + fn verify_right_double_branch() -> Result<(ProofWithPublicInputs, HashOut)> { + let (left_proof, left_hash) = *DELETE_CREATE_BRANCH_PROOF; + let (right_proof, right_hash) = *READ_IGNORED_BRANCH_PROOF; + let summary = hash_branch(left_hash, right_hash); + let proof = BRANCH_2.prove(left_proof, right_proof)?; + assert_branch(&proof, Some((summary, 1))); + BRANCH_2.circuit.verify(proof.clone())?; + Ok((proof, summary)) + } + + #[tested_fixture::tested_fixture(pub ROOT_PROOF: (ProofWithPublicInputs, HashOut))] + fn verify_root() -> Result<(ProofWithPublicInputs, HashOut)> { + let (left_proof, left_hash) = *LEFT_DOUBLE_BRANCH_PROOF; + let (right_proof, right_hash) = *RIGHT_DOUBLE_BRANCH_PROOF; + let summary = hash_branch(left_hash, right_hash); + let proof = BRANCH_3.prove(left_proof, right_proof)?; + assert_branch(&proof, Some((summary, 0))); + BRANCH_3.circuit.verify(proof.clone())?; + Ok((proof, summary)) + } + #[test] - fn verify_branch() -> Result<()> { - let zero_hash = HashOut::from([F::ZERO; 4]); - let non_zero_hash_1 = hash_str("Non-Zero Hash 1"); - let hash_0_and_0 = hash_branch(&zero_hash, &zero_hash); - let hash_0_and_1 = hash_branch(&zero_hash, &non_zero_hash_1); - - let hash_1_and_0 = hash_branch(&non_zero_hash_1, &zero_hash); - let hash_1_and_1 = hash_branch(&non_zero_hash_1, &non_zero_hash_1); - let hash_00_and_00 = hash_branch(&hash_0_and_0, &hash_0_and_0); - let hash_01_and_10 = hash_branch(&hash_0_and_1, &hash_1_and_0); - - let slot_2_r0w1 = hash_write(2, &zero_hash, &non_zero_hash_1); - let slot_3_r0w1 = hash_write(3, &zero_hash, &non_zero_hash_1); - let slot_4_r0w1 = hash_write(4, &zero_hash, &non_zero_hash_1); - - let slot_2_and_3 = hash_branch(&slot_2_r0w1, &slot_3_r0w1); - let slot_3_and_4 = hash_branch(&slot_3_r0w1, &slot_4_r0w1); - - // Leaf proofs - let zero_proof = LEAF.prove(zero_hash, zero_hash, zero_hash, None)?; - LEAF.circuit.verify(zero_proof.clone())?; - - let proof_0_to_1_id_2 = LEAF.prove(zero_hash, non_zero_hash_1, slot_2_r0w1, Some(2))?; - LEAF.circuit.verify(proof_0_to_1_id_2.clone())?; - - let proof_0_to_1_id_3 = LEAF.prove(zero_hash, non_zero_hash_1, slot_3_r0w1, Some(3))?; - LEAF.circuit.verify(proof_0_to_1_id_3.clone())?; - - let proof_0_to_1_id_4 = LEAF.prove(zero_hash, non_zero_hash_1, slot_4_r0w1, Some(4))?; - LEAF.circuit.verify(proof_0_to_1_id_4.clone())?; - - // Branch proofs - let branch_00_and_00_proof = BRANCH_0.prove( - hash_0_and_0, - hash_0_and_0, - zero_hash, - (), - &zero_proof, - &zero_proof, - )?; - BRANCH_0.circuit.verify(branch_00_and_00_proof)?; - - let branch_00_and_01_proof = BRANCH_0.prove( - hash_0_and_0, - hash_0_and_1, - slot_3_r0w1, - (), - &zero_proof, - &proof_0_to_1_id_3, - )?; - BRANCH_0.circuit.verify(branch_00_and_01_proof.clone())?; - - let branch_01_and_00_proof = BRANCH_0.prove( - hash_0_and_0, - hash_1_and_0, - slot_4_r0w1, - (), - &proof_0_to_1_id_4, - &zero_proof, - )?; - BRANCH_0.circuit.verify(branch_01_and_00_proof.clone())?; - - let branch_01_and_01_proof = BRANCH_0.prove( - hash_0_and_0, - hash_1_and_1, - slot_2_and_3, - (), - &proof_0_to_1_id_2, - &proof_0_to_1_id_3, - )?; - BRANCH_0.circuit.verify(branch_01_and_01_proof)?; - - // Double branch proof - let proof = BRANCH_1.prove( - hash_00_and_00, - hash_01_and_10, - slot_3_and_4, - (), - &branch_00_and_01_proof, - &branch_01_and_00_proof, - )?; - BRANCH_1.circuit.verify(proof)?; - - Ok(()) + #[should_panic(expected = "was set twice with different values")] + fn bad_root_address() { + let proof = BRANCH_3 + .prove(&RIGHT_DOUBLE_BRANCH_PROOF.0, &LEFT_DOUBLE_BRANCH_PROOF.0) + .unwrap(); + BRANCH_3.circuit.verify(proof).unwrap(); } } diff --git a/recproofs/src/circuits/verify_program.rs b/recproofs/src/circuits/verify_program.rs index c3f7ec648..3f519b53e 100644 --- a/recproofs/src/circuits/verify_program.rs +++ b/recproofs/src/circuits/verify_program.rs @@ -291,26 +291,25 @@ where #[cfg(test)] pub mod test { - use std::panic::catch_unwind; - - use lazy_static::lazy_static; + use once_cell::sync::Lazy; use plonky2::field::types::Field; + use plonky2::gates::noop::NoopGate; use plonky2::hash::hash_types::HashOutTarget; use plonky2::iop::target::{BoolTarget, Target}; use plonky2::iop::witness::WitnessWrite; use self::core::ProgramPublicIndices; use super::*; - use crate::circuits::build_event_root::test::{BRANCH as EVENT_BRANCH, LEAF as EVENT_LEAF}; - use crate::circuits::merge::test::{BRANCH as MERGE_BRANCH, LEAF as MERGE_LEAF}; + use crate::circuits::build_event_root::test as build_event_root; + use crate::circuits::merge::test as merge; + use crate::circuits::test_data::{CALL_LISTS, CAST_ROOT, PROGRAM_HASHES}; use crate::indices::{ArrayTargetIndex, BoolTargetIndex, HashTargetIndex}; - use crate::test_utils::{hash_branch, hash_branch_bytes, make_fs, C, CONFIG, D, F}; - use crate::{Event, EventType}; + use crate::test_utils::{C, CONFIG, D, F, NON_ZERO_VALUES, ZERO_VAL}; + + pub struct DummyCircuit { + /// The program hash + pub program_hash_val: [F; 4], - pub struct DummyCircuit - where - F: RichField + Extendable, - C: GenericConfig, { /// The program hash pub program_hash: [Target; 4], @@ -329,35 +328,41 @@ pub mod test { pub circuit: CircuitData, } - impl DummyCircuit - where - F: RichField + Extendable, - C: GenericConfig, - { + impl DummyCircuit { #[must_use] - pub fn new(circuit_config: &CircuitConfig, dummy: bool) -> Self { + pub fn new(circuit_config: &CircuitConfig, program_id: impl Into>) -> Self { let mut builder = CircuitBuilder::::new(circuit_config.clone()); let program_hash = builder.add_virtual_target_arr(); let events_present = builder.add_virtual_bool_target_safe(); let event_root = builder.add_virtual_hash(); let call_list = builder.add_virtual_target_arr(); let cast_root = builder.add_virtual_hash(); + builder.register_public_inputs(&program_hash); builder.register_public_input(events_present.target); builder.register_public_inputs(&event_root.elements); builder.register_public_inputs(&call_list); builder.register_public_inputs(&cast_root.elements); - // Make a dummy to change the circuit - if dummy { - let dummy = builder.add_virtual_target(); - let one = builder.one(); - builder.connect(dummy, one); + let program_hash_val = program_id + .into() + .map_or(ZERO_VAL, |pid| PROGRAM_HASHES[pid]); + + let program_hash_calc = program_hash_val.map(|x| builder.constant(x)); + for (p, c) in program_hash.into_iter().zip(program_hash_calc) { + builder.connect(p, c); + } + + // Make sure we have enough gates to match. + builder.add_gate(NoopGate, vec![]); + while builder.num_gates() < (1 << 3) { + builder.add_gate(NoopGate, vec![]); } let circuit = builder.build(); Self { + program_hash_val, program_hash, events_present, event_root, @@ -380,13 +385,11 @@ pub mod test { pub fn prove( &self, - program_hash: [F; 4], event_root: Option>, call_list: [F; 4], cast_root: HashOut, ) -> Result> { let mut inputs = PartialWitness::new(); - inputs.set_target_arr(&self.program_hash, &program_hash); inputs.set_bool_target(self.events_present, event_root.is_some()); inputs.set_hash_target(self.event_root, event_root.unwrap_or_default()); inputs.set_target_arr(&self.call_list, &call_list); @@ -395,481 +398,336 @@ pub mod test { } } - lazy_static! { - pub static ref PROGRAM_1: DummyCircuit = DummyCircuit::new(&CONFIG, false); - pub static ref PROGRAM_1_INDICES: ProgramPublicIndices = PROGRAM_1.get_indices(); - pub static ref PROGRAM_2: DummyCircuit = DummyCircuit::new(&CONFIG, true); - pub static ref PROGRAM_2_INDICES: ProgramPublicIndices = PROGRAM_2.get_indices(); - pub static ref LEAF: LeafCircuit = LeafCircuit::new( + pub static PROGRAM_M: Lazy = Lazy::new(|| DummyCircuit::new(&CONFIG, None)); + pub static PROGRAM_M_INDICES: Lazy = + Lazy::new(|| PROGRAM_M.get_indices()); + pub static PROGRAM_0: Lazy = Lazy::new(|| DummyCircuit::new(&CONFIG, 0)); + pub static PROGRAM_0_INDICES: Lazy = + Lazy::new(|| PROGRAM_0.get_indices()); + pub static PROGRAM_1: Lazy = Lazy::new(|| DummyCircuit::new(&CONFIG, 1)); + pub static PROGRAM_1_INDICES: Lazy = + Lazy::new(|| PROGRAM_1.get_indices()); + pub static PROGRAM_2: Lazy = Lazy::new(|| DummyCircuit::new(&CONFIG, 2)); + pub static PROGRAM_2_INDICES: Lazy = + Lazy::new(|| PROGRAM_2.get_indices()); + + #[tested_fixture::tested_fixture(pub LEAF)] + fn build_leaf() -> LeafCircuit { + assert_eq!(*PROGRAM_M_INDICES, *PROGRAM_0_INDICES); + assert_eq!(*PROGRAM_M_INDICES, *PROGRAM_1_INDICES); + assert_eq!(*PROGRAM_M_INDICES, *PROGRAM_2_INDICES); + + assert_eq!(PROGRAM_M.circuit.common, PROGRAM_0.circuit.common); + assert_eq!(PROGRAM_M.circuit.common, PROGRAM_0.circuit.common); + assert_eq!(PROGRAM_M.circuit.common, PROGRAM_1.circuit.common); + assert_eq!(PROGRAM_M.circuit.common, PROGRAM_2.circuit.common); + + LeafCircuit::new( &CONFIG, - &PROGRAM_1_INDICES, - &PROGRAM_1.circuit.common, - &EVENT_BRANCH - ); - pub static ref BRANCH: BranchCircuit = - BranchCircuit::new(&CONFIG, &MERGE_BRANCH, &LEAF); + &PROGRAM_M_INDICES, + &PROGRAM_M.circuit.common, + &build_event_root::BRANCH, + ) } - fn build_event(e: Event) -> ProofWithPublicInputs { - let proof = EVENT_LEAF - .prove(e, Some(e.hash()), Some(e.byte_wise_hash()), &EVENT_BRANCH) - .unwrap(); - EVENT_LEAF.circuit.verify(proof.clone()).unwrap(); - proof + #[tested_fixture::tested_fixture(pub BRANCH)] + fn build_branch() -> BranchCircuit { + BranchCircuit::new(&CONFIG, &merge::BRANCH, &LEAF) } - pub struct BuiltEvent { - pub proof: ProofWithPublicInputs, - #[allow(dead_code)] - pub hash: HashOut, - pub vm_hash: HashOut, - } - - pub fn build_events(l: Event, r: Event) -> BuiltEvent { - let l_proof = build_event(l); - let r_proof = build_event(r); - let branch_hash = hash_branch(&l.hash(), &r.hash()); - let branch_bytes_hash = hash_branch_bytes(&l.byte_wise_hash(), &r.byte_wise_hash()); - - let branch_proof = EVENT_BRANCH - .prove( - Some(branch_hash), - Some(branch_bytes_hash), - Some(l.owner), - true, - &l_proof, - Some((true, &r_proof)), - ) - .unwrap(); - EVENT_BRANCH.circuit.verify(branch_proof.clone()).unwrap(); - - BuiltEvent { - proof: branch_proof, - hash: branch_hash, - vm_hash: branch_bytes_hash, - } + fn assert_leaf( + proof: &ProofWithPublicInputs, + event_hash: Option>, + pid: [F; 4], + ) { + let indices = &LEAF.events.indices; + assert_eq!(*indices, BRANCH.events.indices); + + let p_present = indices.hash_present.get_any(&proof.public_inputs); + assert_eq!(p_present, F::from_bool(event_hash.is_some())); + let p_hash = indices.hash.get_any(&proof.public_inputs); + assert_eq!(p_hash, event_hash.unwrap_or_default().elements); + + let indices = &LEAF.program_id.indices; + assert_eq!(*indices, BRANCH.program_id.indices); + let p_pid = indices.unpruned_hash.get_any(&proof.public_inputs); + assert_eq!(p_pid, pid); } - pub fn make_program( - program: &DummyCircuit, - program_hash: [F; 4], - event_root: Option>, + #[allow(clippy::type_complexity)] + fn verify_leaf( + event_proof: &ProofWithPublicInputs, + hash: HashOut, + vm_hash: HashOut, + program: &'static DummyCircuit, + program_verifier: &DummyCircuit, call_list: [F; 4], cast_root: HashOut, - ) -> ProofWithPublicInputs { + ) -> Result<(ProofWithPublicInputs, HashOut, [F; 4])> { let program_proof = program - .prove(program_hash, event_root, call_list, cast_root) - .unwrap(); - program.circuit.verify(program_proof.clone()).unwrap(); - program_proof - } + .prove(Some(vm_hash), call_list, cast_root) + .expect("shouldn't fail"); + program + .circuit + .verify(program_proof.clone()) + .expect("shouldn't fail"); - pub fn merge_hashes( - a: Option>, - b: Option>, - ) -> ProofWithPublicInputs { - let merged_hash = match (a, b) { - (None, None) => None, - (None, Some(b)) => Some(b), - (Some(a), None) => Some(a), - (Some(a), Some(b)) => Some(hash_branch(&a, &b)), - }; - let merge_proof = MERGE_LEAF.prove(&MERGE_BRANCH, a, b, merged_hash).unwrap(); - MERGE_LEAF.circuit.verify(merge_proof.clone()).unwrap(); - merge_proof - } - - pub fn merge_events(a: Event, b: Event) -> ProofWithPublicInputs { - merge_hashes(Some(a.hash()), Some(b.hash())) + let proof = LEAF.prove( + &BRANCH, + &program_verifier.circuit.verifier_only, + &program_proof, + event_proof, + )?; + assert_leaf(&proof, Some(hash), program.program_hash_val); + LEAF.circuit.verify(proof.clone())?; + Ok((proof, hash, program.program_hash_val)) } - pub fn merge_merges( - l_leaf: bool, - l: &ProofWithPublicInputs, - r_leaf: bool, - r: &ProofWithPublicInputs, - ) -> ProofWithPublicInputs { - let merge_proof = MERGE_BRANCH.prove(l_leaf, l, r_leaf, r).unwrap(); - MERGE_BRANCH.circuit.verify(merge_proof.clone()).unwrap(); - merge_proof + macro_rules! make_leaf_tests { + ($($($name:ident | $proof:ident = ($event:ident, $program:ident, $tx:literal)),+ $(,)?)?) => {$($( + #[tested_fixture::tested_fixture($proof: (ProofWithPublicInputs, HashOut, [F; 4]))] + fn $name() -> Result<(ProofWithPublicInputs, HashOut, [F; 4])> { + let &(ref event_proof, hash, vm_hash) = *build_event_root::$event; + verify_leaf( + event_proof, + hash, + vm_hash, + &$program, + &$program, + CALL_LISTS[$tx], + CAST_ROOT[$tx], + ) } - - const ZERO_VAL: [F; 4] = [F::ZERO; 4]; - const PROGRAM_1_HASH: [F; 4] = make_fs([4, 8, 15, 16]); - const PROGRAM_2_HASH: [F; 4] = make_fs([2, 3, 4, 2]); - const NON_ZERO_VAL_1: [F; 4] = make_fs([3, 1, 4, 15]); - const NON_ZERO_VAL_2: [F; 4] = make_fs([1, 6, 180, 33]); - const CALL_LIST_1: [F; 4] = make_fs([86, 7, 5, 309]); - const CALL_LIST_2: [F; 4] = make_fs([8, 67, 530, 9]); - - // Duplicate or conflicting events are actually fine as far as this circuit - // cares - const P1_EVENTS: [Event; 2] = [ - Event { - address: 42, - owner: PROGRAM_1_HASH, - ty: EventType::Read, - value: ZERO_VAL, - }, - Event { - address: 84, - owner: PROGRAM_1_HASH, - ty: EventType::Write, - value: ZERO_VAL, - }, - ]; - const P2_EVENTS: [Event; 2] = [ - Event { - address: 42, - owner: PROGRAM_2_HASH, - ty: EventType::Write, - value: NON_ZERO_VAL_2, - }, - Event { - address: 84, - owner: PROGRAM_2_HASH, - ty: EventType::Ensure, - value: NON_ZERO_VAL_1, - }, - ]; - - lazy_static! { - pub static ref P1_BUILT_EVENTS: BuiltEvent = build_events(P1_EVENTS[0], P1_EVENTS[1]); - pub static ref P1_EVENTS_HASH: HashOut = - hash_branch(&P1_EVENTS[0].hash(), &P1_EVENTS[1].hash()); - pub static ref P2_BUILT_EVENTS: BuiltEvent = build_events(P2_EVENTS[0], P2_EVENTS[1]); - pub static ref P2_EVENTS_HASH: HashOut = - hash_branch(&P2_EVENTS[0].hash(), &P2_EVENTS[1].hash()); + )+)?}; } - /// Helpers with P1 to the left of P2 - pub mod p1_p2 { - use super::*; - - lazy_static! { - pub static ref CAST_ROOT: HashOut = - hash_branch_bytes(&PROGRAM_1_HASH.into(), &PROGRAM_2_HASH.into()); - } - - lazy_static! { - pub static ref PROGRAM_1_PROOF: ProofWithPublicInputs = make_program( - &PROGRAM_1, - PROGRAM_1_HASH, - Some(P1_BUILT_EVENTS.vm_hash), - CALL_LIST_1, - *CAST_ROOT - ); - pub static ref PROGRAM_2_PROOF: ProofWithPublicInputs = make_program( - &PROGRAM_2, - PROGRAM_2_HASH, - Some(P2_BUILT_EVENTS.vm_hash), - CALL_LIST_1, - *CAST_ROOT - ); - pub static ref PROGRAM_2B_PROOF: ProofWithPublicInputs = make_program( - &PROGRAM_2, - PROGRAM_2_HASH, - Some(P2_BUILT_EVENTS.vm_hash), - CALL_LIST_2, - *CAST_ROOT - ); - } + make_leaf_tests! { + verify_t0_pm_leaf | T0_PM_LEAF_PROOF = (T0_PM_BRANCH_PROOF, PROGRAM_M, 0), + verify_t0_p0_leaf | T0_P0_LEAF_PROOF = (T0_P0_BRANCH_PROOF, PROGRAM_0, 0), + verify_t0_p2_leaf | T0_P2_LEAF_PROOF = (T0_P2_BRANCH_PROOF, PROGRAM_2, 0), - lazy_static! { - pub static ref MERGE_42_HASH: HashOut = - hash_branch(&P1_EVENTS[0].hash(), &P2_EVENTS[0].hash()); - pub static ref MERGE_80_HASH: HashOut = - hash_branch(&P1_EVENTS[1].hash(), &P2_EVENTS[1].hash()); - pub static ref MERGE_HASH: HashOut = hash_branch(&MERGE_42_HASH, &MERGE_80_HASH); - pub static ref MERGE_42: ProofWithPublicInputs = - merge_events(P1_EVENTS[0], P2_EVENTS[0]); - pub static ref MERGE_80: ProofWithPublicInputs = - merge_events(P1_EVENTS[1], P2_EVENTS[1]); - pub static ref MERGE_PROOF: ProofWithPublicInputs = - merge_merges(true, &MERGE_42, true, &MERGE_80); - } + verify_t1_pm_leaf | T1_PM_LEAF_PROOF = (T1_PM_BRANCH_PROOF, PROGRAM_M, 1), + verify_t1_p1_leaf | T1_P1_LEAF_PROOF = (T1_P1_BRANCH_PROOF, PROGRAM_1, 1), + verify_t1_p2_leaf | T1_P2_LEAF_PROOF = (T1_P2_BRANCH_PROOF, PROGRAM_2, 1), } - /// Helpers with P2 to the left of P1 - pub mod p2_p1 { - use super::*; - - lazy_static! { - pub static ref CAST_ROOT: HashOut = - hash_branch_bytes(&PROGRAM_2_HASH.into(), &PROGRAM_1_HASH.into()); - } - - lazy_static! { - pub static ref PROGRAM_1_PROOF: ProofWithPublicInputs = make_program( - &PROGRAM_1, - PROGRAM_1_HASH, - Some(P1_BUILT_EVENTS.vm_hash), - CALL_LIST_1, - *CAST_ROOT - ); - pub static ref PROGRAM_2_PROOF: ProofWithPublicInputs = make_program( - &PROGRAM_2, - PROGRAM_2_HASH, - Some(P2_BUILT_EVENTS.vm_hash), - CALL_LIST_1, - *CAST_ROOT - ); - } - - lazy_static! { - pub static ref MERGE_42_HASH: HashOut = - hash_branch(&P2_EVENTS[0].hash(), &P1_EVENTS[0].hash()); - pub static ref MERGE_80_HASH: HashOut = - hash_branch(&P2_EVENTS[1].hash(), &P1_EVENTS[1].hash()); - pub static ref MERGE_HASH: HashOut = hash_branch(&MERGE_42_HASH, &MERGE_80_HASH); - pub static ref MERGE_42: ProofWithPublicInputs = - merge_events(P2_EVENTS[0], P1_EVENTS[0]); - pub static ref MERGE_80: ProofWithPublicInputs = - merge_events(P2_EVENTS[1], P1_EVENTS[1]); - pub static ref MERGE_PROOF: ProofWithPublicInputs = - merge_merges(true, &MERGE_42, true, &MERGE_80); - } + #[tested_fixture::tested_fixture(T0_PM_BAD_CALL_LEAF_PROOF: (ProofWithPublicInputs, HashOut, [F; 4]))] + fn verify_t0_pm_bad_call_leaf() -> Result<(ProofWithPublicInputs, HashOut, [F; 4])> + { + let &(ref event_proof, hash, vm_hash) = *build_event_root::T0_PM_BRANCH_PROOF; + verify_leaf( + event_proof, + hash, + vm_hash, + &PROGRAM_M, + &PROGRAM_M, + NON_ZERO_VALUES[0], + CAST_ROOT[0], + ) } #[test] - fn verify_leaf() -> Result<()> { - let proof = LEAF.prove( - &BRANCH, - &PROGRAM_1.circuit.verifier_only, - &p1_p2::PROGRAM_1_PROOF, - &P1_BUILT_EVENTS.proof, - )?; - LEAF.circuit.verify(proof)?; - - let proof = LEAF.prove( - &BRANCH, - &PROGRAM_2.circuit.verifier_only, - &p1_p2::PROGRAM_2_PROOF, - &P2_BUILT_EVENTS.proof, - )?; - LEAF.circuit.verify(proof)?; - - let proof = LEAF.prove( - &BRANCH, - &PROGRAM_1.circuit.verifier_only, - &p2_p1::PROGRAM_1_PROOF, - &P1_BUILT_EVENTS.proof, - )?; - LEAF.circuit.verify(proof)?; - - let proof = LEAF.prove( - &BRANCH, - &PROGRAM_2.circuit.verifier_only, - &p2_p1::PROGRAM_2_PROOF, - &P2_BUILT_EVENTS.proof, - )?; - LEAF.circuit.verify(proof)?; - - Ok(()) + #[should_panic(expected = "was set twice with different values")] + fn bad_leaf_wrong_verifier() { + let &(ref event_proof, hash, vm_hash) = *build_event_root::T0_PM_BRANCH_PROOF; + + verify_leaf( + event_proof, + hash, + vm_hash, + &PROGRAM_M, + &PROGRAM_0, + CALL_LISTS[0], + CAST_ROOT[0], + ) + .unwrap(); } #[test] #[should_panic(expected = "was set twice with different values")] - fn bad_leaf_wrong_verifier() { - let proof = LEAF - .prove( - &BRANCH, - &PROGRAM_2.circuit.verifier_only, - &p1_p2::PROGRAM_1_PROOF, - &P2_BUILT_EVENTS.proof, - ) - .unwrap(); - LEAF.circuit.verify(proof).unwrap(); + fn bad_leaf_wrong_proof() { + let &(ref event_proof, hash, vm_hash) = *build_event_root::T0_PM_BRANCH_PROOF; + + verify_leaf( + event_proof, + hash, + vm_hash, + &PROGRAM_0, + &PROGRAM_M, + CALL_LISTS[0], + CAST_ROOT[0], + ) + .unwrap(); } #[test] #[should_panic(expected = "was set twice with different values")] fn bad_leaf_wrong_events_1() { - let proof = LEAF - .prove( - &BRANCH, - &PROGRAM_1.circuit.verifier_only, - &p1_p2::PROGRAM_1_PROOF, - &P2_BUILT_EVENTS.proof, - ) - .unwrap(); - LEAF.circuit.verify(proof).unwrap(); + let (event_proof, _, _) = *build_event_root::T0_PM_BRANCH_PROOF; + let &(_, hash, vm_hash) = *build_event_root::T1_PM_BRANCH_PROOF; + + verify_leaf( + event_proof, + hash, + vm_hash, + &PROGRAM_M, + &PROGRAM_M, + CALL_LISTS[0], + CAST_ROOT[0], + ) + .unwrap(); } #[test] #[should_panic(expected = "was set twice with different values")] fn bad_leaf_wrong_events_2() { - let proof = LEAF - .prove( - &BRANCH, - &PROGRAM_2.circuit.verifier_only, - &p1_p2::PROGRAM_2_PROOF, - &P1_BUILT_EVENTS.proof, - ) - .unwrap(); - LEAF.circuit.verify(proof).unwrap(); + let (event_proof, _, _) = *build_event_root::T0_PM_BRANCH_PROOF; + let &(_, hash, vm_hash) = *build_event_root::T0_P2_BRANCH_PROOF; + + verify_leaf( + event_proof, + hash, + vm_hash, + &PROGRAM_M, + &PROGRAM_M, + CALL_LISTS[0], + CAST_ROOT[0], + ) + .unwrap(); } #[test] #[should_panic(expected = "was set twice with different values")] fn bad_leaf_wrong_events_3() { - let proof = LEAF - .prove( - &BRANCH, - &PROGRAM_1.circuit.verifier_only, - &p2_p1::PROGRAM_1_PROOF, - &P2_BUILT_EVENTS.proof, - ) - .unwrap(); - LEAF.circuit.verify(proof).unwrap(); + let &(ref event_proof, hash, _) = *build_event_root::T0_PM_BRANCH_PROOF; + let &(_, _, vm_hash) = *build_event_root::T1_PM_BRANCH_PROOF; + + verify_leaf( + event_proof, + hash, + vm_hash, + &PROGRAM_M, + &PROGRAM_M, + CALL_LISTS[0], + CAST_ROOT[0], + ) + .unwrap(); } #[test] - #[should_panic(expected = "was set twice with different values")] + #[should_panic(expected = "assertion `left == right`")] fn bad_leaf_wrong_events_4() { - let proof = LEAF - .prove( - &BRANCH, - &PROGRAM_2.circuit.verifier_only, - &p2_p1::PROGRAM_2_PROOF, - &P1_BUILT_EVENTS.proof, - ) - .unwrap(); - LEAF.circuit.verify(proof).unwrap(); + let &(ref event_proof, _, vm_hash) = *build_event_root::T0_PM_BRANCH_PROOF; + let &(_, hash, _) = *build_event_root::T1_PM_BRANCH_PROOF; + + verify_leaf( + event_proof, + hash, + vm_hash, + &PROGRAM_M, + &PROGRAM_M, + CALL_LISTS[0], + CAST_ROOT[0], + ) + .unwrap(); } - #[test] - fn verify_branch() -> Result<()> { - use p1_p2::{MERGE_PROOF, PROGRAM_1_PROOF, PROGRAM_2_PROOF}; - - let leaf_1_proof = LEAF.prove( - &BRANCH, - &PROGRAM_1.circuit.verifier_only, - &PROGRAM_1_PROOF, - &P1_BUILT_EVENTS.proof, + #[tested_fixture::tested_fixture(T0_PM_P0_BRANCH_PROOF: ProofWithPublicInputs)] + fn verify_t0_pm_p0_branch() -> Result> { + let proof = BRANCH.prove( + &merge::T0_PM_P0_BRANCH_PROOF.0, + true, + &T0_PM_LEAF_PROOF.0, + true, + &T0_P0_LEAF_PROOF.0, )?; - LEAF.circuit.verify(leaf_1_proof.clone())?; + BRANCH.circuit.verify(proof.clone())?; + Ok(proof) + } - let leaf_2_proof = LEAF.prove( - &BRANCH, - &PROGRAM_2.circuit.verifier_only, - &PROGRAM_2_PROOF, - &P2_BUILT_EVENTS.proof, + #[tested_fixture::tested_fixture(pub T0_BRANCH_PROOF: ProofWithPublicInputs)] + fn verify_t0_branch() -> Result> { + let proof = BRANCH.prove( + *merge::T0_BRANCH_PROOF, + false, + &T0_PM_P0_BRANCH_PROOF, + true, + &T0_P2_LEAF_PROOF.0, )?; - LEAF.circuit.verify(leaf_2_proof.clone())?; + BRANCH.circuit.verify(proof.clone())?; + Ok(proof) + } - let branch_proof = BRANCH.prove(&MERGE_PROOF, true, &leaf_1_proof, true, &leaf_2_proof)?; - BRANCH.circuit.verify(branch_proof.clone())?; + #[tested_fixture::tested_fixture(T1_PM_P1_BRANCH_PROOF: ProofWithPublicInputs)] + fn verify_t1_pm_p1_branch() -> Result> { + let proof = BRANCH.prove( + *merge::T1_PM_P1_BRANCH_PROOF, + true, + &T1_PM_LEAF_PROOF.0, + true, + &T1_P1_LEAF_PROOF.0, + )?; + BRANCH.circuit.verify(proof.clone())?; + Ok(proof) + } - Ok(()) + #[tested_fixture::tested_fixture(pub T1_BRANCH_PROOF: ProofWithPublicInputs)] + fn verify_t1_branch() -> Result> { + let proof = BRANCH.prove( + *merge::T1_BRANCH_PROOF, + false, + &T1_PM_P1_BRANCH_PROOF, + true, + &T1_P2_LEAF_PROOF.0, + )?; + BRANCH.circuit.verify(proof.clone())?; + Ok(proof) } #[test] #[should_panic(expected = "was set twice with different values")] fn bad_branch_hash_merge_1() { - let (merge_proof, leaf_1_proof, leaf_2_proof) = catch_unwind(|| { - use p1_p2::{MERGE_80, PROGRAM_1_PROOF, PROGRAM_2_PROOF}; - // Flip the merge to break stuff - use p2_p1::MERGE_42; - - let merge_proof = merge_merges(true, &MERGE_42, true, &MERGE_80); - - let leaf_1_proof = LEAF.prove( - &BRANCH, - &PROGRAM_1.circuit.verifier_only, - &PROGRAM_1_PROOF, - &P1_BUILT_EVENTS.proof, - )?; - LEAF.circuit.verify(leaf_1_proof.clone())?; - - let leaf_2_proof = LEAF.prove( - &BRANCH, - &PROGRAM_2.circuit.verifier_only, - &PROGRAM_2_PROOF, - &P2_BUILT_EVENTS.proof, - )?; - LEAF.circuit.verify(leaf_2_proof.clone())?; - - Result::<_>::Ok((merge_proof, leaf_1_proof, leaf_2_proof)) - }) - .expect("shouldn't fail") - .unwrap(); - - let branch_proof = BRANCH - .prove(&merge_proof, true, &leaf_1_proof, true, &leaf_2_proof) + let proof = BRANCH + .prove( + &merge::T0_PM_P0_BRANCH_PROOF.0, + // Flip the merge to break stuff + true, + &T0_P0_LEAF_PROOF.0, + true, + &T0_PM_LEAF_PROOF.0, + ) .unwrap(); - BRANCH.circuit.verify(branch_proof.clone()).unwrap(); + BRANCH.circuit.verify(proof.clone()).unwrap(); } #[test] #[should_panic(expected = "was set twice with different values")] fn bad_branch_hash_merge_2() { - let (merge_proof, leaf_1_proof, leaf_2_proof) = catch_unwind(|| { - use p1_p2::{MERGE_42, MERGE_80, PROGRAM_1_PROOF, PROGRAM_2_PROOF}; - - // Flip the merge of the merge to break stuff - let merge_proof = merge_merges(true, &MERGE_80, true, &MERGE_42); - - let leaf_1_proof = LEAF.prove( - &BRANCH, - &PROGRAM_1.circuit.verifier_only, - &PROGRAM_1_PROOF, - &P1_BUILT_EVENTS.proof, - )?; - LEAF.circuit.verify(leaf_1_proof.clone())?; - - let leaf_2_proof = LEAF.prove( - &BRANCH, - &PROGRAM_2.circuit.verifier_only, - &PROGRAM_2_PROOF, - &P2_BUILT_EVENTS.proof, - )?; - LEAF.circuit.verify(leaf_2_proof.clone())?; - - Result::<_>::Ok((merge_proof, leaf_1_proof, leaf_2_proof)) - }) - .expect("shouldn't fail") - .unwrap(); - - let branch_proof = BRANCH - .prove(&merge_proof, true, &leaf_1_proof, true, &leaf_2_proof) + let proof = BRANCH + .prove( + *merge::T0_BRANCH_PROOF, + // Flip the merge to break stuff + true, + &T0_P2_LEAF_PROOF.0, + false, + &T0_PM_P0_BRANCH_PROOF, + ) .unwrap(); - BRANCH.circuit.verify(branch_proof.clone()).unwrap(); + BRANCH.circuit.verify(proof.clone()).unwrap(); } #[test] #[should_panic(expected = "was set twice with different values")] fn bad_branch_call_list() { - let (merge_proof, leaf_1_proof, leaf_2_proof) = catch_unwind(|| { - // `PROGRAM_2B_PROOF` uses a different call list - use p1_p2::{MERGE_42, MERGE_80, PROGRAM_1_PROOF, PROGRAM_2B_PROOF}; - - let merge_proof = merge_merges(true, &MERGE_42, true, &MERGE_80); - - let leaf_1_proof = LEAF.prove( - &BRANCH, - &PROGRAM_1.circuit.verifier_only, - &PROGRAM_1_PROOF, - &P1_BUILT_EVENTS.proof, - )?; - LEAF.circuit.verify(leaf_1_proof.clone())?; - - let leaf_2_proof = LEAF.prove( - &BRANCH, - &PROGRAM_2.circuit.verifier_only, - &PROGRAM_2B_PROOF, - &P2_BUILT_EVENTS.proof, - )?; - LEAF.circuit.verify(leaf_2_proof.clone())?; - - Result::<_>::Ok((merge_proof, leaf_1_proof, leaf_2_proof)) - }) - .expect("shouldn't fail") - .unwrap(); - - let branch_proof = BRANCH - .prove(&merge_proof, true, &leaf_1_proof, true, &leaf_2_proof) + let proof = BRANCH + .prove( + &merge::T0_PM_P0_BRANCH_PROOF.0, + true, + &T0_PM_BAD_CALL_LEAF_PROOF.0, + true, + &T0_P0_LEAF_PROOF.0, + ) .unwrap(); - BRANCH.circuit.verify(branch_proof.clone()).unwrap(); + BRANCH.circuit.verify(proof.clone()).unwrap(); } } diff --git a/recproofs/src/circuits/verify_tx.rs b/recproofs/src/circuits/verify_tx.rs index 4fb722107..f4a81e353 100644 --- a/recproofs/src/circuits/verify_tx.rs +++ b/recproofs/src/circuits/verify_tx.rs @@ -168,111 +168,43 @@ where #[cfg(test)] pub mod test { - use lazy_static::lazy_static; - use plonky2::plonk::circuit_data::VerifierOnlyCircuitData; - use super::*; - use crate::circuits::merge::test::BRANCH as MERGE_BRANCH; - use crate::circuits::verify_program::test::{ - merge_hashes, merge_merges, p1_p2 as vp_p1_p2, p2_p1 as vp_p2_p1, BRANCH as VP_BRANCH, - LEAF as VP_LEAF, P1_BUILT_EVENTS, P2_BUILT_EVENTS, PROGRAM_1, PROGRAM_2, - }; + use crate::circuits::merge::test as merge; + use crate::circuits::verify_program::test as verify_program; use crate::test_utils::{C, CONFIG, D, F}; - lazy_static! { - pub static ref LEAF: LeafCircuit = LeafCircuit::new(&CONFIG, &VP_BRANCH,); - pub static ref BRANCH: BranchCircuit = - BranchCircuit::new(&CONFIG, &MERGE_BRANCH, &LEAF); - // This is not how you would do an actual merge as it doesn't intersplice - // the trees based on address, but for testing all we require is - // some kind of merge occurs - pub static ref MERGE_PROOF: ProofWithPublicInputs = merge_merges( - true, - &merge_hashes(Some(*vp_p1_p2::MERGE_HASH), None), - true, - &merge_hashes(None, Some(*vp_p2_p1::MERGE_HASH)), - ); - } - - fn build_verified_program_leaf( - verifier: &VerifierOnlyCircuitData, - program_proof: &ProofWithPublicInputs, - event_proof: &ProofWithPublicInputs, - ) -> ProofWithPublicInputs { - let proof = VP_LEAF - .prove(&VP_BRANCH, verifier, program_proof, event_proof) - .unwrap(); - VP_LEAF.circuit.verify(proof.clone()).unwrap(); - proof - } + #[tested_fixture::tested_fixture(pub LEAF)] + fn build_leaf() -> LeafCircuit { LeafCircuit::new(&CONFIG, &verify_program::BRANCH) } - fn build_verified_program_branch( - merge_proof: &ProofWithPublicInputs, - left_is_leaf: bool, - left: &ProofWithPublicInputs, - right_is_leaf: bool, - right: &ProofWithPublicInputs, - ) -> ProofWithPublicInputs { - let proof = VP_BRANCH - .prove(merge_proof, left_is_leaf, left, right_is_leaf, right) - .unwrap(); - VP_BRANCH.circuit.verify(proof.clone()).unwrap(); - proof + #[tested_fixture::tested_fixture(pub BRANCH)] + fn build_branch() -> BranchCircuit { + BranchCircuit::new(&CONFIG, &merge::BRANCH, &LEAF) } - pub mod p1_p2 { - use vp_p1_p2::{MERGE_PROOF, PROGRAM_1_PROOF, PROGRAM_2_PROOF}; - - use super::*; - - lazy_static! { - pub static ref VP_1_PROOF: ProofWithPublicInputs = build_verified_program_leaf( - &PROGRAM_1.circuit.verifier_only, - &PROGRAM_1_PROOF, - &P1_BUILT_EVENTS.proof, - ); - pub static ref VP_2_PROOF: ProofWithPublicInputs = build_verified_program_leaf( - &PROGRAM_2.circuit.verifier_only, - &PROGRAM_2_PROOF, - &P2_BUILT_EVENTS.proof, - ); - pub static ref VP_MERGE_PROOF: ProofWithPublicInputs = - build_verified_program_branch(&MERGE_PROOF, true, &VP_1_PROOF, true, &VP_2_PROOF,); - } + #[tested_fixture::tested_fixture(pub T0_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_t0_leaf() -> Result> { + let proof = LEAF.prove(&BRANCH, &verify_program::T0_BRANCH_PROOF)?; + LEAF.circuit.verify(proof.clone())?; + Ok(proof) } - pub mod p2_p1 { - use vp_p2_p1::{MERGE_PROOF, PROGRAM_1_PROOF, PROGRAM_2_PROOF}; - - use super::*; - - lazy_static! { - pub static ref VP_1_PROOF: ProofWithPublicInputs = build_verified_program_leaf( - &PROGRAM_1.circuit.verifier_only, - &PROGRAM_1_PROOF, - &P1_BUILT_EVENTS.proof, - ); - pub static ref VP_2_PROOF: ProofWithPublicInputs = build_verified_program_leaf( - &PROGRAM_2.circuit.verifier_only, - &PROGRAM_2_PROOF, - &P2_BUILT_EVENTS.proof, - ); - pub static ref VP_MERGE_PROOF: ProofWithPublicInputs = - build_verified_program_branch(&MERGE_PROOF, true, &VP_2_PROOF, true, &VP_1_PROOF); - } + #[tested_fixture::tested_fixture(pub T1_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_t1_leaf() -> Result> { + let proof = LEAF.prove(&BRANCH, &verify_program::T1_BRANCH_PROOF)?; + LEAF.circuit.verify(proof.clone())?; + Ok(proof) } - #[test] - fn verify_simple() -> Result<()> { - let leaf_1_proof = LEAF.prove(&BRANCH, &p1_p2::VP_MERGE_PROOF)?; - LEAF.circuit.verify(leaf_1_proof.clone())?; - - let leaf_2_proof = LEAF.prove(&BRANCH, &p2_p1::VP_MERGE_PROOF)?; - LEAF.circuit.verify(leaf_2_proof.clone())?; - - let branch_proof = BRANCH.prove(&MERGE_PROOF, true, &leaf_1_proof, true, &leaf_2_proof)?; - BRANCH.circuit.verify(branch_proof.clone())?; - - Ok(()) + #[tested_fixture::tested_fixture(pub BRANCH_PROOF: ProofWithPublicInputs)] + fn verify_branch() -> Result> { + let proof = BRANCH.prove( + &merge::T0_T1_BRANCH_PROOF, + true, + &T0_LEAF_PROOF, + true, + &T1_LEAF_PROOF, + )?; + BRANCH.circuit.verify(proof.clone())?; + Ok(proof) } } diff --git a/recproofs/src/lib.rs b/recproofs/src/lib.rs index 601fa3279..d6ac0d437 100644 --- a/recproofs/src/lib.rs +++ b/recproofs/src/lib.rs @@ -4,6 +4,7 @@ use enumflags2::{bitflags, BitFlags}; use iter_fixed::IntoIteratorFixed; use itertools::{chain, Itertools}; use plonky2::field::extension::Extendable; +use plonky2::field::types::Field; use plonky2::gates::noop::NoopGate; use plonky2::hash::hash_types::{ HashOut, HashOutTarget, MerkleCapTarget, RichField, NUM_HASH_OUT_ELTS, @@ -41,11 +42,47 @@ pub mod test_utils { pub const CONFIG: CircuitConfig = fast_test_circuit_config(); + pub const ZERO_VAL: [F; 4] = [F::ZERO; 4]; + pub const ZERO_HASH: HashOut = HashOut { elements: ZERO_VAL }; + + pub const NON_ZERO_VALUES: [[F; 4]; 5] = [ + make_fs([ + 83744944, + 825537977472684, + 7104047534646208046, + 6842590694912932, + ]), + make_fs([ + 2550604009, + 2770167113900984, + 8824012858361603563, + 7076601047101, + ]), + make_fs([ + 28347913151557, + 4857242454150695950, + 829533116861727855, + 8890750, + ]), + make_fs([ + 0, + 6548586327886593615, + 3381827968230301952, + 3530185296899577362, + ]), + make_fs([0, 0, 1, 0]), + ]; + pub const NON_ZERO_HASHES: [HashOut; 4] = make_hashes(NON_ZERO_VALUES); + pub fn hash_str(v: &str) -> HashOut { let v: Vec<_> = v.bytes().map(F::from_canonical_u8).collect(); Poseidon2Hash::hash_no_pad(&v) } + pub fn hash_val_bytes(left: [F; 4], right: [F; 4]) -> [F; 4] { + hash_branch_bytes(&HashOut { elements: left }, &HashOut { elements: right }).elements + } + pub fn hash_branch(left: &HashOut, right: &HashOut) -> HashOut { let [l0, l1, l2, l3] = left.elements; let [r0, r1, r2, r3] = right.elements; @@ -63,6 +100,8 @@ pub mod test_utils { pub const D: usize = 2; pub type C = Poseidon2GoldilocksConfig; pub type F = >::F; + pub const fn make_f(v: u64) -> F { GoldilocksField(v) } + pub const fn make_fs(vs: [u64; N]) -> [F; N] { let mut f = [F::ZERO; N]; let mut i = 0; @@ -72,9 +111,18 @@ pub mod test_utils { } f } + pub const fn make_hashes(vs: [[F; 4]; N]) -> [HashOut; M] { + let mut out = [HashOut::ZERO; M]; + let mut i = 0; + while i < M { + out[i].elements = vs[i]; + i += 1; + } + out + } } -#[derive(Copy, Clone, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum EventType { Write = 0, Ensure = 1, @@ -109,7 +157,7 @@ impl EventFlags { fn index(self) -> usize { (self as u8).trailing_zeros() as usize } } -#[derive(Copy, Clone, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct Event { owner: [F; 4], ty: EventType, @@ -148,6 +196,44 @@ impl Event { } } +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct Object { + /// Constraint-Owner is the only program which can mutate the fields of this + /// object + constraint_owner: [F; 4], + + /// The block number at which this was last updated + last_updated: F, + + /// Running credits for execution and paying rent + credits: F, + + /// Serialized data object understandable and affectable by + /// `constraint_owner` + data: [F; 4], +} + +impl Object { + pub fn hash(&self) -> HashOut { + let inputs = chain!( + self.constraint_owner, + [self.last_updated, self.credits], + self.data, + ) + .collect_vec(); + Poseidon2Hash::hash_no_pad(&inputs) + } +} + +pub fn summarize( + address: u64, + old: HashOut, + new: HashOut, +) -> HashOut { + let inputs = chain!([F::from_canonical_u64(address)], old.elements, new.elements).collect_vec(); + Poseidon2Hash::hash_no_pad(&inputs) +} + /// Computes `if b { h0 } else { h1 }`. pub(crate) fn select_hash( builder: &mut CircuitBuilder, @@ -202,7 +288,7 @@ where } /// Reduce a hash-sized group of booleans by `&&`ing them together -fn and_helper( +pub fn and_helper( builder: &mut CircuitBuilder, bools: [BoolTarget; 4], ) -> BoolTarget @@ -216,7 +302,7 @@ where } /// Reduce a hash-sized group of booleans by `||`ing them together -fn or_helper( +pub fn or_helper( builder: &mut CircuitBuilder, bools: [BoolTarget; 4], ) -> BoolTarget @@ -230,7 +316,7 @@ where } /// Computes `a == b`. -fn are_equal( +pub fn are_equal( builder: &mut CircuitBuilder, a: [Target; 4], b: [Target; 4], @@ -246,7 +332,7 @@ where } /// Computes `h0 == h1`. -fn hashes_equal( +pub fn hashes_equal( builder: &mut CircuitBuilder, h0: HashOutTarget, h1: HashOutTarget, @@ -257,7 +343,7 @@ where } /// Computes `h0 != ZERO`. -fn hash_is_nonzero( +pub fn hash_is_nonzero( builder: &mut CircuitBuilder, h0: impl Into, ) -> BoolTarget @@ -274,7 +360,10 @@ where } /// Computes `a == ZERO`. -fn are_zero(builder: &mut CircuitBuilder, a: [Target; 4]) -> BoolTarget +pub fn are_zero( + builder: &mut CircuitBuilder, + a: [Target; 4], +) -> BoolTarget where F: RichField + Extendable, { let zero = a diff --git a/recproofs/src/subcircuits/bounded.rs b/recproofs/src/subcircuits/bounded.rs index 0d311e747..8b1c3384b 100644 --- a/recproofs/src/subcircuits/bounded.rs +++ b/recproofs/src/subcircuits/bounded.rs @@ -140,7 +140,6 @@ impl BranchSubCircuit { #[cfg(test)] mod test { use anyhow::Result; - use lazy_static::lazy_static; use plonky2::plonk::circuit_data::CircuitConfig; use super::*; @@ -215,32 +214,35 @@ mod test { } } - lazy_static! { - static ref LEAF: DummyLeafCircuit = DummyLeafCircuit::new(&CONFIG); - static ref BRANCH_1: DummyBranchCircuit = DummyBranchCircuit::from_leaf(&CONFIG, &LEAF); - static ref BRANCH_2: DummyBranchCircuit = - DummyBranchCircuit::from_branch(&CONFIG, &BRANCH_1); + #[tested_fixture::tested_fixture(LEAF)] + fn build_leaf() -> DummyLeafCircuit { DummyLeafCircuit::new(&CONFIG) } + + #[tested_fixture::tested_fixture(BRANCH_1)] + fn build_branch_1() -> DummyBranchCircuit { DummyBranchCircuit::from_leaf(&CONFIG, &LEAF) } + + #[tested_fixture::tested_fixture(BRANCH_2)] + fn build_branch_2() -> DummyBranchCircuit { + DummyBranchCircuit::from_branch(&CONFIG, &BRANCH_1) } - #[test] - fn verify_leaf() -> Result<()> { + #[tested_fixture::tested_fixture(LEAF_PROOF: ProofWithPublicInputs)] + fn verify_leaf() -> Result> { let proof = LEAF.prove()?; - LEAF.circuit.verify(proof)?; + LEAF.circuit.verify(proof.clone())?; + Ok(proof) + } - Ok(()) + #[tested_fixture::tested_fixture(BRANCH_PROOF: ProofWithPublicInputs)] + fn verify_branch() -> Result> { + let proof = BRANCH_1.prove(&LEAF_PROOF, &LEAF_PROOF)?; + BRANCH_1.circuit.verify(proof.clone())?; + Ok(proof) } #[test] - fn verify_branch() -> Result<()> { - let leaf_proof = LEAF.prove()?; - LEAF.circuit.verify(leaf_proof.clone())?; - - let branch_proof_1 = BRANCH_1.prove(&leaf_proof, &leaf_proof)?; - BRANCH_1.circuit.verify(branch_proof_1.clone())?; - - let branch_proof_2 = BRANCH_2.prove(&branch_proof_1, &branch_proof_1)?; + fn verify_double_branch() -> Result<()> { + let branch_proof_2 = BRANCH_2.prove(&BRANCH_PROOF, &BRANCH_PROOF)?; BRANCH_2.circuit.verify(branch_proof_2.clone())?; - Ok(()) } } diff --git a/recproofs/src/subcircuits/make_tree.rs b/recproofs/src/subcircuits/make_tree.rs deleted file mode 100644 index 89065bf95..000000000 --- a/recproofs/src/subcircuits/make_tree.rs +++ /dev/null @@ -1,421 +0,0 @@ -//! Subcircuits for recursively proving the construction of a binary merkle tree -//! out of a single value. - -use plonky2::field::extension::Extendable; -use plonky2::hash::hash_types::{HashOut, HashOutTarget, RichField}; -use plonky2::iop::target::{BoolTarget, Target}; -use plonky2::iop::witness::{PartialWitness, WitnessWrite}; -use plonky2::plonk::circuit_builder::CircuitBuilder; -use plonky2::plonk::proof::ProofWithPublicInputsTarget; - -use crate::indices::HashTargetIndex; -use crate::{hash_is_nonzero, hash_is_zero, hash_or_forward, hashes_equal}; - -/// The indices of the public inputs of this subcircuit in any -/// `ProofWithPublicInputs` -#[derive(Copy, Clone, PartialEq, Eq, Debug)] -pub struct PublicIndices { - /// The indices of each of the elements of the hash - pub hash: HashTargetIndex, - - /// The indices of each of the elements of the `leaf_value` - pub leaf_value: HashTargetIndex, -} - -pub struct SubCircuitInputs { - /// The leaf value or ZERO if absent - pub hash: HashOutTarget, - - /// The value to be propagated throughout the produced tree - pub leaf_value: HashOutTarget, -} - -pub struct LeafTargets { - /// The public inputs - pub inputs: SubCircuitInputs, -} - -impl SubCircuitInputs { - pub fn default(builder: &mut CircuitBuilder) -> Self - where - F: RichField + Extendable, { - let hash = builder.add_virtual_hash(); - let leaf_value = builder.add_virtual_hash(); - builder.register_public_inputs(&hash.elements); - builder.register_public_inputs(&leaf_value.elements); - Self { hash, leaf_value } - } - - #[must_use] - pub fn build_leaf(self, builder: &mut CircuitBuilder) -> LeafTargets - where - F: RichField + Extendable, { - let one = builder.one(); - - let eq = hashes_equal(builder, self.hash, self.leaf_value); - let zero = hash_is_zero(builder, self.hash); - let xor = builder.add(eq.target, zero.target); - builder.connect(xor, one); - - LeafTargets { inputs: self } - } -} - -/// The leaf subcircuit metadata. This subcircuit does basically nothing, simply -/// expressing that a hash exists -pub struct LeafSubCircuit { - pub targets: LeafTargets, - pub indices: PublicIndices, -} - -impl LeafTargets { - #[must_use] - pub fn build(self, public_inputs: &[Target]) -> LeafSubCircuit { - // Find the indices - let indices = PublicIndices { - hash: HashTargetIndex::new(public_inputs, self.inputs.hash), - leaf_value: HashTargetIndex::new(public_inputs, self.inputs.leaf_value), - }; - LeafSubCircuit { - targets: self, - indices, - } - } -} - -impl LeafSubCircuit { - /// Get ready to generate a proof - pub fn set_witness( - &self, - inputs: &mut PartialWitness, - present: bool, - leaf_value: HashOut, - ) { - let hash = if present { - leaf_value - } else { - HashOut::default() - }; - self.set_witness_unsafe(inputs, hash, leaf_value); - } - - pub fn set_witness_unsafe( - &self, - inputs: &mut PartialWitness, - hash: HashOut, - leaf_value: HashOut, - ) { - inputs.set_hash_target(self.targets.inputs.hash, hash); - inputs.set_hash_target(self.targets.inputs.leaf_value, leaf_value); - } -} - -pub struct BranchTargets { - /// The public inputs - pub inputs: SubCircuitInputs, - - /// Indicates if the left branch is a leaf or not - pub left_is_leaf: BoolTarget, - - /// Indicates if the right branch is a leaf or not - pub right_is_leaf: BoolTarget, -} - -impl SubCircuitInputs { - #[must_use] - pub fn build_branch( - self, - builder: &mut CircuitBuilder, - indices: &PublicIndices, - left_proof: &ProofWithPublicInputsTarget, - right_proof: &ProofWithPublicInputsTarget, - ) -> BranchTargets - where - F: RichField + Extendable, { - let l_hash = indices.hash.get_any(&left_proof.public_inputs); - let r_hash = indices.hash.get_any(&right_proof.public_inputs); - - // Get presence - let left_non_zero = hash_is_nonzero(builder, l_hash); - let right_non_zero = hash_is_nonzero(builder, r_hash); - - // Select the hash based on presence - let summary_hash = hash_or_forward(builder, left_non_zero, l_hash, right_non_zero, r_hash); - builder.connect_hashes(self.hash, summary_hash); - - // Make sure the leaf values are the same - let l_leaf = indices.leaf_value.get(&left_proof.public_inputs); - let r_leaf = indices.leaf_value.get(&right_proof.public_inputs); - builder.connect_hashes(self.leaf_value, l_leaf); - builder.connect_hashes(self.leaf_value, r_leaf); - - // Determine the type of the proof by looking at its public input - // This works because only the root is allowed to be a incomplete (one-sided) - // branch - let l_hash = HashOutTarget::from(l_hash); - let left_eq = hashes_equal(builder, l_hash, self.leaf_value); - let left_zero = hash_is_zero(builder, l_hash); - let left_is_leaf = builder.add(left_eq.target, left_zero.target); - let left_is_leaf = BoolTarget::new_unsafe(left_is_leaf); - builder.assert_bool(left_is_leaf); - - let r_hash = HashOutTarget::from(r_hash); - let right_eq = hashes_equal(builder, r_hash, self.leaf_value); - let right_zero = hash_is_zero(builder, r_hash); - let right_is_leaf = builder.add(right_eq.target, right_zero.target); - let right_is_leaf = BoolTarget::new_unsafe(right_is_leaf); - builder.assert_bool(right_is_leaf); - - BranchTargets { - inputs: self, - left_is_leaf, - right_is_leaf, - } - } -} - -pub struct BranchSubCircuit { - pub targets: BranchTargets, - pub indices: PublicIndices, -} - -impl BranchTargets { - #[must_use] - pub fn build(self, child: &PublicIndices, public_inputs: &[Target]) -> BranchSubCircuit { - // Find the indices - let indices = PublicIndices { - hash: HashTargetIndex::new(public_inputs, self.inputs.hash), - leaf_value: HashTargetIndex::new(public_inputs, self.inputs.leaf_value), - }; - debug_assert_eq!(indices, *child); - - BranchSubCircuit { - targets: self, - indices, - } - } -} - -impl BranchSubCircuit { - pub fn set_witness( - &self, - inputs: &mut PartialWitness, - hash: HashOut, - leaf_value: HashOut, - ) { - inputs.set_hash_target(self.targets.inputs.hash, hash); - inputs.set_hash_target(self.targets.inputs.leaf_value, leaf_value); - } -} - -#[cfg(test)] -mod test { - use anyhow::Result; - use lazy_static::lazy_static; - use plonky2::field::types::Field; - use plonky2::hash::hash_types::NUM_HASH_OUT_ELTS; - use plonky2::plonk::circuit_data::{CircuitConfig, CircuitData}; - use plonky2::plonk::proof::ProofWithPublicInputs; - - use super::*; - use crate::subcircuits::bounded; - use crate::test_utils::{hash_branch, hash_str, C, CONFIG, D, F}; - - pub struct DummyLeafCircuit { - pub bounded: bounded::LeafSubCircuit, - pub make_tree: LeafSubCircuit, - pub circuit: CircuitData, - } - - impl DummyLeafCircuit { - #[must_use] - pub fn new(circuit_config: &CircuitConfig) -> Self { - let mut builder = CircuitBuilder::::new(circuit_config.clone()); - - let bounded_inputs = bounded::SubCircuitInputs::default(&mut builder); - let make_tree_inputs = SubCircuitInputs::default(&mut builder); - - let bounded_targets = bounded_inputs.build_leaf(&mut builder); - let make_tree_targets = make_tree_inputs.build_leaf(&mut builder); - - let circuit = builder.build(); - - let public_inputs = &circuit.prover_only.public_inputs; - let bounded = bounded_targets.build(public_inputs); - let make_tree = make_tree_targets.build(public_inputs); - - Self { - bounded, - make_tree, - circuit, - } - } - - pub fn prove( - &self, - present: bool, - leaf_value: HashOut, - ) -> Result> { - let mut inputs = PartialWitness::new(); - self.bounded.set_witness(&mut inputs); - self.make_tree.set_witness(&mut inputs, present, leaf_value); - self.circuit.prove(inputs) - } - - fn prove_unsafe( - &self, - hash: HashOut, - leaf_value: HashOut, - ) -> Result> { - let mut inputs = PartialWitness::new(); - self.bounded.set_witness(&mut inputs); - self.make_tree - .set_witness_unsafe(&mut inputs, hash, leaf_value); - self.circuit.prove(inputs) - } - } - - pub struct DummyBranchCircuit { - pub bounded: bounded::BranchSubCircuit, - pub make_tree: BranchSubCircuit, - pub circuit: CircuitData, - } - - impl DummyBranchCircuit { - #[must_use] - pub fn new( - circuit_config: &CircuitConfig, - indices: &PublicIndices, - child: &CircuitData, - ) -> Self { - let mut builder = CircuitBuilder::::new(circuit_config.clone()); - - let bounded_inputs = bounded::SubCircuitInputs::default(&mut builder); - let make_tree_inputs = SubCircuitInputs::default(&mut builder); - - let bounded_targets = bounded_inputs.build_branch(&mut builder, child); - let make_tree_targets = make_tree_inputs.build_branch( - &mut builder, - indices, - &bounded_targets.left_proof, - &bounded_targets.right_proof, - ); - - let circuit = builder.build(); - - let public_inputs = &circuit.prover_only.public_inputs; - let bounded = bounded_targets.build(public_inputs); - let make_tree = make_tree_targets.build(indices, public_inputs); - - Self { - bounded, - make_tree, - circuit, - } - } - - #[must_use] - pub fn from_leaf(circuit_config: &CircuitConfig, leaf: &DummyLeafCircuit) -> Self { - Self::new(circuit_config, &leaf.make_tree.indices, &leaf.circuit) - } - - #[must_use] - pub fn from_branch(circuit_config: &CircuitConfig, branch: &Self) -> Self { - Self::new(circuit_config, &branch.make_tree.indices, &branch.circuit) - } - - pub fn prove( - &self, - hash: HashOut, - leaf_value: HashOut, - left_proof: &ProofWithPublicInputs, - right_proof: &ProofWithPublicInputs, - ) -> Result> { - let mut inputs = PartialWitness::new(); - self.bounded - .set_witness(&mut inputs, left_proof, right_proof); - self.make_tree.set_witness(&mut inputs, hash, leaf_value); - self.circuit.prove(inputs) - } - } - - lazy_static! { - static ref LEAF: DummyLeafCircuit = DummyLeafCircuit::new(&CONFIG); - static ref BRANCH_1: DummyBranchCircuit = DummyBranchCircuit::from_leaf(&CONFIG, &LEAF); - static ref BRANCH_2: DummyBranchCircuit = - DummyBranchCircuit::from_branch(&CONFIG, &BRANCH_1); - } - - #[test] - fn verify_leaf() -> Result<()> { - let non_zero_hash = hash_str("Non-Zero Hash"); - - let proof = LEAF.prove(true, non_zero_hash)?; - LEAF.circuit.verify(proof)?; - - let proof = LEAF.prove(false, non_zero_hash)?; - LEAF.circuit.verify(proof)?; - - Ok(()) - } - - #[test] - #[should_panic(expected = "was set twice with different values")] - fn bad_all_zero_leaf() { - let zero_hash = HashOut::from([F::ZERO; NUM_HASH_OUT_ELTS]); - - let proof = LEAF.prove(false, zero_hash).unwrap(); - LEAF.circuit.verify(proof).unwrap(); - } - - #[test] - #[should_panic(expected = "was set twice with different values")] - fn bad_zero_leaf() { - let zero_hash = HashOut::from([F::ZERO; NUM_HASH_OUT_ELTS]); - let non_zero_hash = hash_str("Non-Zero Hash"); - - let proof = LEAF.prove_unsafe(non_zero_hash, zero_hash).unwrap(); - LEAF.circuit.verify(proof).unwrap(); - } - - #[test] - #[should_panic(expected = "was set twice with different values")] - fn bad_leaf_mismatch() { - let non_zero_hash_1 = hash_str("Non-Zero Hash 1"); - let non_zero_hash_2 = hash_str("Non-Zero Hash 2"); - - let proof = LEAF.prove_unsafe(non_zero_hash_1, non_zero_hash_2).unwrap(); - LEAF.circuit.verify(proof).unwrap(); - } - - #[test] - fn verify_branch() -> Result<()> { - let non_zero_hash = hash_str("Non-Zero Hash"); - let branch_hash = hash_branch(&non_zero_hash, &non_zero_hash); - let branch_hash_1 = hash_branch(&non_zero_hash, &branch_hash); - - let leaf_1_proof = LEAF.prove(false, non_zero_hash)?; - LEAF.circuit.verify(leaf_1_proof.clone())?; - - let leaf_2_proof = LEAF.prove(true, non_zero_hash)?; - LEAF.circuit.verify(leaf_2_proof.clone())?; - - let branch_proof_1 = - BRANCH_1.prove(non_zero_hash, non_zero_hash, &leaf_1_proof, &leaf_2_proof)?; - BRANCH_1.circuit.verify(branch_proof_1.clone())?; - - let branch_proof_2 = - BRANCH_1.prove(branch_hash, non_zero_hash, &leaf_2_proof, &leaf_2_proof)?; - BRANCH_1.circuit.verify(branch_proof_2.clone())?; - - let double_branch_proof = BRANCH_2.prove( - branch_hash_1, - non_zero_hash, - &branch_proof_1, - &branch_proof_2, - )?; - BRANCH_2.circuit.verify(double_branch_proof)?; - - Ok(()) - } -} diff --git a/recproofs/src/subcircuits/mod.rs b/recproofs/src/subcircuits/mod.rs index 84768e567..e56b06201 100644 --- a/recproofs/src/subcircuits/mod.rs +++ b/recproofs/src/subcircuits/mod.rs @@ -1,5 +1,4 @@ pub mod bounded; -pub mod make_tree; pub mod propagate; pub mod summarized; pub mod unbounded; diff --git a/recproofs/src/subcircuits/propagate.rs b/recproofs/src/subcircuits/propagate.rs index cb71e5563..b1fc66d81 100644 --- a/recproofs/src/subcircuits/propagate.rs +++ b/recproofs/src/subcircuits/propagate.rs @@ -147,18 +147,21 @@ impl BranchSubCircuit { #[cfg(test)] mod test { use anyhow::Result; - use lazy_static::lazy_static; - use plonky2::field::types::Field; + use array_util::{try_from_fn, ArrayExt}; + use plonky2::hash::hash_types::HashOut; use plonky2::plonk::circuit_data::{CircuitConfig, CircuitData}; use plonky2::plonk::proof::ProofWithPublicInputs; use super::*; use crate::subcircuits::bounded; - use crate::test_utils::{C, CONFIG, D, F}; + use crate::test_utils::{self, make_hashes, C, CONFIG, D, F, ZERO_HASH}; + + const LEAF_VALUES: usize = 2; + const NON_ZERO_VALUES: [HashOut; LEAF_VALUES] = make_hashes(test_utils::NON_ZERO_VALUES); pub struct DummyLeafCircuit { pub bounded: bounded::LeafSubCircuit, - pub propagate: LeafSubCircuit<3>, + pub propagate: LeafSubCircuit<4>, pub circuit: CircuitData, } @@ -186,7 +189,7 @@ mod test { } } - pub fn prove(&self, value: [F; 3]) -> Result> { + pub fn prove(&self, value: [F; 4]) -> Result> { let mut inputs = PartialWitness::new(); self.bounded.set_witness(&mut inputs); self.propagate.set_witness(&mut inputs, value); @@ -196,7 +199,7 @@ mod test { pub struct DummyBranchCircuit { pub bounded: bounded::BranchSubCircuit, - pub propagate: BranchSubCircuit<3>, + pub propagate: BranchSubCircuit<4>, pub circuit: CircuitData, } @@ -204,7 +207,7 @@ mod test { #[must_use] pub fn new( circuit_config: &CircuitConfig, - indices: &PublicIndices<3>, + indices: &PublicIndices<4>, child: &CircuitData, ) -> Self { let mut builder = CircuitBuilder::::new(circuit_config.clone()); @@ -245,7 +248,7 @@ mod test { pub fn prove( &self, - value: [F; 3], + value: [F; 4], left_proof: &ProofWithPublicInputs, right_proof: &ProofWithPublicInputs, ) -> Result> { @@ -257,55 +260,109 @@ mod test { } } - lazy_static! { - static ref LEAF: DummyLeafCircuit = DummyLeafCircuit::new(&CONFIG); - static ref BRANCH_1: DummyBranchCircuit = DummyBranchCircuit::from_leaf(&CONFIG, &LEAF); - static ref BRANCH_2: DummyBranchCircuit = - DummyBranchCircuit::from_branch(&CONFIG, &BRANCH_1); - } - - #[test] - fn verify_leaf() -> Result<()> { - let zero = [F::ZERO; 3]; - let non_zero = [1, 2, 99].map(F::from_canonical_u64); + #[tested_fixture::tested_fixture(LEAF)] + fn build_leaf() -> DummyLeafCircuit { DummyLeafCircuit::new(&CONFIG) } - let proof = LEAF.prove(zero)?; - LEAF.circuit.verify(proof)?; + #[tested_fixture::tested_fixture(BRANCH_1)] + fn build_branch_1() -> DummyBranchCircuit { DummyBranchCircuit::from_leaf(&CONFIG, &LEAF) } - let proof = LEAF.prove(non_zero)?; - LEAF.circuit.verify(proof)?; + #[tested_fixture::tested_fixture(BRANCH_2)] + fn build_branch_2() -> DummyBranchCircuit { + DummyBranchCircuit::from_branch(&CONFIG, &BRANCH_1) + } - Ok(()) + #[tested_fixture::tested_fixture(ZERO_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_zero_leaf() -> Result> { + let proof = LEAF.prove(ZERO_HASH.elements)?; + LEAF.circuit.verify(proof.clone())?; + Ok(proof) } - #[test] - fn verify_branch() -> Result<()> { - let zero = [F::ZERO; 3]; - let non_zero = [1, 2, 99].map(F::from_canonical_u64); + #[tested_fixture::tested_fixture(NON_ZERO_LEAF_PROOFS: [ProofWithPublicInputs; LEAF_VALUES])] + fn verify_leaf() -> Result<[ProofWithPublicInputs; LEAF_VALUES]> { + NON_ZERO_VALUES.try_map_ext(|non_zero_hash| { + let proof = LEAF.prove(non_zero_hash.elements)?; + LEAF.circuit.verify(proof.clone())?; + Ok(proof) + }) + } - // Leaf proofs - let zero_proof = LEAF.prove(zero)?; - LEAF.circuit.verify(zero_proof.clone())?; + #[tested_fixture::tested_fixture(ZERO_BRANCH_PROOF: ProofWithPublicInputs)] + fn verify_zero_branch() -> Result> { + let proof = BRANCH_1.prove(ZERO_HASH.elements, &ZERO_LEAF_PROOF, &ZERO_LEAF_PROOF)?; + BRANCH_1.circuit.verify(proof.clone())?; + Ok(proof) + } - let non_zero_proof = LEAF.prove(non_zero)?; - LEAF.circuit.verify(non_zero_proof.clone())?; + #[tested_fixture::tested_fixture(NON_ZERO_BRANCH_PROOFS: [ProofWithPublicInputs; LEAF_VALUES])] + fn verify_branch() -> Result<[ProofWithPublicInputs; LEAF_VALUES]> { + try_from_fn(|i| { + let proof = BRANCH_1.prove( + NON_ZERO_VALUES[i].elements, + &NON_ZERO_LEAF_PROOFS[i], + &NON_ZERO_LEAF_PROOFS[i], + )?; + BRANCH_1.circuit.verify(proof.clone())?; + Ok(proof) + }) + } - // Branch proofs - let branch_zero_proof = BRANCH_1.prove(zero, &zero_proof, &zero_proof)?; - BRANCH_1.circuit.verify(branch_zero_proof.clone())?; + #[test] + #[should_panic(expected = "assertion `left == right` failed")] + fn bad_zero_branch() { + let proof = BRANCH_1 + .prove( + NON_ZERO_VALUES[0].elements, + &ZERO_LEAF_PROOF, + &ZERO_LEAF_PROOF, + ) + .unwrap(); + BRANCH_1.circuit.verify(proof.clone()).unwrap(); + } - let branch_non_zero_proof = BRANCH_1.prove(non_zero, &non_zero_proof, &non_zero_proof)?; - BRANCH_1.circuit.verify(branch_non_zero_proof.clone())?; + #[test] + #[should_panic(expected = "assertion `left == right` failed")] + fn bad_non_zero_branch() { + let proof = BRANCH_1 + .prove( + ZERO_HASH.elements, + &NON_ZERO_LEAF_PROOFS[0], + &NON_ZERO_LEAF_PROOFS[0], + ) + .unwrap(); + BRANCH_1.circuit.verify(proof.clone()).unwrap(); + } - // Double branch proofs - let double_branch_zero_proof = - BRANCH_2.prove(zero, &branch_zero_proof, &branch_zero_proof)?; - BRANCH_2.circuit.verify(double_branch_zero_proof)?; + #[test] + #[should_panic(expected = "assertion `left == right` failed")] + fn bad_mismatch_branch() { + let proof = BRANCH_1 + .prove( + ZERO_HASH.elements, + &ZERO_LEAF_PROOF, + &NON_ZERO_LEAF_PROOFS[0], + ) + .unwrap(); + BRANCH_1.circuit.verify(proof.clone()).unwrap(); + } - let double_branch_non_zero_proof = - BRANCH_2.prove(non_zero, &branch_non_zero_proof, &branch_non_zero_proof)?; - BRANCH_2.circuit.verify(double_branch_non_zero_proof)?; + #[test] + fn verify_zero_double_branch() -> Result<()> { + let proof = BRANCH_2.prove(ZERO_HASH.elements, &ZERO_BRANCH_PROOF, &ZERO_BRANCH_PROOF)?; + BRANCH_2.circuit.verify(proof.clone())?; + Ok(()) + } + #[test] + fn verify_double_branch() -> Result<()> { + for i in 0..LEAF_VALUES { + let proof = BRANCH_2.prove( + NON_ZERO_VALUES[i].elements, + &NON_ZERO_BRANCH_PROOFS[i], + &NON_ZERO_BRANCH_PROOFS[i], + )?; + BRANCH_2.circuit.verify(proof.clone())?; + } Ok(()) } } diff --git a/recproofs/src/subcircuits/summarized.rs b/recproofs/src/subcircuits/summarized.rs index 4ce36184f..96161d8ce 100644 --- a/recproofs/src/subcircuits/summarized.rs +++ b/recproofs/src/subcircuits/summarized.rs @@ -9,8 +9,8 @@ use plonky2::iop::witness::{PartialWitness, WitnessWrite}; use plonky2::plonk::circuit_builder::CircuitBuilder; use plonky2::plonk::proof::ProofWithPublicInputsTarget; -use crate::hash_or_forward; use crate::indices::{BoolTargetIndex, HashTargetIndex}; +use crate::{at_least_one_true, hash_is_zero, hash_or_forward}; #[derive(Copy, Clone, PartialEq, Eq, Debug)] pub struct PublicIndices { @@ -54,11 +54,8 @@ impl SubCircuitInputs { where F: RichField + Extendable, { // prove hashes align with presence - for e in self.summary_hash.elements { - let e = builder.is_nonzero(e); - builder.connect(e.target, self.summary_hash_present.target); - } - + let hash_zero = hash_is_zero(builder, self.summary_hash); + at_least_one_true(builder, [hash_zero, self.summary_hash_present]); LeafTargets { inputs: self } } } @@ -90,9 +87,13 @@ impl LeafSubCircuit { pub fn set_witness( &self, inputs: &mut PartialWitness, - summary_hash: HashOut, + summary_hash: Option>, ) { - self.set_witness_unsafe(inputs, summary_hash != HashOut::ZERO, summary_hash); + self.set_witness_unsafe( + inputs, + summary_hash.is_some(), + summary_hash.unwrap_or(HashOut::ZERO), + ); } fn set_witness_unsafe( @@ -196,9 +197,13 @@ impl BranchSubCircuit { pub fn set_witness( &self, inputs: &mut PartialWitness, - summary_hash: HashOut, + summary_hash: Option>, ) { - self.set_witness_unsafe(inputs, summary_hash != HashOut::ZERO, summary_hash); + self.set_witness_unsafe( + inputs, + summary_hash.is_some(), + summary_hash.unwrap_or(HashOut::ZERO), + ); } fn set_witness_unsafe( @@ -218,15 +223,17 @@ impl BranchSubCircuit { #[cfg(test)] mod test { use anyhow::Result; - use lazy_static::lazy_static; - use plonky2::field::types::Field; - use plonky2::hash::hash_types::NUM_HASH_OUT_ELTS; + use array_util::ArrayExt; + use itertools::chain; use plonky2::plonk::circuit_data::{CircuitConfig, CircuitData}; use plonky2::plonk::proof::ProofWithPublicInputs; use super::*; use crate::subcircuits::bounded; - use crate::test_utils::{hash_branch, hash_str, C, CONFIG, D, F}; + use crate::test_utils::{self, hash_branch, make_hashes, C, CONFIG, D, F, ZERO_HASH}; + + const LEAF_VALUES: usize = 2; + const NON_ZERO_VALUES: [HashOut; LEAF_VALUES] = make_hashes(test_utils::NON_ZERO_VALUES); pub struct DummyLeafCircuit { pub bounded: bounded::LeafSubCircuit, @@ -258,7 +265,10 @@ mod test { } } - pub fn prove(&self, summary_hash: HashOut) -> Result> { + pub fn prove( + &self, + summary_hash: Option>, + ) -> Result> { let mut inputs = PartialWitness::new(); self.bounded.set_witness(&mut inputs); self.summarized.set_witness(&mut inputs, summary_hash); @@ -329,7 +339,7 @@ mod test { pub fn prove( &self, - summary_hash: HashOut, + summary_hash: Option>, left_proof: &ProofWithPublicInputs, right_proof: &ProofWithPublicInputs, ) -> Result> { @@ -356,192 +366,188 @@ mod test { } } - lazy_static! { - static ref LEAF: DummyLeafCircuit = DummyLeafCircuit::new(&CONFIG); - static ref BRANCH_1: DummyBranchCircuit = DummyBranchCircuit::from_leaf(&CONFIG, &LEAF); - static ref BRANCH_2: DummyBranchCircuit = - DummyBranchCircuit::from_branch(&CONFIG, &BRANCH_1); - } - - #[test] - fn verify_leaf() -> Result<()> { - let zero_hash = HashOut::from([F::ZERO; NUM_HASH_OUT_ELTS]); - let non_zero_hash = hash_str("Non-Zero Hash"); + #[tested_fixture::tested_fixture(LEAF)] + fn build_leaf() -> DummyLeafCircuit { DummyLeafCircuit::new(&CONFIG) } - let proof = LEAF.prove(zero_hash)?; - LEAF.circuit.verify(proof)?; + #[tested_fixture::tested_fixture(BRANCH_1)] + fn build_branch_1() -> DummyBranchCircuit { DummyBranchCircuit::from_leaf(&CONFIG, &LEAF) } - let proof = LEAF.prove(non_zero_hash)?; - LEAF.circuit.verify(proof)?; + #[tested_fixture::tested_fixture(BRANCH_2)] + fn build_branch_2() -> DummyBranchCircuit { + DummyBranchCircuit::from_branch(&CONFIG, &BRANCH_1) + } - Ok(()) + #[tested_fixture::tested_fixture(EMPTY_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_empty_leaf() -> Result> { + let proof = LEAF.prove(None)?; + LEAF.circuit.verify(proof.clone())?; + Ok(proof) } - #[test] - #[should_panic(expected = "was set twice with different values")] - fn bad_zero_leaf() { - let zero_hash = HashOut::from([F::ZERO; NUM_HASH_OUT_ELTS]); + #[tested_fixture::tested_fixture(ZERO_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_zero_leaf() -> Result> { + let proof = LEAF.prove(Some(ZERO_HASH))?; + LEAF.circuit.verify(proof.clone())?; + Ok(proof) + } - let proof = LEAF.prove_unsafe(true, zero_hash).unwrap(); - LEAF.circuit.verify(proof).unwrap(); + #[tested_fixture::tested_fixture(NON_ZERO_LEAF_PROOFS: [(HashOut, ProofWithPublicInputs); LEAF_VALUES])] + fn verify_leaf() -> Result<[(HashOut, ProofWithPublicInputs); LEAF_VALUES]> { + NON_ZERO_VALUES.try_map_ext(|non_zero_hash| { + let proof = LEAF.prove(Some(non_zero_hash))?; + LEAF.circuit.verify(proof.clone())?; + Ok((non_zero_hash, proof)) + }) } #[test] - #[should_panic(expected = "was set twice with different values")] + #[should_panic(expected = "Tried to invert zero")] fn bad_non_zero_leaf() { - let non_zero_hash = hash_str("Non-Zero Hash"); - - let proof = LEAF.prove_unsafe(false, non_zero_hash).unwrap(); + let proof = LEAF.prove_unsafe(false, NON_ZERO_VALUES[0]).unwrap(); LEAF.circuit.verify(proof).unwrap(); } - #[test] - #[allow(clippy::too_many_lines)] - fn verify_branch() -> Result<()> { - let zero_hash = HashOut::from([F::ZERO; NUM_HASH_OUT_ELTS]); - let non_zero_hash_1 = hash_str("Non-Zero Hash 1"); - let non_zero_hash_2 = hash_str("Non-Zero Hash 2"); - let both_hash = hash_branch(&non_zero_hash_1, &non_zero_hash_2); - - // Leaf proofs - let zero_proof = LEAF.prove(zero_hash)?; - LEAF.circuit.verify(zero_proof.clone())?; - - let non_zero_proof_1 = LEAF.prove(non_zero_hash_1)?; - LEAF.circuit.verify(non_zero_proof_1.clone())?; - - let non_zero_proof_2 = LEAF.prove(non_zero_hash_2)?; - LEAF.circuit.verify(non_zero_proof_2.clone())?; - - // Branch proofs - let empty_branch_proof = BRANCH_1.prove(zero_hash, &zero_proof, &zero_proof)?; - BRANCH_1.circuit.verify(empty_branch_proof.clone())?; - - let left1_branch_proof = BRANCH_1.prove(non_zero_hash_1, &non_zero_proof_1, &zero_proof)?; - BRANCH_1.circuit.verify(left1_branch_proof.clone())?; - - let left2_branch_proof = BRANCH_1.prove(non_zero_hash_2, &non_zero_proof_2, &zero_proof)?; - BRANCH_1.circuit.verify(left2_branch_proof.clone())?; - - let right1_branch_proof = - BRANCH_1.prove(non_zero_hash_1, &zero_proof, &non_zero_proof_1)?; - BRANCH_1.circuit.verify(right1_branch_proof.clone())?; - - let right2_branch_proof = - BRANCH_1.prove(non_zero_hash_2, &zero_proof, &non_zero_proof_2)?; - BRANCH_1.circuit.verify(right2_branch_proof.clone())?; - - let both_branch_proof = BRANCH_1.prove(both_hash, &non_zero_proof_1, &non_zero_proof_2)?; - BRANCH_1.circuit.verify(both_branch_proof.clone())?; - - // Double branch proofs - let empty_branch_2_proof = - BRANCH_2.prove(zero_hash, &empty_branch_proof, &empty_branch_proof)?; - BRANCH_2.circuit.verify(empty_branch_2_proof)?; - - let left_branch_2_proof = - BRANCH_2.prove(non_zero_hash_1, &left1_branch_proof, &empty_branch_proof)?; - BRANCH_2.circuit.verify(left_branch_2_proof)?; - - let left_branch_2_proof = - BRANCH_2.prove(non_zero_hash_1, &empty_branch_proof, &left1_branch_proof)?; - BRANCH_2.circuit.verify(left_branch_2_proof)?; - - let right_branch_2_proof = - BRANCH_2.prove(non_zero_hash_2, &right2_branch_proof, &empty_branch_proof)?; - BRANCH_2.circuit.verify(right_branch_2_proof)?; - - let right_branch_2_proof = - BRANCH_2.prove(non_zero_hash_2, &empty_branch_proof, &right2_branch_proof)?; - BRANCH_2.circuit.verify(right_branch_2_proof)?; - - let both_branch_2_proof = - BRANCH_2.prove(both_hash, &left1_branch_proof, &left2_branch_proof)?; - BRANCH_2.circuit.verify(both_branch_2_proof)?; - - let both_branch_2_proof = - BRANCH_2.prove(both_hash, &left1_branch_proof, &right2_branch_proof)?; - BRANCH_2.circuit.verify(both_branch_2_proof)?; + #[tested_fixture::tested_fixture(EMPTY_BRANCH_PROOF: ProofWithPublicInputs)] + fn verify_empty_branch() -> Result> { + let proof = BRANCH_1.prove(None, &EMPTY_LEAF_PROOF, &EMPTY_LEAF_PROOF)?; + BRANCH_1.circuit.verify(proof.clone())?; + Ok(proof) + } - let both_branch_2_proof = - BRANCH_2.prove(both_hash, &right1_branch_proof, &left2_branch_proof)?; - BRANCH_2.circuit.verify(both_branch_2_proof)?; + #[tested_fixture::tested_fixture(ZERO_BRANCH_PROOFS: [(HashOut, ProofWithPublicInputs); 2])] + fn verify_zero_branch() -> Result<[(HashOut, ProofWithPublicInputs); 2]> { + let proof_0 = BRANCH_1.prove(Some(ZERO_HASH), &EMPTY_LEAF_PROOF, &ZERO_LEAF_PROOF)?; + BRANCH_1.circuit.verify(proof_0.clone())?; + let proof_1 = BRANCH_1.prove(Some(ZERO_HASH), &ZERO_LEAF_PROOF, &EMPTY_LEAF_PROOF)?; + BRANCH_1.circuit.verify(proof_1.clone())?; + Ok([(ZERO_HASH, proof_0), (ZERO_HASH, proof_1)]) + } - let both_branch_2_proof = - BRANCH_2.prove(both_hash, &right1_branch_proof, &right2_branch_proof)?; - BRANCH_2.circuit.verify(both_branch_2_proof)?; + #[tested_fixture::tested_fixture(LEFT_BRANCH_PROOFS: [(HashOut, ProofWithPublicInputs); LEAF_VALUES])] + fn verify_left_branch() -> Result<[(HashOut, ProofWithPublicInputs); LEAF_VALUES]> { + NON_ZERO_LEAF_PROOFS + .each_ref() + .try_map_ext(|&(non_zero_hash, ref non_zero_leaf)| { + let proof = + BRANCH_1.prove(Some(non_zero_hash), non_zero_leaf, &EMPTY_LEAF_PROOF)?; + BRANCH_1.circuit.verify(proof.clone())?; + Ok((non_zero_hash, proof)) + }) + } - let both_branch_2_proof = - BRANCH_2.prove(both_hash, &both_branch_proof, &empty_branch_proof)?; - BRANCH_2.circuit.verify(both_branch_2_proof)?; + #[tested_fixture::tested_fixture(RIGHT_BRANCH_PROOFS: [(HashOut, ProofWithPublicInputs); LEAF_VALUES])] + fn verify_right_branch() -> Result<[(HashOut, ProofWithPublicInputs); LEAF_VALUES]> + { + NON_ZERO_LEAF_PROOFS + .each_ref() + .try_map_ext(|&(non_zero_hash, ref non_zero_leaf)| { + let proof = + BRANCH_1.prove(Some(non_zero_hash), &EMPTY_LEAF_PROOF, non_zero_leaf)?; + BRANCH_1.circuit.verify(proof.clone())?; + Ok((non_zero_hash, proof)) + }) + } - let both_branch_2_proof = - BRANCH_2.prove(both_hash, &empty_branch_proof, &both_branch_proof)?; - BRANCH_2.circuit.verify(both_branch_2_proof)?; + #[tested_fixture::tested_fixture(FULL_BRANCH_PROOFS: [[(HashOut, ProofWithPublicInputs); LEAF_VALUES]; LEAF_VALUES])] + fn verify_full_branch( + ) -> Result<[[(HashOut, ProofWithPublicInputs); LEAF_VALUES]; LEAF_VALUES]> { + NON_ZERO_LEAF_PROOFS + .each_ref() + .try_map_ext(|(non_zero_hash_1, non_zero_leaf_1)| { + NON_ZERO_LEAF_PROOFS + .each_ref() + .try_map_ext(|(non_zero_hash_2, non_zero_leaf_2)| { + let both_hash = hash_branch(non_zero_hash_1, non_zero_hash_2); + let proof = + BRANCH_1.prove(Some(both_hash), non_zero_leaf_1, non_zero_leaf_2)?; + BRANCH_1.circuit.verify(proof.clone())?; + Ok((both_hash, proof)) + }) + }) + } - Ok(()) + #[tested_fixture::tested_fixture(FULL_ZERO_BRANCH_PROOFS: [[(HashOut, ProofWithPublicInputs); 2]; LEAF_VALUES])] + fn verify_zero_full_branch( + ) -> Result<[[(HashOut, ProofWithPublicInputs); 2]; LEAF_VALUES]> { + NON_ZERO_LEAF_PROOFS + .each_ref() + .try_map_ext(|(non_zero_hash, non_zero_leaf)| { + let hash_0 = hash_branch(&ZERO_HASH, non_zero_hash); + let proof_0 = BRANCH_1.prove(Some(hash_0), &ZERO_LEAF_PROOF, non_zero_leaf)?; + BRANCH_1.circuit.verify(proof_0.clone())?; + + let hash_1 = hash_branch(&ZERO_HASH, non_zero_hash); + let proof_1 = BRANCH_1.prove(Some(hash_1), &ZERO_LEAF_PROOF, non_zero_leaf)?; + BRANCH_1.circuit.verify(proof_1.clone())?; + + Ok([(hash_0, proof_0), (hash_1, proof_1)]) + }) } #[test] - #[should_panic(expected = "was set twice with different values")] - fn bad_proof_branch() { - let zero_hash = HashOut::from([F::ZERO; NUM_HASH_OUT_ELTS]); - - let zero_proof = LEAF.prove(zero_hash).unwrap(); - LEAF.circuit.verify(zero_proof.clone()).unwrap(); - - let bad_proof = LEAF.prove_unsafe(true, zero_hash).unwrap(); + #[ignore = "slow"] + fn verify_double_branch() -> Result<()> { + let branches = chain![ + ZERO_BRANCH_PROOFS.iter(), + LEFT_BRANCH_PROOFS.iter(), + RIGHT_BRANCH_PROOFS.iter(), + FULL_BRANCH_PROOFS.iter().flatten(), + FULL_ZERO_BRANCH_PROOFS.iter().flatten(), + ]; + + for &(hash_1, ref proof_1) in branches.clone() { + let proof = BRANCH_2.prove(Some(hash_1), &EMPTY_BRANCH_PROOF, proof_1)?; + BRANCH_2.circuit.verify(proof)?; + + let proof = BRANCH_2.prove(Some(hash_1), proof_1, &EMPTY_BRANCH_PROOF)?; + BRANCH_2.circuit.verify(proof)?; + + for &(hash_2, ref proof_2) in branches.clone() { + let both_hash = hash_branch(&hash_1, &hash_2); + let proof = BRANCH_2.prove(Some(both_hash), proof_1, proof_2)?; + BRANCH_2.circuit.verify(proof)?; + + let both_hash = hash_branch(&hash_2, &hash_1); + let proof = BRANCH_2.prove(Some(both_hash), proof_2, proof_1)?; + BRANCH_2.circuit.verify(proof)?; + } + } - let empty_branch_proof = BRANCH_1.prove(zero_hash, &zero_proof, &bad_proof).unwrap(); - BRANCH_1.circuit.verify(empty_branch_proof).unwrap(); + Ok(()) } #[test] #[should_panic(expected = "was set twice with different values")] fn bad_zero_branch() { - let zero_hash = HashOut::from([F::ZERO; NUM_HASH_OUT_ELTS]); - - let zero_proof = LEAF.prove(zero_hash).unwrap(); - LEAF.circuit.verify(zero_proof.clone()).unwrap(); - - let branch_proof = BRANCH_1 - .prove_unsafe(true, zero_hash, &zero_proof, &zero_proof) + let proof = BRANCH_2 + .prove_unsafe(true, ZERO_HASH, &EMPTY_BRANCH_PROOF, &EMPTY_BRANCH_PROOF) .unwrap(); - BRANCH_1.circuit.verify(branch_proof).unwrap(); + BRANCH_2.circuit.verify(proof).unwrap(); } #[test] #[should_panic(expected = "was set twice with different values")] fn bad_non_zero_branch() { - let zero_hash = HashOut::from([F::ZERO; NUM_HASH_OUT_ELTS]); - let non_zero_hash = hash_str("Non-Zero Hash"); - - let zero_proof = LEAF.prove(zero_hash).unwrap(); - LEAF.circuit.verify(zero_proof.clone()).unwrap(); - - let non_zero_proof = LEAF.prove(non_zero_hash).unwrap(); - LEAF.circuit.verify(non_zero_proof.clone()).unwrap(); - - let branch_proof = BRANCH_1 - .prove_unsafe(false, non_zero_hash, &zero_proof, &non_zero_proof) + let proof = BRANCH_1 + .prove_unsafe( + false, + NON_ZERO_LEAF_PROOFS[0].0, + &EMPTY_LEAF_PROOF, + &NON_ZERO_LEAF_PROOFS[0].1, + ) .unwrap(); - BRANCH_1.circuit.verify(branch_proof).unwrap(); + BRANCH_1.circuit.verify(proof).unwrap(); } #[test] #[should_panic(expected = "was set twice with different values")] fn bad_wrong_hash_branch() { - let non_zero_hash_1 = hash_str("Non-Zero Hash 1"); - let non_zero_hash_2 = hash_str("Non-Zero Hash 2"); - - let non_zero_proof_1 = LEAF.prove(non_zero_hash_1).unwrap(); - LEAF.circuit.verify(non_zero_proof_1.clone()).unwrap(); - - let non_zero_proof_2 = LEAF.prove(non_zero_hash_2).unwrap(); - LEAF.circuit.verify(non_zero_proof_2.clone()).unwrap(); - let branch_proof = BRANCH_1 - .prove(non_zero_hash_1, &non_zero_proof_1, &non_zero_proof_2) + .prove( + Some(NON_ZERO_LEAF_PROOFS[0].0), + &NON_ZERO_LEAF_PROOFS[0].1, + &NON_ZERO_LEAF_PROOFS[1].1, + ) .unwrap(); BRANCH_1.circuit.verify(branch_proof).unwrap(); } diff --git a/recproofs/src/subcircuits/unbounded.rs b/recproofs/src/subcircuits/unbounded.rs index 83c86dd16..ed0ed0c57 100644 --- a/recproofs/src/subcircuits/unbounded.rs +++ b/recproofs/src/subcircuits/unbounded.rs @@ -237,7 +237,6 @@ impl BranchSubCircuit { #[cfg(test)] mod test { use anyhow::Result; - use lazy_static::lazy_static; use plonky2::plonk::circuit_data::CircuitConfig; use super::*; @@ -311,31 +310,32 @@ mod test { } } - lazy_static! { - static ref LEAF: DummyLeafCircuit = DummyLeafCircuit::new(&CONFIG); - static ref BRANCH: DummyBranchCircuit = DummyBranchCircuit::new(&CONFIG, &LEAF); - } + #[tested_fixture::tested_fixture(LEAF)] + fn build_leaf() -> DummyLeafCircuit { DummyLeafCircuit::new(&CONFIG) } - #[test] - fn verify_leaf() -> Result<()> { + #[tested_fixture::tested_fixture(BRANCH)] + fn build_branch() -> DummyBranchCircuit { DummyBranchCircuit::new(&CONFIG, &LEAF) } + + #[tested_fixture::tested_fixture(LEAF_PROOF: ProofWithPublicInputs)] + fn verify_leaf() -> Result> { let proof = LEAF.prove(&BRANCH)?; - LEAF.circuit.verify(proof)?; + LEAF.circuit.verify(proof.clone())?; + Ok(proof) + } - Ok(()) + #[tested_fixture::tested_fixture(BRANCH_PROOF: ProofWithPublicInputs)] + fn verify_branch() -> Result> { + let proof = BRANCH.prove(true, &LEAF_PROOF, true, &LEAF_PROOF)?; + BRANCH.circuit.verify(proof.clone())?; + Ok(proof) } #[test] - fn verify_branch() -> Result<()> { - let leaf_proof = LEAF.prove(&BRANCH)?; - LEAF.circuit.verify(leaf_proof.clone())?; - - let branch_proof_1 = BRANCH.prove(true, &leaf_proof, true, &leaf_proof)?; - BRANCH.circuit.verify(branch_proof_1.clone())?; - - let branch_proof_2 = BRANCH.prove(true, &leaf_proof, false, &branch_proof_1)?; + fn verify_double_branch() -> Result<()> { + let branch_proof_2 = BRANCH.prove(true, &LEAF_PROOF, false, &BRANCH_PROOF)?; BRANCH.circuit.verify(branch_proof_2.clone())?; - let branch_proof_3 = BRANCH.prove(false, &branch_proof_1, false, &branch_proof_2)?; + let branch_proof_3 = BRANCH.prove(false, &BRANCH_PROOF, false, &branch_proof_2)?; BRANCH.circuit.verify(branch_proof_3)?; Ok(()) diff --git a/recproofs/src/subcircuits/unpruned.rs b/recproofs/src/subcircuits/unpruned.rs index 020764b30..f9017cb3a 100644 --- a/recproofs/src/subcircuits/unpruned.rs +++ b/recproofs/src/subcircuits/unpruned.rs @@ -266,14 +266,18 @@ impl BranchSubCircuit { #[cfg(test)] mod test { use anyhow::Result; - use lazy_static::lazy_static; - use plonky2::field::types::Field; + use array_util::ArrayExt; use plonky2::plonk::circuit_data::{CircuitConfig, CircuitData}; use plonky2::plonk::proof::ProofWithPublicInputs; use super::*; use crate::subcircuits::bounded; - use crate::test_utils::{hash_branch, hash_branch_bytes, hash_str, C, CONFIG, D, F}; + use crate::test_utils::{ + self, hash_branch, hash_branch_bytes, make_hashes, C, CONFIG, D, F, ZERO_HASH, + }; + + const LEAF_VALUES: usize = 2; + const NON_ZERO_VALUES: [HashOut; LEAF_VALUES] = make_hashes(test_utils::NON_ZERO_VALUES); pub struct DummyLeafCircuit { pub bounded: bounded::LeafSubCircuit, @@ -313,10 +317,25 @@ mod test { } } - pub struct DummyBranchCircuit { - pub bounded: bounded::BranchSubCircuit, - pub unpruned: BranchSubCircuit, - pub circuit: CircuitData, + struct DummyBranchCircuit { + bounded: bounded::BranchSubCircuit, + unpruned: BranchSubCircuit, + circuit: CircuitData, + vm_hash: bool, + } + + trait Branch { + type RightHash<'a>; + type RightProof<'a>; + fn hash(&self, l: &HashOut, r: Self::RightHash<'_>) -> HashOut; + fn circuit(&self) -> &CircuitData; + + fn prove( + &self, + unpruned_hash: HashOut, + left_proof: &ProofWithPublicInputs, + right_proof: Self::RightProof<'_>, + ) -> Result>; } impl DummyBranchCircuit { @@ -360,6 +379,15 @@ mod test { bounded, unpruned, circuit, + vm_hash, + } + } + + fn hash(&self, l: &HashOut, r: &HashOut) -> HashOut { + if self.vm_hash { + hash_branch_bytes(l, r) + } else { + hash_branch(l, r) } } } @@ -390,12 +418,21 @@ mod test { SubCircuitInputs::build_branch, ) } + } + + impl Branch for DummyBranchCircuit { + type RightHash<'a> = &'a HashOut; + type RightProof<'a> = &'a ProofWithPublicInputs; - pub fn prove( + fn hash(&self, l: &HashOut, r: &HashOut) -> HashOut { self.hash(l, r) } + + fn circuit(&self) -> &CircuitData { &self.circuit } + + fn prove( &self, unpruned_hash: HashOut, left_proof: &ProofWithPublicInputs, - right_proof: &ProofWithPublicInputs, + right_proof: Self::RightProof<'_>, ) -> Result> { let mut inputs = PartialWitness::new(); self.bounded @@ -431,12 +468,25 @@ mod test { SubCircuitInputs::build_extended_branch, ) } + } + + impl Branch for DummyBranchCircuit { + type RightHash<'a> = Option<&'a HashOut>; + type RightProof<'a> = Option<&'a ProofWithPublicInputs>; - pub fn prove( + fn hash(&self, l: &HashOut, r: Self::RightHash<'_>) -> HashOut { + if let Some(r) = r { + self.hash(l, r) + } else { + *l + } + } + + fn prove( &self, unpruned_hash: HashOut, left_proof: &ProofWithPublicInputs, - right_proof: Option<&ProofWithPublicInputs>, + right_proof: Self::RightProof<'_>, ) -> Result> { let mut inputs = PartialWitness::new(); self.bounded @@ -445,152 +495,369 @@ mod test { .set_witness(&mut inputs, Some(unpruned_hash), right_proof.is_none()); self.circuit.prove(inputs) } + + fn circuit(&self) -> &CircuitData { &self.circuit } } - lazy_static! { - static ref LEAF: DummyLeafCircuit = DummyLeafCircuit::new(&CONFIG); - static ref BRANCH_1: DummyBranchCircuit = - DummyBranchCircuit::::from_leaf(&CONFIG, &LEAF, false); - static ref BRANCH_2: DummyBranchCircuit = - DummyBranchCircuit::::from_branch(&CONFIG, &BRANCH_1, false); - static ref VM_BRANCH_1: DummyBranchCircuit = - DummyBranchCircuit::::from_leaf(&CONFIG, &LEAF, true); - static ref VM_BRANCH_2: DummyBranchCircuit = - DummyBranchCircuit::::from_branch(&CONFIG, &VM_BRANCH_1, true); - static ref PAR_BRANCH_1: DummyBranchCircuit = - DummyBranchCircuit::::from_leaf(&CONFIG, &LEAF, false); - static ref PAR_BRANCH_2: DummyBranchCircuit = - DummyBranchCircuit::::from_branch(&CONFIG, &PAR_BRANCH_1, false); + #[tested_fixture::tested_fixture(LEAF)] + fn build_leaf() -> DummyLeafCircuit { DummyLeafCircuit::new(&CONFIG) } + + #[tested_fixture::tested_fixture(ZERO_LEAF_PROOF: ProofWithPublicInputs)] + fn verify_zero_leaf() -> Result> { + let proof = LEAF.prove(ZERO_HASH)?; + LEAF.circuit.verify(proof.clone())?; + Ok(proof) } - #[test] - fn verify_leaf() -> Result<()> { - let zero_hash = HashOut::from([F::ZERO; 4]); - let non_zero_hash = hash_str("Non-Zero Hash"); + #[tested_fixture::tested_fixture(NON_ZERO_LEAF_PROOFS: [(HashOut, ProofWithPublicInputs); LEAF_VALUES])] + fn verify_leaf() -> Result<[(HashOut, ProofWithPublicInputs); LEAF_VALUES]> { + NON_ZERO_VALUES.try_map_ext(|non_zero_hash| { + let proof = LEAF.prove(non_zero_hash)?; + LEAF.circuit.verify(proof.clone())?; + Ok((non_zero_hash, proof)) + }) + } - let proof = LEAF.prove(zero_hash)?; - LEAF.circuit.verify(proof)?; + #[tested_fixture::tested_fixture(BRANCH_1)] + fn build_branch_1() -> DummyBranchCircuit { + DummyBranchCircuit::::from_leaf(&CONFIG, &LEAF, false) + } - let proof = LEAF.prove(non_zero_hash)?; - LEAF.circuit.verify(proof)?; + #[tested_fixture::tested_fixture(BRANCH_2)] + fn build_branch_2() -> DummyBranchCircuit { + DummyBranchCircuit::::from_branch(&CONFIG, &BRANCH_1, false) + } - Ok(()) + fn verify_branch_helper<'a, B: Branch>( + branch: &B, + l_hash: &HashOut, + l_proof: &ProofWithPublicInputs, + r_hash: B::RightHash<'a>, + r_proof: B::RightProof<'a>, + ) -> Result<(HashOut, ProofWithPublicInputs)> { + let hash = branch.hash(l_hash, r_hash); + let proof = branch.prove(hash, l_proof, r_proof)?; + branch.circuit().verify(proof.clone())?; + Ok((hash, proof)) + } + + #[tested_fixture::tested_fixture(ZERO_BRANCH_PROOF: (HashOut, ProofWithPublicInputs))] + fn verify_zero_branch() -> Result<(HashOut, ProofWithPublicInputs)> { + verify_branch_helper( + *BRANCH_1, + &ZERO_HASH, + *ZERO_LEAF_PROOF, + &ZERO_HASH, + *ZERO_LEAF_PROOF, + ) + } + + #[tested_fixture::tested_fixture(LEFT_BRANCH_PROOFS: [(HashOut, ProofWithPublicInputs); LEAF_VALUES])] + fn verify_left_branch() -> Result<[(HashOut, ProofWithPublicInputs); LEAF_VALUES]> { + NON_ZERO_LEAF_PROOFS + .each_ref() + .try_map_ext(|(non_zero_hash, non_zero_leaf)| { + verify_branch_helper( + *BRANCH_1, + non_zero_hash, + non_zero_leaf, + &ZERO_HASH, + &ZERO_LEAF_PROOF, + ) + }) + } + + #[tested_fixture::tested_fixture(RIGHT_BRANCH_PROOFS: [(HashOut, ProofWithPublicInputs); LEAF_VALUES])] + fn verify_right_branch() -> Result<[(HashOut, ProofWithPublicInputs); LEAF_VALUES]> + { + NON_ZERO_LEAF_PROOFS + .each_ref() + .try_map_ext(|(non_zero_hash, non_zero_leaf)| { + verify_branch_helper( + *BRANCH_1, + &ZERO_HASH, + &ZERO_LEAF_PROOF, + non_zero_hash, + non_zero_leaf, + ) + }) + } + + #[tested_fixture::tested_fixture(FULL_BRANCH_PROOFS: [[(HashOut, ProofWithPublicInputs); LEAF_VALUES]; LEAF_VALUES])] + fn verify_full_branch( + ) -> Result<[[(HashOut, ProofWithPublicInputs); LEAF_VALUES]; LEAF_VALUES]> { + NON_ZERO_LEAF_PROOFS + .each_ref() + .try_map_ext(|(non_zero_hash_1, non_zero_leaf_1)| { + NON_ZERO_LEAF_PROOFS + .each_ref() + .try_map_ext(|(non_zero_hash_2, non_zero_leaf_2)| { + verify_branch_helper( + *BRANCH_1, + non_zero_hash_1, + non_zero_leaf_1, + non_zero_hash_2, + non_zero_leaf_2, + ) + }) + }) } #[test] - fn verify_branch() -> Result<()> { - let zero_hash = HashOut::from([F::ZERO; 4]); - let non_zero_hash_1 = hash_str("Non-Zero Hash 1"); - let non_zero_hash_2 = hash_str("Non-Zero Hash 2"); - let both_hash_1 = hash_branch(&non_zero_hash_1, &zero_hash); - let both_hash_2 = hash_branch(&zero_hash, &non_zero_hash_2); - let both_hash_1_2 = hash_branch(&both_hash_1, &both_hash_2); + #[ignore] + fn verify_double_branch() -> Result<()> { + let branches = chain![ + LEFT_BRANCH_PROOFS.iter(), + RIGHT_BRANCH_PROOFS.iter(), + FULL_BRANCH_PROOFS.iter().flatten(), + ]; + + let (ref zero_hash, ref zero_proof) = *ZERO_BRANCH_PROOF; + verify_branch_helper(*BRANCH_2, zero_hash, zero_proof, zero_hash, zero_proof)?; + + for (ref hash_1, ref proof_1) in branches.clone() { + verify_branch_helper(*BRANCH_2, hash_1, proof_1, zero_hash, zero_proof)?; + verify_branch_helper(*BRANCH_2, zero_hash, zero_proof, hash_1, proof_1)?; + + for (ref hash_2, ref proof_2) in branches.clone() { + verify_branch_helper(*BRANCH_2, hash_1, proof_1, hash_2, proof_2)?; + verify_branch_helper(*BRANCH_2, hash_2, proof_2, hash_1, proof_1)?; + } + } - // Leaf proofs - let zero_proof = LEAF.prove(zero_hash)?; - LEAF.circuit.verify(zero_proof.clone())?; + Ok(()) + } - let non_zero_proof_1 = LEAF.prove(non_zero_hash_1)?; - LEAF.circuit.verify(non_zero_proof_1.clone())?; + #[tested_fixture::tested_fixture(VM_BRANCH_1)] + fn build_vm_branch_1() -> DummyBranchCircuit { + DummyBranchCircuit::::from_leaf(&CONFIG, &LEAF, true) + } - let non_zero_proof_2 = LEAF.prove(non_zero_hash_2)?; - LEAF.circuit.verify(non_zero_proof_2.clone())?; + #[tested_fixture::tested_fixture(VM_BRANCH_2)] + fn build_vm_branch_2() -> DummyBranchCircuit { + DummyBranchCircuit::::from_branch(&CONFIG, &VM_BRANCH_1, true) + } - // Branch proofs - let branch_1_and_0_proof = BRANCH_1.prove(both_hash_1, &non_zero_proof_1, &zero_proof)?; - BRANCH_1.circuit.verify(branch_1_and_0_proof.clone())?; + #[tested_fixture::tested_fixture(ZERO_VM_BRANCH_PROOF: (HashOut, ProofWithPublicInputs))] + fn verify_zero_vm_branch() -> Result<(HashOut, ProofWithPublicInputs)> { + verify_branch_helper( + *VM_BRANCH_1, + &ZERO_HASH, + *ZERO_LEAF_PROOF, + &ZERO_HASH, + *ZERO_LEAF_PROOF, + ) + } - let branch_0_and_2_proof = BRANCH_1.prove(both_hash_2, &zero_proof, &non_zero_proof_2)?; - BRANCH_1.circuit.verify(branch_0_and_2_proof.clone())?; + #[tested_fixture::tested_fixture(LEFT_VM_BRANCH_PROOFS: [(HashOut, ProofWithPublicInputs); LEAF_VALUES])] + fn verify_left_vm_branch() -> Result<[(HashOut, ProofWithPublicInputs); LEAF_VALUES]> + { + NON_ZERO_LEAF_PROOFS + .each_ref() + .try_map_ext(|(non_zero_hash, non_zero_leaf)| { + verify_branch_helper( + *VM_BRANCH_1, + non_zero_hash, + non_zero_leaf, + &ZERO_HASH, + &ZERO_LEAF_PROOF, + ) + }) + } - // Double branch proofs - let double_branch_proof = - BRANCH_2.prove(both_hash_1_2, &branch_1_and_0_proof, &branch_0_and_2_proof)?; - BRANCH_2.circuit.verify(double_branch_proof)?; + #[tested_fixture::tested_fixture(RIGHT_VM_BRANCH_PROOFS: [(HashOut, ProofWithPublicInputs); LEAF_VALUES])] + fn verify_right_vm_branch( + ) -> Result<[(HashOut, ProofWithPublicInputs); LEAF_VALUES]> { + NON_ZERO_LEAF_PROOFS + .each_ref() + .try_map_ext(|(non_zero_hash, non_zero_leaf)| { + verify_branch_helper( + *VM_BRANCH_1, + &ZERO_HASH, + &ZERO_LEAF_PROOF, + non_zero_hash, + non_zero_leaf, + ) + }) + } - Ok(()) + #[tested_fixture::tested_fixture(FULL_VM_BRANCH_PROOFS: [[(HashOut, ProofWithPublicInputs); LEAF_VALUES]; LEAF_VALUES])] + fn verify_full_vm_branch( + ) -> Result<[[(HashOut, ProofWithPublicInputs); LEAF_VALUES]; LEAF_VALUES]> { + NON_ZERO_LEAF_PROOFS + .each_ref() + .try_map_ext(|(non_zero_hash_1, non_zero_leaf_1)| { + NON_ZERO_LEAF_PROOFS + .each_ref() + .try_map_ext(|(non_zero_hash_2, non_zero_leaf_2)| { + verify_branch_helper( + *VM_BRANCH_1, + non_zero_hash_1, + non_zero_leaf_1, + non_zero_hash_2, + non_zero_leaf_2, + ) + }) + }) } #[test] - fn verify_branch_bytes() -> Result<()> { - let zero_hash = HashOut::from([F::ZERO; 4]); - let non_zero_hash_1 = hash_str("Non-Zero Hash 1"); - let non_zero_hash_2 = hash_str("Non-Zero Hash 2"); - let both_hash_1 = hash_branch_bytes(&non_zero_hash_1, &zero_hash); - let both_hash_2 = hash_branch_bytes(&zero_hash, &non_zero_hash_2); - let both_hash_1_2 = hash_branch_bytes(&both_hash_1, &both_hash_2); - - // Leaf proofs - let zero_proof = LEAF.prove(zero_hash)?; - LEAF.circuit.verify(zero_proof.clone())?; - - let non_zero_proof_1 = LEAF.prove(non_zero_hash_1)?; - LEAF.circuit.verify(non_zero_proof_1.clone())?; - - let non_zero_proof_2 = LEAF.prove(non_zero_hash_2)?; - LEAF.circuit.verify(non_zero_proof_2.clone())?; - - // Branch proofs - let branch_1_and_0_proof = - VM_BRANCH_1.prove(both_hash_1, &non_zero_proof_1, &zero_proof)?; - VM_BRANCH_1.circuit.verify(branch_1_and_0_proof.clone())?; - - let branch_0_and_2_proof = - VM_BRANCH_1.prove(both_hash_2, &zero_proof, &non_zero_proof_2)?; - VM_BRANCH_1.circuit.verify(branch_0_and_2_proof.clone())?; - - // Double branch proofs - let double_branch_proof = - VM_BRANCH_2.prove(both_hash_1_2, &branch_1_and_0_proof, &branch_0_and_2_proof)?; - VM_BRANCH_2.circuit.verify(double_branch_proof)?; + fn verify_double_vm_branch() -> Result<()> { + let branches = chain![ + LEFT_VM_BRANCH_PROOFS.iter(), + RIGHT_VM_BRANCH_PROOFS.iter(), + FULL_VM_BRANCH_PROOFS.iter().flatten(), + ]; + + let (ref zero_hash, ref zero_proof) = *ZERO_VM_BRANCH_PROOF; + verify_branch_helper(*VM_BRANCH_2, zero_hash, zero_proof, zero_hash, zero_proof)?; + + for (ref hash_1, ref proof_1) in branches.clone() { + verify_branch_helper(*VM_BRANCH_2, hash_1, proof_1, zero_hash, zero_proof)?; + verify_branch_helper(*VM_BRANCH_2, zero_hash, zero_proof, hash_1, proof_1)?; + + for (ref hash_2, ref proof_2) in branches.clone() { + verify_branch_helper(*VM_BRANCH_2, hash_1, proof_1, hash_2, proof_2)?; + verify_branch_helper(*VM_BRANCH_2, hash_2, proof_2, hash_1, proof_1)?; + } + } Ok(()) } + #[tested_fixture::tested_fixture(PAR_BRANCH_1)] + fn build_par_branch_1() -> DummyBranchCircuit { + DummyBranchCircuit::::from_leaf(&CONFIG, &LEAF, false) + } + + #[tested_fixture::tested_fixture(PAR_BRANCH_2)] + fn build_par_branch_2() -> DummyBranchCircuit { + DummyBranchCircuit::::from_branch(&CONFIG, &PAR_BRANCH_1, false) + } + + #[tested_fixture::tested_fixture(ZERO_PAR_BRANCH_PROOF: [(HashOut, ProofWithPublicInputs); 2])] + fn verify_zero_partial_branch() -> Result<[(HashOut, ProofWithPublicInputs); 2]> { + Ok([ + verify_branch_helper(*PAR_BRANCH_1, &ZERO_HASH, *ZERO_LEAF_PROOF, None, None)?, + verify_branch_helper( + *PAR_BRANCH_1, + &ZERO_HASH, + *ZERO_LEAF_PROOF, + Some(&ZERO_HASH), + Some(*ZERO_LEAF_PROOF), + )?, + ]) + } + + #[tested_fixture::tested_fixture(LEFT_PAR_BRANCH_PROOFS: [[(HashOut, ProofWithPublicInputs); 2]; LEAF_VALUES])] + fn verify_left_partial_branch( + ) -> Result<[[(HashOut, ProofWithPublicInputs); 2]; LEAF_VALUES]> { + NON_ZERO_LEAF_PROOFS + .each_ref() + .try_map_ext(|(non_zero_hash, non_zero_leaf)| { + Ok([ + verify_branch_helper(*PAR_BRANCH_1, non_zero_hash, non_zero_leaf, None, None)?, + verify_branch_helper( + *PAR_BRANCH_1, + non_zero_hash, + non_zero_leaf, + Some(&ZERO_HASH), + Some(*ZERO_LEAF_PROOF), + )?, + ]) + }) + } + + #[tested_fixture::tested_fixture(RIGHT_PAR_BRANCH_PROOFS: [(HashOut, ProofWithPublicInputs); LEAF_VALUES])] + fn verify_right_partial_branch( + ) -> Result<[(HashOut, ProofWithPublicInputs); LEAF_VALUES]> { + NON_ZERO_LEAF_PROOFS + .each_ref() + .try_map_ext(|(non_zero_hash, non_zero_leaf)| { + verify_branch_helper( + *PAR_BRANCH_1, + &ZERO_HASH, + &ZERO_LEAF_PROOF, + Some(non_zero_hash), + Some(non_zero_leaf), + ) + }) + } + + #[tested_fixture::tested_fixture(FULL_PAR_BRANCH_PROOFS: [[(HashOut, ProofWithPublicInputs); LEAF_VALUES]; LEAF_VALUES])] + fn verify_full_partial_branch( + ) -> Result<[[(HashOut, ProofWithPublicInputs); LEAF_VALUES]; LEAF_VALUES]> { + NON_ZERO_LEAF_PROOFS + .each_ref() + .try_map_ext(|(non_zero_hash_1, non_zero_leaf_1)| { + NON_ZERO_LEAF_PROOFS + .each_ref() + .try_map_ext(|(non_zero_hash_2, non_zero_leaf_2)| { + verify_branch_helper( + *PAR_BRANCH_1, + non_zero_hash_1, + non_zero_leaf_1, + Some(non_zero_hash_2), + Some(non_zero_leaf_2), + ) + }) + }) + } + #[test] + #[ignore = "slow"] fn verify_partial_branch() -> Result<()> { - let zero_hash = HashOut::from([F::ZERO; 4]); - let non_zero_hash_1 = hash_str("Non-Zero Hash 1"); - let non_zero_hash_2 = hash_str("Non-Zero Hash 2"); - let both_hash_1 = hash_branch(&non_zero_hash_1, &zero_hash); - let both_hash_2 = hash_branch(&zero_hash, &non_zero_hash_2); - let both_hash_1_2 = hash_branch(&both_hash_1, &both_hash_2); - - // Leaf proofs - let zero_proof = LEAF.prove(zero_hash)?; - LEAF.circuit.verify(zero_proof.clone())?; - - let non_zero_proof_1 = LEAF.prove(non_zero_hash_1)?; - LEAF.circuit.verify(non_zero_proof_1.clone())?; - - let non_zero_proof_2 = LEAF.prove(non_zero_hash_2)?; - LEAF.circuit.verify(non_zero_proof_2.clone())?; - - // Branch proofs - let branch_1_proof = PAR_BRANCH_1.prove(non_zero_hash_1, &non_zero_proof_1, None)?; - PAR_BRANCH_1.circuit.verify(branch_1_proof.clone())?; - - let branch_0_proof = PAR_BRANCH_1.prove(zero_hash, &zero_proof, None)?; - PAR_BRANCH_1.circuit.verify(branch_0_proof.clone())?; - - let branch_1_and_0_proof = - PAR_BRANCH_1.prove(both_hash_1, &non_zero_proof_1, Some(&zero_proof))?; - PAR_BRANCH_1.circuit.verify(branch_1_and_0_proof.clone())?; - - let branch_0_and_2_proof = - PAR_BRANCH_1.prove(both_hash_2, &zero_proof, Some(&non_zero_proof_2))?; - PAR_BRANCH_1.circuit.verify(branch_0_and_2_proof.clone())?; - - // Double branch proofs - let double_branch_proof = PAR_BRANCH_2.prove(both_hash_1, &branch_1_and_0_proof, None)?; - PAR_BRANCH_2.circuit.verify(double_branch_proof)?; - - let double_branch_proof = PAR_BRANCH_2.prove( - both_hash_1_2, - &branch_1_and_0_proof, - Some(&branch_0_and_2_proof), - )?; - PAR_BRANCH_2.circuit.verify(double_branch_proof)?; + let branches = chain![ + LEFT_PAR_BRANCH_PROOFS.iter().flatten(), + RIGHT_PAR_BRANCH_PROOFS.iter(), + FULL_PAR_BRANCH_PROOFS.iter().flatten(), + ]; + + for (ref zero_hash, ref zero_proof) in ZERO_PAR_BRANCH_PROOF.iter() { + verify_branch_helper(*PAR_BRANCH_2, zero_hash, zero_proof, None, None)?; + verify_branch_helper( + *PAR_BRANCH_2, + zero_hash, + zero_proof, + Some(zero_hash), + Some(zero_proof), + )?; + + for (ref hash_1, ref proof_1) in branches.clone() { + verify_branch_helper(*PAR_BRANCH_2, hash_1, proof_1, None, None)?; + verify_branch_helper( + *PAR_BRANCH_2, + hash_1, + proof_1, + Some(zero_hash), + Some(zero_proof), + )?; + verify_branch_helper( + *PAR_BRANCH_2, + zero_hash, + zero_proof, + Some(hash_1), + Some(proof_1), + )?; + + for (ref hash_2, ref proof_2) in branches.clone() { + verify_branch_helper( + *PAR_BRANCH_2, + hash_1, + proof_1, + Some(hash_2), + Some(proof_2), + )?; + verify_branch_helper( + *PAR_BRANCH_2, + hash_2, + proof_2, + Some(hash_1), + Some(proof_1), + )?; + } + } + } Ok(()) } diff --git a/runner/src/code.rs b/runner/src/code.rs index a9e41850c..691d25d9c 100644 --- a/runner/src/code.rs +++ b/runner/src/code.rs @@ -9,7 +9,7 @@ use plonky2::field::goldilocks_field::GoldilocksField; use serde::{Deserialize, Serialize}; use crate::decode::{decode_instruction, ECALL}; -use crate::elf::{Program, RuntimeArguments}; +use crate::elf::Program; use crate::instruction::{Args, DecodingError, Instruction, Op}; use crate::state::{RawTapes, State}; use crate::vm::{step, ExecutionRecord}; @@ -62,7 +62,7 @@ pub fn execute_code_with_ro_memory( ro_mem: &[(u32, u8)], rw_mem: &[(u32, u8)], regs: &[(u8, u32)], - runtime_args: RuntimeArguments, + raw_tapes: RawTapes, ) -> (Program, ExecutionRecord) { let _ = env_logger::try_init(); let ro_code = Code( @@ -86,8 +86,8 @@ pub fn execute_code_with_ro_memory( .collect(), ); - let program = Program::create(ro_mem, rw_mem, ro_code, &runtime_args); - let state0 = State::new(program.clone(), RawTapes::from(runtime_args)); + let program = Program::create(ro_mem, rw_mem, ro_code); + let state0 = State::new(program.clone(), raw_tapes); let state = regs.iter().fold(state0, |state, (rs, val)| { state.set_register_value(*rs, *val) @@ -101,13 +101,12 @@ pub fn execute_code_with_ro_memory( /// Entrypoint for a stream of instructions into the VM. /// /// Creates a [`Program`] and executes given -/// [Instruction]s based on empty pre-initialized -/// [`MozakMemory`](crate::elf::MozakMemory). +/// [Instruction]s #[must_use] pub fn execute( code: impl IntoIterator, rw_mem: &[(u32, u8)], regs: &[(u8, u32)], ) -> (Program, ExecutionRecord) { - execute_code_with_ro_memory(code, &[], rw_mem, regs, RuntimeArguments::default()) + execute_code_with_ro_memory(code, &[], rw_mem, regs, RawTapes::default()) } diff --git a/runner/src/ecall.rs b/runner/src/ecall.rs index 45657a928..7efbac5fd 100644 --- a/runner/src/ecall.rs +++ b/runner/src/ecall.rs @@ -52,16 +52,20 @@ impl State { num_bytes_requested as usize, ), StorageDeviceOpcode::StoreEventsCommitmentTape => read_bytes( - &*self.events_commitment_tape, + &self.events_commitment_tape.0, &mut 0, num_bytes_requested as usize, ), StorageDeviceOpcode::StoreCastListCommitmentTape => read_bytes( - &*self.cast_list_commitment_tape, + &self.cast_list_commitment_tape.0, + &mut 0, + num_bytes_requested as usize, + ), + StorageDeviceOpcode::StoreSelfProgIdTape => read_bytes( + &self.self_prog_id_tape, &mut 0, num_bytes_requested as usize, ), - StorageDeviceOpcode::None => panic!(), }; let data_len = u32::try_from(data.len()).expect("cannot fit data.len() into u32"); @@ -143,6 +147,7 @@ impl State { self.ecall_read(StorageDeviceOpcode::StoreEventsCommitmentTape), ecall::CAST_LIST_COMMITMENT_TAPE => self.ecall_read(StorageDeviceOpcode::StoreCastListCommitmentTape), + ecall::SELF_PROG_ID_TAPE => self.ecall_read(StorageDeviceOpcode::StoreSelfProgIdTape), ecall::PANIC => self.ecall_panic(), ecall::POSEIDON2 => self.ecall_poseidon2(), ecall::VM_TRACE_LOG => self.ecall_trace_log(), diff --git a/runner/src/elf.rs b/runner/src/elf.rs index f1d8a86b2..acb42cdc5 100644 --- a/runner/src/elf.rs +++ b/runner/src/elf.rs @@ -1,243 +1,18 @@ use std::cmp::{max, min}; use std::iter::repeat; -use std::ops::Range; use anyhow::{anyhow, ensure, Result}; use derive_more::{Deref, DerefMut, IntoIterator}; use elf::endian::LittleEndian; use elf::file::Class; use elf::segment::{ProgramHeader, SegmentTable}; -use elf::string_table::StringTable; -use elf::symbol::SymbolTable; use elf::ElfBytes; use im::hashmap::HashMap; -use itertools::{chain, iproduct, izip, Itertools}; -use mozak_sdk::core::ecall::COMMITMENT_SIZE; +use itertools::{chain, iproduct, Itertools}; use serde::{Deserialize, Serialize}; use crate::code::Code; -#[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)] -pub struct MozakMemoryRegion { - pub starting_address: u32, - pub capacity: u32, - pub data: Data, -} - -impl MozakMemoryRegion { - fn memory_range(&self) -> Range { - self.starting_address..self.starting_address + self.capacity - } - - fn fill(&mut self, data: &[u8]) { - assert!( - data.len() <= self.capacity.try_into().unwrap(), - "data of length {:?} does not fit into address ({:x?}) with capacity {:?}", - data.len(), - self.starting_address, - self.capacity, - ); - for (index, &item) in izip!(self.starting_address.., data) { - self.data.insert(index, item); - } - assert!( - self.data.len() <= self.capacity.try_into().unwrap(), - "data of length {:?} does not fit into address ({:x?}) with capacity {:?}", - self.data.len(), - self.starting_address, - self.capacity, - ); - } -} - -#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] -pub struct MozakMemory { - pub self_prog_id: MozakMemoryRegion, - pub cast_list: MozakMemoryRegion, - pub private_tape: MozakMemoryRegion, - pub public_tape: MozakMemoryRegion, - pub call_tape: MozakMemoryRegion, - pub event_tape: MozakMemoryRegion, -} - -impl From for HashMap { - fn from(mem: MozakMemory) -> Self { - [ - mem.self_prog_id, - mem.cast_list, - mem.private_tape, - mem.public_tape, - mem.call_tape, - mem.event_tape, - ] - .into_iter() - .flat_map(|MozakMemoryRegion { data: Data(d), .. }| d.into_iter()) - .collect() - } -} - -impl Default for MozakMemory { - fn default() -> Self { - // These magic numbers taken from mozak-linker-script - // TODO(Roman): Once `end-of-mozak-region` symbol will be added to linker-script - // it will be possible to implement test that load mozak-empty-ELF and check - // that all expected addresses and capacities are indeed aligned with the code. - // We have test, that loads `empty-ELF` compiled with mozak-linker-script. - // This test ensures that assumed symbols are defined. - MozakMemory { - self_prog_id: MozakMemoryRegion { - starting_address: 0x2000_0000_u32, - capacity: 0x20_u32, - ..Default::default() - }, - cast_list: MozakMemoryRegion { - starting_address: 0x2000_0020_u32, - capacity: 0x00FF_FFE0_u32, - ..Default::default() - }, - public_tape: MozakMemoryRegion { - starting_address: 0x2100_0000_u32, - capacity: 0x0F00_0000_u32, - ..Default::default() - }, - private_tape: MozakMemoryRegion { - starting_address: 0x3000_0000_u32, - capacity: 0x1000_0000_u32, - ..Default::default() - }, - call_tape: MozakMemoryRegion { - starting_address: 0x4000_0000_u32, - capacity: 0x0800_0000_u32, - ..Default::default() - }, - event_tape: MozakMemoryRegion { - starting_address: 0x4800_0000_u32, - capacity: 0x0800_0000_u32, - ..Default::default() - }, - } - } -} - -impl MozakMemory { - // TODO(Roman): refactor this function, caller can parse p_vaddr, so pure u32 - // address will be enough - fn is_mozak_ro_memory_address(&self, program_header: &ProgramHeader) -> bool { - self.is_address_belongs_to_mozak_ro_memory( - u32::try_from(program_header.p_vaddr) - .expect("p_vaddr for zk-vm expected to be cast-able to u32"), - ) - } - - #[must_use] - pub fn is_address_belongs_to_mozak_ro_memory(&self, address: u32) -> bool { - let mem_addresses = [ - self.self_prog_id.memory_range(), - self.cast_list.memory_range(), - self.public_tape.memory_range(), - self.private_tape.memory_range(), - self.call_tape.memory_range(), - self.event_tape.memory_range(), - ]; - log::trace!( - "mozak-memory-addresses: {:?}, address: {:?}", - mem_addresses, - address - ); - mem_addresses.iter().any(|r| r.contains(&address)) - } - - fn fill(&mut self, (symbol_table, string_table): &(SymbolTable, StringTable)) { - let symbol_map: HashMap<_, _> = symbol_table - .iter() - .map(|s| (string_table.get(s.st_name as usize).unwrap(), s.st_value)) - .collect(); - let get = |sym_name: &str| { - u32::try_from( - *symbol_map - .get(sym_name) - .unwrap_or_else(|| panic!("{sym_name} not found")), - ) - .unwrap_or_else(|err| { - panic!( - "{sym_name}'s address should be u32 cast-able: - {err}" - ) - }) - }; - - self.self_prog_id.starting_address = get("_mozak_self_prog_id"); - self.cast_list.starting_address = get("_mozak_cast_list"); - self.public_tape.starting_address = get("_mozak_public_tape"); - self.private_tape.starting_address = get("_mozak_private_tape"); - self.call_tape.starting_address = get("_mozak_call_tape"); - self.event_tape.starting_address = get("_mozak_event_tape"); - // log::debug!("_mozak_call_tape: 0x{:0x}", self.call_tape.starting_address); - - // compute capacity, assume single memory region (refer to linker-script) - self.self_prog_id.capacity = 0x20_u32; - self.cast_list.capacity = 0x00FF_FFE0_u32; - - self.public_tape.capacity = - self.private_tape.starting_address - self.public_tape.starting_address; - // refer to linker-script to understand this magic number ... - // TODO(Roman): to get rid off this magic number, we need to have `_end` symbol - // in linker script This way we can compute capacity directly from - // linker-script. Currently, test that loads empty ELF, compiled with - // linker-script we not help us, since there is not symbol that defines - // `end-of-mozak-region`... - self.private_tape.capacity = - self.call_tape.starting_address - self.private_tape.starting_address; - self.call_tape.capacity = - self.event_tape.starting_address - self.call_tape.starting_address; - self.event_tape.capacity = 0x5000_0000 - self.event_tape.starting_address; - } -} - -/// A Mozak program runtime arguments, all fields are 4 LE bytes length prefixed -#[derive(Clone, Debug, Default, Serialize, Deserialize)] -pub struct RuntimeArguments { - pub self_prog_id: Vec, - pub events_commitment_tape: [u8; COMMITMENT_SIZE], - pub cast_list_commitment_tape: [u8; COMMITMENT_SIZE], - pub cast_list: Vec, - pub private_tape: Vec, - pub public_tape: Vec, - pub call_tape: Vec, - pub event_tape: Vec, -} - -impl RuntimeArguments { - #[must_use] - pub fn is_empty(&self) -> bool { - self.self_prog_id.is_empty() - && self.cast_list.is_empty() - && self.private_tape.is_empty() - && self.public_tape.is_empty() - && self.call_tape.is_empty() - && self.event_tape.is_empty() - } -} - -impl From<&RuntimeArguments> for MozakMemory { - fn from(args: &RuntimeArguments) -> Self { - let mut mozak_ro_memory = MozakMemory::default(); - mozak_ro_memory - .self_prog_id - .fill(args.self_prog_id.as_slice()); - mozak_ro_memory.cast_list.fill(args.cast_list.as_slice()); - mozak_ro_memory - .public_tape - .fill(args.public_tape.as_slice()); - mozak_ro_memory - .private_tape - .fill(args.private_tape.as_slice()); - mozak_ro_memory.call_tape.fill(args.call_tape.as_slice()); - mozak_ro_memory.event_tape.fill(args.event_tape.as_slice()); - mozak_ro_memory - } -} - /// A RISC-V program #[derive(Clone, Debug, Default, Serialize, Deserialize, PartialEq)] pub struct Program { @@ -254,18 +29,6 @@ pub struct Program { /// Executable code of the ELF, read only pub ro_code: Code, - - /// Mozak run-time memory - // Earlier our Program was completely determined by the ELF, and did not - // differ from one run to the next. - // Compare how the existing code doesn't add tape information to the Program, but to the - // State. Conceptually, we are trying to replace this existing mechanism here, but currently we - // decided to leave it as is, later on we may refactor it to be 3 structs (something like - // this): Program, State, Init-Data. Currently during execution we have chain of states, and - // each state has Aux-Data that has some debug-help info (like memory snapshot) of the whole - // program. It is not really a perf problem since its actually a reference but, maybe later on - // we will decide to refactor it, because this debug-help info wasn't really usefull much. - pub mozak_ro_memory: Option, } /// Memory of RISC-V Program @@ -290,7 +53,6 @@ impl From> for Program { ro_code: Code::from(&image), ro_memory: Data::default(), rw_memory: Data(image), - mozak_ro_memory: None, } } } @@ -337,11 +99,10 @@ impl Program { input, entry_point, segments, - |flags, _, _| { + |flags, _| { (flags & elf::abi::PF_R == elf::abi::PF_R) && (flags & elf::abi::PF_W == elf::abi::PF_NONE) }, - None, )) } @@ -353,7 +114,7 @@ impl Program { #[must_use] pub fn mozak_load_elf( input: &[u8], - (elf, entry_point, segments): (ElfBytes, u32, SegmentTable), + (_elf, entry_point, segments): (ElfBytes, u32, SegmentTable), ) -> Program { // Information related to the `check_program_flags` // `&& (!mozak_memory.is_mozak_ro_memory_address(ph))` --> this line is used to @@ -363,24 +124,10 @@ impl Program { // with arguments provided from outside. Mozak-ROM can be accessed as Read-ONLY // from rust code and currently no init code to this section is // supported. - Program::internal_load_elf( - input, - entry_point, - segments, - |flags, ph, mozak_memory: &Option| { - (flags & elf::abi::PF_R == elf::abi::PF_R) - && (flags & elf::abi::PF_W == elf::abi::PF_NONE) - && (!mozak_memory - .as_ref() - .expect("Expected to exist for mozak-elf") - .is_mozak_ro_memory_address(ph)) - }, - Some({ - let mut mm = MozakMemory::default(); - mm.fill(&elf.symbol_table().unwrap().unwrap()); - mm - }), - ) + Program::internal_load_elf(input, entry_point, segments, |flags, _| { + (flags & elf::abi::PF_R == elf::abi::PF_R) + && (flags & elf::abi::PF_W == elf::abi::PF_NONE) + }) } fn parse_and_validate_elf( @@ -411,25 +158,18 @@ impl Program { input: &[u8], entry_point: u32, segments: SegmentTable, - check_program_flags: fn( - flags: u32, - program_headers: &ProgramHeader, - mozak_memory: &Option, - ) -> bool, - mozak_ro_memory: Option, + check_program_flags: fn(flags: u32, program_headers: &ProgramHeader) -> bool, ) -> Program { let ro_memory = Data(Program::extract_elf_data( check_program_flags, input, &segments, - &mozak_ro_memory, )); let rw_memory = Data(Program::extract_elf_data( - |flags, _, _| flags == elf::abi::PF_R | elf::abi::PF_W, + |flags, _| flags == elf::abi::PF_R | elf::abi::PF_W, input, &segments, - &mozak_ro_memory, )); // Because we are implementing a modified Harvard Architecture, we make an @@ -437,10 +177,9 @@ impl Program { // instructions will be in a R_X segment, so their data will show up in ro_code // and ro_memory. (RWX segments would show up in ro_code and rw_memory.) let ro_code = Code::from(&Program::extract_elf_data( - |flags, _, _| flags & elf::abi::PF_X == elf::abi::PF_X, + |flags, _| flags & elf::abi::PF_X == elf::abi::PF_X, input, &segments, - &mozak_ro_memory, )); Program { @@ -448,25 +187,17 @@ impl Program { ro_memory, rw_memory, ro_code, - mozak_ro_memory, } } fn extract_elf_data( - check_program_flags: fn( - flags: u32, - program_headers: &ProgramHeader, - mozak_memory: &Option, - ) -> bool, + check_program_flags: fn(flags: u32, program_headers: &ProgramHeader) -> bool, input: &[u8], segments: &SegmentTable, - mozak_memory: &Option, ) -> HashMap { segments .iter() - .filter(|program_header| { - check_program_flags(program_header.p_flags, program_header, mozak_memory) - }) + .filter(|program_header| check_program_flags(program_header.p_flags, program_header)) .map(|program_header| -> anyhow::Result<_> { let file_size: usize = program_header.p_filesz.try_into()?; let mem_size: usize = program_header.p_memsz.try_into()?; @@ -486,8 +217,7 @@ impl Program { .expect("extract elf data should always succeed") } - /// Loads a "mozak program" from static ELF and populates the reserved - /// memory with runtime arguments + /// Loads a [`Program`] from static ELF. /// /// # Errors /// Will return `Err` if the ELF file is invalid or if the entrypoint is @@ -496,74 +226,20 @@ impl Program { /// # Panics /// When `Program::load_elf` or index as address is not cast-able to be u32 /// cast-able - pub fn mozak_load_program(elf_bytes: &[u8], args: &RuntimeArguments) -> Result { - let mut program = + pub fn mozak_load_program(elf_bytes: &[u8]) -> Result { + let program = Program::mozak_load_elf(elf_bytes, Program::parse_and_validate_elf(elf_bytes)?); - let mozak_ro_memory = program - .mozak_ro_memory - .as_mut() - .expect("MozakMemory should exist for mozak-elf case"); - // Context Variables address - mozak_ro_memory - .self_prog_id - .fill(args.self_prog_id.as_slice()); - mozak_ro_memory.cast_list.fill(args.cast_list.as_slice()); - mozak_ro_memory - .public_tape - .fill(args.public_tape.as_slice()); - mozak_ro_memory - .private_tape - .fill(args.private_tape.as_slice()); - mozak_ro_memory.call_tape.fill(args.call_tape.as_slice()); - mozak_ro_memory.event_tape.fill(args.event_tape.as_slice()); - Ok(program) } - /// Creates a [`Program`] with preinitialized mozak memory given its memory, - /// [`Code`] and [`RuntimeArguments`]. - /// - /// # Panics - /// - /// Panics if any of `ro_mem`, `rw_mem` or `ro_code` violates the memory - /// space that [`MozakMemory`] takes. + /// Creates a [`Program`] with [`Code`]. #[must_use] #[allow(clippy::similar_names)] - pub fn create( - ro_mem: &[(u32, u8)], - rw_mem: &[(u32, u8)], - ro_code: Code, - args: &RuntimeArguments, - ) -> Program { - let ro_memory = Data(ro_mem.iter().copied().collect()); - let rw_memory = Data(rw_mem.iter().copied().collect()); - - // Non-strict behavior is to allow successful creation when arguments parameter - // is empty - if args.is_empty() { - return Program { - ro_memory, - rw_memory, - ro_code, - mozak_ro_memory: None, - ..Default::default() - }; - } - - let mozak_ro_memory = MozakMemory::from(args); - let mem_iters = chain!(ro_mem.iter(), rw_mem.iter()).map(|(addr, _)| addr); - let code_iter = ro_code.iter().map(|(addr, _)| addr); - chain!(mem_iters, code_iter).for_each(|addr| { - assert!( - !mozak_ro_memory.is_address_belongs_to_mozak_ro_memory(*addr), - "address: {addr} belongs to mozak-ro-memory - it is forbidden" - ); - }); + pub fn create(ro_mem: &[(u32, u8)], rw_mem: &[(u32, u8)], ro_code: Code) -> Program { Program { - ro_memory, - rw_memory, + ro_memory: Data(ro_mem.iter().copied().collect()), + rw_memory: Data(rw_mem.iter().copied().collect()), ro_code, - mozak_ro_memory: Some(mozak_ro_memory), ..Default::default() } } @@ -583,33 +259,6 @@ mod test { #[test] fn test_mozak_load_program_default() { - Program::mozak_load_program(mozak_examples::EMPTY_ELF, &RuntimeArguments::default()) - .unwrap(); - } - - #[test] - fn test_mozak_load_program() { - let data = vec![0, 1, 2, 3]; - - let mozak_ro_memory = - Program::mozak_load_program(mozak_examples::EMPTY_ELF, &RuntimeArguments { - self_prog_id: data.clone(), - cast_list: data.clone(), - private_tape: data.clone(), - public_tape: data.clone(), - event_tape: data.clone(), - call_tape: data.clone(), - ..Default::default() - }) - .unwrap() - .mozak_ro_memory - .unwrap(); - - assert_eq!(mozak_ro_memory.self_prog_id.data.len(), data.len()); - assert_eq!(mozak_ro_memory.cast_list.data.len(), data.len()); - assert_eq!(mozak_ro_memory.private_tape.data.len(), data.len()); - assert_eq!(mozak_ro_memory.public_tape.data.len(), data.len()); - assert_eq!(mozak_ro_memory.call_tape.data.len(), data.len()); - assert_eq!(mozak_ro_memory.event_tape.data.len(), data.len()); + Program::mozak_load_program(mozak_examples::EMPTY_ELF).unwrap(); } } diff --git a/runner/src/poseidon2.rs b/runner/src/poseidon2.rs index c6e9423db..c87cfeadf 100644 --- a/runner/src/poseidon2.rs +++ b/runner/src/poseidon2.rs @@ -1,6 +1,7 @@ use std::iter::repeat; use itertools::{chain, izip}; +use mozak_sdk::core::constants::DIGEST_BYTES; use mozak_sdk::core::reg_abi::{REG_A1, REG_A2, REG_A3}; use plonky2::hash::hash_types::{HashOut, RichField, NUM_HASH_OUT_ELTS}; use plonky2::hash::hashing::PlonkyPermutation; @@ -93,7 +94,7 @@ impl State { let (hash, sponge_data) = hash_n_to_m_no_pad::>(input.as_slice()); let hash = hash.to_bytes(); - assert_eq!(32, hash.len()); + assert_eq!(DIGEST_BYTES, hash.len()); let mem_addresses_used: Vec = chain!( (0..input_len).map(|i| input_ptr.wrapping_add(i)), diff --git a/runner/src/state.rs b/runner/src/state.rs index 304ef7b3e..d4362e9bd 100644 --- a/runner/src/state.rs +++ b/runner/src/state.rs @@ -7,17 +7,17 @@ use derive_more::{Deref, Display}; use im::hashmap::HashMap; use im::HashSet; use log::trace; -use mozak_sdk::core::ecall::COMMITMENT_SIZE; +use mozak_sdk::core::constants::DIGEST_BYTES; use plonky2::hash::hash_types::RichField; use serde::{Deserialize, Serialize}; use crate::code::Code; -use crate::elf::{Data, Program, RuntimeArguments}; +use crate::elf::{Data, Program}; use crate::instruction::{Args, DecodingError, Instruction}; use crate::poseidon2; #[derive(Debug, Clone, Deref)] -pub struct CommitmentTape(pub [u8; COMMITMENT_SIZE]); +pub struct CommitmentTape(pub [u8; DIGEST_BYTES]); pub fn read_bytes(buf: &[u8], index: &mut usize, num_bytes: usize) -> Vec { let remaining_len = buf.len() - *index; @@ -69,6 +69,7 @@ pub struct State { pub event_tape: StorageDeviceTape, pub events_commitment_tape: CommitmentTape, pub cast_list_commitment_tape: CommitmentTape, + pub self_prog_id_tape: [u8; DIGEST_BYTES], _phantom: PhantomData, } @@ -139,8 +140,9 @@ impl Default for State { public_tape: StorageDeviceTape::default(), call_tape: StorageDeviceTape::default(), event_tape: StorageDeviceTape::default(), - events_commitment_tape: CommitmentTape([0; COMMITMENT_SIZE]), - cast_list_commitment_tape: CommitmentTape([0; COMMITMENT_SIZE]), + events_commitment_tape: CommitmentTape([0; DIGEST_BYTES]), + cast_list_commitment_tape: CommitmentTape([0; DIGEST_BYTES]), + self_prog_id_tape: [0; 32], _phantom: PhantomData, } } @@ -154,28 +156,13 @@ impl From for State { rw_memory: Data(rw_memory), ro_memory: Data(ro_memory), entry_point: pc, - mozak_ro_memory, }: Program, ) -> Self { - let mut state: State = State::default(); - - if let Some(ref mrm) = mozak_ro_memory { - state.private_tape = StorageDeviceTape::from(mrm.private_tape.data.clone()); - state.public_tape = StorageDeviceTape::from(mrm.public_tape.data.clone()); - state.call_tape = StorageDeviceTape::from(mrm.call_tape.data.clone()); - state.event_tape = StorageDeviceTape::from(mrm.event_tape.data.clone()); - }; + let state: State = State::default(); Self { pc, - memory: StateMemory::new( - [ - ro_memory, - mozak_ro_memory.map(HashMap::from).unwrap_or_default(), - ] - .into_iter(), - [rw_memory].into_iter(), - ), + memory: StateMemory::new(once(ro_memory), once(rw_memory)), ..state } } @@ -198,6 +185,7 @@ pub enum StorageDeviceOpcode { StoreEventTape, StoreEventsCommitmentTape, StoreCastListCommitmentTape, + StoreSelfProgIdTape, } #[derive(Debug, Default, Clone)] @@ -230,25 +218,9 @@ pub struct RawTapes { pub public_tape: Vec, pub call_tape: Vec, pub event_tape: Vec, - pub events_commitment_tape: [u8; COMMITMENT_SIZE], - pub cast_list_commitment_tape: [u8; COMMITMENT_SIZE], -} - -/// Converts pre-init memory compatible [`RuntimeArguments`] into ecall -/// compatible `RawTapes`. -/// -/// TODO(bing): Remove when we no longer rely on preinit memory. -impl From for RawTapes { - fn from(args: RuntimeArguments) -> Self { - Self { - private_tape: args.private_tape, - public_tape: args.public_tape, - call_tape: args.call_tape, - event_tape: args.event_tape, - cast_list_commitment_tape: args.cast_list_commitment_tape, - events_commitment_tape: args.events_commitment_tape, - } - } + pub events_commitment_tape: [u8; DIGEST_BYTES], + pub cast_list_commitment_tape: [u8; DIGEST_BYTES], + pub self_prog_id_tape: [u8; 32], } impl State { @@ -262,21 +234,13 @@ impl State { rw_memory: Data(rw_memory), ro_memory: Data(ro_memory), entry_point: pc, - mozak_ro_memory, .. }: Program, raw_tapes: RawTapes, ) -> Self { Self { pc, - memory: StateMemory::new( - [ - ro_memory, - mozak_ro_memory.map(HashMap::from).unwrap_or_default(), - ] - .into_iter(), - once(rw_memory), - ), + memory: StateMemory::new(once(ro_memory), once(rw_memory)), private_tape: StorageDeviceTape { data: raw_tapes.private_tape.into(), read_index: 0, @@ -295,6 +259,7 @@ impl State { }, cast_list_commitment_tape: CommitmentTape(raw_tapes.cast_list_commitment_tape), events_commitment_tape: CommitmentTape(raw_tapes.events_commitment_tape), + self_prog_id_tape: raw_tapes.self_prog_id_tape, ..Default::default() } } diff --git a/runner/src/test_utils.rs b/runner/src/test_utils.rs index b899f83fd..570074535 100644 --- a/runner/src/test_utils.rs +++ b/runner/src/test_utils.rs @@ -3,8 +3,6 @@ use proptest::prelude::any; use proptest::prop_oneof; use proptest::strategy::{Just, Strategy}; -use crate::elf::MozakMemory; - #[allow(clippy::cast_sign_loss)] pub fn u32_extra() -> impl Strategy { prop_oneof![ @@ -45,9 +43,3 @@ pub fn u16_extra() -> impl Strategy { u32_extra().prop_map(|x| x as pub fn u8_extra() -> impl Strategy { u32_extra().prop_map(|x| x as u8) } pub fn reg() -> impl Strategy { u8_extra().prop_map(|x| 1 + (x % 31)) } - -pub fn u32_extra_except_mozak_ro_memory() -> impl Strategy { - u32_extra().prop_filter("filter out mozak-ro-memory addresses", |addr| { - !MozakMemory::default().is_address_belongs_to_mozak_ro_memory(*addr) - }) -} diff --git a/sdk/Cargo.lock b/sdk/Cargo.lock index 97653077c..9b6115790 100644 --- a/sdk/Cargo.lock +++ b/sdk/Cargo.lock @@ -23,9 +23,9 @@ checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" [[package]] name = "anyhow" -version = "1.0.82" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" +checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3" [[package]] name = "array-init" @@ -38,9 +38,9 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" +checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "bitvec" @@ -106,9 +106,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "getrandom" -version = "0.2.14" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "js-sys", @@ -119,9 +119,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ "ahash", "allocator-api2", @@ -160,9 +160,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.153" +version = "0.2.154" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" [[package]] name = "log" @@ -202,9 +202,9 @@ checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" [[package]] name = "num" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3135b08af27d103b0a51f2ae0f8632117b7b185ccf931445affa8df530576a41" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" dependencies = [ "num-bigint", "num-complex", @@ -216,11 +216,10 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7" dependencies = [ - "autocfg", "num-integer", "num-traits", "rand", @@ -228,9 +227,9 @@ dependencies = [ [[package]] name = "num-complex" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" dependencies = [ "num-traits", "rand", @@ -247,9 +246,9 @@ dependencies = [ [[package]] name = "num-iter" -version = "0.1.44" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d869c01cc0c455284163fd0092f1f93835385ccab5a98a0dcc497b2f8bf055a9" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" dependencies = [ "autocfg", "num-integer", @@ -258,11 +257,10 @@ dependencies = [ [[package]] name = "num-rational" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" dependencies = [ - "autocfg", "num-bigint", "num-integer", "num-traits", @@ -270,9 +268,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] @@ -286,7 +284,7 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "plonky2" version = "0.2.2" -source = "git+https://github.com/0xmozak/plonky2.git#29187d339279da1b6ed3f8508bd1a356e224e60e" +source = "git+https://github.com/0xmozak/plonky2.git#13cb36256626f53134758894897010c1506acd80" dependencies = [ "ahash", "anyhow", @@ -308,7 +306,7 @@ dependencies = [ [[package]] name = "plonky2_field" version = "0.2.2" -source = "git+https://github.com/0xmozak/plonky2.git#29187d339279da1b6ed3f8508bd1a356e224e60e" +source = "git+https://github.com/0xmozak/plonky2.git#13cb36256626f53134758894897010c1506acd80" dependencies = [ "anyhow", "itertools", @@ -323,12 +321,12 @@ dependencies = [ [[package]] name = "plonky2_maybe_rayon" version = "0.2.0" -source = "git+https://github.com/0xmozak/plonky2.git#29187d339279da1b6ed3f8508bd1a356e224e60e" +source = "git+https://github.com/0xmozak/plonky2.git#13cb36256626f53134758894897010c1506acd80" [[package]] name = "plonky2_util" version = "0.2.0" -source = "git+https://github.com/0xmozak/plonky2.git#29187d339279da1b6ed3f8508bd1a356e224e60e" +source = "git+https://github.com/0xmozak/plonky2.git#13cb36256626f53134758894897010c1506acd80" [[package]] name = "ppv-lite86" @@ -338,9 +336,9 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.81" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" +checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" dependencies = [ "unicode-ident", ] @@ -453,15 +451,15 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "serde" -version = "1.0.198" +version = "1.0.201" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc" +checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c" dependencies = [ "serde_derive", ] @@ -479,9 +477,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.198" +version = "1.0.201" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" +checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" dependencies = [ "proc-macro2", "quote", @@ -490,9 +488,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.116" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" dependencies = [ "itoa", "ryu", @@ -522,9 +520,9 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "syn" -version = "2.0.59" +version = "2.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a6531ffc7b071655e4ce2e04bd464c4830bb585a61cabb96cf808f05172615a" +checksum = "c993ed8ccba56ae856363b1845da7266a7cb78e1d146c8a32d54b45a8b831fc9" dependencies = [ "proc-macro2", "quote", @@ -570,7 +568,7 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unroll" version = "0.2.0" -source = "git+https://github.com/0xmozak/unroll.git#1602aa5cd0b9babacb883ae369389444071f0402" +source = "git+https://github.com/0xmozak/unroll.git#ad890f01b927ff5650937f5edaa3a03c582d1369" dependencies = [ "quote", "syn", @@ -653,18 +651,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.32" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.32" +version = "0.7.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml index c45f3f113..640617f28 100644 --- a/sdk/Cargo.toml +++ b/sdk/Cargo.toml @@ -28,8 +28,7 @@ serde-hex = "0.1" serde_json = "1.0" [features] -default = ["std", "preinitmem_inputtape", "stdread"] +default = ["std", "stdread"] -preinitmem_inputtape = [] std = ["plonky2/std"] stdread = [] diff --git a/sdk/src/common/merkle.rs b/sdk/src/common/merkle.rs index 1ed39cc75..8dd83cd34 100644 --- a/sdk/src/common/merkle.rs +++ b/sdk/src/common/merkle.rs @@ -51,19 +51,20 @@ mod tests { use crate::common::merkle::merkleize; use crate::common::types::Poseidon2Hash; + use crate::core::constants::DIGEST_BYTES; use crate::native::helpers::poseidon2_hash_no_pad; #[test] #[rustfmt::skip] fn merkleize_test() { let hashes_with_addr = vec![ - (0x010, Poseidon2Hash([1u8; 32])),// ------------| + (0x010, Poseidon2Hash([1u8; DIGEST_BYTES])),// ------------| // |--h_2---| - (0x011, Poseidon2Hash([2u8; 32])),// ----| | | + (0x011, Poseidon2Hash([2u8; DIGEST_BYTES])),// ----| | | // |-h_1---| |---root - (0x011, Poseidon2Hash([3u8; 32])),// ----| | + (0x011, Poseidon2Hash([3u8; DIGEST_BYTES])),// ----| | // | - (0x111, Poseidon2Hash([4u8; 32])),//--------------------- | + (0x111, Poseidon2Hash([4u8; DIGEST_BYTES])),//--------------------- | ]; let h_1 = poseidon2_hash_no_pad( &chain![ diff --git a/sdk/src/common/system.rs b/sdk/src/common/system.rs index b4866c6ac..89f12a1d4 100644 --- a/sdk/src/common/system.rs +++ b/sdk/src/common/system.rs @@ -1,27 +1,25 @@ -#[cfg(not(target_os = "mozakvm"))] -use core::cell::RefCell; -#[cfg(not(target_os = "mozakvm"))] -use std::rc::Rc; - use once_cell::unsync::Lazy; #[cfg(target_os = "mozakvm")] -use rkyv::rancor::{Panic, Strategy}; -#[cfg(target_os = "mozakvm")] -use rkyv::Deserialize; +use { + crate::common::merkle::merkleize, + crate::common::types::{ + CanonicalOrderedTemporalHints, CrossProgramCall, Poseidon2Hash, ProgramIdentifier, + }, + crate::core::constants::DIGEST_BYTES, + crate::core::ecall::{ + call_tape_read, event_tape_read, ioread_private, ioread_public, self_prog_id_tape_read, + }, + core::ptr::slice_from_raw_parts, + rkyv::rancor::{Panic, Strategy}, + rkyv::Deserialize, + std::collections::BTreeSet, +}; +#[cfg(not(target_os = "mozakvm"))] +use {core::cell::RefCell, std::rc::Rc}; -use super::types::{ +use crate::common::types::{ CallTapeType, EventTapeType, PrivateInputTapeType, PublicInputTapeType, SystemTape, }; -#[cfg(target_os = "mozakvm")] -use crate::common::types::{CanonicalOrderedTemporalHints, CrossProgramCall, ProgramIdentifier}; -#[cfg(target_os = "mozakvm")] -use crate::common::{merkle::merkleize, types::Poseidon2Hash}; -#[cfg(target_os = "mozakvm")] -use crate::mozakvm::helpers::{ - archived_repr, get_rkyv_archived, get_rkyv_deserialized, get_self_prog_id, -}; -#[cfg(target_os = "mozakvm")] -use crate::mozakvm::linker_symbols::{_mozak_call_tape, _mozak_cast_list, _mozak_event_tape}; /// `SYSTEM_TAPE` is a global singleton for interacting with /// all the `IO-Tapes`, `CallTape` and the `EventTape` both in @@ -65,27 +63,95 @@ pub(crate) static mut SYSTEM_TAPE: Lazy = Lazy::new(|| { // pre-populated data elements #[cfg(target_os = "mozakvm")] { - let events = get_rkyv_archived!(Vec, _mozak_event_tape); + let mut self_prog_id_bytes = [0; DIGEST_BYTES]; + self_prog_id_tape_read(self_prog_id_bytes.as_mut_ptr()); + let self_prog_id = ProgramIdentifier(Poseidon2Hash::from(self_prog_id_bytes)); + + let call_tape = populate_call_tape(self_prog_id); + let event_tape = populate_event_tape(self_prog_id); + + let mut size_hint_bytes = [0; 4]; + + ioread_public(size_hint_bytes.as_mut_ptr(), 4); + let size_hint: usize = u32::from_le_bytes(size_hint_bytes).try_into().unwrap(); + let public_input_tape = PublicInputTapeType::with_size_hint(size_hint); + + ioread_private(size_hint_bytes.as_mut_ptr(), 4); + let size_hint: usize = u32::from_le_bytes(size_hint_bytes).try_into().unwrap(); + let private_input_tape = PrivateInputTapeType::with_size_hint(size_hint); SystemTape { - private_input_tape: PrivateInputTapeType::default(), - public_input_tape: PublicInputTapeType::default(), - call_tape: CallTapeType { - self_prog_id: get_self_prog_id(), - cast_list: get_rkyv_deserialized!(Vec, _mozak_cast_list), - reader: Some(get_rkyv_archived!(Vec, _mozak_call_tape)), - index: 0, - }, - event_tape: EventTapeType { - self_prog_id: get_self_prog_id(), - reader: Some(events), - seen: vec![false; events.len()], - index: 0, - }, + private_input_tape, + public_input_tape, + call_tape, + event_tape, } } }); +#[cfg(target_os = "mozakvm")] +/// Populates a `MozakVM` [`CallTapeType`] via ECALLs. +/// +/// At this point, the [`CrossProgramCall`] messages are still rkyv-serialized, +/// and must be deserialized at the point of consumption. Only the `callee`s are +/// deserialized for persistence of the `cast_list`. +fn populate_call_tape(self_prog_id: ProgramIdentifier) -> CallTapeType { + let mut len_bytes = [0; 4]; + call_tape_read(len_bytes.as_mut_ptr(), 4); + let len: usize = u32::from_le_bytes(len_bytes).try_into().unwrap(); + let mut buf = Vec::with_capacity(len); + call_tape_read(buf.as_mut_ptr(), len); + + let archived_cpc_messages = unsafe { + rkyv::access_unchecked::>(&*slice_from_raw_parts(buf.as_ptr(), len)) + }; + + let cast_list: Vec = archived_cpc_messages + .iter() + .map(|m| { + m.callee + .deserialize(Strategy::<_, Panic>::wrap(&mut ())) + .unwrap() + }) + .collect::>() + .into_iter() + .collect(); + + CallTapeType { + cast_list, + self_prog_id, + reader: Some(archived_cpc_messages), + index: 0, + } +} + +#[cfg(target_os = "mozakvm")] +/// Populates a `MozakVM` [`EventTapeType`] via ECALLs. +/// +/// At this point, the vector of [`CanonicalOrderedTemporalHints`] are still +/// rkyv-serialized, and must be deserialized at the point of consumption. +fn populate_event_tape(self_prog_id: ProgramIdentifier) -> EventTapeType { + let mut len_bytes = [0; 4]; + event_tape_read(len_bytes.as_mut_ptr(), 4); + let len: usize = u32::from_le_bytes(len_bytes).try_into().unwrap(); + let mut buf = Vec::with_capacity(len); + event_tape_read(buf.as_mut_ptr(), len); + + let canonical_ordered_temporal_hints = unsafe { + rkyv::access_unchecked::>(&*slice_from_raw_parts( + buf.as_ptr(), + len, + )) + }; + + EventTapeType { + self_prog_id, + reader: Some(canonical_ordered_temporal_hints), + seen: vec![false; canonical_ordered_temporal_hints.len()], + index: 0, + } +} + #[cfg(target_os = "mozakvm")] #[allow(dead_code)] pub fn ensure_clean_shutdown() { @@ -94,10 +160,14 @@ pub fn ensure_clean_shutdown() { use itertools::izip; unsafe { // Should have read the full call tape - assert!(SYSTEM_TAPE.call_tape.index == SYSTEM_TAPE.call_tape.reader.unwrap().len()); + assert!( + SYSTEM_TAPE.call_tape.index == SYSTEM_TAPE.call_tape.reader.as_ref().unwrap().len() + ); // Should have read the full event tape - assert!(SYSTEM_TAPE.event_tape.index == SYSTEM_TAPE.event_tape.reader.unwrap().len()); + assert!( + SYSTEM_TAPE.event_tape.index == SYSTEM_TAPE.event_tape.reader.as_ref().unwrap().len() + ); // Assert that event commitment tape has the same bytes // as Event Tape's actual commitment observable to us @@ -110,7 +180,6 @@ pub fn ensure_clean_shutdown() { .unwrap() .deserialize(Strategy::<_, Panic>::wrap(&mut ())) .unwrap(); - let calculated_commitment_ev = merkleize( canonical_event_temporal_hints .iter() diff --git a/sdk/src/common/types/poseidon2hash.rs b/sdk/src/common/types/poseidon2hash.rs index 84a475937..31b4a00bd 100644 --- a/sdk/src/common/types/poseidon2hash.rs +++ b/sdk/src/common/types/poseidon2hash.rs @@ -1,8 +1,7 @@ #[cfg(not(target_os = "mozakvm"))] use serde_hex::{SerHex, StrictPfx}; -pub const DIGEST_BYTES: usize = 32; -#[allow(dead_code)] -pub const RATE: usize = 8; + +use crate::core::constants::DIGEST_BYTES; #[derive( Clone, diff --git a/sdk/src/common/types/program_identifier.rs b/sdk/src/common/types/program_identifier.rs index a18a86819..f67105ab3 100644 --- a/sdk/src/common/types/program_identifier.rs +++ b/sdk/src/common/types/program_identifier.rs @@ -1,4 +1,4 @@ -use super::poseidon2hash::DIGEST_BYTES; +use crate::core::constants::DIGEST_BYTES; #[cfg(target_os = "mozakvm")] use crate::mozakvm::helpers::poseidon2_hash_with_pad; #[cfg(not(target_os = "mozakvm"))] diff --git a/sdk/src/core/ecall.rs b/sdk/src/core/ecall.rs index 9442e4e9f..681bf7309 100644 --- a/sdk/src/core/ecall.rs +++ b/sdk/src/core/ecall.rs @@ -1,6 +1,9 @@ #[cfg(target_os = "mozakvm")] use core::arch::asm; +#[cfg(target_os = "mozakvm")] +use crate::core::constants::DIGEST_BYTES; + pub const HALT: u32 = 0; pub const PANIC: u32 = 1; pub const PRIVATE_TAPE: u32 = 2; @@ -10,12 +13,9 @@ pub const CALL_TAPE: u32 = 5; pub const EVENT_TAPE: u32 = 6; pub const EVENTS_COMMITMENT_TAPE: u32 = 7; pub const CAST_LIST_COMMITMENT_TAPE: u32 = 8; +pub const SELF_PROG_ID_TAPE: u32 = 9; /// Syscall to output the VM trace log at `clk`. Useful for debugging. -pub const VM_TRACE_LOG: u32 = 9; - -/// The number of bytes requested for events commitment tape and -/// cast list commitment tape is hardcoded to 32 bytes. -pub const COMMITMENT_SIZE: usize = 32; +pub const VM_TRACE_LOG: u32 = 10; #[must_use] pub fn log<'a>(raw_id: u32) -> &'a str { @@ -29,6 +29,7 @@ pub fn log<'a>(raw_id: u32) -> &'a str { EVENT_TAPE => "ioread event tape", EVENTS_COMMITMENT_TAPE => "ioread events commitment tape", CAST_LIST_COMMITMENT_TAPE => "ioread cast list commitment tape", + SELF_PROG_ID_TAPE => "self prog id tape", VM_TRACE_LOG => "vm trace log", _ => "", } @@ -71,7 +72,7 @@ pub fn ioread_public(buf_ptr: *mut u8, buf_len: usize) { } } -#[cfg(all(target_os = "mozakvm", not(feature = "mozak-ro-memory")))] +#[cfg(target_os = "mozakvm")] pub fn call_tape_read(buf_ptr: *mut u8, buf_len: usize) { unsafe { core::arch::asm!( @@ -95,26 +96,38 @@ pub fn event_tape_read(buf_ptr: *mut u8, buf_len: usize) { } } -#[cfg(all(target_os = "mozakvm", not(feature = "mozak-ro-memory")))] +#[cfg(target_os = "mozakvm")] pub fn events_commitment_tape_read(buf_ptr: *mut u8) { unsafe { core::arch::asm!( "ecall", in ("a0") EVENTS_COMMITMENT_TAPE, in ("a1") buf_ptr, - in ("a2") COMMITMENT_SIZE, + in ("a2") DIGEST_BYTES, ); } } -#[cfg(all(target_os = "mozakvm", not(feature = "mozak-ro-memory")))] +#[cfg(target_os = "mozakvm")] pub fn cast_list_commitment_tape_read(buf_ptr: *mut u8) { unsafe { core::arch::asm!( "ecall", in ("a0") CAST_LIST_COMMITMENT_TAPE, in ("a1") buf_ptr, - in ("a2") COMMITMENT_SIZE, + in ("a2") DIGEST_BYTES, + ); + } +} + +#[cfg(target_os = "mozakvm")] +pub fn self_prog_id_tape_read(buf_ptr: *mut u8) { + unsafe { + core::arch::asm!( + "ecall", + in ("a0") SELF_PROG_ID_TAPE, + in ("a1") buf_ptr, + in ("a2") DIGEST_BYTES, ); } } diff --git a/sdk/src/core/mod.rs b/sdk/src/core/mod.rs index f184089b5..cc419024c 100644 --- a/sdk/src/core/mod.rs +++ b/sdk/src/core/mod.rs @@ -4,6 +4,15 @@ pub mod ecall; pub mod env; pub mod reg_abi; +pub mod constants { + /// The size of a `Poseidon2Hash` digest in bytes. + pub const DIGEST_BYTES: usize = 32; + + /// `RATE` of `Poseidon2Permutation` we use + #[allow(dead_code)] + pub const RATE: usize = 8; +} + #[macro_export] macro_rules! entry { ($path:path) => { diff --git a/sdk/src/mozakvm/calltape.rs b/sdk/src/mozakvm/calltape.rs index 5acfc5fb2..e1d175ad0 100644 --- a/sdk/src/mozakvm/calltape.rs +++ b/sdk/src/mozakvm/calltape.rs @@ -39,9 +39,8 @@ impl Call for CallTape { ::Archived: Deserialize>, ::Archived: Deserialize>, { // Ensure we aren't validating past the length of the event tape - assert!(self.index < self.reader.unwrap().len()); + assert!(self.index < self.reader.as_ref().unwrap().len()); - // Deserialize into rust form: CrossProgramCall. let zcd_cpcmsg = &self.reader.unwrap()[self.index]; let cpcmsg: CrossProgramCall = zcd_cpcmsg .deserialize(Strategy::<_, Panic>::wrap(&mut ())) @@ -86,10 +85,10 @@ impl Call for CallTape { // Loop until we completely traverse the call tape in the // worst case. Hopefully, we see a message directed towards us // before the end - while self.index < self.reader.unwrap().len() { + while self.index < self.reader.as_ref().unwrap().len() { // Get the "archived" version of the message, where we will // pick and choose what we will deserialize - let zcd_cpcmsg = &self.reader.unwrap()[self.index]; + let zcd_cpcmsg = &self.reader.as_ref().unwrap()[self.index]; // Mark this as "processed" regardless of what happens next. self.index += 1; diff --git a/sdk/src/mozakvm/eventtape.rs b/sdk/src/mozakvm/eventtape.rs index 59f389602..7a2d79e5d 100644 --- a/sdk/src/mozakvm/eventtape.rs +++ b/sdk/src/mozakvm/eventtape.rs @@ -23,9 +23,8 @@ impl SelfIdentify for EventTape { impl EventEmit for EventTape { fn emit(&mut self, event: Event) { - assert!(self.index < self.reader.unwrap().len()); + assert!(self.index < self.reader.as_ref().unwrap().len()); let generated_canonical_event = CanonicalEvent::from_event(&event); - let elem_idx: usize = self.reader.unwrap()[self.index] .1 .to_native() @@ -34,7 +33,7 @@ impl EventEmit for EventTape { assert!(!self.seen[elem_idx]); self.seen[elem_idx] = true; - let zcd_canonical_event = &self.reader.unwrap()[elem_idx].0; + let zcd_canonical_event = &self.reader.as_ref().unwrap()[elem_idx].0; let canonical_event: CanonicalEvent = zcd_canonical_event .deserialize(Strategy::<_, Panic>::wrap(&mut ())) .unwrap(); diff --git a/sdk/src/mozakvm/helpers.rs b/sdk/src/mozakvm/helpers.rs index ec2931e5e..fd8bd432f 100644 --- a/sdk/src/mozakvm/helpers.rs +++ b/sdk/src/mozakvm/helpers.rs @@ -1,48 +1,7 @@ // This file contains code snippets used in mozakvm execution -use std::ptr::{addr_of, slice_from_raw_parts}; - -use crate::common::types::poseidon2hash::{DIGEST_BYTES, RATE}; -use crate::common::types::{Poseidon2Hash, ProgramIdentifier}; -use crate::mozakvm::linker_symbols::_mozak_self_prog_id; - -/// Get a owned reference to a length-prefixed memory region. -/// It is expected that the memory region is length-prefixed -/// in little-endian 4-bytes and (addr+4) marks the begining -/// of the data. -#[allow(clippy::ptr_as_ptr)] -#[allow(clippy::cast_ptr_alignment)] -#[allow(clippy::ptr_cast_constness)] -pub fn owned_buffer(addr: *const u8) -> Box<[u8]> { - let mem_len = unsafe { *{ addr as *const u32 } } as usize; - unsafe { - let mem_slice = slice_from_raw_parts::(addr.add(4), mem_len); - Box::from_raw(mem_slice as *mut [u8]) - } -} - -/// Zero-copy archived format derivation of any given type (rkyv) -/// on a memory region starting at `addr`. It is expected that -/// the memory region is length-prefixed in little-endian 4-bytes -/// and (addr+4) marks the begining of the archived format. -#[allow(clippy::ptr_as_ptr)] -#[allow(clippy::cast_ptr_alignment)] -pub fn archived_repr(addr: *const u8) -> &'static ::Archived { - let mem_len = unsafe { *{ addr as *const u32 } } as usize; - unsafe { - let mem_slice = &*slice_from_raw_parts::(addr.add(4), mem_len); - rkyv::access_unchecked::(mem_slice) - } -} - -/// Get the Program Identifier of the running program, assumes -/// pre-populated memory region starting `_mozak_self_prog_id`. -#[allow(clippy::ptr_as_ptr)] -pub fn get_self_prog_id() -> ProgramIdentifier { - let self_prog_id = unsafe { *{ addr_of!(_mozak_self_prog_id) as *const ProgramIdentifier } }; - assert!(self_prog_id != ProgramIdentifier::default()); - self_prog_id -} +use crate::common::types::Poseidon2Hash; +use crate::core::constants::{DIGEST_BYTES, RATE}; /// Hashes the input slice to `Poseidon2Hash` after padding. /// We use the well known "Bit padding scheme". @@ -75,43 +34,3 @@ pub fn poseidon2_hash_no_pad(input: &[u8]) -> Poseidon2Hash { crate::core::ecall::poseidon2(input.as_ptr(), input.len(), output.as_mut_ptr()); Poseidon2Hash(output) } - -/// Given a memory start address with 4-byte length prefix -/// for underlying data, get an owned buffer -macro_rules! get_owned_buffer { - ($x:expr) => { - #[allow(clippy::ptr_as_ptr)] - { - owned_buffer(unsafe { core::ptr::addr_of!($x) as *const u8 }) - } - }; -} - -/// Given a type and the memory start address with 4-byte length prefix -/// for underlying data, get an archived (not fully deserialized) object -macro_rules! get_rkyv_archived { - ($t:ty, $x:expr) => { - #[allow(clippy::ptr_as_ptr)] - { - archived_repr::<$t>(unsafe { core::ptr::addr_of!($x) as *const u8 }) - } - }; -} - -/// Given a type and the memory start address with 4-byte lenght prefix -/// for underlying data, get a fully deserialized object -macro_rules! get_rkyv_deserialized { - ($t:ty, $x:expr) => { - #[allow(clippy::ptr_as_ptr)] - { - use rkyv::rancor::{Panic, Strategy}; - let archived_repr = get_rkyv_archived!($t, $x); - let deserialized_repr: $t = archived_repr - .deserialize(Strategy::<(), Panic>::wrap(&mut ())) - .unwrap(); - deserialized_repr - } - }; -} - -pub(crate) use {get_owned_buffer, get_rkyv_archived, get_rkyv_deserialized}; diff --git a/sdk/src/mozakvm/inputtape.rs b/sdk/src/mozakvm/inputtape.rs index c9e6bb2d5..17b979f4d 100644 --- a/sdk/src/mozakvm/inputtape.rs +++ b/sdk/src/mozakvm/inputtape.rs @@ -1,11 +1,19 @@ -use super::helpers::owned_buffer; -use super::linker_symbols::{_mozak_private_tape, _mozak_public_tape}; -use crate::mozakvm::helpers::get_owned_buffer; +use core::ops::{Deref, DerefMut}; + +use crate::core::ecall; #[derive(Default, Clone)] -pub struct RandomAccessPreinitMemTape { - pub tape: Box<[u8]>, +pub struct RandomAccessEcallTape { + pub ecall_id: u32, pub read_offset: usize, + /// This holds the max readable bytes from the tape + /// TODO: Populate this via `SIZE_HINT` ecall + pub size_hint: usize, + /// `internal_buf` contains already read bytes + /// via ecalls but which can be referenced again + /// due to access to `std::io::Seek`. + #[cfg(feature = "stdread")] + pub internal_buf: Vec, } /// Implementing `std::io::Read` allows seekability later as @@ -13,44 +21,78 @@ pub struct RandomAccessPreinitMemTape { /// copies of relevant data asked is returned back to the caller. /// This suffers from spent cpu cycles in `memcpy`. #[cfg(feature = "stdread")] -impl std::io::Read for RandomAccessPreinitMemTape { +impl std::io::Read for RandomAccessEcallTape { fn read(&mut self, buf: &mut [u8]) -> std::io::Result { - let (mut read_bytes, remaining_buf) = (buf.len(), self.tape.len() - self.read_offset); - // In case we don't have enough bytes to read - if read_bytes > remaining_buf { - read_bytes = remaining_buf; + // While we want the whole buffer to be filled, it may + // not be possible due to us reaching the end of tape + // at times. Hence `required_bytes` encodes the desired + // request, while `serviced_bytes` encode the actual + // serviced len. + let required_bytes = buf.len(); + let mut serviced_bytes = required_bytes; + if (self.read_offset + required_bytes) > self.size_hint { + serviced_bytes = self.size_hint - self.read_offset; } - buf[..read_bytes] - .clone_from_slice(&self.tape[self.read_offset..(self.read_offset + read_bytes)]); + // In cases where `Seek` was used, we may be reading from + // `internal_buf` as we may have previously "consumed" those + // bytes already from the `ECALL` mechanism. + let mut populatable_from_internal_buf = self.internal_buf.len() - self.read_offset; + if serviced_bytes < populatable_from_internal_buf { + populatable_from_internal_buf = serviced_bytes; + } + + // These are the actual bytes we get from doing an `ECALL` + let remaining_from_ecall = serviced_bytes - populatable_from_internal_buf; + + // Populate partial buf from `internal_buf` + buf[..populatable_from_internal_buf].clone_from_slice( + &self.internal_buf + [self.read_offset..(self.read_offset + populatable_from_internal_buf)], + ); - self.read_offset += read_bytes; + let old_len = self.internal_buf.len(); + self.internal_buf.resize(old_len + remaining_from_ecall, 0); - Ok(read_bytes) + // TODO: maybe move out this function into `ecall.rs` somehow? + unsafe { + core::arch::asm!( + "ecall", + in ("a0") self.ecall_id, + in ("a1") self.internal_buf.as_mut_ptr().add(old_len), + in ("a2") remaining_from_ecall, + ); + }; + + // Populate partial buf from newly fetched bytes via `ECALL` + buf[populatable_from_internal_buf..].clone_from_slice(&self.internal_buf[old_len..]); + self.read_offset += serviced_bytes; + + Ok(serviced_bytes) } } #[cfg(feature = "stdread")] -impl std::io::Seek for RandomAccessPreinitMemTape { +impl std::io::Seek for RandomAccessEcallTape { fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result { match pos { std::io::SeekFrom::Start(x) => - if x >= self.tape.len().try_into().unwrap() { - self.read_offset = self.tape.len() - 1; + if x >= self.size_hint.try_into().unwrap() { + self.read_offset = self.size_hint - 1; } else { self.read_offset = usize::try_from(x).unwrap(); }, std::io::SeekFrom::End(x) => - if x >= self.tape.len().try_into().unwrap() { + if x >= self.size_hint.try_into().unwrap() { self.read_offset = 0; } else { - self.read_offset = self.tape.len() - usize::try_from(x).unwrap() - 1; + self.read_offset = self.size_hint - usize::try_from(x).unwrap() - 1; }, std::io::SeekFrom::Current(x) => { if x + i64::try_from(self.read_offset).unwrap() - >= self.tape.len().try_into().unwrap() + >= self.size_hint.try_into().unwrap() { - self.read_offset = self.tape.len() - 1; + self.read_offset = self.size_hint - 1; } else { self.read_offset += usize::try_from(x).unwrap(); } @@ -60,135 +102,80 @@ impl std::io::Seek for RandomAccessPreinitMemTape { } } -impl RandomAccessPreinitMemTape { - /// Returns the "safe" readable length of the tape, - /// reading outside which may result in accessing - /// garbage values. `len` reduces after elements are - /// accessed in case the tape is "consuming" i.e. - /// tape is accessed without feature `stdread` enabled. - /// If `stdread` is enabled, `std::io::Seek` is - /// implemented for the tape and length of tape never - /// reduces. - #[allow(dead_code)] - pub fn len(&self) -> usize { self.tape.len() } - - /// Provides the read offset in tape which can be - /// changed via `std::io::Seek` implementation on - /// a tape with feature `stdread`. In case this feature - /// is not enabled, this function always returns `0` - /// and the tape elements when accessed are "consumed". - #[allow(dead_code)] - pub fn read_ptr(&self) -> usize { self.read_offset } -} - -/// Not implementing `std::io::Read` allows for consumption of -/// data slices from the Tape, albeit linearly. This still leaves -/// room for seekability (in principle), but any seek is only -/// allowed on currently owned data elements -/// (a.k.a. ahead from current `read_offset` ). -/// When that happens, slice uptil that point will be thrown away. -#[cfg(not(feature = "stdread"))] -impl RandomAccessPreinitMemTape { - /// consumes the underlying buffer upto a maximum length - /// `max_readlen` and returns an owned slice. - fn read(&mut self, max_readlen: usize) -> Box<[u8]> { - let (mut read_bytes, remaining_buf) = (buf.len(), self.tape.len()); - // In case we don't have enough bytes to read - if read_bytes > remaining_buf { - read_bytes = remaining_buf; - } - self.read_offset += read_bytes; - - let read_ptr = self.tape.as_ptr(); - - self.tape = unsafe { - let mem_slice = slice_from_raw_parts::( - read_ptr.add(read_bytes), - (self.tape.len() - read_bytes), - ); - Box::from_raw(mem_slice as *mut [u8]) - }; - unsafe { - let mem_slice = slice_from_raw_parts::(read_ptr, read_bytes); - Box::from_raw(mem_slice as *mut [u8]) - } - } -} - -#[derive(Default, Clone)] -#[allow(warnings)] -pub struct RandomAccessEcallTape { - pub ecall_id: u32, - pub read_offset: usize, -} - -#[cfg(feature = "preinitmem_inputtape")] -type FreeformTape = RandomAccessPreinitMemTape; -#[cfg(not(feature = "preinitmem_inputtape"))] -type FreeformTape = RandomAccessEcallTape; - #[derive(Clone)] -pub struct PrivateInputTape(FreeformTape); +pub struct PrivateInputTape(RandomAccessEcallTape); #[derive(Clone)] -pub struct PublicInputTape(FreeformTape); +pub struct PublicInputTape(RandomAccessEcallTape); impl Default for PrivateInputTape { fn default() -> Self { - #[cfg(feature = "preinitmem_inputtape")] - { - Self(FreeformTape { - tape: get_owned_buffer!(_mozak_private_tape), - read_offset: 0, - }) - } - #[cfg(not(feature = "preinitmem_inputtape"))] - { - // TODO: Implement this when we want to revert back to - // ecall based systems. Unimplemented for now. - unimplemented!() - } + Self(RandomAccessEcallTape { + ecall_id: ecall::PRIVATE_TAPE, + read_offset: 0, + size_hint: 0, + internal_buf: vec![], + }) + } +} + +impl PrivateInputTape { + /// Creates a new `PrivateInputTape` with a given `size_hint`. + pub fn with_size_hint(size_hint: usize) -> Self { + Self(RandomAccessEcallTape { + ecall_id: ecall::PRIVATE_TAPE, + read_offset: 0, + size_hint, + internal_buf: vec![], + }) } } impl Default for PublicInputTape { fn default() -> Self { - #[cfg(feature = "preinitmem_inputtape")] - { - Self(FreeformTape { - tape: get_owned_buffer!(_mozak_public_tape), - read_offset: 0, - }) - } - #[cfg(not(feature = "preinitmem_inputtape"))] - { - // TODO: Implement this when we want to revert back to - // ecall based systems. Unimplemented for now. - unimplemented!() - } + Self(RandomAccessEcallTape { + ecall_id: ecall::PUBLIC_TAPE, + read_offset: 0, + size_hint: 0, + internal_buf: vec![], + }) } } -#[cfg(feature = "stdread")] -impl std::io::Read for PrivateInputTape { - fn read(&mut self, buf: &mut [u8]) -> std::io::Result { self.0.read(buf) } +impl PublicInputTape { + /// Creates a new `PublicInputTape` with a given `size_hint`. + pub fn with_size_hint(size_hint: usize) -> Self { + Self(RandomAccessEcallTape { + ecall_id: ecall::PUBLIC_TAPE, + read_offset: 0, + size_hint, + internal_buf: vec![], + }) + } } -#[cfg(feature = "stdread")] -impl std::io::Read for PublicInputTape { - fn read(&mut self, buf: &mut [u8]) -> std::io::Result { self.0.read(buf) } +impl Deref for PrivateInputTape { + type Target = RandomAccessEcallTape; + + fn deref(&self) -> &Self::Target { &self.0 } } -impl PrivateInputTape { - pub fn len(&self) -> usize { self.0.len() } +impl DerefMut for PrivateInputTape { + fn deref_mut(&mut self) -> &mut RandomAccessEcallTape { &mut self.0 } +} - pub fn read_ptr(&self) -> usize { self.0.read_ptr() } +impl Deref for PublicInputTape { + type Target = RandomAccessEcallTape; + + fn deref(&self) -> &Self::Target { &self.0 } } -impl PublicInputTape { - pub fn len(&self) -> usize { self.0.len() } +impl DerefMut for PublicInputTape { + fn deref_mut(&mut self) -> &mut RandomAccessEcallTape { &mut self.0 } +} - pub fn read_ptr(&self) -> usize { self.0.read_ptr() } +impl RandomAccessEcallTape { + pub(crate) fn len(&self) -> usize { self.size_hint } } /// Provides the length of tape available to read diff --git a/sdk/src/native/helpers.rs b/sdk/src/native/helpers.rs index 4805324ee..8a4059953 100644 --- a/sdk/src/native/helpers.rs +++ b/sdk/src/native/helpers.rs @@ -6,8 +6,8 @@ use plonky2::field::types::Field; use plonky2::hash::poseidon2::Poseidon2Hash as Plonky2Poseidon2Hash; use plonky2::plonk::config::{GenericHashOut, Hasher}; -use crate::common::types::poseidon2hash::RATE; use crate::common::types::{Poseidon2Hash, ProgramIdentifier}; +use crate::core::constants::RATE; /// Represents a stack for call contexts during native execution. #[derive(Default, Clone, Debug, serde::Serialize, serde::Deserialize)] @@ -22,17 +22,6 @@ impl IdentityStack { pub fn rm_identity(&mut self) { self.0.truncate(self.0.len().saturating_sub(1)); } } -#[derive(Debug, serde::Deserialize, serde::Serialize)] -pub struct GuestProgramTomlCfg { - bin: Vec, -} - -#[derive(Debug, serde::Deserialize, serde::Serialize)] -pub(crate) struct Bin { - name: String, - path: String, -} - /// Manually add a `ProgramIdentifier` onto `IdentityStack`. Useful /// when one want to escape automatic management of `IdentityStack` /// via cross-program-calls sends (ideally temporarily).