Skip to main content

snarkvm_circuit_program/data/plaintext/
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
16use std::cell::OnceCell;
17
18#[cfg(test)]
19use snarkvm_circuit_types::environment::assert_scope;
20
21mod encrypt;
22mod equal;
23mod find;
24mod from_bits;
25mod from_fields;
26mod num_randomizers;
27mod size_in_fields;
28mod to_bits;
29mod to_bits_raw;
30mod to_fields;
31mod to_fields_raw;
32
33use crate::{Access, Ciphertext, Identifier, Literal, Visibility};
34use console::PlaintextType;
35use snarkvm_circuit_network::Aleo;
36use snarkvm_circuit_types::{Address, Boolean, Field, Scalar, U8, U16, U32, environment::prelude::*};
37
38#[derive(Clone)]
39pub enum Plaintext<A: Aleo> {
40    /// A plaintext literal.
41    Literal(Literal<A>, OnceCell<Vec<Boolean<A>>>),
42    /// A plaintext struct.
43    Struct(IndexMap<Identifier<A>, Plaintext<A>>, OnceCell<Vec<Boolean<A>>>),
44    /// A plaintext array.
45    Array(Vec<Plaintext<A>>, OnceCell<Vec<Boolean<A>>>),
46}
47
48impl<A: Aleo> Plaintext<A> {
49    /// Returns a new `Plaintext::Array` from `Vec<Boolean<A>>`, checking that the length is correct.
50    pub fn from_bit_array(bits: Vec<Boolean<A>>, length: u32) -> Result<Self> {
51        ensure!(bits.len() == length as usize, "Expected '{length}' bits, got '{}' bits", bits.len());
52        Ok(Self::Array(bits.into_iter().map(|bit| Plaintext::from(Literal::Boolean(bit))).collect(), OnceCell::new()))
53    }
54
55    /// Returns the `Plaintext` as a `Vec<Boolean<A>>`, if it is a bit array.
56    pub fn as_bit_array(&self) -> Result<Vec<Boolean<A>>> {
57        match self {
58            Self::Array(elements, _) => {
59                let mut bits = Vec::with_capacity(elements.len());
60                for element in elements {
61                    match element {
62                        Self::Literal(Literal::Boolean(bit), _) => bits.push(bit.clone()),
63                        _ => bail!("Expected a bit array, found a non-boolean element."),
64                    }
65                }
66                Ok(bits)
67            }
68            _ => bail!("Expected a bit array, found a non-array plaintext."),
69        }
70    }
71}
72
73impl<A: Aleo> Inject for Plaintext<A> {
74    type Primitive = console::Plaintext<A::Network>;
75
76    /// Initializes a new plaintext circuit from a primitive.
77    fn new(mode: Mode, plaintext: Self::Primitive) -> Self {
78        match plaintext {
79            Self::Primitive::Literal(literal, _) => Self::Literal(Literal::new(mode, literal), Default::default()),
80            Self::Primitive::Struct(struct_, _) => Self::Struct(
81                struct_
82                    .into_iter()
83                    .map(|(identifier, member)| (Identifier::constant(identifier), Inject::new(mode, member)))
84                    .collect(),
85                Default::default(),
86            ),
87            Self::Primitive::Array(array, _) => Self::Array(Inject::new(mode, array), Default::default()),
88        }
89    }
90}
91
92impl<A: Aleo> Eject for Plaintext<A> {
93    type Primitive = console::Plaintext<A::Network>;
94
95    /// Ejects the mode of the plaintext value.
96    fn eject_mode(&self) -> Mode {
97        match self {
98            Self::Literal(literal, _) => literal.eject_mode(),
99            Self::Struct(struct_, _) => struct_
100                .iter()
101                .map(|(identifier, value)| (identifier, value).eject_mode())
102                .collect::<Vec<_>>()
103                .eject_mode(),
104            Self::Array(array, _) => array.iter().map(Eject::eject_mode).collect::<Vec<_>>().eject_mode(),
105        }
106    }
107
108    /// Ejects the plaintext value.
109    fn eject_value(&self) -> Self::Primitive {
110        match self {
111            Self::Literal(literal, _) => console::Plaintext::Literal(literal.eject_value(), Default::default()),
112            Self::Struct(struct_, _) => {
113                console::Plaintext::Struct(struct_.iter().map(|pair| pair.eject_value()).collect(), Default::default())
114            }
115            Self::Array(array, _) => {
116                console::Plaintext::Array(array.iter().map(Eject::eject_value).collect(), Default::default())
117            }
118        }
119    }
120}
121
122impl<A: Aleo> From<Literal<A>> for Plaintext<A> {
123    /// Returns a new `Plaintext` from a `Literal`.
124    fn from(literal: Literal<A>) -> Self {
125        Self::Literal(literal, OnceCell::new())
126    }
127}
128
129impl<A: Aleo> From<&Literal<A>> for Plaintext<A> {
130    /// Returns a new `Plaintext` from a `Literal`.
131    fn from(literal: &Literal<A>) -> Self {
132        Self::Literal((*literal).clone(), OnceCell::new())
133    }
134}
135
136// A macro that derives implementations of `From` for arrays of a plaintext literals of various sizes.
137macro_rules! impl_plaintext_from_array {
138    ($element:ident, $($size:literal),+) => {
139        $(
140            impl<A: Aleo> From<[$element<A>; $size]> for Plaintext<A> {
141                fn from(value: [$element<A>; $size]) -> Self {
142                    Self::Array(
143                        value
144                            .into_iter()
145                            .map(|element| Plaintext::from(Literal::$element(element)))
146                            .collect(),
147                        OnceCell::new(),
148                    )
149                }
150            }
151        )+
152    };
153}
154
155// Implement for `[U8<N>, SIZE]` for sizes 1 through 32.
156impl_plaintext_from_array!(
157    U8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30,
158    31, 32
159);
160
161#[cfg(test)]
162mod tests {
163    use super::*;
164    use crate::Circuit;
165    use snarkvm_utilities::{TestRng, Uniform};
166
167    use anyhow::Result;
168
169    #[test]
170    fn test_plaintext() -> Result<()> {
171        let run_test = |value: Plaintext<Circuit>| {
172            assert_eq!(
173                value.to_bits_le().eject(),
174                Plaintext::<Circuit>::from_bits_le(&value.to_bits_le()).to_bits_le().eject()
175            );
176            assert_eq!(value.eject(), Plaintext::<Circuit>::from_fields(&value.to_fields()).eject());
177            assert!(value.is_equal(&value).eject_value());
178            assert!(!value.is_not_equal(&value).eject_value());
179        };
180
181        let mut rng = TestRng::default();
182
183        // Test booleans.
184        run_test(Plaintext::<Circuit>::Literal(Literal::Boolean(Boolean::new(Mode::Private, true)), OnceCell::new()));
185        run_test(Plaintext::<Circuit>::Literal(Literal::Boolean(Boolean::new(Mode::Private, false)), OnceCell::new()));
186
187        // Test a random field element.
188        run_test(Plaintext::<Circuit>::Literal(
189            Literal::Field(Field::new(Mode::Private, Uniform::rand(&mut rng))),
190            OnceCell::new(),
191        ));
192
193        // Test a random struct with literal members.
194        run_test(Plaintext::<Circuit>::Struct(
195            IndexMap::from_iter(vec![
196                (
197                    Identifier::constant("a".try_into()?),
198                    Plaintext::<Circuit>::Literal(Literal::Boolean(Boolean::new(Mode::Private, true)), OnceCell::new()),
199                ),
200                (
201                    Identifier::constant("b".try_into()?),
202                    Plaintext::<Circuit>::Literal(
203                        Literal::Field(Field::new(Mode::Private, Uniform::rand(&mut rng))),
204                        OnceCell::new(),
205                    ),
206                ),
207            ]),
208            OnceCell::new(),
209        ));
210
211        // Test a random struct with array members.
212        run_test(Plaintext::<Circuit>::Struct(
213            IndexMap::from_iter(vec![
214                (
215                    Identifier::constant("a".try_into()?),
216                    Plaintext::<Circuit>::Literal(Literal::Boolean(Boolean::new(Mode::Private, true)), OnceCell::new()),
217                ),
218                (
219                    Identifier::constant("b".try_into()?),
220                    Plaintext::<Circuit>::Array(
221                        vec![
222                            Plaintext::<Circuit>::Literal(
223                                Literal::Boolean(Boolean::new(Mode::Private, true)),
224                                OnceCell::new(),
225                            ),
226                            Plaintext::<Circuit>::Literal(
227                                Literal::Boolean(Boolean::new(Mode::Private, false)),
228                                OnceCell::new(),
229                            ),
230                        ],
231                        OnceCell::new(),
232                    ),
233                ),
234            ]),
235            OnceCell::new(),
236        ));
237
238        // Test random deeply-nested struct.
239        run_test(Plaintext::<Circuit>::Struct(
240            IndexMap::from_iter(vec![
241                (
242                    Identifier::constant("a".try_into()?),
243                    Plaintext::<Circuit>::Literal(Literal::Boolean(Boolean::new(Mode::Private, true)), OnceCell::new()),
244                ),
245                (
246                    Identifier::constant("b".try_into()?),
247                    Plaintext::<Circuit>::Struct(
248                        IndexMap::from_iter(vec![
249                            (
250                                Identifier::constant("c".try_into()?),
251                                Plaintext::<Circuit>::Literal(
252                                    Literal::Boolean(Boolean::new(Mode::Private, true)),
253                                    OnceCell::new(),
254                                ),
255                            ),
256                            (
257                                Identifier::constant("d".try_into()?),
258                                Plaintext::<Circuit>::Struct(
259                                    IndexMap::from_iter(vec![
260                                        (
261                                            Identifier::constant("e".try_into()?),
262                                            Plaintext::<Circuit>::Literal(
263                                                Literal::Boolean(Boolean::new(Mode::Private, true)),
264                                                OnceCell::new(),
265                                            ),
266                                        ),
267                                        (
268                                            Identifier::constant("f".try_into()?),
269                                            Plaintext::<Circuit>::Literal(
270                                                Literal::Field(Field::new(Mode::Private, Uniform::rand(&mut rng))),
271                                                OnceCell::new(),
272                                            ),
273                                        ),
274                                    ]),
275                                    OnceCell::new(),
276                                ),
277                            ),
278                            (
279                                Identifier::constant("g".try_into()?),
280                                Plaintext::<Circuit>::Array(
281                                    vec![
282                                        Plaintext::<Circuit>::Literal(
283                                            Literal::Boolean(Boolean::new(Mode::Private, true)),
284                                            OnceCell::new(),
285                                        ),
286                                        Plaintext::<Circuit>::Literal(
287                                            Literal::Boolean(Boolean::new(Mode::Private, false)),
288                                            OnceCell::new(),
289                                        ),
290                                    ],
291                                    OnceCell::new(),
292                                ),
293                            ),
294                        ]),
295                        OnceCell::new(),
296                    ),
297                ),
298                (
299                    Identifier::constant("h".try_into()?),
300                    Plaintext::<Circuit>::Literal(
301                        Literal::Field(Field::new(Mode::Private, Uniform::rand(&mut rng))),
302                        OnceCell::new(),
303                    ),
304                ),
305            ]),
306            OnceCell::new(),
307        ));
308
309        // Test an array of literals.
310        run_test(Plaintext::<Circuit>::Array(
311            vec![
312                Plaintext::<Circuit>::Literal(
313                    Literal::Field(Field::new(Mode::Private, Uniform::rand(&mut rng))),
314                    OnceCell::new(),
315                ),
316                Plaintext::<Circuit>::Literal(
317                    Literal::Field(Field::new(Mode::Private, Uniform::rand(&mut rng))),
318                    OnceCell::new(),
319                ),
320                Plaintext::<Circuit>::Literal(
321                    Literal::Field(Field::new(Mode::Private, Uniform::rand(&mut rng))),
322                    OnceCell::new(),
323                ),
324                Plaintext::<Circuit>::Literal(
325                    Literal::Field(Field::new(Mode::Private, Uniform::rand(&mut rng))),
326                    OnceCell::new(),
327                ),
328                Plaintext::<Circuit>::Literal(
329                    Literal::Field(Field::new(Mode::Private, Uniform::rand(&mut rng))),
330                    OnceCell::new(),
331                ),
332            ],
333            OnceCell::new(),
334        ));
335
336        // Test an array of structs.
337        run_test(Plaintext::<Circuit>::Array(
338            vec![
339                Plaintext::<Circuit>::Struct(
340                    IndexMap::from_iter(vec![
341                        (
342                            Identifier::constant("x".try_into()?),
343                            Plaintext::<Circuit>::Literal(
344                                Literal::Field(Field::new(Mode::Private, Uniform::rand(&mut rng))),
345                                OnceCell::new(),
346                            ),
347                        ),
348                        (
349                            Identifier::constant("y".try_into()?),
350                            Plaintext::<Circuit>::Literal(
351                                Literal::Field(Field::new(Mode::Private, Uniform::rand(&mut rng))),
352                                OnceCell::new(),
353                            ),
354                        ),
355                    ]),
356                    OnceCell::new(),
357                ),
358                Plaintext::<Circuit>::Struct(
359                    IndexMap::from_iter(vec![
360                        (
361                            Identifier::constant("x".try_into()?),
362                            Plaintext::<Circuit>::Literal(
363                                Literal::Field(Field::new(Mode::Private, Uniform::rand(&mut rng))),
364                                OnceCell::new(),
365                            ),
366                        ),
367                        (
368                            Identifier::constant("y".try_into()?),
369                            Plaintext::<Circuit>::Literal(
370                                Literal::Field(Field::new(Mode::Private, Uniform::rand(&mut rng))),
371                                OnceCell::new(),
372                            ),
373                        ),
374                    ]),
375                    OnceCell::new(),
376                ),
377                Plaintext::<Circuit>::Struct(
378                    IndexMap::from_iter(vec![
379                        (
380                            Identifier::constant("x".try_into()?),
381                            Plaintext::<Circuit>::Literal(
382                                Literal::Field(Field::new(Mode::Private, Uniform::rand(&mut rng))),
383                                OnceCell::new(),
384                            ),
385                        ),
386                        (
387                            Identifier::constant("y".try_into()?),
388                            Plaintext::<Circuit>::Literal(
389                                Literal::Field(Field::new(Mode::Private, Uniform::rand(&mut rng))),
390                                OnceCell::new(),
391                            ),
392                        ),
393                    ]),
394                    OnceCell::new(),
395                ),
396            ],
397            OnceCell::new(),
398        ));
399
400        // Test a non-uniform array.
401        run_test(Plaintext::<Circuit>::Array(
402            vec![
403                Plaintext::<Circuit>::Literal(Literal::Boolean(Boolean::new(Mode::Private, true)), OnceCell::new()),
404                Plaintext::<Circuit>::Literal(
405                    Literal::Field(Field::new(Mode::Private, Uniform::rand(&mut rng))),
406                    OnceCell::new(),
407                ),
408                Plaintext::<Circuit>::Struct(
409                    IndexMap::from_iter(vec![
410                        (
411                            Identifier::constant("x".try_into()?),
412                            Plaintext::<Circuit>::Literal(
413                                Literal::Field(Field::new(Mode::Private, Uniform::rand(&mut rng))),
414                                OnceCell::new(),
415                            ),
416                        ),
417                        (
418                            Identifier::constant("y".try_into()?),
419                            Plaintext::<Circuit>::Literal(
420                                Literal::Field(Field::new(Mode::Private, Uniform::rand(&mut rng))),
421                                OnceCell::new(),
422                            ),
423                        ),
424                    ]),
425                    OnceCell::new(),
426                ),
427            ],
428            OnceCell::new(),
429        ));
430
431        Ok(())
432    }
433}