Skip to content

Commit bdb4831

Browse files
jonathanpwangHrikByi-sun
authored
fix(recursion): final_poly & FRI missing randomness (#1703)
Updating the recursive verifier to match with the fix in GHSA-f69f-5fx9-w9r9 ## Final poly length plonky3 has changed so that `proof.final_poly.len()` must equal `config.final_poly_len()`. Since our recursion program only supports final poly length = 0, we can remove the previous checks that higher degree coefficients are zero. ## Missing randomness in mixed domain handling in FRI folding The folding needs to add `beta^2` to roll in new terms from reduced opening. We introduce a new array `betas_squared` to precompute and cache the squares since the `betas` are re-used across different FRI queries. (Side note on optimization: Zach and I discussed and it seemed like there is no difference between iter_zip'ing two arrays and loading them both versus storing a pair in one array since we load each Ext as a separate instruction). I changed the recursion code to use `iter_zip` now that another `betas_squared` needed to be passed in and we generally want to use `iter_zip` everywhere. There is no enumerate support in `iter_zip`, so I created another array of the indices to also zip over. Note: I think we previously were just skipping and not checking `ro[log_blowup]` corresponding to trace height 1 matrices. This is now fixed as explained in the comments (the reasoning is slightly different than in the Plonky3 implementation). --------- Co-authored-by: HrikB <23041116+HrikB@users.noreply.github.com> Co-authored-by: Yi Sun <yi-sun@users.noreply.github.com>
1 parent 7548bdf commit bdb4831

File tree

8 files changed

+239
-190
lines changed

8 files changed

+239
-190
lines changed

Cargo.lock

Lines changed: 87 additions & 87 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[workspace.package]
2-
version = "1.1.2"
2+
version = "1.2.0"
33
edition = "2021"
44
rust-version = "1.82"
55
authors = ["OpenVM Authors"]
@@ -107,8 +107,8 @@ lto = "thin"
107107

108108
[workspace.dependencies]
109109
# Stark Backend
110-
openvm-stark-backend = { git = "https://github.com/openvm-org/stark-backend.git", tag = "v1.0.1", default-features = false }
111-
openvm-stark-sdk = { git = "https://github.com/openvm-org/stark-backend.git", tag = "v1.0.1", default-features = false }
110+
openvm-stark-backend = { git = "https://github.com/openvm-org/stark-backend.git", tag = "v1.1.0", default-features = false }
111+
openvm-stark-sdk = { git = "https://github.com/openvm-org/stark-backend.git", tag = "v1.1.0", default-features = false }
112112

113113
# OpenVM
114114
openvm-sdk = { path = "crates/sdk", default-features = false }
@@ -166,18 +166,18 @@ openvm-pairing-guest = { path = "extensions/pairing/guest", default-features = f
166166
openvm-benchmarks-utils = { path = "benchmarks/utils", default-features = false }
167167

168168
# Plonky3
169-
p3-field = { git = "https://github.com/Plonky3/Plonky3.git", rev = "1ba4e5c" }
169+
p3-field = { git = "https://github.com/Plonky3/Plonky3.git", rev = "539bbc84085efb609f4f62cb03cf49588388abdb" }
170170
p3-baby-bear = { git = "https://github.com/Plonky3/Plonky3.git", features = [
171171
"nightly-features",
172-
], rev = "1ba4e5c" }
173-
p3-dft = { git = "https://github.com/Plonky3/Plonky3.git", rev = "1ba4e5c" }
174-
p3-fri = { git = "https://github.com/Plonky3/Plonky3.git", rev = "1ba4e5c" }
175-
p3-keccak-air = { git = "https://github.com/Plonky3/Plonky3.git", rev = "1ba4e5c" }
176-
p3-merkle-tree = { git = "https://github.com/Plonky3/Plonky3.git", rev = "1ba4e5c" }
177-
p3-monty-31 = { git = "https://github.com/Plonky3/Plonky3.git", rev = "1ba4e5c" }
178-
p3-poseidon2 = { git = "https://github.com/Plonky3/Plonky3.git", rev = "1ba4e5c" }
179-
p3-poseidon2-air = { git = "https://github.com/Plonky3/Plonky3.git", rev = "1ba4e5c" }
180-
p3-symmetric = { git = "https://github.com/Plonky3/Plonky3.git", rev = "1ba4e5c" }
172+
], rev = "539bbc84085efb609f4f62cb03cf49588388abdb" }
173+
p3-dft = { git = "https://github.com/Plonky3/Plonky3.git", rev = "539bbc84085efb609f4f62cb03cf49588388abdb" }
174+
p3-fri = { git = "https://github.com/Plonky3/Plonky3.git", rev = "539bbc84085efb609f4f62cb03cf49588388abdb" }
175+
p3-keccak-air = { git = "https://github.com/Plonky3/Plonky3.git", rev = "539bbc84085efb609f4f62cb03cf49588388abdb" }
176+
p3-merkle-tree = { git = "https://github.com/Plonky3/Plonky3.git", rev = "539bbc84085efb609f4f62cb03cf49588388abdb" }
177+
p3-monty-31 = { git = "https://github.com/Plonky3/Plonky3.git", rev = "539bbc84085efb609f4f62cb03cf49588388abdb" }
178+
p3-poseidon2 = { git = "https://github.com/Plonky3/Plonky3.git", rev = "539bbc84085efb609f4f62cb03cf49588388abdb" }
179+
p3-poseidon2-air = { git = "https://github.com/Plonky3/Plonky3.git", rev = "539bbc84085efb609f4f62cb03cf49588388abdb" }
180+
p3-symmetric = { git = "https://github.com/Plonky3/Plonky3.git", rev = "539bbc84085efb609f4f62cb03cf49588388abdb" }
181181

182182
zkhash = { git = "https://github.com/HorizenLabs/poseidon2.git", rev = "bb476b9" }
183183
snark-verifier-sdk = { version = "0.2.0", default-features = false, features = [

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ OpenVM is a performant and modular zkVM framework built for customization and ex
3232

3333
## Status
3434

35-
As of May 2025, OpenVM v1.1.0 and later are recommended for production use. OpenVM completed an external [audit](https://github.com/openvm-org/openvm/blob/main/audits/v1-cantina-report.pdf) on [Cantina](https://cantina.xyz/) from January to March 2025 as well as an internal [audit](https://github.com/openvm-org/openvm/blob/main/audits/v1-internal/README.md) by members of the [Axiom](https://axiom.xyz/) team during the same timeframe.
35+
As of June 2025, OpenVM v1.2.0 and later are recommended for production use. OpenVM completed an external [audit](https://github.com/openvm-org/openvm/blob/main/audits/v1-cantina-report.pdf) on [Cantina](https://cantina.xyz/) from January to March 2025 as well as an internal [audit](https://github.com/openvm-org/openvm/blob/main/audits/v1-internal/README.md) by members of the [Axiom](https://axiom.xyz/) team during the same timeframe.
3636

3737
## For Users
3838

book/src/getting-started/install.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ To use OpenVM for generating proofs, you must install the OpenVM command line to
99
Begin the installation:
1010

1111
```bash
12-
cargo install --locked --git http://github.com/openvm-org/openvm.git --tag v1.1.2 cargo-openvm
12+
cargo install --locked --git http://github.com/openvm-org/openvm.git --tag v1.2.0 cargo-openvm
1313
```
1414

1515
This will globally install `cargo-openvm`. You can validate a successful installation with:
@@ -23,7 +23,7 @@ cargo openvm --version
2323
To build from source, clone the repository and begin the installation.
2424

2525
```bash
26-
git clone --branch v1.1.2 --single-branch https://github.com/openvm-org/openvm.git
26+
git clone --branch v1.2.0 --single-branch https://github.com/openvm-org/openvm.git
2727
cd openvm
2828
cargo install --locked --force --path crates/cli
2929
```

book/src/introduction.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ The following chapters will guide you through:
3333

3434
## Security Status
3535

36-
As of May 2025, OpenVM v1.1.0 and later are recommended for production use. OpenVM completed an external [audit](https://github.com/openvm-org/openvm/blob/main/audits/v1-cantina-report.pdf) on [Cantina](https://cantina.xyz/) from January to March 2025 as well as an internal [audit](https://github.com/openvm-org/openvm/blob/main/audits/v1-internal/README.md) by members of the [Axiom](https://axiom.xyz/) team during the same timeframe.
36+
As of June 2025, OpenVM v1.2.0 and later are recommended for production use. OpenVM completed an external [audit](https://github.com/openvm-org/openvm/blob/main/audits/v1-cantina-report.pdf) on [Cantina](https://cantina.xyz/) from January to March 2025 as well as an internal [audit](https://github.com/openvm-org/openvm/blob/main/audits/v1-internal/README.md) by members of the [Axiom](https://axiom.xyz/) team during the same timeframe.
3737

3838
> 📖 **About this book**
3939
>

book/src/writing-apps/solidity.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Solidity SDK
22

3-
As a supplement to OpenVM, we provide a [Solidity SDK](https://github.com/openvm-org/openvm-solidity-sdk) containing OpenVM verifier contracts generated at official release commits using the `cargo openvm setup` [command](../advanced-usage/sdk.md#setup). The contracts are built at every _minor_ release as OpenVM guarantees verifier backward compatibility across patch releases.
3+
As a supplement to OpenVM, we provide a [Solidity SDK](https://github.com/openvm-org/openvm-solidity-sdk) containing OpenVM verifier contracts generated at official release commits using the `cargo openvm setup` [command](../advanced-usage/sdk.md#setup). The contracts are built at every _minor_ release as OpenVM guarantees verifier backward compatibility across patch releases.
44

55
Note that these builds are for the default aggregation VM config which should be sufficient for most users. If you use a custom config, you will need to manually generate the verifier contract using the [OpenVM SDK](../advanced-usage/sdk.md).
66

@@ -17,13 +17,13 @@ forge install openvm-org/openvm-solidity-sdk
1717
Once you have the SDK installed, you can import the SDK contracts into your Solidity project:
1818

1919
```solidity
20-
import "openvm-solidity-sdk/v1.1/OpenVmHalo2Verifier.sol";
20+
import "openvm-solidity-sdk/v1.2/OpenVmHalo2Verifier.sol";
2121
```
2222

2323
If you are using an already-deployed verifier contract, you can simply import the `IOpenVmHalo2Verifier` interface:
2424

2525
```solidity
26-
import { IOpenVmHalo2Verifier } from "openvm-solidity-sdk/v1.1/interfaces/IOpenVmHalo2Verifier.sol";
26+
import { IOpenVmHalo2Verifier } from "openvm-solidity-sdk/v1.2/interfaces/IOpenVmHalo2Verifier.sol";
2727
2828
contract MyContract {
2929
function myFunction() public view {
@@ -49,7 +49,7 @@ To deploy an instance of a verifier contract, you can clone the repo and simply
4949
```bash
5050
git clone --recursive https://github.com/openvm-org/openvm-solidity-sdk.git
5151
cd openvm-solidity-sdk
52-
forge create src/v1.1/OpenVmHalo2Verifier.sol:OpenVmHalo2Verifier --rpc-url $RPC --private-key $PRIVATE_KEY --broadcast
52+
forge create src/v1.2/OpenVmHalo2Verifier.sol:OpenVmHalo2Verifier --rpc-url $RPC --private-key $PRIVATE_KEY --broadcast
5353
```
5454

5555
We recommend a direct deployment from the SDK repo since the proper compiler configurations are all pre-set.

extensions/native/recursion/src/fri/mod.rs

Lines changed: 100 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -30,23 +30,25 @@ pub mod witness;
3030
///
3131
/// Reference: <https://github.com/Plonky3/Plonky3/blob/4809fa7bedd9ba8f6f5d3267b1592618e3776c57/fri/src/verifier.rs#L101>
3232
#[allow(clippy::too_many_arguments)]
33-
#[allow(unused_variables)]
34-
pub fn verify_query<C: Config>(
33+
fn verify_query<C: Config>(
3534
builder: &mut Builder<C>,
3635
config: &FriConfigVariable<C>,
3736
commit_phase_commits: &Array<C, DigestVariable<C>>,
3837
index_bits: &Array<C, Var<C::N>>,
3938
proof: &FriQueryProofVariable<C>,
4039
betas: &Array<C, Ext<C::F, C::EF>>,
40+
betas_squared: &Array<C, Ext<C::F, C::EF>>,
4141
reduced_openings: &Array<C, Ext<C::F, C::EF>>,
4242
log_max_lde_height: RVar<C::N>,
43+
i_plus_one_arr: &Array<C, Usize<C::N>>,
4344
) -> Ext<C::F, C::EF>
4445
where
4546
C::F: TwoAdicField,
4647
C::EF: TwoAdicField,
4748
{
4849
builder.cycle_tracker_start("verify-query");
49-
let folded_eval: Ext<C::F, C::EF> = builder.eval(C::F::ZERO);
50+
// reduced_openings.len() == MAX_TWO_ADICITY >= log_max_lde_height
51+
let folded_eval: Ext<C::F, C::EF> = builder.get(reduced_openings, log_max_lde_height);
5052
let two_adic_generator_f = config.get_two_adic_generator(builder, log_max_lde_height);
5153

5254
let two_adic_gen_ext = two_adic_generator_f.to_operand().symbolic();
@@ -55,84 +57,113 @@ where
5557
let index_bits_truncated = index_bits.slice(builder, 0, log_max_lde_height);
5658
let x = builder.exp_bits_big_endian(two_adic_generator_ef, &index_bits_truncated);
5759

58-
// proof.commit_phase_openings.len() == log_max_lde_height - log_blowup
60+
// assert proof.commit_phase_openings.len() == log_max_height
61+
// where
62+
// - commit_phase_commits.len() = log_max_height is compile-time constant (assuming
63+
// log_final_poly_len = 0)
64+
// - log_max_lde_height = log_max_height + log_blowup by definition in verify_two_adic_pcs
5965
builder.assert_usize_eq(
6066
proof.commit_phase_openings.len(),
6167
commit_phase_commits.len(),
6268
);
63-
builder
64-
.range(0, commit_phase_commits.len())
65-
.for_each(|i_vec, builder| {
66-
let i = i_vec[0];
67-
let log_folded_height = builder.eval_expr(log_max_lde_height - i - C::N::ONE);
68-
let log_folded_height_plus_one = builder.eval_expr(log_folded_height + C::N::ONE);
69-
let commit = builder.get(commit_phase_commits, i);
70-
let step = builder.get(&proof.commit_phase_openings, i);
71-
let beta = builder.get(betas, i);
72-
73-
// reduced_openings.len() == MAX_TWO_ADICITY >= log_max_lde_height >=
74-
// log_folded_height_plus_one
75-
let reduced_opening = builder.get(reduced_openings, log_folded_height_plus_one);
76-
builder.assign(&folded_eval, folded_eval + reduced_opening);
77-
78-
let index_bit = builder.get(index_bits, i);
79-
let index_sibling_mod_2: Var<C::N> =
80-
builder.eval(SymbolicVar::from(C::N::ONE) - index_bit);
81-
let i_plus_one = builder.eval_expr(i + RVar::one());
82-
let index_pair = index_bits.shift(builder, i_plus_one);
83-
84-
let evals: Array<C, Ext<C::F, C::EF>> = builder.array(2);
85-
let eval_0: Ext<C::F, C::EF>;
86-
let eval_1: Ext<C::F, C::EF>;
87-
if builder.flags.static_only {
88-
[eval_0, eval_1] = cond_eval(
89-
builder,
90-
index_sibling_mod_2,
91-
step.sibling_value,
92-
folded_eval,
93-
);
94-
builder.set_value(&evals, 0, eval_0);
95-
builder.set_value(&evals, 1, eval_1);
96-
} else {
97-
builder.set_value(&evals, 0, folded_eval);
98-
builder.set_value(&evals, 1, folded_eval);
99-
// This is faster than branching.
100-
builder.set_value(&evals, index_sibling_mod_2, step.sibling_value);
101-
eval_0 = builder.get(&evals, 0);
102-
eval_1 = builder.get(&evals, 1);
103-
}
69+
// By definition in verify_two_adic_pcs:
70+
// - betas, betas_squared, i_plus_one_arr have length log_max_height
71+
// - index_bits has length log_max_lde_height
72+
iter_zip!(
73+
builder,
74+
commit_phase_commits,
75+
proof.commit_phase_openings,
76+
betas,
77+
betas_squared,
78+
i_plus_one_arr,
79+
index_bits
80+
)
81+
.for_each(|ptr_vec, builder| {
82+
let [comm_ptr, opening_ptr, beta_ptr, beta_sq_ptr, i_plus_one_ptr, index_bit_ptr] =
83+
ptr_vec.try_into().unwrap();
10484

105-
let dims = DimensionsVariable::<C> {
106-
log_height: builder.eval(log_folded_height),
107-
};
108-
let dims_slice: Array<C, DimensionsVariable<C>> = builder.array(1);
109-
builder.set_value(&dims_slice, 0, dims);
85+
let i_plus_one = builder.iter_ptr_get(i_plus_one_arr, i_plus_one_ptr);
86+
let i_plus_one = RVar::from(i_plus_one);
87+
let log_folded_height = builder.eval_expr(log_max_lde_height - i_plus_one);
88+
let commit = builder.iter_ptr_get(commit_phase_commits, comm_ptr);
89+
let step = builder.iter_ptr_get(&proof.commit_phase_openings, opening_ptr);
90+
let beta = builder.iter_ptr_get(betas, beta_ptr);
91+
let beta_sq = builder.iter_ptr_get(betas_squared, beta_sq_ptr);
11092

111-
let opened_values = builder.array(1);
112-
builder.set_value(&opened_values, 0, evals);
113-
builder.cycle_tracker_start("verify-batch-ext");
114-
verify_batch::<C>(
93+
let index_bit = builder.iter_ptr_get(index_bits, index_bit_ptr);
94+
let index_sibling_mod_2: Var<C::N> = builder.eval(SymbolicVar::from(C::N::ONE) - index_bit);
95+
let index_pair = index_bits.shift(builder, i_plus_one);
96+
97+
let evals: Array<C, Ext<C::F, C::EF>> = builder.array(2);
98+
let eval_0: Ext<C::F, C::EF>;
99+
let eval_1: Ext<C::F, C::EF>;
100+
if builder.flags.static_only {
101+
[eval_0, eval_1] = cond_eval(
115102
builder,
116-
&commit,
117-
dims_slice,
118-
index_pair,
119-
&NestedOpenedValues::Ext(opened_values),
120-
&step.opening_proof,
103+
index_sibling_mod_2,
104+
step.sibling_value,
105+
folded_eval,
121106
);
122-
builder.cycle_tracker_end("verify-batch-ext");
107+
builder.set_value(&evals, 0, eval_0);
108+
builder.set_value(&evals, 1, eval_1);
109+
} else {
110+
builder.set_value(&evals, 0, folded_eval);
111+
builder.set_value(&evals, 1, folded_eval);
112+
// This is faster than branching.
113+
builder.set_value(&evals, index_sibling_mod_2, step.sibling_value);
114+
eval_0 = builder.get(&evals, 0);
115+
eval_1 = builder.get(&evals, 1);
116+
}
117+
118+
let dims = DimensionsVariable::<C> {
119+
log_height: builder.eval(log_folded_height),
120+
};
121+
let dims_slice: Array<C, DimensionsVariable<C>> = builder.array(1);
122+
builder.set_value(&dims_slice, 0, dims);
123123

124-
let two_adic_generator_one = config.get_two_adic_generator(builder, Usize::from(1));
124+
let opened_values = builder.array(1);
125+
builder.set_value(&opened_values, 0, evals);
126+
builder.cycle_tracker_start("verify-batch-ext");
127+
verify_batch::<C>(
128+
builder,
129+
&commit,
130+
dims_slice,
131+
index_pair,
132+
&NestedOpenedValues::Ext(opened_values),
133+
&step.opening_proof,
134+
);
135+
builder.cycle_tracker_end("verify-batch-ext");
125136

126-
let [xs_0, xs_1]: [Ext<_, _>; 2] =
127-
cond_eval(builder, index_sibling_mod_2, x * two_adic_generator_one, x);
137+
let two_adic_generator_one = config.get_two_adic_generator(builder, Usize::from(1));
128138

129-
builder.assign(
130-
&folded_eval,
131-
eval_0 + (beta - xs_0) * (eval_1 - eval_0) / (xs_1 - xs_0),
132-
);
139+
let [xs_0, xs_1]: [Ext<_, _>; 2] =
140+
cond_eval(builder, index_sibling_mod_2, x * two_adic_generator_one, x);
133141

134-
builder.assign(&x, x * x);
135-
});
142+
builder.assign(
143+
&folded_eval,
144+
eval_0 + (beta - xs_0) * (eval_1 - eval_0) / (xs_1 - xs_0),
145+
);
146+
147+
builder.assign(&x, x * x);
148+
149+
// reduced_openings.len() == MAX_TWO_ADICITY >= log_max_lde_height >= log_folded_height
150+
let reduced_opening = builder.get(reduced_openings, log_folded_height);
151+
// Roll in new reduced opening polynomial at the folded height. This is 0 if there is no
152+
// reduced opening at the folded height.
153+
//
154+
// Each `reduced_opening` is the evaluation of a reduced opening polynomial, which is itself
155+
// a random linear combination `f_{i, 0}(x) + alpha f_{i, 1}(x) + ...`, but when we add it
156+
// to the current folded polynomial evaluation claim, we need to multiply by a new random
157+
// factor since `f_{i, 0}` has no leading coefficient.
158+
//
159+
// We use `beta^2` as the random factor since `beta` is already used in the folding.
160+
//
161+
// Note: this will include the case `reduced_opening = reduced_openings[log_blowup]` in the
162+
// last iteration of the loop. Since we roll this in with the beta^2 factor, the low degree
163+
// test will also include this case (corresponding to `log_height = 0`, which is not done in
164+
// the Plonky3 implementation).
165+
builder.assign(&folded_eval, folded_eval + beta_sq * reduced_opening);
166+
});
136167

137168
builder.cycle_tracker_end("verify-query");
138169
folded_eval

0 commit comments

Comments
 (0)