diff --git a/emulator/src/state.rs b/emulator/src/state.rs index 9cfda633..b09eea98 100644 --- a/emulator/src/state.rs +++ b/emulator/src/state.rs @@ -702,6 +702,10 @@ impl InstrumentedState { log::trace!("set local user {:X} {:X} {:X}", a0, a1, a2); self.state.local_user = a0; } + 0xF2 => { + log::trace!("sys_verify {:X} {:X} {:X}", a0, a1, a2); + // DO Nothing Here + } _ => {} } diff --git a/emulator/src/utils.rs b/emulator/src/utils.rs index 62115b48..a11ac7f2 100644 --- a/emulator/src/utils.rs +++ b/emulator/src/utils.rs @@ -36,7 +36,7 @@ pub fn split_prog_into_segs( break; } let cycles = instrumented_state.step(); - if cycles >= seg_size as u64 { + if cycles > (seg_size - 1) as u64 { instrumented_state.split_segment(true, seg_path, new_writer); } } diff --git a/prover/examples/README.md b/prover/examples/README.md index b5f2eabf..5e2e1bd1 100644 --- a/prover/examples/README.md +++ b/prover/examples/README.md @@ -88,3 +88,23 @@ Or RUST_LOG=info ELF_PATH=examples/revme/target/mips-unknown-linux-musl/release/evm HOST_PROGRAM=revm JSON_PATH=../emulator/test-vectors/test.json SEG_OUTPUT=/tmp/output SEG_SIZE=262144 cargo run --release --example zkmips prove_host_program ``` + +## Prove precompile code +* Build the sha2-rust +``` +cd prover/examples/sha2-rust +cargo build -r --target=mips-unknown-linux-musl +``` + +* Build the sha2-precompile +``` +cd prover/examples/sha2-precompile +cargo build -r --target=mips-unknown-linux-musl +``` + +* Run the host program +``` +cd ../.. + +RUST_LOG=info PRECOMPILE_PATH=examples/sha2-rust/target/mips-unknown-linux-musl/release/sha2-rust ELF_PATH=examples/sha2-precompile/target/mips-unknown-linux-musl/release/sha2-precompile HOST_PROGRAM=sha2_precompile SEG_OUTPUT=/tmp/output cargo run --release --example zkmips prove_host_program +``` \ No newline at end of file diff --git a/prover/examples/add-go/add.go b/prover/examples/add-go/add.go deleted file mode 100644 index d1f603f8..00000000 --- a/prover/examples/add-go/add.go +++ /dev/null @@ -1,48 +0,0 @@ -package main - -import "github.com/zkMIPS/zkm/go-runtime/zkm_runtime" - -type DataId uint32 - -// use iota to create enum -const ( - TYPE1 DataId = iota - TYPE2 - TYPE3 -) - -type Data struct { - Input1 [10]byte - Input2 uint8 - Input3 int8 - Input4 uint16 - Input5 int16 - Input6 uint32 - Input7 int32 - Input8 uint64 - Input9 int64 - Input10 []byte - Input11 DataId - Input12 string -} - -func main() { - a := zkm_runtime.Read[Data]() - a.Input1[0] = a.Input1[0] + a.Input1[1] - a.Input2 = a.Input2 + a.Input2 - a.Input3 = a.Input3 + a.Input3 - a.Input4 = a.Input4 + a.Input4 - a.Input5 = a.Input5 + a.Input5 - a.Input6 = a.Input6 + a.Input6 - a.Input7 = a.Input7 + a.Input7 - a.Input8 = a.Input8 + a.Input8 - a.Input9 = a.Input9 + a.Input9 - if a.Input11 != TYPE3 { - println("enum type error") - } - if a.Input12 != "hello" { - println("string type error") - } - a.Input10[0] = a.Input10[0] + a.Input10[1] - zkm_runtime.Commit[Data](a) -} diff --git a/prover/examples/add-go/go.mod b/prover/examples/add-go/go.mod deleted file mode 100644 index 4ca1128f..00000000 --- a/prover/examples/add-go/go.mod +++ /dev/null @@ -1,7 +0,0 @@ -module go-add - -go 1.22.5 - -replace github.com/zkMIPS/zkm/go-runtime/zkm_runtime => ../../../go-runtime/zkm_runtime - -require github.com/zkMIPS/zkm/go-runtime/zkm_runtime v0.0.0-00010101000000-000000000000 diff --git a/prover/examples/sha2-precompile/Cargo.lock b/prover/examples/sha2-precompile/Cargo.lock new file mode 100644 index 00000000..9733c342 --- /dev/null +++ b/prover/examples/sha2-precompile/Cargo.lock @@ -0,0 +1,278 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cpufeatures" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ca741a962e1b0bff6d724a1a0958b686406e853bb14061f218562e1896f95e6" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.164" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "433bfe06b8c75da9b2e3fbea6e5329ff87748f0b144ef75306e674c3f6f7c13f" + +[[package]] +name = "libm" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "307e3004becf10f5a6e0d59d20f3cd28231b0e0827a96cd3e0ce6d14bc1e4bb3" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "serde" +version = "1.0.215" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.215" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2-precompile" +version = "0.1.0" +dependencies = [ + "sha2", + "zkm-runtime", +] + +[[package]] +name = "syn" +version = "2.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-ident" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zkm-precompiles" +version = "0.1.0" +dependencies = [ + "bincode", + "cfg-if", + "serde", + "sha2", +] + +[[package]] +name = "zkm-runtime" +version = "0.1.0" +dependencies = [ + "bincode", + "cfg-if", + "getrandom", + "lazy_static", + "libm", + "rand", + "serde", + "sha2", + "zkm-precompiles", +] diff --git a/prover/examples/sha2-precompile/Cargo.toml b/prover/examples/sha2-precompile/Cargo.toml new file mode 100644 index 00000000..b8199ea8 --- /dev/null +++ b/prover/examples/sha2-precompile/Cargo.toml @@ -0,0 +1,10 @@ +[workspace] +[package] +version = "0.1.0" +name = "sha2-precompile" +edition = "2021" + +[dependencies] +# zkm-runtime = { git = "https://github.com/zkMIPS/zkm", package = "zkm-runtime" } +zkm-runtime = { path = "../../../runtime/entrypoint" } +sha2 = { version = "0.10.8", default-features = false } diff --git a/prover/examples/sha2-precompile/rust-toolchain.toml b/prover/examples/sha2-precompile/rust-toolchain.toml new file mode 100644 index 00000000..f0ae574b --- /dev/null +++ b/prover/examples/sha2-precompile/rust-toolchain.toml @@ -0,0 +1,4 @@ +[toolchain] +channel = "nightly-2023-04-06" +targets = ["mips-unknown-linux-musl"] +profile = "minimal" diff --git a/prover/examples/sha2-precompile/src/main.rs b/prover/examples/sha2-precompile/src/main.rs new file mode 100644 index 00000000..275b0585 --- /dev/null +++ b/prover/examples/sha2-precompile/src/main.rs @@ -0,0 +1,25 @@ +#![no_std] +#![no_main] + +use sha2::{Digest, Sha256}; +extern crate alloc; +use alloc::vec::Vec; + +zkm_runtime::entrypoint!(main); +const SHA_ELF:[u8; 32] = [83, 70, 149, 120, 71, 122, 247, 101, 174, 227, 186, 199, 6, 32, 152, 39, 176, 153, 148, 65, 154, 248, 140, 95, 163, 122, 249, 151, 112, 84, 68, 192]; + +pub fn main() { + let public_input: Vec = zkm_runtime::io::read(); + let input: Vec = zkm_runtime::io::read(); + + + zkm_runtime::io::verify(SHA_ELF.to_vec(), &input); + let mut hasher = Sha256::new(); + hasher.update(input); + let result = hasher.finalize(); + + let output: [u8; 32] = result.into(); + assert_eq!(output.to_vec(), public_input); + + zkm_runtime::io::commit::<[u8; 32]>(&output); +} diff --git a/prover/examples/sha2-rust/Cargo.lock b/prover/examples/sha2-rust/Cargo.lock index 6f264411..5e1d7282 100644 --- a/prover/examples/sha2-rust/Cargo.lock +++ b/prover/examples/sha2-rust/Cargo.lock @@ -225,17 +225,16 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "zkm-precompiles" version = "0.1.0" -source = "git+https://github.com/zkMIPS/zkm#ab68657c118b076c430cef3a303a267a9ade88c7" dependencies = [ "bincode", "cfg-if", "serde", + "sha2", ] [[package]] name = "zkm-runtime" version = "0.1.0" -source = "git+https://github.com/zkMIPS/zkm#ab68657c118b076c430cef3a303a267a9ade88c7" dependencies = [ "bincode", "cfg-if", diff --git a/prover/examples/sha2-rust/Cargo.toml b/prover/examples/sha2-rust/Cargo.toml index b5485569..4727671b 100644 --- a/prover/examples/sha2-rust/Cargo.toml +++ b/prover/examples/sha2-rust/Cargo.toml @@ -5,5 +5,6 @@ name = "sha2-rust" edition = "2021" [dependencies] -zkm-runtime = { git = "https://github.com/zkMIPS/zkm", package = "zkm-runtime" } +#zkm-runtime = { git = "https://github.com/zkMIPS/zkm", package = "zkm-runtime" } +zkm-runtime = { path = "../../../runtime/entrypoint" } sha2 = { version = "0.10.8", default-features = false } diff --git a/prover/examples/zkmips.rs b/prover/examples/zkmips.rs index 5149c604..7581a319 100644 --- a/prover/examples/zkmips.rs +++ b/prover/examples/zkmips.rs @@ -20,6 +20,7 @@ use zkm_prover::all_stark::AllStark; use zkm_prover::config::StarkConfig; use zkm_prover::cpu::kernel::assembler::segment_kernel; use zkm_prover::fixed_recursive_verifier::AllRecursiveCircuits; +use zkm_prover::generation::state::{AssumptionReceipt, AssumptionReceipts, Receipt}; use zkm_prover::proof; use zkm_prover::proof::PublicValues; use zkm_prover::prover::prove; @@ -61,14 +62,14 @@ fn split_segments() { let _ = split_prog_into_segs(state, &seg_path, &block_path, seg_size); } +const D: usize = 2; +type C = PoseidonGoldilocksConfig; +type F = >::F; + fn prove_single_seg_common(seg_file: &str, basedir: &str, block: &str, file: &str) { let seg_reader = BufReader::new(File::open(seg_file).unwrap()); let kernel = segment_kernel(basedir, block, file, seg_reader); - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - type F = >::F; - let allstark: AllStark = AllStark::default(); let config = StarkConfig::standard_fast_config(); let mut timing = TimingTree::new("prove", log::Level::Info); @@ -97,10 +98,6 @@ fn prove_multi_seg_common( type InnerParameters = DefaultParameters; type OuterParameters = Groth16WrapperParameters; - type F = GoldilocksField; - const D: usize = 2; - type C = PoseidonGoldilocksConfig; - if seg_file_number < 2 { panic!("seg file number must >= 2\n"); } @@ -292,6 +289,132 @@ fn prove_sha2_rust() { } } +fn u32_array_to_u8_vec(u32_array: &[u32; 8]) -> Vec { + let mut u8_vec = Vec::with_capacity(u32_array.len() * 4); + for &item in u32_array { + u8_vec.extend_from_slice(&item.to_le_bytes()); + } + u8_vec +} + +fn prove_sha_5_precompile( + elf_path: &str, + seg_path: &str, +) -> Receipt<>::F, C, D> { + let mut state = load_elf_with_patch(elf_path, vec![]); + let n: u32 = 5; + let public_input: [u8; 32] = [ + 37, 148, 182, 169, 46, 191, 177, 195, 49, 45, 235, 125, 1, 192, 21, 251, 149, 233, 251, + 233, 189, 123, 198, 181, 39, 175, 7, 129, 62, 199, 185, 16, + ]; + state.add_input_stream(&public_input.to_vec()); + state.add_input_stream(&n.to_le_bytes().to_vec()); + + let (_total_steps, seg_num, mut state) = split_prog_into_segs(state, seg_path, "", 0); + + let value = state.read_public_values::<[u8; 32]>(); + log::info!("public value: {:?}", value); + + assert!(seg_num == 1); + + let all_stark = AllStark::::default(); + let config = StarkConfig::standard_fast_config(); + // Preprocess all circuits. + let all_circuits = + AllRecursiveCircuits::::new(&all_stark, &DEGREE_BITS_RANGE, &config); + + let seg_file: String = format!("{}/{}", seg_path, 0); + log::info!("Process segment {}", seg_file); + let seg_reader = BufReader::new(File::open(seg_file).unwrap()); + let input_first = segment_kernel("", "", "", seg_reader); + let mut timing = TimingTree::new("prove root first", log::Level::Info); + let (agg_proof, updated_agg_public_values) = all_circuits + .prove_root(&all_stark, &input_first, &config, &mut timing) + .unwrap(); + + timing.filter(Duration::from_millis(100)).print(); + all_circuits.verify_root(agg_proof.clone()).unwrap(); + + Receipt:: { + proof: agg_proof, + root_before: u32_array_to_u8_vec(&updated_agg_public_values.roots_before.root), + userdata: updated_agg_public_values.userdata.clone(), + } +} + +fn prove_sha2_precompile() { + // 1. split ELF into segs + let elf_path = env::var("ELF_PATH").expect("ELF file is missing"); + let precompile_path = env::var("PRECOMPILE_PATH").expect("PRECOMPILE ELF file is missing"); + let seg_path = env::var("SEG_OUTPUT").expect("Segment output path is missing"); + let mut receipts: AssumptionReceipts = vec![]; + let receipt = prove_sha_5_precompile(&precompile_path, &seg_path); + + log::info!( + "elf_id: {:?}, data: {:?}", + receipt.root_before, + receipt.userdata + ); + + receipts.push(receipt.into()); + + let mut state = load_elf_with_patch(&elf_path, vec![]); + + let public_input: [u8; 32] = [ + 91, 15, 50, 181, 63, 91, 186, 46, 9, 26, 167, 190, 200, 232, 40, 101, 149, 181, 253, 89, + 24, 150, 142, 102, 14, 67, 78, 221, 18, 205, 95, 28, + ]; + state.add_input_stream(&public_input.to_vec()); + log::info!("expected public value: {:?}", public_input); + + let private_input: [u8; 32] = [ + 37, 148, 182, 169, 46, 191, 177, 195, 49, 45, 235, 125, 1, 192, 21, 251, 149, 233, 251, + 233, 189, 123, 198, 181, 39, 175, 7, 129, 62, 199, 185, 16, + ]; + log::info!("private input value: {:?}", private_input); + state.add_input_stream(&private_input.to_vec()); + + let (_total_steps, _seg_num, mut state) = split_prog_into_segs(state, &seg_path, "", 0); + + let value = state.read_public_values::<[u8; 32]>(); + log::info!("public value: {:X?}", value); + log::info!("public value: {} in hex", hex::encode(value)); + + let all_stark = AllStark::::default(); + let config = StarkConfig::standard_fast_config(); + // Preprocess all circuits. + let all_circuits = + AllRecursiveCircuits::::new(&all_stark, &DEGREE_BITS_RANGE, &config); + + let seg_file: String = format!("{}/{}", seg_path, 0); + log::info!("Process segment {}", seg_file); + let seg_reader = BufReader::new(File::open(seg_file).unwrap()); + let kernel = segment_kernel("", "", "", seg_reader); + + let mut timing = TimingTree::new("prove", log::Level::Info); + let (agg_proof, _updated_agg_public_values, receipts_used) = all_circuits + .prove_root_with_assumption(&all_stark, &kernel, &config, &mut timing, receipts) + .unwrap(); + + log::info!("Process assumptions"); + timing = TimingTree::new("prove aggression", log::Level::Info); + + for assumption in receipts_used.borrow_mut().iter_mut() { + let receipt = assumption.1.clone(); + match receipt { + AssumptionReceipt::Proven(receipt) => { + all_circuits.verify_root(receipt.proof.clone()).unwrap(); + } + AssumptionReceipt::Unresolved(assumpt) => { + log::error!("unresolved assumption: {:X?}", assumpt); + } + } + } + log::info!("verify"); + timing.filter(Duration::from_millis(100)).print(); + all_circuits.verify_root(agg_proof.clone()).unwrap(); +} + fn prove_sha2_go() { // 1. split ELF into segs let elf_path = env::var("ELF_PATH").expect("ELF file is missing"); @@ -444,6 +567,7 @@ fn prove_host() { let host_program = env::var("HOST_PROGRAM").expect("host_program name is missing"); match host_program.as_str() { "sha2_rust" => prove_sha2_rust(), + "sha2_precompile" => prove_sha2_precompile(), "sha2_go" => prove_sha2_go(), "revm" => prove_revm(), "add_example" => prove_add_example(), diff --git a/prover/src/arithmetic/arithmetic_stark.rs b/prover/src/arithmetic/arithmetic_stark.rs index ab312234..2cd6d21e 100644 --- a/prover/src/arithmetic/arithmetic_stark.rs +++ b/prover/src/arithmetic/arithmetic_stark.rs @@ -188,7 +188,8 @@ impl ArithmeticStark { } impl, const D: usize> Stark for ArithmeticStark { - type EvaluationFrame = StarkFrame + type EvaluationFrame + = StarkFrame where FE: FieldExtension, P: PackedField; diff --git a/prover/src/cpu/bootstrap_kernel.rs b/prover/src/cpu/bootstrap_kernel.rs index e072ef06..134008c5 100644 --- a/prover/src/cpu/bootstrap_kernel.rs +++ b/prover/src/cpu/bootstrap_kernel.rs @@ -5,6 +5,7 @@ use plonky2::field::types::Field; use plonky2::hash::hash_types::RichField; use plonky2::iop::ext_target::ExtensionTarget; use plonky2::plonk::circuit_builder::CircuitBuilder; +use plonky2::plonk::config::GenericConfig; use crate::constraint_consumer::{ConstraintConsumer, RecursiveConstraintConsumer}; use crate::cpu::columns::CpuColumnsView; @@ -22,8 +23,12 @@ use zkm_emulator::memory::{ }; use zkm_emulator::page::{PAGE_ADDR_MASK, PAGE_SIZE}; -pub(crate) fn generate_bootstrap_kernel( - state: &mut GenerationState, +pub(crate) fn generate_bootstrap_kernel< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( + state: &mut GenerationState, kernel: &Kernel, ) { // Iterate through chunks of the code, such that we can write one chunk to memory per row. @@ -66,8 +71,12 @@ pub(crate) fn generate_bootstrap_kernel( log::info!("Bootstrapping took {} cycles", state.traces.clock()); } -pub(crate) fn check_image_id( - state: &mut GenerationState, +pub(crate) fn check_image_id< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( + state: &mut GenerationState, kernel: &Kernel, post: bool, ) { @@ -170,8 +179,12 @@ pub(crate) fn check_image_id( state.traces.push_cpu(cpu_row); } -pub(crate) fn check_memory_page_hash( - state: &mut GenerationState, +pub(crate) fn check_memory_page_hash< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( + state: &mut GenerationState, kernel: &Kernel, addr: u32, update: bool, diff --git a/prover/src/cpu/cpu_stark.rs b/prover/src/cpu/cpu_stark.rs index f3a0b894..d6c9585c 100644 --- a/prover/src/cpu/cpu_stark.rs +++ b/prover/src/cpu/cpu_stark.rs @@ -181,7 +181,8 @@ pub struct CpuStark { } impl, const D: usize> Stark for CpuStark { - type EvaluationFrame = StarkFrame + type EvaluationFrame + = StarkFrame where FE: FieldExtension, P: PackedField; diff --git a/prover/src/cpu/exit_kernel.rs b/prover/src/cpu/exit_kernel.rs index 28e2a2d0..c7615eb9 100644 --- a/prover/src/cpu/exit_kernel.rs +++ b/prover/src/cpu/exit_kernel.rs @@ -15,10 +15,18 @@ use crate::memory::segments::Segment; use crate::witness::memory::MemoryAddress; use crate::witness::util::mem_write_gp_log_and_fill; use crate::witness::util::reg_zero_write_with_log; +use plonky2::plonk::config::GenericConfig; use zkm_emulator::page::PAGE_ADDR_MASK; use zkm_emulator::state::REGISTERS_START; -pub(crate) fn generate_exit_kernel(state: &mut GenerationState, kernel: &Kernel) { +pub(crate) fn generate_exit_kernel< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( + state: &mut GenerationState, + kernel: &Kernel, +) { // check exit pc = end pc assert_eq!(kernel.program.end_pc, state.registers.program_counter); let mut cpu_row = CpuColumnsView::default(); diff --git a/prover/src/fixed_recursive_verifier.rs b/prover/src/fixed_recursive_verifier.rs index 276a44a5..45cf7024 100644 --- a/prover/src/fixed_recursive_verifier.rs +++ b/prover/src/fixed_recursive_verifier.rs @@ -33,9 +33,10 @@ use crate::cross_table_lookup::{ get_grand_product_challenge_set_target, verify_cross_table_lookups_circuit, CrossTableLookup, GrandProductChallengeSet, }; +use crate::generation::state::{AssumptionReceipts, AssumptionUsage}; use crate::get_challenges::observe_public_values_target; use crate::proof::{MemRootsTarget, PublicValues, PublicValuesTarget, StarkProofWithMetadata}; -use crate::prover::prove; +use crate::prover::{prove, prove_with_assumptions}; use crate::recursive_verifier::{ add_common_recursion_gates, add_virtual_public_values, recursive_stark_circuit, set_public_value_targets, PlonkWrapperCircuit, PublicInputs, StarkWrapperCircuit, @@ -43,6 +44,7 @@ use crate::recursive_verifier::{ use crate::stark::Stark; use crate::verifier::verify_proof; //use crate::util::h256_limbs; +use std::{cell::RefCell, rc::Rc}; /// The recursion threshold. We end a chain of recursive proofs once we reach this size. const THRESHOLD_DEGREE_BITS: usize = 13; @@ -750,6 +752,69 @@ where Ok((root_proof, all_proof.public_values)) } + pub fn prove_root_with_assumption( + &self, + all_stark: &AllStark, + kernel: &Kernel, + config: &StarkConfig, + timing: &mut TimingTree, + assumptions: AssumptionReceipts, + ) -> anyhow::Result<( + ProofWithPublicInputs, + PublicValues, + Rc>>, + )> { + let (all_proof, receipts) = + prove_with_assumptions::(all_stark, kernel, config, timing, assumptions)?; + verify_proof(all_stark, all_proof.clone(), config).unwrap(); + let mut root_inputs = PartialWitness::new(); + + for table in 0..NUM_TABLES { + let stark_proof = &all_proof.stark_proofs[table]; + let original_degree_bits = stark_proof.proof.recover_degree_bits(config); + let table_circuits = &self.by_table[table]; + let shrunk_proof = table_circuits + .by_stark_size + .get(&original_degree_bits) + .ok_or_else(|| { + anyhow::Error::msg(format!( + "Missing preprocessed circuits for {:?} table with size {}.", + Table::all()[table], + original_degree_bits, + )) + })? + .shrink(stark_proof, &all_proof.ctl_challenges)?; + let index_verifier_data = table_circuits + .by_stark_size + .keys() + .position(|&size| size == original_degree_bits) + .unwrap(); + root_inputs.set_target( + self.root.index_verifier_data[table], + F::from_canonical_usize(index_verifier_data), + ); + root_inputs.set_proof_with_pis_target(&self.root.proof_with_pis[table], &shrunk_proof); + } + + root_inputs.set_verifier_data_target( + &self.root.cyclic_vk, + &self.aggregation.circuit.verifier_only, + ); + + set_public_value_targets( + &mut root_inputs, + &self.root.public_values, + &all_proof.public_values, + ) + .map_err(|_| { + anyhow::Error::msg("Invalid conversion when setting public values targets.") + })?; + + let root_proof = self.root.circuit.prove(root_inputs)?; + + Ok((root_proof, all_proof.public_values, receipts)) + } + pub fn verify_root(&self, agg_proof: ProofWithPublicInputs) -> anyhow::Result<()> { self.root.circuit.verify(agg_proof) } diff --git a/prover/src/generation/mod.rs b/prover/src/generation/mod.rs index 1bb36189..54e3297d 100644 --- a/prover/src/generation/mod.rs +++ b/prover/src/generation/mod.rs @@ -1,10 +1,12 @@ pub(crate) mod outputs; -pub(crate) mod state; +pub mod state; +use crate::generation::state::{AssumptionReceipts, AssumptionUsage}; use crate::proof::{MemRoots, PublicValues}; use anyhow::anyhow; use plonky2::field::extension::Extendable; use plonky2::field::polynomial::PolynomialValues; use plonky2::hash::hash_types::RichField; +use plonky2::plonk::config::GenericConfig; use plonky2::timed; use plonky2::util::timing::TimingTree; @@ -18,7 +20,9 @@ use crate::generation::outputs::{get_outputs, GenerationOutputs}; use crate::generation::state::GenerationState; use crate::witness::transition::transition; -pub fn generate_traces, const D: usize>( +use std::{cell::RefCell, rc::Rc}; + +pub fn generate_traces, C: GenericConfig, const D: usize>( all_stark: &AllStark, kernel: &Kernel, config: &StarkConfig, @@ -31,8 +35,8 @@ pub fn generate_traces, const D: usize>( // Decode the trace record // 1. Decode instruction and fill in cpu columns // 2. Decode memory and fill in memory columns - let mut state = GenerationState::::new(kernel.program.step, kernel).unwrap(); - generate_bootstrap_kernel::(&mut state, kernel); + let mut state = GenerationState::::new(kernel.program.step, kernel).unwrap(); + generate_bootstrap_kernel::(&mut state, kernel); timed!(timing, "simulate CPU", simulate_cpu(&mut state, kernel)?); @@ -71,9 +75,75 @@ pub fn generate_traces, const D: usize>( Ok((tables, public_values, outputs)) } +pub fn generate_traces_with_assumptions< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( + all_stark: &AllStark, + kernel: &Kernel, + config: &StarkConfig, + timing: &mut TimingTree, + assumptions: AssumptionReceipts, +) -> anyhow::Result<( + [Vec>; NUM_TABLES], + PublicValues, + GenerationOutputs, + Rc>>, +)> { + // Decode the trace record + // 1. Decode instruction and fill in cpu columns + // 2. Decode memory and fill in memory columns + let mut state = GenerationState::::new(kernel.program.step, kernel).unwrap(); + for assumption in assumptions.iter() { + state.add_assumption(assumption.clone()); + } + generate_bootstrap_kernel::(&mut state, kernel); + + timed!(timing, "simulate CPU", simulate_cpu(&mut state, kernel)?); + + log::info!( + "Trace lengths (before padding): {:?}", + state.traces.get_lengths() + ); + + let outputs = get_outputs(&mut state) + .map_err(|err| anyhow!("Failed to generate post-state info: {:?}", err))?; + + // Execute the trace record + + // Generate the public values and outputs + // let mut userdata = kernel.read_public_inputs(); + // assert!(userdata.len() <= NUM_PUBLIC_INPUT_USERDATA); + // userdata.resize(NUM_PUBLIC_INPUT_USERDATA, 0u8); + let userdata = kernel.read_public_inputs(); + + assert!(userdata.len() == NUM_PUBLIC_INPUT_USERDATA); + + let public_values = PublicValues { + roots_before: MemRoots { + root: unsafe { std::mem::transmute::<[u8; 32], [u32; 8]>(kernel.program.pre_image_id) }, + }, + roots_after: MemRoots { + root: unsafe { std::mem::transmute::<[u8; 32], [u32; 8]>(kernel.program.image_id) }, + }, + userdata, + }; + let tables = timed!( + timing, + "convert trace data to tables", + state.traces.into_tables(all_stark, config, timing) + ); + Ok((tables, public_values, outputs, state.assumptions_used)) +} + /// Perform MIPS instruction and transit state -pub(crate) fn simulate_cpu, const D: usize>( - state: &mut GenerationState, +pub(crate) fn simulate_cpu< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( + state: &mut GenerationState, kernel: &Kernel, ) -> anyhow::Result<()> { let mut step = 0; diff --git a/prover/src/generation/outputs.rs b/prover/src/generation/outputs.rs index 103b362a..6b851e50 100644 --- a/prover/src/generation/outputs.rs +++ b/prover/src/generation/outputs.rs @@ -1,4 +1,6 @@ -use plonky2::field::types::Field; +use plonky2::field::extension::Extendable; +use plonky2::hash::hash_types::RichField; +use plonky2::plonk::config::GenericConfig; use std::collections::HashMap; use crate::generation::state::GenerationState; @@ -9,8 +11,12 @@ pub struct GenerationOutputs { pub new_state: HashMap, } -pub(crate) fn get_outputs( - _state: &mut GenerationState, +pub(crate) fn get_outputs< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( + _state: &mut GenerationState, ) -> Result { // FIXME Ok(GenerationOutputs { diff --git a/prover/src/generation/state.rs b/prover/src/generation/state.rs index 6a83cdae..93e89f32 100644 --- a/prover/src/generation/state.rs +++ b/prover/src/generation/state.rs @@ -1,19 +1,107 @@ // use keccak_hash::keccak; -use plonky2::field::types::Field; - use crate::cpu::kernel::assembler::Kernel; use crate::witness::errors::ProgramError; use crate::witness::memory::MemoryState; use crate::witness::state::RegistersState; use crate::witness::traces::{TraceCheckpoint, Traces}; +use plonky2::field::extension::Extendable; +use plonky2::hash::hash_types::RichField; +use plonky2::plonk::config::GenericConfig; +use plonky2::plonk::proof::ProofWithPublicInputs; +use sha2::{Digest, Sha256}; +use std::{cell::RefCell, rc::Rc}; + +pub const ZERO: [u8; 32] = [0u8; 32]; + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Receipt, C: GenericConfig, const D: usize> { + pub proof: ProofWithPublicInputs, + pub root_before: Vec, + pub userdata: Vec, +} + +impl Receipt +where + F: RichField + Extendable, + C: GenericConfig, +{ + pub fn claim_digest(&self) -> [u8; 32] { + let mut hasher = Sha256::new(); + + hasher.update(self.root_before.clone()); + hasher.update(self.userdata.clone()); + let digest: [u8; 32] = hasher.finalize().into(); + digest + } +} pub(crate) struct GenerationStateCheckpoint { pub(crate) registers: RegistersState, pub(crate) traces: TraceCheckpoint, } +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +pub struct Assumption { + pub claim: [u8; 32], + pub control_root: [u8; 32], +} + +#[derive(Clone, Debug)] +pub enum AssumptionReceipt, C: GenericConfig, const D: usize> +{ + // A [Receipt] for a proven assumption. + Proven(Box>), + + // An [Assumption] that is not directly proven to be true. + Unresolved(Assumption), +} + +impl AssumptionReceipt +where + F: RichField + Extendable, + C: GenericConfig, +{ + /// Returns the digest of the claim for this [AssumptionReceipt]. + pub fn claim_digest(&self) -> [u8; 32] { + match self { + Self::Proven(receipt) => receipt.claim_digest(), + Self::Unresolved(assumption) => assumption.claim, + } + } +} + +/// Container for assumptions in the executor environment. +pub type AssumptionReceipts = Vec>; +pub type AssumptionUsage = Vec<(Assumption, AssumptionReceipt)>; + +impl From> for AssumptionReceipt +where + F: RichField + Extendable, + C: GenericConfig, +{ + /// Create a proven assumption from a [Receipt]. + fn from(receipt: Receipt) -> Self { + Self::Proven(Box::new(receipt)) + } +} + +impl From for AssumptionReceipt +where + F: RichField + Extendable, + C: GenericConfig, +{ + /// Create an unresolved assumption from an [Assumption]. + fn from(assumption: Assumption) -> Self { + Self::Unresolved(assumption) + } +} + #[derive(Clone)] -pub(crate) struct GenerationState { +pub(crate) struct GenerationState< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +> { pub(crate) registers: RegistersState, pub(crate) memory: MemoryState, pub(crate) input_stream: Vec>, @@ -21,10 +109,16 @@ pub(crate) struct GenerationState { pub(crate) public_values_stream: Vec, pub(crate) public_values_stream_ptr: usize, pub(crate) traces: Traces, + pub(crate) assumptions: Rc>>, + pub(crate) assumptions_used: Rc>>, pub(crate) step: usize, } -impl GenerationState { +impl GenerationState +where + F: RichField + Extendable, + C: GenericConfig, +{ pub(crate) fn new(step: usize, kernel: &Kernel) -> Result { Ok(GenerationState { registers: RegistersState::new(kernel), @@ -34,10 +128,50 @@ impl GenerationState { input_stream_ptr: kernel.program.input_stream_ptr, public_values_stream: kernel.program.public_values_stream.clone(), public_values_stream_ptr: kernel.program.public_values_stream_ptr, + assumptions: Rc::new(RefCell::new(Vec::new())), + assumptions_used: Rc::new(RefCell::new(Vec::new())), step, }) } + pub fn add_assumption( + &mut self, + assumption: impl Into>, + ) -> &mut Self { + let receipt: AssumptionReceipt = assumption.into(); + log::info!("add assumption {:?}", receipt.claim_digest()); + self.assumptions.borrow_mut().push(receipt); + self + } + + pub(crate) fn find_assumption( + &self, + claim_digest: &[u8; 32], + control_root: &[u8; 32], + ) -> Option<(Assumption, AssumptionReceipt)> { + for assumption_receipt in self.assumptions.borrow().iter() { + let cached_claim_digest = assumption_receipt.claim_digest(); + + if cached_claim_digest != *claim_digest { + log::debug!( + "receipt with claim {:?} does not match", + cached_claim_digest + ); + continue; + } + + return Some(( + Assumption { + claim: *claim_digest, + control_root: *control_root, + }, + assumption_receipt.clone(), + )); + } + + None + } + pub fn checkpoint(&self) -> GenerationStateCheckpoint { GenerationStateCheckpoint { registers: self.registers, diff --git a/prover/src/keccak/keccak_stark.rs b/prover/src/keccak/keccak_stark.rs index c86a863a..8d7a26da 100644 --- a/prover/src/keccak/keccak_stark.rs +++ b/prover/src/keccak/keccak_stark.rs @@ -249,7 +249,8 @@ impl, const D: usize> KeccakStark { } impl, const D: usize> Stark for KeccakStark { - type EvaluationFrame = StarkFrame + type EvaluationFrame + = StarkFrame where FE: FieldExtension, P: PackedField; diff --git a/prover/src/keccak_sponge/keccak_sponge_stark.rs b/prover/src/keccak_sponge/keccak_sponge_stark.rs index 5d828156..07012e22 100644 --- a/prover/src/keccak_sponge/keccak_sponge_stark.rs +++ b/prover/src/keccak_sponge/keccak_sponge_stark.rs @@ -458,7 +458,8 @@ impl, const D: usize> KeccakSpongeStark { } impl, const D: usize> Stark for KeccakSpongeStark { - type EvaluationFrame = StarkFrame + type EvaluationFrame + = StarkFrame where FE: FieldExtension, P: PackedField; diff --git a/prover/src/logic.rs b/prover/src/logic.rs index 89450662..a8108ce5 100644 --- a/prover/src/logic.rs +++ b/prover/src/logic.rs @@ -187,7 +187,8 @@ impl LogicStark { } impl, const D: usize> Stark for LogicStark { - type EvaluationFrame = StarkFrame + type EvaluationFrame + = StarkFrame where FE: FieldExtension, P: PackedField; diff --git a/prover/src/memory/memory_stark.rs b/prover/src/memory/memory_stark.rs index dc2d62c6..32e5903f 100644 --- a/prover/src/memory/memory_stark.rs +++ b/prover/src/memory/memory_stark.rs @@ -252,7 +252,8 @@ impl, const D: usize> MemoryStark { } impl, const D: usize> Stark for MemoryStark { - type EvaluationFrame = StarkFrame + type EvaluationFrame + = StarkFrame where FE: FieldExtension, P: PackedField; diff --git a/prover/src/poseidon/poseidon_stark.rs b/prover/src/poseidon/poseidon_stark.rs index 23cdb7a8..078f7d26 100644 --- a/prover/src/poseidon/poseidon_stark.rs +++ b/prover/src/poseidon/poseidon_stark.rs @@ -606,10 +606,11 @@ fn eval_packed_generic(lv: &[P], yield_constr: &mut ConstraintCo } impl, const D: usize> Stark for PoseidonStark { - type EvaluationFrame = StarkFrame - where - FE: FieldExtension, - P: PackedField; + type EvaluationFrame + = StarkFrame + where + FE: FieldExtension, + P: PackedField; type EvaluationFrameTarget = StarkFrame, NUM_COLUMNS>; diff --git a/prover/src/poseidon_sponge/poseidon_sponge_stark.rs b/prover/src/poseidon_sponge/poseidon_sponge_stark.rs index ba7d7567..9ec6665c 100644 --- a/prover/src/poseidon_sponge/poseidon_sponge_stark.rs +++ b/prover/src/poseidon_sponge/poseidon_sponge_stark.rs @@ -377,10 +377,11 @@ impl, const D: usize> PoseidonSpongeStark { } impl, const D: usize> Stark for PoseidonSpongeStark { - type EvaluationFrame = StarkFrame - where - FE: FieldExtension, - P: PackedField; + type EvaluationFrame + = StarkFrame + where + FE: FieldExtension, + P: PackedField; type EvaluationFrameTarget = StarkFrame, NUM_POSEIDON_SPONGE_COLUMNS>; diff --git a/prover/src/prover.rs b/prover/src/prover.rs index e6ca41af..837a3f85 100644 --- a/prover/src/prover.rs +++ b/prover/src/prover.rs @@ -27,13 +27,15 @@ use crate::cross_table_lookup::{ GrandProductChallengeSet, }; use crate::evaluation_frame::StarkEvaluationFrame; -use crate::generation::generate_traces; use crate::generation::outputs::GenerationOutputs; +use crate::generation::state::{AssumptionReceipts, AssumptionUsage}; +use crate::generation::{generate_traces, generate_traces_with_assumptions}; use crate::get_challenges::observe_public_values; use crate::lookup::{lookup_helper_columns, Lookup, LookupCheckVars}; use crate::proof::{AllProof, PublicValues, StarkOpeningSet, StarkProof, StarkProofWithMetadata}; use crate::stark::Stark; use crate::vanishing_poly::eval_vanishing_poly; +use std::{cell::RefCell, rc::Rc}; #[cfg(any(feature = "test", test))] use crate::cross_table_lookup::testutils::check_ctls; @@ -53,6 +55,22 @@ where Ok(proof) } +pub fn prove_with_assumptions( + all_stark: &AllStark, + kernel: &Kernel, + config: &StarkConfig, + timing: &mut TimingTree, + assumptions: AssumptionReceipts, +) -> Result<(AllProof, Rc>>)> +where + F: RichField + Extendable, + C: GenericConfig, +{ + let (proof, _outputs, receipts) = + prove_with_output_and_assumptions(all_stark, kernel, config, timing, assumptions)?; + Ok((proof, receipts)) +} + /// Generate traces, then create all STARK proofs. Returns information about the post-state, /// intended for debugging, in addition to the proof. pub fn prove_with_outputs( @@ -68,13 +86,46 @@ where let (traces, public_values, outputs) = timed!( timing, "generate all traces", - generate_traces(all_stark, kernel, config, timing)? + generate_traces::(all_stark, kernel, config, timing)? ); let proof = prove_with_traces(all_stark, config, traces, public_values, timing)?; Ok((proof, outputs)) } +/// Generate traces, then create all STARK proofs. Returns information about the post-state, +/// intended for debugging, in addition to the proof. +pub fn prove_with_output_and_assumptions( + all_stark: &AllStark, + kernel: &Kernel, + config: &StarkConfig, + timing: &mut TimingTree, + assumptions: AssumptionReceipts, +) -> Result<( + AllProof, + GenerationOutputs, + Rc>>, +)> +where + F: RichField + Extendable, + C: GenericConfig, +{ + let (traces, public_values, outputs, receipts) = timed!( + timing, + "generate all traces", + generate_traces_with_assumptions::( + all_stark, + kernel, + config, + timing, + assumptions + )? + ); + + let proof = prove_with_traces(all_stark, config, traces, public_values, timing)?; + Ok((proof, outputs, receipts)) +} + /// Compute all STARK proofs. pub(crate) fn prove_with_traces( all_stark: &AllStark, diff --git a/prover/src/witness/operation.rs b/prover/src/witness/operation.rs index 37047aa0..c33e4f15 100644 --- a/prover/src/witness/operation.rs +++ b/prover/src/witness/operation.rs @@ -6,13 +6,16 @@ use crate::memory::segments::Segment; use crate::witness::errors::ProgramError; use crate::witness::memory::MemoryAddress; use crate::{arithmetic, logic}; + use anyhow::{Context, Result}; use plonky2::field::types::Field; use crate::poseidon_sponge::columns::POSEIDON_RATE_BYTES; use itertools::Itertools; +use plonky2::field::extension::Extendable; use plonky2::hash::hash_types::RichField; +use plonky2::plonk::config::GenericConfig; use std::fs; pub const WORD_SIZE: usize = core::mem::size_of::(); @@ -78,6 +81,7 @@ pub(crate) const SYSSETTHREADAREA: usize = 4283; pub(crate) const SYSHINTLEN: usize = 240; pub(crate) const SYSHINTREAD: usize = 241; +pub(crate) const SYSVERIFY: usize = 242; pub(crate) const FD_STDIN: usize = 0; pub(crate) const FD_STDOUT: usize = 1; @@ -136,12 +140,16 @@ pub(crate) enum Operation { Teq(u8, u8), } -pub(crate) fn generate_cond_mov_op( +pub(crate) fn generate_cond_mov_op< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( cond: MovCond, rs: u8, rt: u8, rd: u8, - state: &mut GenerationState, + state: &mut GenerationState, mut row: CpuColumnsView, ) -> Result<(), ProgramError> { let (in0, log_in0) = reg_read_with_log(rs, 0, state, &mut row)?; @@ -169,11 +177,15 @@ pub(crate) fn generate_cond_mov_op( Ok(()) } -pub(crate) fn generate_count_op( +pub(crate) fn generate_count_op< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( is_clo: bool, rs: u8, rd: u8, - state: &mut GenerationState, + state: &mut GenerationState, mut row: CpuColumnsView, ) -> Result<(), ProgramError> { let (in0, log_in0) = reg_read_with_log(rs, 0, state, &mut row)?; @@ -212,12 +224,16 @@ pub(crate) fn generate_count_op( Ok(()) } -pub(crate) fn generate_binary_logic_op( +pub(crate) fn generate_binary_logic_op< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( op: logic::Op, rs: u8, rt: u8, rd: u8, - state: &mut GenerationState, + state: &mut GenerationState, mut row: CpuColumnsView, ) -> Result<(), ProgramError> { let (in0, log_in0) = reg_read_with_log(rs, 0, state, &mut row)?; @@ -235,12 +251,16 @@ pub(crate) fn generate_binary_logic_op( Ok(()) } -pub(crate) fn generate_binary_logic_imm_op( +pub(crate) fn generate_binary_logic_imm_op< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( op: logic::Op, rs: u8, rd: u8, imm: u32, - state: &mut GenerationState, + state: &mut GenerationState, mut row: CpuColumnsView, ) -> Result<(), ProgramError> { let (in0, log_in0) = reg_read_with_log(rs, 0, state, &mut row)?; @@ -257,12 +277,16 @@ pub(crate) fn generate_binary_logic_imm_op( Ok(()) } -pub(crate) fn generate_binary_arithmetic_op( +pub(crate) fn generate_binary_arithmetic_op< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( operator: arithmetic::BinaryOperator, rs: u8, rt: u8, rd: u8, - state: &mut GenerationState, + state: &mut GenerationState, mut row: CpuColumnsView, ) -> Result<(), ProgramError> { assert!(![ @@ -287,12 +311,16 @@ pub(crate) fn generate_binary_arithmetic_op( Ok(()) } -pub(crate) fn generate_binary_arithmetic_hilo_op( +pub(crate) fn generate_binary_arithmetic_hilo_op< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( operator: arithmetic::BinaryOperator, rs: u8, rt: u8, _rd: u8, - state: &mut GenerationState, + state: &mut GenerationState, mut row: CpuColumnsView, ) -> Result<(), ProgramError> { assert!([ @@ -340,12 +368,16 @@ pub(crate) fn generate_binary_arithmetic_hilo_op( Ok(()) } -pub(crate) fn generate_binary_arithmetic_imm_op( +pub(crate) fn generate_binary_arithmetic_imm_op< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( rs: u8, rt: u8, imm: u32, operator: arithmetic::BinaryOperator, - state: &mut GenerationState, + state: &mut GenerationState, mut row: CpuColumnsView, ) -> Result<(), ProgramError> { let (in0, log_in0) = reg_read_with_log(rs, 0, state, &mut row)?; @@ -364,11 +396,15 @@ pub(crate) fn generate_binary_arithmetic_imm_op( Ok(()) } -pub(crate) fn generate_lui( +pub(crate) fn generate_lui< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( _rs: u8, rt: u8, imm: u32, - state: &mut GenerationState, + state: &mut GenerationState, mut row: CpuColumnsView, ) -> Result<(), ProgramError> { let in0 = sign_extend::<16>(imm); @@ -390,8 +426,12 @@ pub(crate) fn generate_lui( Ok(()) } -pub(crate) fn generate_keccak_general( - _state: &mut GenerationState, +pub(crate) fn generate_keccak_general< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( + _state: &mut GenerationState, _row: CpuColumnsView, ) -> Result<(), ProgramError> { //row.is_keccak_sponge = F::ONE; @@ -432,10 +472,14 @@ pub(crate) fn generate_keccak_general( Ok(()) } -pub(crate) fn generate_jump( +pub(crate) fn generate_jump< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( link: u8, target: u8, - state: &mut GenerationState, + state: &mut GenerationState, mut row: CpuColumnsView, ) -> Result<(), ProgramError> { let (target_pc, target_op) = reg_read_with_log(target, 0, state, &mut row)?; @@ -448,12 +492,16 @@ pub(crate) fn generate_jump( Ok(()) } -pub(crate) fn generate_branch( +pub(crate) fn generate_branch< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( cond: BranchCond, src1: u8, src2: u8, target: u32, - state: &mut GenerationState, + state: &mut GenerationState, mut row: CpuColumnsView, ) -> Result<(), ProgramError> { let (src1, src1_op) = reg_read_with_log(src1, 0, state, &mut row)?; @@ -513,10 +561,14 @@ pub(crate) fn generate_branch( Ok(()) } -pub(crate) fn generate_jumpi( +pub(crate) fn generate_jumpi< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( link: u8, target: u32, - state: &mut GenerationState, + state: &mut GenerationState, mut row: CpuColumnsView, ) -> Result<(), ProgramError> { let (mut target_pc, _) = (target as usize).overflowing_shl(2); @@ -537,10 +589,14 @@ pub(crate) fn generate_jumpi( Ok(()) } -pub(crate) fn generate_jumpdirect( +pub(crate) fn generate_jumpdirect< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( link: u8, target: u32, - state: &mut GenerationState, + state: &mut GenerationState, mut row: CpuColumnsView, ) -> Result<(), ProgramError> { let target = sign_extend::<16>(target); @@ -559,16 +615,24 @@ pub(crate) fn generate_jumpdirect( Ok(()) } -pub(crate) fn generate_pc( - state: &mut GenerationState, +pub(crate) fn generate_pc< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( + state: &mut GenerationState, row: CpuColumnsView, ) -> Result<(), ProgramError> { state.traces.push_cpu(row); Ok(()) } -pub(crate) fn generate_get_context( - _state: &mut GenerationState, +pub(crate) fn generate_get_context< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( + _state: &mut GenerationState, _row: CpuColumnsView, ) -> Result<(), ProgramError> { /* @@ -578,8 +642,12 @@ pub(crate) fn generate_get_context( Ok(()) } -pub(crate) fn generate_set_context( - _state: &mut GenerationState, +pub(crate) fn generate_set_context< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( + _state: &mut GenerationState, _row: CpuColumnsView, ) -> Result<(), ProgramError> { /* @@ -654,12 +722,16 @@ pub(crate) fn generate_set_context( Ok(()) } -pub(crate) fn generate_shift_imm( +pub(crate) fn generate_shift_imm< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( op: arithmetic::BinaryOperator, sa: u8, rt: u8, rd: u8, - state: &mut GenerationState, + state: &mut GenerationState, mut row: CpuColumnsView, ) -> Result<(), ProgramError> { assert!([ @@ -690,11 +762,15 @@ pub(crate) fn generate_shift_imm( Ok(()) } -pub(crate) fn generate_sllv( +pub(crate) fn generate_sllv< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( rs: u8, rt: u8, rd: u8, - state: &mut GenerationState, + state: &mut GenerationState, mut row: CpuColumnsView, ) -> Result<(), ProgramError> { let (input0, log_in0) = reg_read_with_log(rs, 0, state, &mut row)?; @@ -720,11 +796,15 @@ pub(crate) fn generate_sllv( Ok(()) } -pub(crate) fn generate_srlv( +pub(crate) fn generate_srlv< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( rs: u8, rt: u8, rd: u8, - state: &mut GenerationState, + state: &mut GenerationState, mut row: CpuColumnsView, ) -> Result<(), ProgramError> { let (input0, log_in0) = reg_read_with_log(rs, 0, state, &mut row)?; @@ -750,11 +830,15 @@ pub(crate) fn generate_srlv( Ok(()) } -pub(crate) fn generate_srav( +pub(crate) fn generate_srav< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( rs: u8, rt: u8, rd: u8, - state: &mut GenerationState, + state: &mut GenerationState, mut row: CpuColumnsView, ) -> Result<(), ProgramError> { let (input0, log_in0) = reg_read_with_log(rs, 0, state, &mut row)?; @@ -781,11 +865,15 @@ pub(crate) fn generate_srav( Ok(()) } -pub(crate) fn generate_ror( +pub(crate) fn generate_ror< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( rd: u8, rt: u8, sa: u8, - state: &mut GenerationState, + state: &mut GenerationState, mut row: CpuColumnsView, ) -> Result<(), ProgramError> { let (input0, log_in0) = reg_read_with_log(rt, 0, state, &mut row)?; @@ -811,8 +899,12 @@ pub(crate) fn generate_ror( Ok(()) } -pub(crate) fn load_preimage( - state: &mut GenerationState, +pub(crate) fn load_preimage< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( + state: &mut GenerationState, kernel: &Kernel, ) -> Result<()> { let mut hash_bytes = [0u8; 32]; @@ -890,8 +982,58 @@ pub(crate) fn load_preimage( Ok(()) } -pub(crate) fn load_input( - state: &mut GenerationState, +pub(crate) fn verify, C: GenericConfig, const D: usize>( + state: &mut GenerationState, + addr: usize, + size: usize, +) -> Result<()> { + assert!(size == 64); + let mut claim_digest = [0u8; 32]; + { + let mut cpu_row = CpuColumnsView::default(); + cpu_row.clock = F::from_canonical_usize(state.traces.clock()); + for i in 0..8 { + let address = MemoryAddress::new(0, Segment::Code, addr + i * 4); + let (mem, op) = mem_read_gp_with_log_and_fill(i, address, state, &mut cpu_row); + claim_digest[i * 4..i * 4 + 4].copy_from_slice(mem.to_be_bytes().as_ref()); + state.traces.push_memory(op); + } + state.traces.push_cpu(cpu_row); + } + + let mut control_root = [0u8; 32]; + { + let mut cpu_row = CpuColumnsView::default(); + cpu_row.clock = F::from_canonical_usize(state.traces.clock()); + for i in 0..8 { + let address = MemoryAddress::new(0, Segment::Code, addr + 32 + i * 4); + let (mem, op) = mem_read_gp_with_log_and_fill(i, address, state, &mut cpu_row); + control_root[i * 4..i * 4 + 4].copy_from_slice(mem.to_be_bytes().as_ref()); + state.traces.push_memory(op); + } + state.traces.push_cpu(cpu_row); + } + + log::debug!("SYS_VERIFY: ({:?}, {:?})", claim_digest, control_root); + + let assumption = state.find_assumption(&claim_digest, &control_root); + + // Mark the assumption as accessed, pushing it to the head of the list, and return the success code. + match assumption { + Some(assumpt) => { + state.assumptions_used.borrow_mut().insert(0, assumpt); + } + None => panic!("Assumption Not Found"), + } + Ok(()) +} + +pub(crate) fn load_input< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( + state: &mut GenerationState, addr: usize, size: usize, ) -> Result<()> { @@ -931,8 +1073,12 @@ pub(crate) fn load_input( Ok(()) } -pub(crate) fn generate_syscall( - state: &mut GenerationState, +pub(crate) fn generate_syscall< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( + state: &mut GenerationState, mut row: CpuColumnsView, kernel: &Kernel, ) -> Result<(), ProgramError> { @@ -944,6 +1090,7 @@ pub(crate) fn generate_syscall( let mut v1 = 0usize; let mut is_load_preimage = false; let mut is_load_input = false; + let mut is_verify = false; let result = match sys_num { SYSGETPID => { row.general.syscall_mut().sysnum[0] = F::ONE; @@ -1086,6 +1233,10 @@ pub(crate) fn generate_syscall( is_load_input = true; Ok(()) } + SYSVERIFY => { + is_verify = true; + Ok(()) + } _ => { row.general.syscall_mut().sysnum[11] = F::ONE; Ok(()) @@ -1107,15 +1258,23 @@ pub(crate) fn generate_syscall( if is_load_input { let _ = load_input(state, a0, a1); } + + if is_verify { + let _ = verify(state, a1, a2); + } result } -pub(crate) fn generate_mload_general( +pub(crate) fn generate_mload_general< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( op: MemOp, base: u8, rt_reg: u8, offset: u32, - state: &mut GenerationState, + state: &mut GenerationState, mut row: CpuColumnsView, ) -> Result<(), ProgramError> { let (rs, log_in1) = reg_read_with_log(base, 0, state, &mut row)?; @@ -1224,12 +1383,16 @@ pub(crate) fn generate_mload_general( Ok(()) } -pub(crate) fn generate_mstore_general( +pub(crate) fn generate_mstore_general< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( op: MemOp, base: u8, rt_reg: u8, offset: u32, - state: &mut GenerationState, + state: &mut GenerationState, mut row: CpuColumnsView, ) -> Result<(), ProgramError> { let (rs, log_in1) = reg_read_with_log(base, 0, state, &mut row)?; @@ -1346,8 +1509,12 @@ pub(crate) fn generate_mstore_general( Ok(()) } -pub(crate) fn generate_nop( - state: &mut GenerationState, +pub(crate) fn generate_nop< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( + state: &mut GenerationState, row: CpuColumnsView, ) -> Result<(), ProgramError> { state.traces.push_cpu(row); @@ -1355,12 +1522,16 @@ pub(crate) fn generate_nop( Ok(()) } -pub(crate) fn generate_extract( +pub(crate) fn generate_extract< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( rt: u8, rs: u8, msbd: u8, lsb: u8, - state: &mut GenerationState, + state: &mut GenerationState, mut row: CpuColumnsView, ) -> Result<(), ProgramError> { assert!(msbd + lsb < 32); @@ -1394,12 +1565,16 @@ pub(crate) fn generate_extract( Ok(()) } -pub(crate) fn generate_insert( +pub(crate) fn generate_insert< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( rt: u8, rs: u8, msb: u8, lsb: u8, - state: &mut GenerationState, + state: &mut GenerationState, mut row: CpuColumnsView, ) -> Result<(), ProgramError> { assert!(msb < 32); @@ -1438,10 +1613,14 @@ pub(crate) fn generate_insert( Ok(()) } -pub(crate) fn generate_maddu( +pub(crate) fn generate_maddu< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( rt: u8, rs: u8, - state: &mut GenerationState, + state: &mut GenerationState, mut row: CpuColumnsView, ) -> Result<(), ProgramError> { let (in0, log_in0) = reg_read_with_log(rs, 0, state, &mut row)?; @@ -1463,10 +1642,14 @@ pub(crate) fn generate_maddu( state.traces.push_cpu(row); Ok(()) } -pub(crate) fn generate_rdhwr( +pub(crate) fn generate_rdhwr< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( rt: u8, rd: u8, - state: &mut GenerationState, + state: &mut GenerationState, mut row: CpuColumnsView, ) -> Result<(), ProgramError> { row.general.misc_mut().rd_index = F::from_canonical_u8(rd); @@ -1490,11 +1673,15 @@ pub(crate) fn generate_rdhwr( Ok(()) } -pub(crate) fn generate_signext( +pub(crate) fn generate_signext< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( rd: u8, rt: u8, bits: u8, - state: &mut GenerationState, + state: &mut GenerationState, mut row: CpuColumnsView, ) -> Result<(), ProgramError> { let (in0, log_in0) = reg_read_with_log(rt, 0, state, &mut row)?; @@ -1526,10 +1713,14 @@ pub(crate) fn generate_signext( Ok(()) } -pub(crate) fn generate_swaphalf( +pub(crate) fn generate_swaphalf< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( rd: u8, rt: u8, - state: &mut GenerationState, + state: &mut GenerationState, mut row: CpuColumnsView, ) -> Result<(), ProgramError> { let (in0, log_in0) = reg_read_with_log(rt, 0, state, &mut row)?; @@ -1556,10 +1747,14 @@ pub(crate) fn generate_swaphalf( Ok(()) } -pub(crate) fn generate_teq( +pub(crate) fn generate_teq< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( rs: u8, rt: u8, - state: &mut GenerationState, + state: &mut GenerationState, mut row: CpuColumnsView, ) -> Result<(), ProgramError> { let (in0, log_in0) = reg_read_with_log(rs, 0, state, &mut row)?; diff --git a/prover/src/witness/transition.rs b/prover/src/witness/transition.rs index b1710ab4..741389fa 100644 --- a/prover/src/witness/transition.rs +++ b/prover/src/witness/transition.rs @@ -1,7 +1,9 @@ use anyhow::bail; use log::log_enabled; +use plonky2::field::extension::Extendable; use plonky2::field::types::Field; use plonky2::hash::hash_types::RichField; +use plonky2::plonk::config::GenericConfig; use crate::cpu::columns::CpuColumnsView; @@ -16,7 +18,10 @@ use crate::witness::state::RegistersState; use crate::witness::util::mem_read_code_with_log_and_fill; use crate::{arithmetic, logic}; -fn read_code_memory(state: &mut GenerationState, row: &mut CpuColumnsView) -> u32 { +fn read_code_memory, C: GenericConfig, const D: usize>( + state: &mut GenerationState, + row: &mut CpuColumnsView, +) -> u32 { let code_context = state.registers.code_context(); row.code_context = F::from_canonical_usize(code_context); @@ -347,8 +352,8 @@ fn fill_op_flag(op: Operation, row: &mut CpuColumnsView) { } = F::ONE; } -fn perform_op( - state: &mut GenerationState, +fn perform_op, C: GenericConfig, const D: usize>( + state: &mut GenerationState, op: Operation, row: CpuColumnsView, kernel: &Kernel, @@ -500,7 +505,9 @@ fn perform_op( /// Row that has the correct values for system registers and the code channel, but is otherwise /// blank. It fulfills the constraints that are common to successful operations and the exception /// operation. It also returns the opcode. -fn base_row(state: &mut GenerationState) -> (CpuColumnsView, u32) { +fn base_row, C: GenericConfig, const D: usize>( + state: &mut GenerationState, +) -> (CpuColumnsView, u32) { let mut row: CpuColumnsView = CpuColumnsView::default(); row.clock = F::from_canonical_usize(state.traces.clock()); row.context = F::from_canonical_usize(state.registers.context); @@ -512,8 +519,12 @@ fn base_row(state: &mut GenerationState) -> (CpuColumnsView, u32 (row, opcode) } -fn try_perform_instruction( - state: &mut GenerationState, +fn try_perform_instruction< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( + state: &mut GenerationState, kernel: &Kernel, ) -> Result<(), ProgramError> { let (mut row, opcode) = base_row(state); @@ -545,7 +556,15 @@ fn try_perform_instruction( perform_op(state, op, row, kernel) } -fn log_kernel_instruction(state: &GenerationState, op: Operation, kernel: &Kernel) { +fn log_kernel_instruction< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( + state: &GenerationState, + op: Operation, + kernel: &Kernel, +) { // The logic below is a bit costly, so skip it if debug logs aren't enabled. if !log_enabled!(log::Level::Debug) { return; @@ -575,7 +594,10 @@ fn log_kernel_instruction(state: &GenerationState, op: Operation, k //assert!(pc < KERNEL.program.image.len(), "Kernel PC is out of range: {}", pc); } -fn handle_error(state: &mut GenerationState, err: ProgramError) -> anyhow::Result<()> { +fn handle_error, C: GenericConfig, const D: usize>( + state: &mut GenerationState, + err: ProgramError, +) -> anyhow::Result<()> { let exc_code: u8 = match err { ProgramError::OutOfGas => 0, ProgramError::InvalidOpcode => 1, @@ -595,8 +617,12 @@ fn handle_error(state: &mut GenerationState, err: ProgramError) -> Ok(()) } -pub(crate) fn transition( - state: &mut GenerationState, +pub(crate) fn transition< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( + state: &mut GenerationState, kernel: &Kernel, ) -> anyhow::Result<()> { let checkpoint = state.checkpoint(); diff --git a/prover/src/witness/util.rs b/prover/src/witness/util.rs index 8aaf8ac9..c786624f 100644 --- a/prover/src/witness/util.rs +++ b/prover/src/witness/util.rs @@ -19,6 +19,8 @@ use crate::poseidon_sponge::columns::POSEIDON_RATE_BYTES; use crate::poseidon_sponge::poseidon_sponge_stark::PoseidonSpongeOp; use crate::witness::errors::ProgramError; use crate::witness::memory::{MemoryAddress, MemoryChannel, MemoryOp, MemoryOpKind}; +use plonky2::field::extension::Extendable; +use plonky2::plonk::config::GenericConfig; fn to_byte_checked(n: u32) -> u8 { let res: u8 = n.to_le_bytes()[0]; @@ -47,9 +49,13 @@ pub(crate) fn fill_channel_with_value(row: &mut CpuColumnsView, n: channel.value = F::from_canonical_u32(val); } -pub(crate) fn mem_read_code_with_log_and_fill( +pub(crate) fn mem_read_code_with_log_and_fill< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( address: MemoryAddress, - state: &GenerationState, + state: &GenerationState, row: &mut CpuColumnsView, ) -> (u32, MemoryOp) { let (val, op) = mem_read_with_log(MemoryChannel::Code, address, state); @@ -94,10 +100,14 @@ pub(crate) fn sign_extend(value: u32) -> u32 { } } -pub(crate) fn reg_read_with_log( +pub(crate) fn reg_read_with_log< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( index: u8, channel: usize, - state: &GenerationState, + state: &GenerationState, row: &mut CpuColumnsView, ) -> Result<(usize, MemoryOp), ProgramError> { let result = { @@ -143,11 +153,15 @@ pub(crate) fn reg_read_with_log( Ok((result, op)) } -pub(crate) fn reg_write_with_log( +pub(crate) fn reg_write_with_log< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( index: u8, channel: usize, value: usize, - state: &mut GenerationState, + state: &mut GenerationState, row: &mut CpuColumnsView, ) -> Result { if index == 0 { @@ -204,10 +218,14 @@ pub(crate) fn reg_write_with_log( Ok(op) } -pub(crate) fn reg_zero_write_with_log( +pub(crate) fn reg_zero_write_with_log< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( channel: usize, value: usize, - state: &mut GenerationState, + state: &mut GenerationState, row: &mut CpuColumnsView, ) -> MemoryOp { let address = MemoryAddress::new(0, Segment::RegisterFile, 0); @@ -232,10 +250,14 @@ pub(crate) fn reg_zero_write_with_log( op } -pub(crate) fn mem_read_with_log( +pub(crate) fn mem_read_with_log< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( channel: MemoryChannel, address: MemoryAddress, - state: &GenerationState, + state: &GenerationState, ) -> (u32, MemoryOp) { let val = state.memory.get(address).to_be(); let op = MemoryOp::new( @@ -250,8 +272,12 @@ pub(crate) fn mem_read_with_log( /// Pushes without writing in memory. This happens in opcodes where a push immediately follows a pop. /// The pushed value may be loaded in a memory channel, without creating a memory operation. -pub(crate) fn push_no_write( - _state: &mut GenerationState, +pub(crate) fn push_no_write< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( + _state: &mut GenerationState, row: &mut CpuColumnsView, val: u32, channel_opt: Option, @@ -270,10 +296,14 @@ pub(crate) fn push_no_write( } } -pub(crate) fn mem_read_gp_with_log_and_fill( +pub(crate) fn mem_read_gp_with_log_and_fill< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( n: usize, address: MemoryAddress, - state: &GenerationState, + state: &GenerationState, row: &mut CpuColumnsView, ) -> (u32, MemoryOp) { let (val, op) = mem_read_with_log(MemoryChannel::GeneralPurpose(n), address, state); @@ -289,10 +319,14 @@ pub(crate) fn mem_read_gp_with_log_and_fill( (val, op) } -pub(crate) fn mem_write_gp_log_and_fill( +pub(crate) fn mem_write_gp_log_and_fill< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( n: usize, address: MemoryAddress, - state: &GenerationState, + state: &GenerationState, row: &mut CpuColumnsView, val: u32, // LE ) -> MemoryOp { @@ -309,10 +343,14 @@ pub(crate) fn mem_write_gp_log_and_fill( op } -pub(crate) fn mem_write_log( +pub(crate) fn mem_write_log< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( channel: MemoryChannel, address: MemoryAddress, - state: &GenerationState, + state: &GenerationState, val: u32, // LE ) -> MemoryOp { MemoryOp::new( @@ -324,8 +362,12 @@ pub(crate) fn mem_write_log( ) } -pub(crate) fn poseidon_sponge_log( - state: &mut GenerationState, +pub(crate) fn poseidon_sponge_log< + F: RichField + Extendable, + C: GenericConfig, + const D: usize, +>( + state: &mut GenerationState, base_address: Vec, input: Vec, // BE ) { @@ -421,8 +463,8 @@ pub(crate) fn poseidon_sponge_log( }); } -fn xor_into_sponge( - state: &mut GenerationState, +fn xor_into_sponge, C: GenericConfig, const D: usize>( + state: &mut GenerationState, sponge_state: &mut [u8; KECCAK_WIDTH_BYTES], block: &[u8; KECCAK_RATE_BYTES], ) { diff --git a/runtime/entrypoint/src/lib.rs b/runtime/entrypoint/src/lib.rs index 0eb07329..60e4660b 100644 --- a/runtime/entrypoint/src/lib.rs +++ b/runtime/entrypoint/src/lib.rs @@ -1,5 +1,5 @@ //! Ported from Entrypoint for SP1 zkVM. - +#![feature(asm_experimental_arch)] pub mod heap; pub mod syscalls; pub mod io { diff --git a/runtime/entrypoint/src/syscalls/io.rs b/runtime/entrypoint/src/syscalls/io.rs index 173074bb..5ba44363 100644 --- a/runtime/entrypoint/src/syscalls/io.rs +++ b/runtime/entrypoint/src/syscalls/io.rs @@ -61,3 +61,26 @@ pub extern "C" fn syscall_hint_read(ptr: *mut u8, len: usize) { #[cfg(not(target_os = "zkvm"))] unreachable!() } + +#[allow(unused_variables)] +#[no_mangle] +pub extern "C" fn syscall_verify(claim_digest: &[u8; 32], control_root: &[u8; 32]) { + let mut to_host = [0u8; 64]; + to_host[..32].copy_from_slice(claim_digest); + to_host[32..].copy_from_slice(control_root); + + cfg_if::cfg_if! { + if #[cfg(target_os = "zkvm")] { + unsafe { + asm!( + "syscall", + in("$2") crate::syscalls::VERIFY, + in("$5") to_host.as_ptr() as u32, + in("$6") 64u32, + ) + } + } else { + unreachable!() + } + } +} diff --git a/runtime/entrypoint/src/syscalls/mod.rs b/runtime/entrypoint/src/syscalls/mod.rs index f08ab8e5..f63b0b90 100644 --- a/runtime/entrypoint/src/syscalls/mod.rs +++ b/runtime/entrypoint/src/syscalls/mod.rs @@ -24,3 +24,6 @@ pub const HINT_LEN: u32 = 0x00_00_00_F0; /// Executes `HINT_READ`. pub const HINT_READ: u32 = 0x00_00_00_F1; + +/// Executes `HINT_READ`. +pub const VERIFY: u32 = 0x00_00_00_F2; diff --git a/runtime/precompiles/Cargo.toml b/runtime/precompiles/Cargo.toml index f98c490d..1b82dc1b 100644 --- a/runtime/precompiles/Cargo.toml +++ b/runtime/precompiles/Cargo.toml @@ -7,3 +7,4 @@ edition = "2021" bincode = "1.3.3" cfg-if = "1.0.0" serde = { version = "1.0.201", features = ["derive"] } +sha2 = { version = "0.10.8", default-features = false } diff --git a/runtime/precompiles/src/io.rs b/runtime/precompiles/src/io.rs index 5e228072..42ad325a 100644 --- a/runtime/precompiles/src/io.rs +++ b/runtime/precompiles/src/io.rs @@ -1,15 +1,18 @@ //! Ported from Precompiles for SP1 zkVM. #![allow(unused_unsafe)] +use crate::syscall_verify; use crate::syscall_write; use crate::{syscall_hint_len, syscall_hint_read}; use serde::de::DeserializeOwned; use serde::Serialize; +use sha2::{Digest, Sha256}; use std::alloc::Layout; use std::io::Write; const FD_HINT: u32 = 4; pub const FD_PUBLIC_VALUES: u32 = 3; +pub const ZERO: [u8; 32] = [0u8; 32]; #[allow(dead_code)] pub struct SyscallWriter { @@ -73,6 +76,22 @@ pub fn commit(value: &T) { commit_slice(buf.as_slice()); } +pub fn verify(image_id: Vec, public_input: &T) { + let mut buf = Vec::new(); + bincode::serialize_into(&mut buf, public_input).expect("serialization failed"); + + let mut hasher = Sha256::new(); + hasher.update(buf); + let input_digest: [u8; 32] = hasher.finalize().into(); + + let mut hasher = Sha256::new(); + hasher.update(image_id); + hasher.update(input_digest); + let digest: [u8; 32] = hasher.finalize().into(); + + unsafe { syscall_verify(&digest, &ZERO) } +} + pub fn hint_slice(buf: &[u8]) { let mut my_reader: SyscallWriter = SyscallWriter { fd: FD_HINT }; my_reader.write_all(buf).unwrap(); diff --git a/runtime/precompiles/src/lib.rs b/runtime/precompiles/src/lib.rs index 070485c7..5f61ef0d 100644 --- a/runtime/precompiles/src/lib.rs +++ b/runtime/precompiles/src/lib.rs @@ -16,4 +16,5 @@ extern "C" { pub fn syscall_hint_len() -> usize; pub fn syscall_hint_read(ptr: *mut u8, len: usize); pub fn sys_alloc_aligned(bytes: usize, align: usize) -> *mut u8; + pub fn syscall_verify(claim_digest: &[u8; 32], control_root: &[u8; 32]); }