|
1 | | -use alloy_primitives::B256; |
2 | 1 | use anyhow::{Ok, Result, anyhow, ensure}; |
3 | | -use discv5::enr::k256::{ |
4 | | - Scalar, |
5 | | - elliptic_curve::{Field, PrimeField}, |
6 | | -}; |
7 | 2 | use ream_consensus_misc::{ |
8 | | - constants::beacon::{ |
9 | | - BYTES_PER_BLOB, BYTES_PER_FIELD_ELEMENT, CELLS_PER_EXT_BLOB, FIELD_ELEMENTS_PER_BLOB, |
10 | | - FIELD_ELEMENTS_PER_CELL, FIELD_ELEMENTS_PER_EXT_BLOB, |
11 | | - }, |
12 | | - polynomial_commitments::kzg_proof::KZGProof, |
| 3 | + constants::beacon::CELLS_PER_EXT_BLOB, polynomial_commitments::kzg_proof::KZGProof, |
13 | 4 | }; |
14 | 5 | use ream_execution_rpc_types::get_blobs::Blob; |
15 | | -use rust_eth_kzg::{Cell as KZGCell, CellIndex, DASContext, KZGProof as Proof}; |
| 6 | +use rust_eth_kzg::{Cell as KZGCell, DASContext, KZGProof as Proof}; |
16 | 7 | use ssz_types::FixedVector; |
17 | 8 |
|
18 | 9 | use crate::data_column_sidecar::Cell; |
@@ -144,231 +135,25 @@ fn convert_kzg_proof(kzg_proof: Proof) -> KZGProof { |
144 | 135 | KZGProof::from(kzg_proof) |
145 | 136 | } |
146 | 137 |
|
147 | | -/// Convert untrusted bytes to a trusted and validated BLS scalar field element. |
148 | | -/// This function does not accept inputs greater than the BLS modulus. |
149 | | -pub fn bytes_to_bls_field(b: B256) -> anyhow::Result<Scalar> { |
150 | | - ensure!(b.len() == 32, "Invalid input length for field element"); |
151 | | - |
152 | | - let field_element = Scalar::from_repr((*b).into()) |
153 | | - .into_option() |
154 | | - .ok_or_else(|| anyhow!("Bytes exceed the field modulus"))?; |
155 | | - Ok(field_element) |
156 | | -} |
157 | | - |
158 | | -/// Convert a blob to list of BLS field scalars. |
159 | | -pub fn blob_to_polynomial(blob: Blob) -> anyhow::Result<[Scalar; FIELD_ELEMENTS_PER_BLOB]> { |
160 | | - let mut polynomial = [Scalar::default(); FIELD_ELEMENTS_PER_BLOB]; |
161 | | - |
162 | | - for (i, polynomial_element) in polynomial.iter_mut().enumerate() { |
163 | | - let start = i * BYTES_PER_FIELD_ELEMENT; |
164 | | - let end = (i + 1) * BYTES_PER_FIELD_ELEMENT; |
165 | | - |
166 | | - let chunk: B256 = blob.inner[start..end] |
167 | | - .try_into() |
168 | | - .map_err(|err| anyhow!("Invalid chunk size at index {err:?}"))?; |
169 | | - |
170 | | - *polynomial_element = bytes_to_bls_field(chunk)?; |
171 | | - } |
172 | | - |
173 | | - Ok(polynomial) |
174 | | -} |
175 | | - |
176 | | -/// Return ``x`` to power of [0, n-1], if n > 0. When n==0, an empty array is returned. |
177 | | -pub fn compute_powers(x: Scalar, n: u64) -> anyhow::Result<Vec<Scalar>> { |
178 | | - let mut powers = Vec::with_capacity(n as usize); |
179 | | - let mut current_power = Scalar::ONE; |
180 | | - |
181 | | - for _ in 0..n { |
182 | | - powers.push(current_power); |
183 | | - current_power *= x; |
184 | | - } |
185 | | - |
186 | | - Ok(powers) |
187 | | -} |
188 | | - |
189 | | -/// Return roots of unity of ``order``. |
190 | | -pub fn compute_roots_of_unity(order: u64) -> anyhow::Result<Vec<Scalar>> { |
191 | | - ensure!( |
192 | | - order <= (1 << Scalar::S), |
193 | | - "Order exceeds maximum supported by field" |
194 | | - ); |
195 | | - let ratio = (1 << Scalar::S) / order; |
196 | | - let exponent: [u64; 4] = [ratio, 0, 0, 0]; |
197 | | - |
198 | | - let root = Scalar::ROOT_OF_UNITY.pow_vartime(exponent); |
199 | | - |
200 | | - Ok(compute_powers(root, order)?) |
201 | | -} |
202 | | - |
203 | | -fn _fft_field(vals: Vec<Scalar>, roots_of_unity: Vec<Scalar>) -> anyhow::Result<Vec<Scalar>> { |
204 | | - let n = vals.len(); |
205 | | - if n <= 1 { |
206 | | - return Ok(vals); |
207 | | - } |
208 | | - let mut evens = Vec::with_capacity(n / 2); |
209 | | - let mut odds = Vec::with_capacity(n / 2); |
210 | | - for (i, val) in vals.into_iter().enumerate() { |
211 | | - if i % 2 == 0 { |
212 | | - evens.push(val); |
213 | | - } else { |
214 | | - odds.push(val); |
215 | | - } |
216 | | - } |
217 | | - let next_roots: Vec<Scalar> = roots_of_unity.iter().step_by(2).cloned().collect(); |
218 | | - let left = _fft_field(evens, next_roots.clone())?; |
219 | | - let right = _fft_field(odds, next_roots)?; |
220 | | - |
221 | | - let mut result = vec![Scalar::ZERO; n]; |
222 | | - for i in 0..(n / 2) { |
223 | | - let root = roots_of_unity[i]; |
224 | | - let y_times_root = right[i] * root; |
225 | | - result[i] = left[i] + y_times_root; |
226 | | - result[i + (n / 2)] = left[i] - y_times_root; |
227 | | - } |
228 | | - |
229 | | - Ok(result) |
230 | | -} |
231 | | - |
232 | | -pub fn fft_field( |
233 | | - vals: Vec<Scalar>, |
234 | | - roots_of_unity: Vec<Scalar>, |
235 | | - inv: bool, |
236 | | -) -> anyhow::Result<Vec<Scalar>> { |
237 | | - if inv { |
238 | | - let n = vals.len() as u64; |
239 | | - let invlen = Scalar::from(n).invert().unwrap(); |
240 | | - |
241 | | - let mut reversed_roots = Vec::with_capacity(roots_of_unity.len()); |
242 | | - if !roots_of_unity.is_empty() { |
243 | | - reversed_roots.push(roots_of_unity[0]); |
244 | | - reversed_roots.extend(roots_of_unity.iter().skip(1).rev()); |
245 | | - } |
246 | | - |
247 | | - let fft_result = _fft_field(vals, reversed_roots)?; |
248 | | - let result = fft_result.into_iter().map(|x| x * invlen).collect(); |
249 | | - |
250 | | - Ok(result) |
251 | | - } else { |
252 | | - Ok(_fft_field(vals, roots_of_unity)?) |
253 | | - } |
254 | | -} |
255 | | - |
256 | | -/// Reverse the bit order of an integer ``n``. |
257 | | -pub fn reverse_bits(n: usize, order: usize) -> anyhow::Result<usize> { |
258 | | - ensure!(order.is_power_of_two(), "Order must be a power of two"); |
259 | | - |
260 | | - let width = order.trailing_zeros(); |
261 | | - let mut result = 0; |
262 | | - let mut temp_n = n; |
263 | | - |
264 | | - for _ in 0..width { |
265 | | - result = (result << 1) | (temp_n & 1); |
266 | | - temp_n >>= 1; |
267 | | - } |
268 | | - |
269 | | - Ok(result) |
270 | | -} |
271 | | - |
272 | | -/// Return a copy with bit-reversed permutation. The permutation is an involution (inverts itself). |
273 | | -/// |
274 | | -/// The input and output are a sequence of generic type ``T`` objects. |
275 | | -pub fn bit_reversal_permutation<T: Clone>(sequence: Vec<T>) -> anyhow::Result<Vec<T>> { |
276 | | - let n = sequence.len(); |
277 | | - let mut result = Vec::with_capacity(n); |
278 | | - |
279 | | - for i in 0..n { |
280 | | - let rev_idx = reverse_bits(i, n)?; |
281 | | - result.push(sequence[rev_idx].clone()); |
282 | | - } |
283 | | - |
284 | | - Ok(result) |
285 | | -} |
286 | | - |
287 | | -/// Interpolates a polynomial (given in evaluation form) to a polynomial in coefficient form. |
288 | | -pub fn polynomial_eval_to_coeff( |
289 | | - polynomial: [Scalar; FIELD_ELEMENTS_PER_BLOB], |
290 | | -) -> anyhow::Result<Vec<Scalar>> { |
291 | | - let roots_of_unity = compute_roots_of_unity(FIELD_ELEMENTS_PER_BLOB as u64)?; |
292 | | - let rearranged = bit_reversal_permutation(polynomial.to_vec())?; |
293 | | - |
294 | | - fft_field(rearranged, roots_of_unity, true) |
295 | | -} |
296 | | - |
297 | | -/// Get the coset for a given ``cell_index``. |
298 | | -/// Precisely, consider the group of roots of unity of order FIELD_ELEMENTS_PER_CELL * |
299 | | -/// CELLS_PER_EXT_BLOB. Let G = {1, g, g^2, ...} denote its subgroup of order |
300 | | -/// FIELD_ELEMENTS_PER_CELL. Then, the coset is defined as h * G = {h, hg, hg^2, ...}. |
301 | | -/// This function, returns the coset. |
302 | | -pub fn coset_for_cell(cell_index: CellIndex) -> anyhow::Result<[Scalar; FIELD_ELEMENTS_PER_CELL]> { |
303 | | - ensure!( |
304 | | - cell_index < CELLS_PER_EXT_BLOB, |
305 | | - "Cell index great then CELLS_PER_EXT_BLOB" |
306 | | - ); |
307 | | - let roots = compute_roots_of_unity(FIELD_ELEMENTS_PER_EXT_BLOB as u64)?; |
308 | | - let roots_of_unity_brp = bit_reversal_permutation(roots)?; |
309 | | - |
310 | | - let start = (cell_index as usize) * FIELD_ELEMENTS_PER_CELL; |
311 | | - let end = start + FIELD_ELEMENTS_PER_CELL; |
312 | | - |
313 | | - let coset_slice = &roots_of_unity_brp[start..end]; |
314 | | - |
315 | | - let coset: [Scalar; FIELD_ELEMENTS_PER_CELL] = coset_slice |
316 | | - .try_into() |
317 | | - .map_err(|err| anyhow!("Slice length mismatch for coset conversion {err:?}"))?; |
318 | | - |
319 | | - Ok(coset) |
320 | | -} |
321 | | - |
322 | | -/// Evaluate a coefficient form polynomial at ``z`` using Horner's schema. |
323 | | -pub fn evaluate_polynomialcoeff(polynomial_coeff: &[Scalar], z: Scalar) -> anyhow::Result<Scalar> { |
324 | | - let mut y = Scalar::ZERO; |
325 | | - for coef in polynomial_coeff.iter().rev() { |
326 | | - y = (y * z) + *coef; |
327 | | - } |
328 | | - |
329 | | - Ok(y) |
330 | | -} |
331 | | - |
332 | | -pub fn bls_field_to_bytes(x: Scalar) -> B256 { |
333 | | - let bytes: [u8; 32] = x.to_bytes().into(); |
334 | | - B256::from(bytes) |
335 | | -} |
336 | | - |
337 | | -/// Convert a trusted ``CosetEval`` into an untrusted ``Cell``. |
338 | | -pub fn coset_evals_to_cell(coset_evals: [Scalar; FIELD_ELEMENTS_PER_CELL]) -> anyhow::Result<Cell> { |
339 | | - let mut cell_bytes = Vec::with_capacity(FIELD_ELEMENTS_PER_CELL * 32); |
340 | | - for eval in coset_evals { |
341 | | - let bytes: B256 = bls_field_to_bytes(eval); |
342 | | - cell_bytes.extend_from_slice(bytes.as_slice()); |
343 | | - } |
344 | | - |
345 | | - let cell = cell_bytes |
| 138 | +pub fn compute_cells( |
| 139 | + blob: Blob, |
| 140 | + das_context: &DASContext, |
| 141 | +) -> anyhow::Result<[Cell; CELLS_PER_EXT_BLOB as usize]> { |
| 142 | + let blob_data: Vec<u8> = blob.inner.into(); |
| 143 | + let blob_bytes: &[u8; 131072] = blob_data |
| 144 | + .as_slice() |
346 | 145 | .try_into() |
347 | | - .map_err(|err| anyhow!("Failed to convert bytes to Cell FixedVector {err:?}"))?; |
348 | | - Ok(cell) |
349 | | -} |
350 | | - |
351 | | -pub fn compute_cells(blob: Blob) -> anyhow::Result<[Cell; CELLS_PER_EXT_BLOB as usize]> { |
352 | | - ensure!(blob.inner.len() == BYTES_PER_BLOB, "Invalid blob length"); |
353 | | - let polynomial = blob_to_polynomial(blob)?; |
354 | | - let polynomial_coeff = polynomial_eval_to_coeff(polynomial)?; |
355 | | - let mut cells = Vec::with_capacity(CELLS_PER_EXT_BLOB as usize); |
| 146 | + .map_err(|err| anyhow!("Invalid blob size {err:?}"))?; |
356 | 147 |
|
357 | | - for i in 0..CELLS_PER_EXT_BLOB { |
358 | | - let coset = coset_for_cell(i as CellIndex)?; |
359 | | - |
360 | | - let mut ys = [Scalar::ZERO; FIELD_ELEMENTS_PER_CELL]; |
361 | | - for (j, &z) in coset.iter().enumerate() { |
362 | | - ys[j] = evaluate_polynomialcoeff(&polynomial_coeff, z)?; |
363 | | - } |
| 148 | + let kzg_cells = das_context |
| 149 | + .compute_cells(blob_bytes) |
| 150 | + .map_err(|err| anyhow!("KZG error: {err:?}"))?; |
364 | 151 |
|
365 | | - let cell = coset_evals_to_cell(ys)?; |
366 | | - cells.push(cell); |
367 | | - } |
| 152 | + let cells: Vec<Cell> = kzg_cells.into_iter().map(convert_cell).collect(); |
368 | 153 |
|
369 | 154 | let final_cells: [Cell; CELLS_PER_EXT_BLOB as usize] = cells |
370 | 155 | .try_into() |
371 | | - .map_err(|err| anyhow!("Failed to convert cells to fixed array {err:?}"))?; |
| 156 | + .map_err(|err| anyhow!("Failed to convert to fixed array {err:?}"))?; |
372 | 157 |
|
373 | 158 | Ok(final_cells) |
374 | 159 | } |
0 commit comments