From 14eb569d41d24721ffbd407d6060e202482d659c Mon Sep 17 00:00:00 2001 From: Chris T Date: Wed, 19 Jun 2024 17:02:41 -0700 Subject: [PATCH] fix: v1.0.8-testnet (#957) Co-authored-by: John Guibas --- .github/workflows/main.yml | 4 +- Cargo.lock | 40 +++++++-------- Cargo.toml | 36 +++++++------- core/src/lib.rs | 2 +- core/src/runtime/mod.rs | 46 +++++++++--------- core/src/runtime/record.rs | 8 +-- core/src/stark/machine.rs | 12 +++++ core/src/stark/types.rs | 2 +- examples/Cargo.lock | 3 ++ examples/aggregation/program/Cargo.lock | 2 +- .../program/elf/riscv32im-succinct-zkvm-elf | Bin 123408 -> 141280 bytes examples/ssz-withdrawals/program/Cargo.lock | 2 +- examples/tendermint/program/Cargo.lock | 4 +- .../program/elf/riscv32im-succinct-zkvm-elf | Bin 1171920 -> 1171252 bytes prover/src/lib.rs | 8 ++- prover/src/verify.rs | 10 ++-- recursion/core/src/air/public_values.rs | 5 +- recursion/program/src/hints.rs | 9 ++++ recursion/program/src/machine/compress.rs | 20 ++++++-- recursion/program/src/machine/core.rs | 11 ++++- recursion/program/src/machine/deferred.rs | 7 ++- recursion/program/src/machine/mod.rs | 3 ++ recursion/program/src/machine/root.rs | 11 ++++- recursion/program/src/machine/utils.rs | 5 ++ recursion/program/src/stark.rs | 5 +- tests/tendermint-benchmark/Cargo.lock | 11 ++++- 26 files changed, 175 insertions(+), 91 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a2547409d0..50358ee7d1 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -65,7 +65,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check branch - if: github.head_ref != 'dev' + if: github.head_ref != 'dev' && !startsWith(github.ref, 'release/') && !startsWith(github.ref, 'hotfix/') run: | - echo "ERROR: You can only merge to main from dev." + echo "ERROR: You can only merge to main from dev, release/*, or hotfix/*." exit 1 diff --git a/Cargo.lock b/Cargo.lock index adfcf808b6..e1703793e1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3042,7 +3042,7 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "p3-air" version = "0.1.0" -source = "git+https://github.com/Plonky3/Plonky3.git?rev=3b5265f9d5af36534a46caebf0617595cfb42c5a#3b5265f9d5af36534a46caebf0617595cfb42c5a" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=88ea2b866e41329817e4761429b4a5a2a9751c07#88ea2b866e41329817e4761429b4a5a2a9751c07" dependencies = [ "p3-field", "p3-matrix", @@ -3051,7 +3051,7 @@ dependencies = [ [[package]] name = "p3-baby-bear" version = "0.1.0" -source = "git+https://github.com/Plonky3/Plonky3.git?rev=3b5265f9d5af36534a46caebf0617595cfb42c5a#3b5265f9d5af36534a46caebf0617595cfb42c5a" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=88ea2b866e41329817e4761429b4a5a2a9751c07#88ea2b866e41329817e4761429b4a5a2a9751c07" dependencies = [ "num-bigint 0.4.5", "p3-field", @@ -3065,7 +3065,7 @@ dependencies = [ [[package]] name = "p3-blake3" version = "0.1.0" -source = "git+https://github.com/Plonky3/Plonky3.git?rev=3b5265f9d5af36534a46caebf0617595cfb42c5a#3b5265f9d5af36534a46caebf0617595cfb42c5a" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=88ea2b866e41329817e4761429b4a5a2a9751c07#88ea2b866e41329817e4761429b4a5a2a9751c07" dependencies = [ "blake3", "p3-symmetric", @@ -3074,7 +3074,7 @@ dependencies = [ [[package]] name = "p3-bn254-fr" version = "0.1.0" -source = "git+https://github.com/Plonky3/Plonky3.git?rev=3b5265f9d5af36534a46caebf0617595cfb42c5a#3b5265f9d5af36534a46caebf0617595cfb42c5a" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=88ea2b866e41329817e4761429b4a5a2a9751c07#88ea2b866e41329817e4761429b4a5a2a9751c07" dependencies = [ "ff 0.13.0", "num-bigint 0.4.5", @@ -3088,7 +3088,7 @@ dependencies = [ [[package]] name = "p3-challenger" version = "0.1.0" -source = "git+https://github.com/Plonky3/Plonky3.git?rev=3b5265f9d5af36534a46caebf0617595cfb42c5a#3b5265f9d5af36534a46caebf0617595cfb42c5a" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=88ea2b866e41329817e4761429b4a5a2a9751c07#88ea2b866e41329817e4761429b4a5a2a9751c07" dependencies = [ "p3-field", "p3-maybe-rayon", @@ -3100,7 +3100,7 @@ dependencies = [ [[package]] name = "p3-commit" version = "0.1.0" -source = "git+https://github.com/Plonky3/Plonky3.git?rev=3b5265f9d5af36534a46caebf0617595cfb42c5a#3b5265f9d5af36534a46caebf0617595cfb42c5a" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=88ea2b866e41329817e4761429b4a5a2a9751c07#88ea2b866e41329817e4761429b4a5a2a9751c07" dependencies = [ "itertools 0.12.1", "p3-challenger", @@ -3113,7 +3113,7 @@ dependencies = [ [[package]] name = "p3-dft" version = "0.1.0" -source = "git+https://github.com/Plonky3/Plonky3.git?rev=3b5265f9d5af36534a46caebf0617595cfb42c5a#3b5265f9d5af36534a46caebf0617595cfb42c5a" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=88ea2b866e41329817e4761429b4a5a2a9751c07#88ea2b866e41329817e4761429b4a5a2a9751c07" dependencies = [ "p3-field", "p3-matrix", @@ -3125,7 +3125,7 @@ dependencies = [ [[package]] name = "p3-field" version = "0.1.0" -source = "git+https://github.com/Plonky3/Plonky3.git?rev=3b5265f9d5af36534a46caebf0617595cfb42c5a#3b5265f9d5af36534a46caebf0617595cfb42c5a" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=88ea2b866e41329817e4761429b4a5a2a9751c07#88ea2b866e41329817e4761429b4a5a2a9751c07" dependencies = [ "itertools 0.12.1", "num-bigint 0.4.5", @@ -3138,7 +3138,7 @@ dependencies = [ [[package]] name = "p3-fri" version = "0.1.0" -source = "git+https://github.com/Plonky3/Plonky3.git?rev=3b5265f9d5af36534a46caebf0617595cfb42c5a#3b5265f9d5af36534a46caebf0617595cfb42c5a" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=88ea2b866e41329817e4761429b4a5a2a9751c07#88ea2b866e41329817e4761429b4a5a2a9751c07" dependencies = [ "itertools 0.12.1", "p3-challenger", @@ -3156,7 +3156,7 @@ dependencies = [ [[package]] name = "p3-interpolation" version = "0.1.0" -source = "git+https://github.com/Plonky3/Plonky3.git?rev=3b5265f9d5af36534a46caebf0617595cfb42c5a#3b5265f9d5af36534a46caebf0617595cfb42c5a" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=88ea2b866e41329817e4761429b4a5a2a9751c07#88ea2b866e41329817e4761429b4a5a2a9751c07" dependencies = [ "p3-field", "p3-matrix", @@ -3166,7 +3166,7 @@ dependencies = [ [[package]] name = "p3-keccak" version = "0.1.0" -source = "git+https://github.com/Plonky3/Plonky3.git?rev=3b5265f9d5af36534a46caebf0617595cfb42c5a#3b5265f9d5af36534a46caebf0617595cfb42c5a" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=88ea2b866e41329817e4761429b4a5a2a9751c07#88ea2b866e41329817e4761429b4a5a2a9751c07" dependencies = [ "p3-symmetric", "tiny-keccak", @@ -3175,7 +3175,7 @@ dependencies = [ [[package]] name = "p3-keccak-air" version = "0.1.0" -source = "git+https://github.com/Plonky3/Plonky3.git?rev=3b5265f9d5af36534a46caebf0617595cfb42c5a#3b5265f9d5af36534a46caebf0617595cfb42c5a" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=88ea2b866e41329817e4761429b4a5a2a9751c07#88ea2b866e41329817e4761429b4a5a2a9751c07" dependencies = [ "p3-air", "p3-field", @@ -3188,7 +3188,7 @@ dependencies = [ [[package]] name = "p3-matrix" version = "0.1.0" -source = "git+https://github.com/Plonky3/Plonky3.git?rev=3b5265f9d5af36534a46caebf0617595cfb42c5a#3b5265f9d5af36534a46caebf0617595cfb42c5a" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=88ea2b866e41329817e4761429b4a5a2a9751c07#88ea2b866e41329817e4761429b4a5a2a9751c07" dependencies = [ "itertools 0.12.1", "p3-field", @@ -3202,7 +3202,7 @@ dependencies = [ [[package]] name = "p3-maybe-rayon" version = "0.1.0" -source = "git+https://github.com/Plonky3/Plonky3.git?rev=3b5265f9d5af36534a46caebf0617595cfb42c5a#3b5265f9d5af36534a46caebf0617595cfb42c5a" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=88ea2b866e41329817e4761429b4a5a2a9751c07#88ea2b866e41329817e4761429b4a5a2a9751c07" dependencies = [ "rayon", ] @@ -3210,7 +3210,7 @@ dependencies = [ [[package]] name = "p3-mds" version = "0.1.0" -source = "git+https://github.com/Plonky3/Plonky3.git?rev=3b5265f9d5af36534a46caebf0617595cfb42c5a#3b5265f9d5af36534a46caebf0617595cfb42c5a" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=88ea2b866e41329817e4761429b4a5a2a9751c07#88ea2b866e41329817e4761429b4a5a2a9751c07" dependencies = [ "itertools 0.12.1", "p3-dft", @@ -3224,7 +3224,7 @@ dependencies = [ [[package]] name = "p3-merkle-tree" version = "0.1.0" -source = "git+https://github.com/Plonky3/Plonky3.git?rev=3b5265f9d5af36534a46caebf0617595cfb42c5a#3b5265f9d5af36534a46caebf0617595cfb42c5a" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=88ea2b866e41329817e4761429b4a5a2a9751c07#88ea2b866e41329817e4761429b4a5a2a9751c07" dependencies = [ "itertools 0.12.1", "p3-commit", @@ -3240,7 +3240,7 @@ dependencies = [ [[package]] name = "p3-poseidon2" version = "0.1.0" -source = "git+https://github.com/Plonky3/Plonky3.git?rev=3b5265f9d5af36534a46caebf0617595cfb42c5a#3b5265f9d5af36534a46caebf0617595cfb42c5a" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=88ea2b866e41329817e4761429b4a5a2a9751c07#88ea2b866e41329817e4761429b4a5a2a9751c07" dependencies = [ "gcd", "p3-field", @@ -3252,7 +3252,7 @@ dependencies = [ [[package]] name = "p3-symmetric" version = "0.1.0" -source = "git+https://github.com/Plonky3/Plonky3.git?rev=3b5265f9d5af36534a46caebf0617595cfb42c5a#3b5265f9d5af36534a46caebf0617595cfb42c5a" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=88ea2b866e41329817e4761429b4a5a2a9751c07#88ea2b866e41329817e4761429b4a5a2a9751c07" dependencies = [ "itertools 0.12.1", "p3-field", @@ -3262,7 +3262,7 @@ dependencies = [ [[package]] name = "p3-uni-stark" version = "0.1.0" -source = "git+https://github.com/Plonky3/Plonky3.git?rev=3b5265f9d5af36534a46caebf0617595cfb42c5a#3b5265f9d5af36534a46caebf0617595cfb42c5a" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=88ea2b866e41329817e4761429b4a5a2a9751c07#88ea2b866e41329817e4761429b4a5a2a9751c07" dependencies = [ "itertools 0.12.1", "p3-air", @@ -3280,7 +3280,7 @@ dependencies = [ [[package]] name = "p3-util" version = "0.1.0" -source = "git+https://github.com/Plonky3/Plonky3.git?rev=3b5265f9d5af36534a46caebf0617595cfb42c5a#3b5265f9d5af36534a46caebf0617595cfb42c5a" +source = "git+https://github.com/Plonky3/Plonky3.git?rev=88ea2b866e41329817e4761429b4a5a2a9751c07#88ea2b866e41329817e4761429b4a5a2a9751c07" dependencies = [ "serde", ] diff --git a/Cargo.toml b/Cargo.toml index d88f8b7a60..063a57c60a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,26 +30,26 @@ debug = true debug-assertions = true [workspace.dependencies] -p3-air = { git = "https://github.com/Plonky3/Plonky3.git", rev = "3b5265f9d5af36534a46caebf0617595cfb42c5a" } -p3-field = { git = "https://github.com/Plonky3/Plonky3.git", rev = "3b5265f9d5af36534a46caebf0617595cfb42c5a" } -p3-commit = { git = "https://github.com/Plonky3/Plonky3.git", rev = "3b5265f9d5af36534a46caebf0617595cfb42c5a" } -p3-matrix = { git = "https://github.com/Plonky3/Plonky3.git", rev = "3b5265f9d5af36534a46caebf0617595cfb42c5a" } +p3-air = { git = "https://github.com/Plonky3/Plonky3.git", rev = "88ea2b866e41329817e4761429b4a5a2a9751c07" } +p3-field = { git = "https://github.com/Plonky3/Plonky3.git", rev = "88ea2b866e41329817e4761429b4a5a2a9751c07" } +p3-commit = { git = "https://github.com/Plonky3/Plonky3.git", rev = "88ea2b866e41329817e4761429b4a5a2a9751c07" } +p3-matrix = { git = "https://github.com/Plonky3/Plonky3.git", rev = "88ea2b866e41329817e4761429b4a5a2a9751c07" } p3-baby-bear = { git = "https://github.com/Plonky3/Plonky3.git", features = [ "nightly-features", -], rev = "3b5265f9d5af36534a46caebf0617595cfb42c5a" } -p3-util = { git = "https://github.com/Plonky3/Plonky3.git", rev = "3b5265f9d5af36534a46caebf0617595cfb42c5a" } -p3-challenger = { git = "https://github.com/Plonky3/Plonky3.git", rev = "3b5265f9d5af36534a46caebf0617595cfb42c5a" } -p3-dft = { git = "https://github.com/Plonky3/Plonky3.git", rev = "3b5265f9d5af36534a46caebf0617595cfb42c5a" } -p3-fri = { git = "https://github.com/Plonky3/Plonky3.git", rev = "3b5265f9d5af36534a46caebf0617595cfb42c5a" } -p3-keccak = { git = "https://github.com/Plonky3/Plonky3.git", rev = "3b5265f9d5af36534a46caebf0617595cfb42c5a" } -p3-keccak-air = { git = "https://github.com/Plonky3/Plonky3.git", rev = "3b5265f9d5af36534a46caebf0617595cfb42c5a" } -p3-blake3 = { git = "https://github.com/Plonky3/Plonky3.git", rev = "3b5265f9d5af36534a46caebf0617595cfb42c5a" } -p3-merkle-tree = { git = "https://github.com/Plonky3/Plonky3.git", rev = "3b5265f9d5af36534a46caebf0617595cfb42c5a" } -p3-poseidon2 = { git = "https://github.com/Plonky3/Plonky3.git", rev = "3b5265f9d5af36534a46caebf0617595cfb42c5a" } -p3-symmetric = { git = "https://github.com/Plonky3/Plonky3.git", rev = "3b5265f9d5af36534a46caebf0617595cfb42c5a" } -p3-uni-stark = { git = "https://github.com/Plonky3/Plonky3.git", rev = "3b5265f9d5af36534a46caebf0617595cfb42c5a" } -p3-maybe-rayon = { git = "https://github.com/Plonky3/Plonky3.git", rev = "3b5265f9d5af36534a46caebf0617595cfb42c5a" } -p3-bn254-fr = { git = "https://github.com/Plonky3/Plonky3.git", rev = "3b5265f9d5af36534a46caebf0617595cfb42c5a" } +], rev = "88ea2b866e41329817e4761429b4a5a2a9751c07" } +p3-util = { git = "https://github.com/Plonky3/Plonky3.git", rev = "88ea2b866e41329817e4761429b4a5a2a9751c07" } +p3-challenger = { git = "https://github.com/Plonky3/Plonky3.git", rev = "88ea2b866e41329817e4761429b4a5a2a9751c07" } +p3-dft = { git = "https://github.com/Plonky3/Plonky3.git", rev = "88ea2b866e41329817e4761429b4a5a2a9751c07" } +p3-fri = { git = "https://github.com/Plonky3/Plonky3.git", rev = "88ea2b866e41329817e4761429b4a5a2a9751c07" } +p3-keccak = { git = "https://github.com/Plonky3/Plonky3.git", rev = "88ea2b866e41329817e4761429b4a5a2a9751c07" } +p3-keccak-air = { git = "https://github.com/Plonky3/Plonky3.git", rev = "88ea2b866e41329817e4761429b4a5a2a9751c07" } +p3-blake3 = { git = "https://github.com/Plonky3/Plonky3.git", rev = "88ea2b866e41329817e4761429b4a5a2a9751c07" } +p3-merkle-tree = { git = "https://github.com/Plonky3/Plonky3.git", rev = "88ea2b866e41329817e4761429b4a5a2a9751c07" } +p3-poseidon2 = { git = "https://github.com/Plonky3/Plonky3.git", rev = "88ea2b866e41329817e4761429b4a5a2a9751c07" } +p3-symmetric = { git = "https://github.com/Plonky3/Plonky3.git", rev = "88ea2b866e41329817e4761429b4a5a2a9751c07" } +p3-uni-stark = { git = "https://github.com/Plonky3/Plonky3.git", rev = "88ea2b866e41329817e4761429b4a5a2a9751c07" } +p3-maybe-rayon = { git = "https://github.com/Plonky3/Plonky3.git", rev = "88ea2b866e41329817e4761429b4a5a2a9751c07" } +p3-bn254-fr = { git = "https://github.com/Plonky3/Plonky3.git", rev = "88ea2b866e41329817e4761429b4a5a2a9751c07" } # For local development. diff --git a/core/src/lib.rs b/core/src/lib.rs index 8a862fcc2b..ac8524027d 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -39,4 +39,4 @@ use stark::StarkGenericConfig; /// This string should be updated whenever any step in verifying an SP1 proof changes, including /// core, recursion, and plonk-bn254. This string is used to download SP1 artifacts and the gnark /// docker image. -pub const SP1_CIRCUIT_VERSION: &str = "v1.0.7-testnet"; +pub const SP1_CIRCUIT_VERSION: &str = "v1.0.8-testnet"; diff --git a/core/src/runtime/mod.rs b/core/src/runtime/mod.rs index 1692181d91..b223e1503e 100644 --- a/core/src/runtime/mod.rs +++ b/core/src/runtime/mod.rs @@ -264,16 +264,9 @@ impl<'a> Runtime<'a> { Entry::Occupied(entry) => entry.into_mut(), Entry::Vacant(entry) => { // If addr has a specific value to be initialized with, use that, otherwise 0. - let value = self.state.uninitialized_memory.remove(&addr).unwrap_or(0); - - // Do not emit memory initialize events for address 0 as that is done in initialize. - if addr != 0 { - self.record - .memory_initialize_events - .push(MemoryInitializeFinalizeEvent::initialize(addr, value, true)); - } + let value = self.state.uninitialized_memory.get(&addr).unwrap_or(&0); entry.insert(MemoryRecord { - value, + value: *value, shard: 0, timestamp: 0, }) @@ -312,16 +305,10 @@ impl<'a> Runtime<'a> { Entry::Occupied(entry) => entry.into_mut(), Entry::Vacant(entry) => { // If addr has a specific value to be initialized with, use that, otherwise 0. - let value = self.state.uninitialized_memory.remove(&addr).unwrap_or(0); + let value = self.state.uninitialized_memory.get(&addr).unwrap_or(&0); - // Do not emit memory initialize events for address 0 as that is done in initialize. - if addr != 0 { - self.record - .memory_initialize_events - .push(MemoryInitializeFinalizeEvent::initialize(addr, value, true)); - } entry.insert(MemoryRecord { - value, + value: *value, shard: 0, timestamp: 0, }) @@ -1026,11 +1013,6 @@ impl<'a> Runtime<'a> { }, ); } - - // Create init event for register 0 because it needs to be the first row in MemoryInit. - self.record - .memory_initialize_events - .push(MemoryInitializeFinalizeEvent::initialize(0, 0, true)); } pub fn run_untraced(&mut self) -> Result<(), ExecutionError> { @@ -1134,13 +1116,29 @@ impl<'a> Runtime<'a> { addr_0_final_record, )); + let memory_initialize_events = &mut self.record.memory_initialize_events; + let addr_0_initialize_event = + MemoryInitializeFinalizeEvent::initialize(0, 0, addr_0_record.is_some()); + memory_initialize_events.push(addr_0_initialize_event); + for addr in self.state.memory.keys() { if addr == &0 { - continue; // We handle addr = 0 separately above. + // Handled above. + continue; } - let record = *self.state.memory.get(addr).unwrap(); + // Program memory is initialized in the MemoryProgram chip and doesn't require any events, + // so we only send init events for other memory addresses. + if !self.record.program.memory_image.contains_key(addr) { + let initial_value = self.state.uninitialized_memory.get(addr).unwrap_or(&0); + memory_initialize_events.push(MemoryInitializeFinalizeEvent::initialize( + *addr, + *initial_value, + true, + )); + } + let record = *self.state.memory.get(addr).unwrap(); memory_finalize_events.push(MemoryInitializeFinalizeEvent::finalize_from_record( *addr, &record, )); diff --git a/core/src/runtime/record.rs b/core/src/runtime/record.rs index 7742c2aa6c..0e80a40841 100644 --- a/core/src/runtime/record.rs +++ b/core/src/runtime/record.rs @@ -572,11 +572,11 @@ impl MachineRecord for ExecutionRecord { self.nonce_lookup.insert(event.lookup_id, i as u32); } - first - .memory_initialize_events + // Put MemoryInit / MemoryFinalize events in the last shard. + let last = shards.last_mut().unwrap(); + last.memory_initialize_events .extend_from_slice(&self.memory_initialize_events); - first - .memory_finalize_events + last.memory_finalize_events .extend_from_slice(&self.memory_finalize_events); // Copy the nonce lookup to all shards. diff --git a/core/src/stark/machine.rs b/core/src/stark/machine.rs index 8891fdc147..3c80832343 100644 --- a/core/src/stark/machine.rs +++ b/core/src/stark/machine.rs @@ -682,6 +682,18 @@ pub mod tests { run_test(program).unwrap(); } + #[test] + fn test_fibonacci_prove_checkpoints() { + setup_logger(); + + let program = fibonacci_program(); + let stdin = SP1Stdin::new(); + let mut opts = SP1CoreOpts::default(); + opts.shard_size = 1024; + opts.shard_batch_size = 2; + prove(program, &stdin, BabyBearPoseidon2::new(), opts).unwrap(); + } + #[test] fn test_fibonacci_prove_batch() { setup_logger(); diff --git a/core/src/stark/types.rs b/core/src/stark/types.rs index 67f07cc032..e3eede2874 100644 --- a/core/src/stark/types.rs +++ b/core/src/stark/types.rs @@ -124,7 +124,7 @@ pub struct ShardOpenedValues { /// The maximum number of elements that can be stored in the public values vec. Both SP1 and recursive /// proofs need to pad their public_values vec to this length. This is required since the recursion /// verification program expects the public values vec to be fixed length. -pub const PROOF_MAX_NUM_PVS: usize = 240; +pub const PROOF_MAX_NUM_PVS: usize = 241; #[derive(Serialize, Deserialize, Clone)] #[serde(bound = "")] diff --git a/examples/Cargo.lock b/examples/Cargo.lock index bc5ac9e3ef..cb3302af4d 100644 --- a/examples/Cargo.lock +++ b/examples/Cargo.lock @@ -4907,6 +4907,7 @@ dependencies = [ "bindgen", "cc", "cfg-if", + "hex", "log", "num-bigint 0.4.5", "p3-baby-bear", @@ -4915,6 +4916,7 @@ dependencies = [ "rand", "serde", "serde_json", + "sha2 0.10.8", "sp1-core", "sp1-recursion-compiler", "tempfile", @@ -4977,6 +4979,7 @@ dependencies = [ "strum", "strum_macros", "tempfile", + "thiserror", "tokio", "tracing", "twirp", diff --git a/examples/aggregation/program/Cargo.lock b/examples/aggregation/program/Cargo.lock index de974399f6..71a2d8afcd 100644 --- a/examples/aggregation/program/Cargo.lock +++ b/examples/aggregation/program/Cargo.lock @@ -725,7 +725,7 @@ dependencies = [ [[package]] name = "sha2" version = "0.10.8" -source = "git+https://github.com/sp1-patches/RustCrypto-hashes.git?branch=patch-v0.10.8#3d692aa90b91513886d757d01f8fc2d51c0ec0d7" +source = "git+https://github.com/sp1-patches/RustCrypto-hashes.git?branch=patch-v0.10.8#1f224388fdede7cef649bce0d63876d1a9e3f515" dependencies = [ "cfg-if", "cpufeatures", diff --git a/examples/fibonacci/program/elf/riscv32im-succinct-zkvm-elf b/examples/fibonacci/program/elf/riscv32im-succinct-zkvm-elf index 2d2682e462cc97014ca234e8da4c66cf59aae948..cd36cd92d4fc619a56a4cf7287eba0a63411940d 100755 GIT binary patch delta 46765 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

puzPe!t3)Gn^m%BGmeEiz43yyTUp9orA~w;Vgrn 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, BabyBear>, deferred_proofs: &[ShardProof], batch_size: usize, + total_core_shards: usize, ) -> Vec>> { // Prepare the inputs for the deferred proofs recursive verification. let mut deferred_digest = [Val::::zero(); DIGEST_SIZE]; @@ -337,10 +339,11 @@ impl SP1Prover { sp1_vk: vk, sp1_machine: &self.core_machine, end_pc: Val::::zero(), - end_shard: last_proof_pv.shard, + end_shard: last_proof_pv.shard + BabyBear::one(), leaf_challenger: leaf_challenger.clone(), committed_value_digest: last_proof_pv.committed_value_digest.to_vec(), deferred_proofs_digest: last_proof_pv.deferred_proofs_digest.to_vec(), + total_core_shards, }); deferred_digest = Self::hash_deferred_proofs(deferred_digest, batch); @@ -377,6 +380,7 @@ impl SP1Prover { &last_proof_pv, deferred_proofs, batch_size, + shard_proofs.len(), ); (core_inputs, deferred_inputs) } @@ -393,6 +397,7 @@ impl SP1Prover { let batch_size = 2; let shard_proofs = &proof.proof.0; + let total_core_shards = shard_proofs.len(); // Get the leaf challenger. let mut leaf_challenger = self.core_machine.config().challenger(); vk.vk.observe_into(&mut leaf_challenger); @@ -465,6 +470,7 @@ impl SP1Prover { shard_proofs, kinds, is_complete, + total_core_shards, }; let proof = self.compress_machine_proof( diff --git a/prover/src/verify.rs b/prover/src/verify.rs index 95865e255d..3e7ef1c645 100644 --- a/prover/src/verify.rs +++ b/prover/src/verify.rs @@ -47,6 +47,8 @@ impl SP1Prover { self.core_machine .verify(&vk.vk, &machine_proof, &mut challenger)?; + let num_shards = proof.0.len(); + // Verify shard transitions. for (i, shard_proof) in proof.0.iter().enumerate() { let public_values = PublicValues::from_vec(shard_proof.public_values.clone()); @@ -124,15 +126,15 @@ impl SP1Prover { .count(); // Assert that the `MemoryInit` and `MemoryFinalize` chips only exist in the last shard. - if i != 0 && (memory_final_count > 0 || memory_init_count > 0) { + if i != num_shards - 1 && (memory_final_count > 0 || memory_init_count > 0) { return Err(MachineVerificationError::InvalidChipOccurence( - "memory init and finalize should not eixst anywhere but the last chip" + "memory init and finalize should not exist anywhere but the last chip" .to_string(), )); } - if i == 0 && (memory_init_count != 1 || memory_final_count != 1) { + if i == num_shards - 1 && (memory_init_count != 1 || memory_final_count != 1) { return Err(MachineVerificationError::InvalidChipOccurence( - "memory init and finalize should exist the last chip".to_string(), + "memory init and finalize should exist in the last chip".to_string(), )); } } diff --git a/recursion/core/src/air/public_values.rs b/recursion/core/src/air/public_values.rs index 484342f17f..4f29393761 100644 --- a/recursion/core/src/air/public_values.rs +++ b/recursion/core/src/air/public_values.rs @@ -80,7 +80,7 @@ pub struct RecursionPublicValues { /// First shard being proven. pub start_shard: T, - /// Next shard that should be proven, or 0 if the program halted. + /// Next shard that should be proven, if there are more. pub next_shard: T, /// Start state of reconstruct_challenger. @@ -110,6 +110,9 @@ pub struct RecursionPublicValues { /// Whether the proof completely proves the program execution. pub is_complete: T, + /// Total number of core shards in the program execution. + pub total_core_shards: T, + /// The digest of all the previous public values elements. pub digest: [T; DIGEST_SIZE], diff --git a/recursion/program/src/hints.rs b/recursion/program/src/hints.rs index 353f760b91..e5d889e195 100644 --- a/recursion/program/src/hints.rs +++ b/recursion/program/src/hints.rs @@ -517,6 +517,7 @@ impl<'a, A: MachineAir> Hintable let initial_reconstruct_challenger = DuplexChallenger::::read(builder); let is_complete = builder.hint_var(); + let total_core_shards = builder.hint_var(); SP1RecursionMemoryLayoutVariable { vk, @@ -524,6 +525,7 @@ impl<'a, A: MachineAir> Hintable leaf_challenger, initial_reconstruct_challenger, is_complete, + total_core_shards, } } @@ -543,6 +545,7 @@ impl<'a, A: MachineAir> Hintable stream.extend(self.leaf_challenger.write()); stream.extend(self.initial_reconstruct_challenger.write()); stream.extend((self.is_complete as usize).write()); + stream.extend(self.total_core_shards.write()); stream } @@ -556,12 +559,14 @@ impl<'a, A: MachineAir> Hintable for SP1ReduceMemoryLayout<'a, Baby let shard_proofs = Vec::>::read(builder); let kinds = Vec::::read(builder); let is_complete = builder.hint_var(); + let total_core_shards = builder.hint_var(); SP1ReduceMemoryLayoutVariable { compress_vk, shard_proofs, kinds, is_complete, + total_core_shards, } } @@ -585,6 +590,7 @@ impl<'a, A: MachineAir> Hintable for SP1ReduceMemoryLayout<'a, Baby stream.extend(proof_hints.write()); stream.extend(kinds.write()); stream.extend((self.is_complete as usize).write()); + stream.extend(self.total_core_shards.write()); stream } @@ -629,6 +635,7 @@ impl<'a, A: MachineAir> Hintable let leaf_challenger = DuplexChallenger::::read(builder); let end_pc = InnerVal::read(builder); let end_shard = InnerVal::read(builder); + let total_core_shards = builder.hint_var(); SP1DeferredMemoryLayoutVariable { compress_vk, @@ -641,6 +648,7 @@ impl<'a, A: MachineAir> Hintable leaf_challenger, end_pc, end_shard, + total_core_shards, } } @@ -676,6 +684,7 @@ impl<'a, A: MachineAir> Hintable stream.extend(self.leaf_challenger.write()); stream.extend(self.end_pc.write()); stream.extend(self.end_shard.write()); + stream.extend(self.total_core_shards.write()); stream } diff --git a/recursion/program/src/machine/compress.rs b/recursion/program/src/machine/compress.rs index f8fbc857bc..60a3a6458f 100644 --- a/recursion/program/src/machine/compress.rs +++ b/recursion/program/src/machine/compress.rs @@ -30,7 +30,7 @@ use crate::types::ShardProofVariable; use crate::types::VerifyingKeyVariable; use crate::utils::{ assert_challenger_eq_pv, assign_challenger_from_pv, const_fri_config, - get_challenger_public_values, hash_vkey, + get_challenger_public_values, hash_vkey, var2felt, }; use super::utils::{commit_public_values, proof_data_from_vk, verify_public_values_hash}; @@ -59,6 +59,7 @@ pub struct SP1ReduceMemoryLayout<'a, SC: StarkGenericConfig, A: MachineAir>, pub is_complete: bool, pub kinds: Vec, + pub total_core_shards: usize, } #[derive(DslVariable, Clone)] @@ -67,6 +68,7 @@ pub struct SP1ReduceMemoryLayoutVariable { pub shard_proofs: Array>, pub kinds: Array>, pub is_complete: Var, + pub total_core_shards: Var, } impl SP1CompressVerifier @@ -138,7 +140,9 @@ where shard_proofs, kinds, is_complete, + total_core_shards, } = input; + let total_core_shards_felt = var2felt(builder, total_core_shards); // Initialize the values for the aggregated public output. @@ -236,6 +240,7 @@ where challenger.observe(builder, element); } // verify proof. + let one_var = builder.constant(C::N::one()); StarkVerifier::::verify_shard( builder, &vk, @@ -243,6 +248,7 @@ where machine, &mut challenger, &proof, + one_var, ); // Load the public values from the proof. @@ -370,7 +376,7 @@ where // Assert that the start pc is equal to the current pc. builder.assert_felt_eq(pc, current_public_values.start_pc); // Verfiy that the shard is equal to the current shard. - // builder.assert_felt_eq(shard, current_public_values.start_shard); + builder.assert_felt_eq(shard, current_public_values.start_shard); // Assert that the leaf challenger is always the same. assert_challenger_eq_pv( @@ -403,9 +409,15 @@ where builder.assert_felt_eq(*digest, *current_digest); } + // Assert that total_core_shards is the same. + builder.assert_felt_eq( + total_core_shards_felt, + current_public_values.total_core_shards, + ); + // Update the accumulated values. - // Update the deffered proof digest. + // Update the deferred proof digest. for (digest, current_digest) in reconstruct_deferred_digest .iter() .zip_eq(current_public_values.end_reconstruct_deferred_digest.iter()) @@ -459,6 +471,8 @@ where reduce_public_values.committed_value_digest = committed_value_digest; // Assign the cumulative sum. reduce_public_values.cumulative_sum = cumulative_sum; + // Assign the total number of shards. + reduce_public_values.total_core_shards = total_core_shards_felt; // If the proof is complete, make completeness assertions and set the flag. Otherwise, check // the flag is zero and set the public value to zero. diff --git a/recursion/program/src/machine/core.rs b/recursion/program/src/machine/core.rs index e8a547cc09..c5d0b6ee9e 100644 --- a/recursion/program/src/machine/core.rs +++ b/recursion/program/src/machine/core.rs @@ -42,6 +42,7 @@ pub struct SP1RecursionMemoryLayout<'a, SC: StarkGenericConfig, A: MachineAir { pub initial_reconstruct_challenger: DuplexChallengerVariable, pub is_complete: Var, + + pub total_core_shards: Var, } impl SP1RecursiveVerifier { @@ -131,6 +134,7 @@ where leaf_challenger, initial_reconstruct_challenger, is_complete, + total_core_shards, } = input; // Initialize values we will commit to public outputs. @@ -179,6 +183,7 @@ where machine, &mut challenger, &proof, + total_core_shards, ); // Extract public values. @@ -223,10 +228,10 @@ where builder.assign(exit_code, public_values.exit_code); }); - // If the shard is zero, verify the global initial conditions hold on challenger and pc. + // If it's first shard, verify the global initial conditions hold on challenger and pc. let shard = felt2var(builder, public_values.shard); builder.if_eq(shard, C::N::one()).then(|builder| { - // This should be the first proof as well + // This should be the 0th proof in this batch. builder.assert_var_eq(i, C::N::zero()); // Start pc should be vk.pc_start @@ -333,6 +338,7 @@ where let end_deferred_digest = [zero; POSEIDON_NUM_WORDS]; let is_complete_felt = var2felt(builder, is_complete); + let total_core_shards_felt = var2felt(builder, total_core_shards); recursion_public_values.committed_value_digest = committed_value_digest; recursion_public_values.deferred_proofs_digest = deferred_proofs_digest; @@ -348,6 +354,7 @@ where recursion_public_values.start_reconstruct_deferred_digest = start_deferred_digest; recursion_public_values.end_reconstruct_deferred_digest = end_deferred_digest; recursion_public_values.is_complete = is_complete_felt; + recursion_public_values.total_core_shards = total_core_shards_felt; // If the proof represents a complete proof, make completeness assertions. // diff --git a/recursion/program/src/machine/deferred.rs b/recursion/program/src/machine/deferred.rs index a370a5b3e4..1c6ac7e1b0 100644 --- a/recursion/program/src/machine/deferred.rs +++ b/recursion/program/src/machine/deferred.rs @@ -54,6 +54,7 @@ where pub leaf_challenger: SC::Challenger, pub end_pc: SC::Val, pub end_shard: SC::Val, + pub total_core_shards: usize, } /// A variable version of the [SP1DeferredMemoryLayout] struct. @@ -73,6 +74,7 @@ pub struct SP1DeferredMemoryLayoutVariable { pub leaf_challenger: DuplexChallengerVariable, pub end_pc: Felt, pub end_shard: Felt, + pub total_core_shards: Var, } impl SP1DeferredVerifier @@ -129,7 +131,7 @@ where proofs, start_reconstruct_deferred_digest, is_complete, - + total_core_shards, sp1_vk, committed_value_digest, deferred_proofs_digest, @@ -189,6 +191,7 @@ where } // Verify the proof. + let one_var = builder.constant(C::N::one()); StarkVerifier::::verify_shard( builder, &compress_vk, @@ -196,6 +199,7 @@ where machine, &mut challenger, &proof, + one_var, ); // Load the public values from the proof. @@ -290,6 +294,7 @@ where // Set the is_complete flag. deferred_public_values.is_complete = var2felt(builder, is_complete); + deferred_public_values.total_core_shards = var2felt(builder, total_core_shards); commit_public_values(builder, deferred_public_values); } diff --git a/recursion/program/src/machine/mod.rs b/recursion/program/src/machine/mod.rs index 645e46b911..23235b260f 100644 --- a/recursion/program/src/machine/mod.rs +++ b/recursion/program/src/machine/mod.rs @@ -91,6 +91,7 @@ mod tests { ) .unwrap(); machine.verify(&vk, &proof, &mut challenger).unwrap(); + let total_core_shards = proof.shard_proofs.len(); tracing::info!("Proof generated successfully"); let elapsed = time.elapsed(); tracing::info!("Execution proof time: {:?}", elapsed); @@ -121,6 +122,7 @@ mod tests { leaf_challenger: &leaf_challenger, initial_reconstruct_challenger: reconstruct_challenger.clone(), is_complete, + total_core_shards, }); for proof in batch.iter() { @@ -226,6 +228,7 @@ mod tests { shard_proofs: batch.to_vec(), kinds, is_complete, + total_core_shards, }; let mut runtime = Runtime::::new( diff --git a/recursion/program/src/machine/root.rs b/recursion/program/src/machine/root.rs index 8e8eb72c67..e5a9613d08 100644 --- a/recursion/program/src/machine/root.rs +++ b/recursion/program/src/machine/root.rs @@ -107,7 +107,16 @@ where challenger.observe(builder, element); } // verify proof. - StarkVerifier::::verify_shard(builder, &vk, pcs, machine, &mut challenger, proof); + let one = builder.constant(C::N::one()); + StarkVerifier::::verify_shard( + builder, + &vk, + pcs, + machine, + &mut challenger, + proof, + one, + ); // Get the public inputs from the proof. let public_values_elements = (0..RECURSIVE_PROOF_NUM_PV_ELTS) diff --git a/recursion/program/src/machine/utils.rs b/recursion/program/src/machine/utils.rs index c721dae725..514fe08337 100644 --- a/recursion/program/src/machine/utils.rs +++ b/recursion/program/src/machine/utils.rs @@ -33,10 +33,12 @@ pub(crate) fn assert_complete( deferred_proofs_digest, next_pc, start_shard, + next_shard, cumulative_sum, start_reconstruct_deferred_digest, end_reconstruct_deferred_digest, leaf_challenger, + total_core_shards, .. } = public_values; @@ -46,6 +48,9 @@ pub(crate) fn assert_complete( // Assert that the start shard is equal to 1. builder.assert_felt_eq(*start_shard, C::F::one()); + // Assert that total_core_shards is correct by ensuring it equals next_shard - 1. + builder.assert_felt_eq(*total_core_shards, *next_shard - C::F::one()); + // The challenger has been fully verified. // The start_reconstruct_challenger should be the same as an empty challenger observing the diff --git a/recursion/program/src/stark.rs b/recursion/program/src/stark.rs index c9c8e4000e..4daa1f2561 100644 --- a/recursion/program/src/stark.rs +++ b/recursion/program/src/stark.rs @@ -120,6 +120,7 @@ where machine: &StarkMachine, challenger: &mut DuplexChallengerVariable, proof: &ShardProofVariable, + total_shards: Var, ) where A: MachineAir + for<'a> Air>, C::F: TwoAdicField, @@ -327,7 +328,7 @@ where } if chip.name() == "MemoryInit" { - builder.if_eq(shard, C::N::one()).then_or_else( + builder.if_eq(shard, total_shards).then_or_else( |builder| { builder.assert_var_ne(index, C::N::from_canonical_usize(EMPTY)); }, @@ -338,7 +339,7 @@ where } if chip.name() == "MemoryFinalize" { - builder.if_eq(shard, C::N::one()).then_or_else( + builder.if_eq(shard, total_shards).then_or_else( |builder| { builder.assert_var_ne(index, C::N::from_canonical_usize(EMPTY)); }, diff --git a/tests/tendermint-benchmark/Cargo.lock b/tests/tendermint-benchmark/Cargo.lock index 1ebe15982b..a7fff84f09 100644 --- a/tests/tendermint-benchmark/Cargo.lock +++ b/tests/tendermint-benchmark/Cargo.lock @@ -138,7 +138,7 @@ dependencies = [ [[package]] name = "curve25519-dalek-ng" version = "4.1.1" -source = "git+https://github.com/sp1-patches/curve25519-dalek-ng.git#01f43665631d2c33385708d41d0c26dbb1baa0ea" +source = "git+https://github.com/sp1-patches/curve25519-dalek-ng.git?branch=patch-v4.1.1#f5607edd61ad8e9d80c9be933cd119f4008044d9" dependencies = [ "byteorder", "digest 0.9.0", @@ -225,7 +225,7 @@ dependencies = [ [[package]] name = "ed25519-consensus" version = "2.1.0" -source = "git+https://github.com/sp1-patches/ed25519-consensus?branch=patch-v2.1.0#59f16610526726fa5c96dd77ca792f821021b66e" +source = "git+https://github.com/sp1-patches/ed25519-consensus?branch=patch-v2.1.0#4fba9b0acc9fcf7a87d00da84c340d5988e3d7cb" dependencies = [ "curve25519-dalek-ng", "hex", @@ -457,6 +457,12 @@ dependencies = [ "signature", ] +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + [[package]] name = "libc" version = "0.2.153" @@ -919,6 +925,7 @@ dependencies = [ "cfg-if", "getrandom", "k256", + "lazy_static", "libm", "once_cell", "rand",