midnight_circuits/vec/vector.rs
1// This file is part of MIDNIGHT-ZK.
2// Copyright (C) Midnight Foundation
3// SPDX-License-Identifier: Apache-2.0
4// Licensed under the Apache License, Version 2.0 (the "License");
5// You may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7// http://www.apache.org/licenses/LICENSE-2.0
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14use std::ops::Range;
15
16use midnight_proofs::circuit::Value;
17
18use crate::{
19 field::AssignedNative,
20 types::{AssignedByte, InnerValue},
21 CircuitField,
22};
23
24/// A variable-length vector of elements of type T, with size bound M.
25/// - `len` is the (potentially secret) effective length of the vector, its
26/// value is guaranteed to be in the range `[0, M]`.
27/// - `buffer` is the padded payload of this vector; it contains the effective
28/// data of the vector as well as filler values, which are UNCONSTRAINED.
29///
30/// The effective payload in the data is aligned in A sized chunks. This
31/// enables more efficient implementations of instructions like hashing
32/// over this type. As a result of this alignment, the data may contain filler
33/// values before and after the effective payload. The padding in front of
34/// the payload will always be 0 mod A, so that the payload begins aligned in A
35/// sized chunks. The padding at the end of the payload will have a size in
36/// [0, A) such that | front_pad | + | payload | + | back_pad | = M.
37///
38/// **Invariant: `M` must be a multiple of `A`** (and `A > 0`, `M >= A`).
39/// Several operations (`padding_flag`, `get_limits`, hashing, …) rely on this
40/// to guarantee that the buffer decomposes into exactly `M / A` full chunks
41/// and that a full-capacity vector (`len == M`) can always be placed without
42/// overflow. This is enforced by the entry points
43/// [`assign_with_filler`](crate::instructions::VectorInstructions::assign_with_filler) and
44/// [`assign`](crate::instructions::AssignmentInstructions::assign).
45#[derive(Clone, Debug)]
46pub struct AssignedVector<F: CircuitField, T: Vectorizable, const M: usize, const A: usize> {
47 /// Padded payload of the vector. Boxed to keep large buffers
48 /// off the stack.
49 pub(crate) buffer: Box<[T; M]>,
50
51 /// Effective length of the vector.
52 pub(crate) len: AssignedNative<F>,
53}
54
55/// Returns the range where the data should be placed in the buffer.
56pub fn get_lims<const M: usize, const A: usize>(len: usize) -> Range<usize> {
57 let final_pad_len = (A - (len % A)) % A;
58 M - len - final_pad_len..M - final_pad_len
59}
60
61impl<F: CircuitField, const M: usize, T: Vectorizable, const A: usize> InnerValue
62 for AssignedVector<F, T, M, A>
63{
64 type Element = Vec<T::Element>;
65
66 fn value(&self) -> Value<Self::Element> {
67 let data = Value::<Vec<T::Element>>::from_iter(self.buffer.iter().map(|v| v.value()));
68 let idxs: Value<_> = self.len.value().map(|len| {
69 let len: usize = len.to_biguint().try_into().unwrap();
70
71 let end_pad = (A - (len % A)) % A;
72 (M - len - end_pad, M - end_pad)
73 });
74 data.zip(idxs).map(|(data, idxs)| data[idxs.0..idxs.1].to_vec())
75 }
76}
77
78/// Trait for the individual elements of an AssignedVector.
79pub trait Vectorizable: InnerValue {
80 /// Value to fill the space in the buffer that is not occupied with vector
81 /// data.
82 const FILLER: Self::Element;
83}
84
85impl<F: CircuitField> Vectorizable for AssignedNative<F> {
86 const FILLER: F = F::ZERO;
87}
88
89impl<F: CircuitField> Vectorizable for AssignedByte<F> {
90 const FILLER: u8 = 0u8;
91}