Skip to main content

snarkvm_circuit_program/data/record/
mod.rs

1// Copyright (c) 2019-2026 Provable Inc.
2// This file is part of the snarkVM library.
3
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
8// http://www.apache.org/licenses/LICENSE-2.0
9
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16#[cfg(test)]
17use snarkvm_circuit_types::environment::assert_scope;
18
19mod entry;
20pub use entry::Entry;
21
22mod helpers;
23pub use helpers::Owner;
24
25mod decrypt;
26mod encrypt;
27mod equal;
28mod find;
29mod num_randomizers;
30mod serial_number;
31mod tag;
32mod to_bits;
33mod to_commitment;
34mod to_fields;
35
36use crate::{Access, Ciphertext, Identifier, Literal, Plaintext, ProgramID, Visibility};
37use snarkvm_circuit_account::{PrivateKey, ViewKey};
38use snarkvm_circuit_network::Aleo;
39use snarkvm_circuit_types::{Boolean, Field, Group, Scalar, U8, U32, environment::prelude::*, prelude::ToFields};
40
41#[derive(Clone)]
42pub struct Record<A: Aleo, Private: Visibility<A>> {
43    /// The owner of the program record.
44    owner: Owner<A, Private>,
45    /// The program data.
46    data: IndexMap<Identifier<A>, Entry<A, Private>>,
47    /// The nonce of the program record.
48    nonce: Group<A>,
49    /// The version of the program record.
50    ///   - Version 0 uses a BHP hash to derive the record commitment.
51    ///   - Version 1 uses a BHP commitment to derive the record commitment.
52    version: U8<A>,
53}
54
55impl<A: Aleo> Inject for Record<A, Plaintext<A>> {
56    type Primitive = console::Record<A::Network, console::Plaintext<A::Network>>;
57
58    /// Initializes a plaintext record from a primitive.
59    fn new(_: Mode, record: Self::Primitive) -> Self {
60        Self {
61            owner: Owner::new(Mode::Private, record.owner().clone()),
62            data: record
63                .data()
64                .clone()
65                .into_iter()
66                .map(|(identifier, entry)| (Identifier::constant(identifier), Inject::new(Mode::Private, entry)))
67                .collect(),
68            nonce: Group::new(Mode::Private, *record.nonce()),
69            version: U8::new(Mode::Private, *record.version()),
70        }
71    }
72}
73
74impl<A: Aleo> Inject for Record<A, Ciphertext<A>> {
75    type Primitive = console::Record<A::Network, console::Ciphertext<A::Network>>;
76
77    /// Initializes a ciphertext record from a primitive.
78    fn new(_: Mode, record: Self::Primitive) -> Self {
79        Self {
80            owner: Owner::new(Mode::Private, record.owner().clone()),
81            data: record
82                .data()
83                .clone()
84                .into_iter()
85                .map(|(identifier, entry)| (Identifier::constant(identifier), Inject::new(Mode::Private, entry)))
86                .collect(),
87            nonce: Group::new(Mode::Private, *record.nonce()),
88            version: U8::new(Mode::Private, *record.version()),
89        }
90    }
91}
92
93impl<A: Aleo, Private: Visibility<A>> Record<A, Private> {
94    /// Initializes a new record plaintext.
95    pub fn from_plaintext(
96        owner: Owner<A, Plaintext<A>>,
97        data: IndexMap<Identifier<A>, Entry<A, Plaintext<A>>>,
98        nonce: Group<A>,
99        version: U8<A>,
100    ) -> Result<Record<A, Plaintext<A>>> {
101        // Ensure the members has no duplicate names.
102        ensure!(!has_duplicates(data.iter().map(|(name, ..)| name)), "A duplicate entry name was found in a record");
103        // Ensure the number of entries is within the maximum limit.
104        ensure!(
105            data.len() <= <A::Network as console::Network>::MAX_DATA_ENTRIES,
106            "Found a record that exceeds size ({})",
107            data.len()
108        );
109        // Return the record.
110        Ok(Record { owner, data, nonce, version })
111    }
112
113    /// Initializes a new record ciphertext.
114    pub fn from_ciphertext(
115        owner: Owner<A, Ciphertext<A>>,
116        data: IndexMap<Identifier<A>, Entry<A, Ciphertext<A>>>,
117        nonce: Group<A>,
118        version: U8<A>,
119    ) -> Result<Record<A, Ciphertext<A>>> {
120        // Ensure the members has no duplicate names.
121        ensure!(!has_duplicates(data.iter().map(|(name, ..)| name)), "A duplicate entry name was found in a record");
122        // Ensure the number of entries is within the maximum limit.
123        ensure!(
124            data.len() <= <A::Network as console::Network>::MAX_DATA_ENTRIES,
125            "Found a record that exceeds size ({})",
126            data.len()
127        );
128        // Return the record.
129        Ok(Record { owner, data, nonce, version })
130    }
131}
132
133impl<A: Aleo, Private: Visibility<A>> Record<A, Private> {
134    /// Returns the owner of the program record.
135    pub const fn owner(&self) -> &Owner<A, Private> {
136        &self.owner
137    }
138
139    /// Returns the program data.
140    pub const fn data(&self) -> &IndexMap<Identifier<A>, Entry<A, Private>> {
141        &self.data
142    }
143
144    /// Returns the nonce of the program record.
145    pub const fn nonce(&self) -> &Group<A> {
146        &self.nonce
147    }
148
149    /// Returns the version of the program record.
150    pub const fn version(&self) -> &U8<A> {
151        &self.version
152    }
153
154    /// Returns `true` if the program record is a hiding variant.
155    pub fn is_hiding(&self) -> Boolean<A> {
156        !self.version.is_zero()
157    }
158}
159
160impl<A: Aleo> Eject for Record<A, Plaintext<A>> {
161    type Primitive = console::Record<A::Network, console::Plaintext<A::Network>>;
162
163    /// Ejects the mode of the record.
164    fn eject_mode(&self) -> Mode {
165        let owner = match &self.owner {
166            Owner::Public(owner) => match owner.eject_mode() == Mode::Public {
167                true => Mode::Public,
168                false => A::halt("Record::<Plaintext>::eject_mode: 'owner' is not public."),
169            },
170            Owner::Private(plaintext) => match plaintext.eject_mode() == Mode::Private {
171                true => Mode::Private,
172                false => A::halt("Record::<Plaintext>::eject_mode: 'owner' is not private."),
173            },
174        };
175
176        let data = self.data.iter().map(|(_, entry)| entry.eject_mode()).collect::<Vec<_>>().eject_mode();
177        let nonce = self.nonce.eject_mode();
178        let version = self.version.eject_mode();
179
180        Mode::combine(owner, [data, nonce, version])
181    }
182
183    /// Ejects the record.
184    fn eject_value(&self) -> Self::Primitive {
185        let owner = match &self.owner {
186            Owner::Public(owner) => console::Owner::Public(owner.eject_value()),
187            Owner::Private(plaintext) => console::Owner::Private(plaintext.eject_value()),
188        };
189
190        match Self::Primitive::from_plaintext(
191            owner,
192            self.data.iter().map(|(identifier, entry)| (identifier, entry).eject_value()).collect::<IndexMap<_, _>>(),
193            self.nonce.eject_value(),
194            self.version.eject_value(),
195        ) {
196            Ok(record) => record,
197            Err(error) => A::halt(format!("Record::<Plaintext>::eject_value: {error}")),
198        }
199    }
200}
201
202impl<A: Aleo> Eject for Record<A, Ciphertext<A>> {
203    type Primitive = console::Record<A::Network, console::Ciphertext<A::Network>>;
204
205    /// Ejects the mode of the record.
206    fn eject_mode(&self) -> Mode {
207        let owner = match &self.owner {
208            Owner::Public(owner) => match owner.eject_mode() == Mode::Public {
209                true => Mode::Public,
210                false => A::halt("Record::<Ciphertext>::eject_mode: 'owner' is not public."),
211            },
212            Owner::Private(plaintext) => match plaintext.eject_mode() == Mode::Private {
213                true => Mode::Private,
214                false => A::halt("Record::<Ciphertext>::eject_mode: 'owner' is not private."),
215            },
216        };
217
218        let data = self.data.iter().map(|(_, entry)| entry.eject_mode()).collect::<Vec<_>>().eject_mode();
219        let nonce = self.nonce.eject_mode();
220        let version = self.version.eject_mode();
221
222        Mode::combine(owner, [data, nonce, version])
223    }
224
225    /// Ejects the record.
226    fn eject_value(&self) -> Self::Primitive {
227        let owner = match &self.owner {
228            Owner::Public(owner) => console::Owner::Public(owner.eject_value()),
229            Owner::Private(plaintext) => console::Owner::Private(plaintext.eject_value()),
230        };
231
232        match Self::Primitive::from_ciphertext(
233            owner,
234            self.data.iter().map(|(identifier, entry)| (identifier, entry).eject_value()).collect::<IndexMap<_, _>>(),
235            self.nonce.eject_value(),
236            self.version.eject_value(),
237        ) {
238            Ok(record) => record,
239            Err(error) => A::halt(format!("Record::<Ciphertext>::eject_value: {error}")),
240        }
241    }
242}
243
244impl<A: Aleo, Private: Visibility<A>> TypeName for Record<A, Private> {
245    fn type_name() -> &'static str {
246        "record"
247    }
248}