snarkvm_circuit_program/data/record/
mod.rs

1// Copyright 2024-2025 Aleo Network Foundation
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, Plaintext, ProgramID, Visibility};
37use snarkvm_circuit_account::{PrivateKey, ViewKey};
38use snarkvm_circuit_network::Aleo;
39use snarkvm_circuit_types::{Boolean, Field, Group, Scalar, U32, environment::prelude::*};
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}
50
51#[cfg(feature = "console")]
52impl<A: Aleo> Inject for Record<A, Plaintext<A>> {
53    type Primitive = console::Record<A::Network, console::Plaintext<A::Network>>;
54
55    /// Initializes a plaintext record from a primitive.
56    fn new(_: Mode, record: Self::Primitive) -> Self {
57        Self {
58            owner: Owner::new(Mode::Private, record.owner().clone()),
59            data: Inject::new(Mode::Private, record.data().clone()),
60            nonce: Group::new(Mode::Private, *record.nonce()),
61        }
62    }
63}
64
65#[cfg(feature = "console")]
66impl<A: Aleo> Inject for Record<A, Ciphertext<A>> {
67    type Primitive = console::Record<A::Network, console::Ciphertext<A::Network>>;
68
69    /// Initializes a ciphertext record from a primitive.
70    fn new(_: Mode, record: Self::Primitive) -> Self {
71        Self {
72            owner: Owner::new(Mode::Private, record.owner().clone()),
73            data: Inject::new(Mode::Private, record.data().clone()),
74            nonce: Group::new(Mode::Private, *record.nonce()),
75        }
76    }
77}
78
79#[cfg(feature = "console")]
80impl<A: Aleo, Private: Visibility<A>> Record<A, Private> {
81    /// Initializes a new record plaintext.
82    pub fn from_plaintext(
83        owner: Owner<A, Plaintext<A>>,
84        data: IndexMap<Identifier<A>, Entry<A, Plaintext<A>>>,
85        nonce: Group<A>,
86    ) -> Result<Record<A, Plaintext<A>>> {
87        // Ensure the members has no duplicate names.
88        ensure!(!has_duplicates(data.iter().map(|(name, ..)| name)), "A duplicate entry name was found in a record");
89        // Ensure the number of entries is within the maximum limit.
90        ensure!(
91            data.len() <= <A::Network as console::Network>::MAX_DATA_ENTRIES,
92            "Found a record that exceeds size ({})",
93            data.len()
94        );
95        // Return the record.
96        Ok(Record { owner, data, nonce })
97    }
98
99    /// Initializes a new record ciphertext.
100    pub fn from_ciphertext(
101        owner: Owner<A, Ciphertext<A>>,
102        data: IndexMap<Identifier<A>, Entry<A, Ciphertext<A>>>,
103        nonce: Group<A>,
104    ) -> Result<Record<A, Ciphertext<A>>> {
105        // Ensure the members has no duplicate names.
106        ensure!(!has_duplicates(data.iter().map(|(name, ..)| name)), "A duplicate entry name was found in a record");
107        // Ensure the number of entries is within the maximum limit.
108        ensure!(
109            data.len() <= <A::Network as console::Network>::MAX_DATA_ENTRIES,
110            "Found a record that exceeds size ({})",
111            data.len()
112        );
113        // Return the record.
114        Ok(Record { owner, data, nonce })
115    }
116}
117
118impl<A: Aleo, Private: Visibility<A>> Record<A, Private> {
119    /// Returns the owner of the program record.
120    pub const fn owner(&self) -> &Owner<A, Private> {
121        &self.owner
122    }
123
124    /// Returns the program data.
125    pub const fn data(&self) -> &IndexMap<Identifier<A>, Entry<A, Private>> {
126        &self.data
127    }
128
129    /// Returns the nonce of the program record.
130    pub const fn nonce(&self) -> &Group<A> {
131        &self.nonce
132    }
133}
134
135#[cfg(feature = "console")]
136impl<A: Aleo> Eject for Record<A, Plaintext<A>> {
137    type Primitive = console::Record<A::Network, console::Plaintext<A::Network>>;
138
139    /// Ejects the mode of the record.
140    fn eject_mode(&self) -> Mode {
141        let owner = match &self.owner {
142            Owner::Public(owner) => match owner.eject_mode() == Mode::Public {
143                true => Mode::Public,
144                false => A::halt("Record::<Plaintext>::eject_mode: 'owner' is not public."),
145            },
146            Owner::Private(plaintext) => match plaintext.eject_mode() == Mode::Private {
147                true => Mode::Private,
148                false => A::halt("Record::<Plaintext>::eject_mode: 'owner' is not private."),
149            },
150        };
151
152        let data = self.data.iter().map(|(_, entry)| entry.eject_mode()).collect::<Vec<_>>().eject_mode();
153        let nonce = self.nonce.eject_mode();
154
155        Mode::combine(owner, [data, nonce])
156    }
157
158    /// Ejects the record.
159    fn eject_value(&self) -> Self::Primitive {
160        let owner = match &self.owner {
161            Owner::Public(owner) => console::Owner::Public(owner.eject_value()),
162            Owner::Private(plaintext) => console::Owner::Private(plaintext.eject_value()),
163        };
164
165        match Self::Primitive::from_plaintext(
166            owner,
167            self.data.iter().map(|(identifier, entry)| (identifier, entry).eject_value()).collect::<IndexMap<_, _>>(),
168            self.nonce.eject_value(),
169        ) {
170            Ok(record) => record,
171            Err(error) => A::halt(format!("Record::<Plaintext>::eject_value: {error}")),
172        }
173    }
174}
175
176#[cfg(feature = "console")]
177impl<A: Aleo> Eject for Record<A, Ciphertext<A>> {
178    type Primitive = console::Record<A::Network, console::Ciphertext<A::Network>>;
179
180    /// Ejects the mode of the record.
181    fn eject_mode(&self) -> Mode {
182        let owner = match &self.owner {
183            Owner::Public(owner) => match owner.eject_mode() == Mode::Public {
184                true => Mode::Public,
185                false => A::halt("Record::<Ciphertext>::eject_mode: 'owner' is not public."),
186            },
187            Owner::Private(plaintext) => match plaintext.eject_mode() == Mode::Private {
188                true => Mode::Private,
189                false => A::halt("Record::<Ciphertext>::eject_mode: 'owner' is not private."),
190            },
191        };
192
193        let data = self.data.iter().map(|(_, entry)| entry.eject_mode()).collect::<Vec<_>>().eject_mode();
194        let nonce = self.nonce.eject_mode();
195
196        Mode::combine(owner, [data, nonce])
197    }
198
199    /// Ejects the record.
200    fn eject_value(&self) -> Self::Primitive {
201        let owner = match &self.owner {
202            Owner::Public(owner) => console::Owner::Public(owner.eject_value()),
203            Owner::Private(plaintext) => console::Owner::Private(plaintext.eject_value()),
204        };
205
206        match Self::Primitive::from_ciphertext(
207            owner,
208            self.data.iter().map(|(identifier, entry)| (identifier, entry).eject_value()).collect::<IndexMap<_, _>>(),
209            self.nonce.eject_value(),
210        ) {
211            Ok(record) => record,
212            Err(error) => A::halt(format!("Record::<Ciphertext>::eject_value: {error}")),
213        }
214    }
215}
216
217#[cfg(feature = "console")]
218impl<A: Aleo, Private: Visibility<A>> TypeName for Record<A, Private> {
219    fn type_name() -> &'static str {
220        "record"
221    }
222}