Skip to main content

snarkvm_console_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
16mod bytes;
17mod encrypt;
18mod equal;
19mod find;
20mod from_bits;
21mod from_fields;
22mod num_randomizers;
23mod parse;
24mod serialize;
25mod size_in_fields;
26mod to_bits;
27mod to_bits_raw;
28mod to_fields;
29mod to_fields_raw;
30
31use crate::{Access, Ciphertext, Identifier, Literal, PlaintextType};
32use snarkvm_console_network::Network;
33use snarkvm_console_types::prelude::*;
34
35use indexmap::IndexMap;
36use std::{
37    hash::{Hash, Hasher},
38    sync::OnceLock,
39};
40
41#[derive(Clone)]
42pub enum Plaintext<N: Network> {
43    /// A literal.
44    Literal(Literal<N>, OnceLock<Vec<bool>>),
45    /// A struct.
46    Struct(IndexMap<Identifier<N>, Plaintext<N>>, OnceLock<Vec<bool>>),
47    /// An array.
48    Array(Vec<Plaintext<N>>, OnceLock<Vec<bool>>),
49}
50
51impl<N: Network> Plaintext<N> {
52    /// Returns a new `Plaintext::Array` from `Vec<bool>`, checking that the length is correct.
53    pub fn from_bit_array(bits: Vec<bool>, length: u32) -> Result<Self> {
54        ensure!(bits.len() == length as usize, "Expected '{length}' bits, got '{}' bits", bits.len());
55        Ok(Self::Array(
56            bits.into_iter().map(|bit| Plaintext::from(Literal::Boolean(Boolean::new(bit)))).collect(),
57            OnceLock::new(),
58        ))
59    }
60
61    /// Returns the `Plaintext` as a `Vec<bool>`, if it is a bit array.
62    pub fn as_bit_array(&self) -> Result<Vec<bool>> {
63        match self {
64            Self::Array(elements, _) => {
65                let mut bits = Vec::with_capacity(elements.len());
66                for element in elements {
67                    match element {
68                        Self::Literal(Literal::Boolean(bit), _) => bits.push(**bit),
69                        _ => bail!("Expected a bit array, found a non-boolean element."),
70                    }
71                }
72                Ok(bits)
73            }
74            _ => bail!("Expected a bit array, found a non-array plaintext."),
75        }
76    }
77
78    /// Returns the `Plaintext` as a `Vec<u8>`, if it is a u8 array.
79    pub fn as_byte_array(&self) -> Result<Vec<u8>> {
80        match self {
81            Self::Array(elements, _) => {
82                let mut bytes = Vec::with_capacity(elements.len());
83                for element in elements {
84                    match element {
85                        Self::Literal(Literal::U8(byte), _) => bytes.push(**byte),
86                        _ => bail!("Expected a u8 array, found a non-u8 element."),
87                    }
88                }
89                Ok(bytes)
90            }
91            _ => bail!("Expected a u8 array, found a non-array plaintext."),
92        }
93    }
94
95    /// Returns the `Plaintext` as a `Vec<N::Field>`, if it is a field array.
96    pub fn as_field_array(&self) -> Result<Vec<Field<N>>> {
97        match self {
98            Self::Array(elements, _) => {
99                let mut fields = Vec::with_capacity(elements.len());
100                for element in elements {
101                    match element {
102                        Self::Literal(Literal::Field(field), _) => fields.push(*field),
103                        _ => bail!("Expected an array of fields, found a non-field element."),
104                    }
105                }
106                Ok(fields)
107            }
108            _ => bail!("Expected an array of fields, found a non-array plaintext."),
109        }
110    }
111}
112
113impl<N: Network> From<Literal<N>> for Plaintext<N> {
114    /// Returns a new `Plaintext` from a `Literal`.
115    fn from(literal: Literal<N>) -> Self {
116        Self::Literal(literal, OnceLock::new())
117    }
118}
119
120impl<N: Network> From<&Literal<N>> for Plaintext<N> {
121    /// Returns a new `Plaintext` from a `&Literal`.
122    fn from(literal: &Literal<N>) -> Self {
123        Self::Literal(literal.clone(), OnceLock::new())
124    }
125}
126
127impl<N: Network> Hash for Plaintext<N> {
128    fn hash<H: Hasher>(&self, state: &mut H) {
129        match self {
130            Self::Literal(literal, _bits) => {
131                literal.hash(state);
132            }
133            Self::Struct(fields, _bits) => {
134                for (name, value) in fields {
135                    name.hash(state);
136                    value.hash(state);
137                }
138            }
139            Self::Array(array, _bits) => {
140                array.hash(state);
141            }
142        }
143    }
144}
145
146// A macro that derives implementations of `From` for arrays of a plaintext literals of various sizes.
147macro_rules! impl_plaintext_from_array {
148    ($element:ident, $($size:literal),+) => {
149        $(
150            impl<N: Network> From<[$element<N>; $size]> for Plaintext<N> {
151                fn from(value: [$element<N>; $size]) -> Self {
152                    Self::Array(
153                        value
154                            .into_iter()
155                            .map(|element| Plaintext::from(Literal::$element(element)))
156                            .collect(),
157                        OnceLock::new(),
158                    )
159                }
160            }
161        )+
162    };
163}
164
165// Implement for `[U8<N>, SIZE]` for sizes 1 through 256.
166seq_macro::seq!(S in 1..=256 {
167    impl_plaintext_from_array!(U8, S);
168});
169
170#[cfg(test)]
171mod tests {
172    use super::*;
173    use snarkvm_console_network::MainnetV0;
174    use snarkvm_console_types::Field;
175
176    use core::str::FromStr;
177
178    type CurrentNetwork = MainnetV0;
179
180    #[test]
181    fn test_plaintext() -> Result<()> {
182        let run_test = |value: Plaintext<CurrentNetwork>| {
183            assert_eq!(
184                value.to_bits_le(),
185                Plaintext::<CurrentNetwork>::from_bits_le(&value.to_bits_le()).unwrap().to_bits_le()
186            );
187            assert_eq!(value, Plaintext::<CurrentNetwork>::from_fields(&value.to_fields().unwrap()).unwrap());
188            assert_eq!(value, Plaintext::<CurrentNetwork>::from_str(&value.to_string()).unwrap());
189            assert!(*value.is_equal(&value));
190            assert!(*!value.is_not_equal(&value));
191        };
192
193        let mut rng = TestRng::default();
194
195        // Test booleans.
196        run_test(Plaintext::<CurrentNetwork>::from_str("true")?);
197        run_test(Plaintext::<CurrentNetwork>::from_str("false")?);
198
199        // Test a random field element.
200        run_test(Plaintext::<CurrentNetwork>::Literal(
201            Literal::Field(Field::new(Uniform::rand(&mut rng))),
202            OnceLock::new(),
203        ));
204
205        // Test a random struct with literal members.
206        run_test(Plaintext::<CurrentNetwork>::Struct(
207            IndexMap::from_iter(vec![
208                (Identifier::from_str("a")?, Plaintext::<CurrentNetwork>::from_str("true")?),
209                (
210                    Identifier::from_str("b")?,
211                    Plaintext::<CurrentNetwork>::Literal(
212                        Literal::Field(Field::new(Uniform::rand(&mut rng))),
213                        OnceLock::new(),
214                    ),
215                ),
216            ]),
217            OnceLock::new(),
218        ));
219
220        // Test a random struct with array members.
221        run_test(Plaintext::<CurrentNetwork>::Struct(
222            IndexMap::from_iter(vec![
223                (Identifier::from_str("a")?, Plaintext::<CurrentNetwork>::from_str("true")?),
224                (
225                    Identifier::from_str("b")?,
226                    Plaintext::<CurrentNetwork>::Array(
227                        vec![
228                            Plaintext::<CurrentNetwork>::from_str("true")?,
229                            Plaintext::<CurrentNetwork>::from_str("false")?,
230                        ],
231                        OnceLock::new(),
232                    ),
233                ),
234            ]),
235            OnceLock::new(),
236        ));
237
238        // Test random deeply-nested struct.
239        run_test(Plaintext::<CurrentNetwork>::Struct(
240            IndexMap::from_iter(vec![
241                (Identifier::from_str("a")?, Plaintext::<CurrentNetwork>::from_str("true")?),
242                (
243                    Identifier::from_str("b")?,
244                    Plaintext::<CurrentNetwork>::Struct(
245                        IndexMap::from_iter(vec![
246                            (Identifier::from_str("c")?, Plaintext::<CurrentNetwork>::from_str("true")?),
247                            (
248                                Identifier::from_str("d")?,
249                                Plaintext::<CurrentNetwork>::Struct(
250                                    IndexMap::from_iter(vec![
251                                        (Identifier::from_str("e")?, Plaintext::<CurrentNetwork>::from_str("true")?),
252                                        (
253                                            Identifier::from_str("f")?,
254                                            Plaintext::<CurrentNetwork>::Literal(
255                                                Literal::Field(Field::new(Uniform::rand(&mut rng))),
256                                                OnceLock::new(),
257                                            ),
258                                        ),
259                                    ]),
260                                    OnceLock::new(),
261                                ),
262                            ),
263                            (
264                                Identifier::from_str("g")?,
265                                Plaintext::Array(
266                                    vec![
267                                        Plaintext::<CurrentNetwork>::from_str("true")?,
268                                        Plaintext::<CurrentNetwork>::from_str("false")?,
269                                    ],
270                                    OnceLock::new(),
271                                ),
272                            ),
273                        ]),
274                        OnceLock::new(),
275                    ),
276                ),
277                (
278                    Identifier::from_str("h")?,
279                    Plaintext::<CurrentNetwork>::Literal(
280                        Literal::Field(Field::new(Uniform::rand(&mut rng))),
281                        OnceLock::new(),
282                    ),
283                ),
284            ]),
285            OnceLock::new(),
286        ));
287
288        // Test an array of literals.
289        run_test(Plaintext::<CurrentNetwork>::Array(
290            vec![
291                Plaintext::<CurrentNetwork>::from_str("0field")?,
292                Plaintext::<CurrentNetwork>::from_str("1field")?,
293                Plaintext::<CurrentNetwork>::from_str("2field")?,
294                Plaintext::<CurrentNetwork>::from_str("3field")?,
295                Plaintext::<CurrentNetwork>::from_str("4field")?,
296            ],
297            OnceLock::new(),
298        ));
299
300        // Test an array of structs.
301        run_test(Plaintext::<CurrentNetwork>::Array(
302            vec![
303                Plaintext::<CurrentNetwork>::from_str("{ x: 0field, y: 1field }")?,
304                Plaintext::<CurrentNetwork>::from_str("{ x: 2field, y: 3field }")?,
305                Plaintext::<CurrentNetwork>::from_str("{ x: 4field, y: 5field }")?,
306                Plaintext::<CurrentNetwork>::from_str("{ x: 6field, y: 7field }")?,
307                Plaintext::<CurrentNetwork>::from_str("{ x: 8field, y: 9field }")?,
308            ],
309            OnceLock::new(),
310        ));
311
312        // Test a non-uniform array.
313        run_test(Plaintext::<CurrentNetwork>::Array(
314            vec![
315                Plaintext::<CurrentNetwork>::from_str("true")?,
316                Plaintext::<CurrentNetwork>::from_str("1field")?,
317                Plaintext::<CurrentNetwork>::from_str("{ x: 4field, y: 1u8 }")?,
318            ],
319            OnceLock::new(),
320        ));
321
322        Ok(())
323    }
324}