openvm_continuations/verifier/
utils.rs1use std::array;
2
3use openvm_native_compiler::prelude::*;
4use openvm_native_recursion::{config::outer::OuterConfig, hints::Hintable, types::InnerConfig};
5use openvm_stark_backend::p3_field::PrimeField32;
6use openvm_stark_sdk::{
7 openvm_stark_backend::p3_field::PrimeCharacteristicRing, p3_baby_bear::BabyBear,
8};
9use p3_bn254::Bn254;
10
11pub fn compress_babybear_var_to_bn254(
12 builder: &mut Builder<OuterConfig>,
13 var: [Var<Bn254>; DIGEST_SIZE],
14) -> Var<Bn254> {
15 let mut ret = SymbolicVar::ZERO;
16 let order = Bn254::from_u32(BabyBear::ORDER_U32);
17 let mut base = Bn254::ONE;
18 var.iter().for_each(|&x| {
19 ret += x * base;
20 base *= order;
21 });
22 builder.eval(ret)
23}
24
25pub(crate) fn assign_array_to_slice<C: Config>(
26 builder: &mut Builder<C>,
27 dst_slice: &[Felt<C::F>],
28 src: &Array<C, Felt<C::F>>,
29 src_offset: usize,
30) {
31 for (i, dst) in dst_slice.iter().enumerate() {
32 let pv = builder.get(src, i + src_offset);
33 builder.assign(dst, pv);
34 }
35}
36
37pub(crate) fn assign_slice_to_array<C: Config>(
38 builder: &mut Builder<C>,
39 dst: &Array<C, Felt<C::F>>,
40 src_slice: &[Felt<C::F>],
41) {
42 for (i, &src) in src_slice.iter().enumerate() {
43 builder.set_value(dst, i, src);
44 }
45}
46
47pub(crate) fn write_field_slice(arr: &[BabyBear; DIGEST_SIZE]) -> Vec<Vec<BabyBear>> {
48 arr.iter()
49 .flat_map(Hintable::<InnerConfig>::write)
50 .collect()
51}
52
53pub(crate) fn eq_felt_slice<C: Config, const N: usize>(
55 builder: &mut Builder<C>,
56 lhs: &[Felt<C::F>; N],
57 rhs: &[Felt<C::F>; N],
58) -> Var<C::N> {
59 let sub_res: [Felt<C::F>; N] = array::from_fn(|i| builder.eval(lhs[i] - rhs[i]));
60 let var_res = sub_res.map(|f| builder.cast_felt_to_var(f));
61 let ret: Var<C::N> = builder.eval(C::N::ONE);
62 var_res.into_iter().for_each(|v| {
63 builder
64 .if_ne(v, C::N::ZERO)
65 .then(|builder| builder.assign(&ret, C::N::ZERO))
66 });
67 ret
68}
69
70#[derive(Clone)]
71pub(crate) struct VariableP2Compressor<C: Config> {
72 state: Array<C, Felt<C::F>>,
73 lhs: Array<C, Felt<C::F>>,
74 rhs: Array<C, Felt<C::F>>,
75}
76
77impl<C: Config> VariableP2Compressor<C> {
78 pub fn new(builder: &mut Builder<C>) -> Self {
79 Self {
80 state: builder.array(PERMUTATION_WIDTH),
81 lhs: builder.array(DIGEST_SIZE),
82 rhs: builder.array(DIGEST_SIZE),
83 }
84 }
85
86 pub fn compress(
87 &self,
88 builder: &mut Builder<C>,
89 lhs: &[Felt<C::F>; DIGEST_SIZE],
90 rhs: &[Felt<C::F>; DIGEST_SIZE],
91 ) -> [Felt<C::F>; DIGEST_SIZE] {
92 assign_slice_to_array(builder, &self.lhs, lhs);
93 assign_slice_to_array(builder, &self.rhs, rhs);
94 builder.poseidon2_compress_x(&self.state, &self.lhs, &self.rhs);
95 array::from_fn(|i| builder.get(&self.state, i))
96 }
97
98 pub fn compress_array(
99 &self,
100 builder: &mut Builder<C>,
101 lhs: &Array<C, Felt<C::F>>,
102 rhs: &Array<C, Felt<C::F>>,
103 ) -> Array<C, Felt<C::F>> {
104 let ret = builder.array(DIGEST_SIZE);
105 builder.poseidon2_compress_x(&self.state, lhs, rhs);
106 for i in 0..DIGEST_SIZE {
107 let v = builder.get(&self.state, i);
108 builder.set_value(&ret, i, v);
109 }
110 ret
111 }
112}
113
114#[derive(Clone)]
115pub(crate) struct VariableP2Hasher<C: Config> {
116 pub compressor: VariableP2Compressor<C>,
117 pub const_zeros: Array<C, Felt<C::F>>,
118 pub const_zero: Felt<C::F>,
119}
120
121impl<C: Config> VariableP2Hasher<C> {
122 pub fn new(builder: &mut Builder<C>) -> Self {
123 let const_zero: Felt<C::F> = builder.eval(C::F::ZERO);
124 let const_zeros = builder.array(DIGEST_SIZE);
125 for i in 0..DIGEST_SIZE {
126 builder.set_value(&const_zeros, i, const_zero);
127 }
128 Self {
129 compressor: VariableP2Compressor::new(builder),
130 const_zeros,
131 const_zero,
132 }
133 }
134 pub fn hash(
135 &self,
136 builder: &mut Builder<C>,
137 payload: &[Felt<C::F>; DIGEST_SIZE],
138 ) -> [Felt<C::F>; DIGEST_SIZE] {
139 self.compressor
140 .compress(builder, payload, &[self.const_zero; DIGEST_SIZE])
141 }
142 pub fn hash_array(
143 &self,
144 builder: &mut Builder<C>,
145 payload: &Array<C, Felt<C::F>>,
146 ) -> Array<C, Felt<C::F>> {
147 self.compressor
148 .compress_array(builder, payload, &self.const_zeros)
149 }
150
151 pub fn merkle_root(
152 &self,
153 builder: &mut Builder<C>,
154 values: &[Felt<C::F>],
155 ) -> [Felt<C::F>; DIGEST_SIZE] {
156 assert_eq!(values.len() % DIGEST_SIZE, 0);
157 assert!((values.len() / DIGEST_SIZE).is_power_of_two());
158 let buffer = builder.array(DIGEST_SIZE);
159 let mut leaves: Vec<_> = values
160 .chunks_exact(DIGEST_SIZE)
161 .map(|chunk| {
162 assign_slice_to_array(builder, &buffer, chunk);
163 self.hash_array(builder, &buffer)
164 })
165 .collect();
166 while leaves.len() > 1 {
167 leaves = leaves
168 .chunks_exact(2)
169 .map(|chunk| {
170 self.compressor
171 .compress_array(builder, &chunk[0], &chunk[1])
172 })
173 .collect();
174 }
175 array::from_fn(|i| builder.get(&leaves[0], i))
176 }
177}