snarkvm_circuit_types_field/
lib.rs

1// Copyright 2024 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#![forbid(unsafe_code)]
17#![allow(clippy::too_many_arguments)]
18#![cfg_attr(test, allow(clippy::assertions_on_result_states))]
19
20mod helpers;
21
22pub mod add;
23pub mod compare;
24pub mod div;
25pub mod div_unchecked;
26pub mod double;
27pub mod equal;
28pub mod inverse;
29pub mod mul;
30pub mod neg;
31pub mod pow;
32pub mod square;
33pub mod square_root;
34pub mod sub;
35pub mod ternary;
36
37#[cfg(test)]
38use console::{TestRng, Uniform};
39#[cfg(test)]
40use snarkvm_circuit_environment::{assert_count, assert_output_mode, assert_scope, count, output_mode};
41
42use snarkvm_circuit_environment::prelude::*;
43use snarkvm_circuit_types_boolean::Boolean;
44
45#[derive(Clone)]
46pub struct Field<E: Environment> {
47    /// The linear combination contains the primary representation of the field.
48    linear_combination: LinearCombination<E::BaseField>,
49    /// An optional secondary representation in little-endian bits is provided,
50    /// so that calls to `ToBits` only incur constraint costs once.
51    bits_le: OnceCell<Vec<Boolean<E>>>,
52}
53
54impl<E: Environment> FieldTrait for Field<E> {}
55
56impl<E: Environment> Default for Field<E> {
57    /// Returns the default field element.
58    fn default() -> Self {
59        Self::zero()
60    }
61}
62
63#[cfg(feature = "console")]
64impl<E: Environment> Inject for Field<E> {
65    type Primitive = console::Field<E::Network>;
66
67    /// Initializes a field circuit from a console field.
68    fn new(mode: Mode, field: Self::Primitive) -> Self {
69        Self { linear_combination: E::new_variable(mode, *field).into(), bits_le: Default::default() }
70    }
71}
72
73#[cfg(feature = "console")]
74impl<E: Environment> Eject for Field<E> {
75    type Primitive = console::Field<E::Network>;
76
77    /// Ejects the mode of the field circuit.
78    fn eject_mode(&self) -> Mode {
79        self.linear_combination.mode()
80    }
81
82    /// Ejects the field circuit as a console field.
83    fn eject_value(&self) -> Self::Primitive {
84        console::Field::new(self.linear_combination.value())
85    }
86}
87
88#[cfg(feature = "console")]
89impl<E: Environment> Parser for Field<E> {
90    /// Parses a string into a field circuit.
91    #[inline]
92    fn parse(string: &str) -> ParserResult<Self> {
93        // Parse the field from the string.
94        let (string, field) = console::Field::parse(string)?;
95        // Parse the mode from the string.
96        let (string, mode) = opt(pair(tag("."), Mode::parse))(string)?;
97
98        match mode {
99            Some((_, mode)) => Ok((string, Field::new(mode, field))),
100            None => Ok((string, Field::new(Mode::Constant, field))),
101        }
102    }
103}
104
105#[cfg(feature = "console")]
106impl<E: Environment> FromStr for Field<E> {
107    type Err = Error;
108
109    /// Parses a string into a field circuit.
110    #[inline]
111    fn from_str(string: &str) -> Result<Self> {
112        match Self::parse(string) {
113            Ok((remainder, object)) => {
114                // Ensure the remainder is empty.
115                ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
116                // Return the object.
117                Ok(object)
118            }
119            Err(error) => bail!("Failed to parse string. {error}"),
120        }
121    }
122}
123
124#[cfg(feature = "console")]
125impl<E: Environment> TypeName for Field<E> {
126    /// Returns the type name of the circuit as a string.
127    #[inline]
128    fn type_name() -> &'static str {
129        console::Field::<E::Network>::type_name()
130    }
131}
132
133#[cfg(feature = "console")]
134impl<E: Environment> Debug for Field<E> {
135    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
136        Display::fmt(self, f)
137    }
138}
139
140#[cfg(feature = "console")]
141impl<E: Environment> Display for Field<E> {
142    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
143        write!(f, "{}.{}", self.eject_value(), self.eject_mode())
144    }
145}
146
147impl<E: Environment> From<LinearCombination<E::BaseField>> for Field<E> {
148    fn from(linear_combination: LinearCombination<E::BaseField>) -> Self {
149        Self { linear_combination, bits_le: Default::default() }
150    }
151}
152
153impl<E: Environment> From<&LinearCombination<E::BaseField>> for Field<E> {
154    fn from(linear_combination: &LinearCombination<E::BaseField>) -> Self {
155        From::from(linear_combination.clone())
156    }
157}
158
159impl<E: Environment> From<Field<E>> for LinearCombination<E::BaseField> {
160    fn from(field: Field<E>) -> Self {
161        From::from(&field)
162    }
163}
164
165impl<E: Environment> From<&Field<E>> for LinearCombination<E::BaseField> {
166    fn from(field: &Field<E>) -> Self {
167        field.linear_combination.clone()
168    }
169}
170
171#[cfg(test)]
172mod tests {
173    use super::*;
174    use snarkvm_circuit_environment::Circuit;
175
176    const ITERATIONS: u64 = 10_000;
177
178    /// Attempts to construct a field from the given element and mode,
179    /// format it in display mode, and recover a field from it.
180    fn check_display(mode: Mode, element: console::Field<<Circuit as Environment>::Network>) -> Result<()> {
181        let candidate = Field::<Circuit>::new(mode, element);
182        assert_eq!(format!("{element}.{mode}"), format!("{candidate}"));
183
184        let candidate_recovered = Field::<Circuit>::from_str(&format!("{candidate}"))?;
185        assert_eq!(candidate.eject_value(), candidate_recovered.eject_value());
186        Ok(())
187    }
188
189    #[test]
190    fn test_display() -> Result<()> {
191        let mut rng = TestRng::default();
192
193        for _ in 0..ITERATIONS {
194            let element = Uniform::rand(&mut rng);
195
196            // Constant
197            check_display(Mode::Constant, element)?;
198            // Public
199            check_display(Mode::Public, element)?;
200            // Private
201            check_display(Mode::Private, element)?;
202        }
203        Ok(())
204    }
205
206    #[test]
207    fn test_display_zero() {
208        let zero = console::Field::<<Circuit as Environment>::Network>::zero();
209
210        // Constant
211        let candidate = Field::<Circuit>::new(Mode::Constant, zero);
212        assert_eq!("0field.constant", &format!("{candidate}"));
213
214        // Public
215        let candidate = Field::<Circuit>::new(Mode::Public, zero);
216        assert_eq!("0field.public", &format!("{candidate}"));
217
218        // Private
219        let candidate = Field::<Circuit>::new(Mode::Private, zero);
220        assert_eq!("0field.private", &format!("{candidate}"));
221    }
222
223    #[test]
224    fn test_display_one() {
225        let one = console::Field::<<Circuit as Environment>::Network>::one();
226
227        // Constant
228        let candidate = Field::<Circuit>::new(Mode::Constant, one);
229        assert_eq!("1field.constant", &format!("{candidate}"));
230
231        // Public
232        let candidate = Field::<Circuit>::new(Mode::Public, one);
233        assert_eq!("1field.public", &format!("{candidate}"));
234
235        // Private
236        let candidate = Field::<Circuit>::new(Mode::Private, one);
237        assert_eq!("1field.private", &format!("{candidate}"));
238    }
239
240    #[test]
241    fn test_display_two() {
242        let one = console::Field::<<Circuit as Environment>::Network>::one();
243        let two = one + one;
244
245        // Constant
246        let candidate = Field::<Circuit>::new(Mode::Constant, two);
247        assert_eq!("2field.constant", &format!("{candidate}"));
248
249        // Public
250        let candidate = Field::<Circuit>::new(Mode::Public, two);
251        assert_eq!("2field.public", &format!("{candidate}"));
252
253        // Private
254        let candidate = Field::<Circuit>::new(Mode::Private, two);
255        assert_eq!("2field.private", &format!("{candidate}"));
256    }
257
258    #[test]
259    fn test_parser() {
260        type Primitive = console::Field<<Circuit as Environment>::Network>;
261
262        // Constant
263
264        let (_, candidate) = Field::<Circuit>::parse("5field").unwrap();
265        assert_eq!(Primitive::from_str("5field").unwrap(), candidate.eject_value());
266        assert!(candidate.is_constant());
267
268        let (_, candidate) = Field::<Circuit>::parse("5_field").unwrap();
269        assert_eq!(Primitive::from_str("5field").unwrap(), candidate.eject_value());
270        assert!(candidate.is_constant());
271
272        let (_, candidate) = Field::<Circuit>::parse("1_5_field").unwrap();
273        assert_eq!(Primitive::from_str("15field").unwrap(), candidate.eject_value());
274        assert!(candidate.is_constant());
275
276        let (_, candidate) = Field::<Circuit>::parse("5field.constant").unwrap();
277        assert_eq!(Primitive::from_str("5field").unwrap(), candidate.eject_value());
278        assert!(candidate.is_constant());
279
280        let (_, candidate) = Field::<Circuit>::parse("5_field.constant").unwrap();
281        assert_eq!(Primitive::from_str("5field").unwrap(), candidate.eject_value());
282        assert!(candidate.is_constant());
283
284        let (_, candidate) = Field::<Circuit>::parse("1_5_field.constant").unwrap();
285        assert_eq!(Primitive::from_str("15field").unwrap(), candidate.eject_value());
286        assert!(candidate.is_constant());
287
288        // Public
289
290        let (_, candidate) = Field::<Circuit>::parse("5field.public").unwrap();
291        assert_eq!(Primitive::from_str("5field").unwrap(), candidate.eject_value());
292        assert!(candidate.is_public());
293
294        let (_, candidate) = Field::<Circuit>::parse("5_field.public").unwrap();
295        assert_eq!(Primitive::from_str("5field").unwrap(), candidate.eject_value());
296        assert!(candidate.is_public());
297
298        let (_, candidate) = Field::<Circuit>::parse("1_5_field.public").unwrap();
299        assert_eq!(Primitive::from_str("15field").unwrap(), candidate.eject_value());
300        assert!(candidate.is_public());
301
302        // Private
303
304        let (_, candidate) = Field::<Circuit>::parse("5field.private").unwrap();
305        assert_eq!(Primitive::from_str("5field").unwrap(), candidate.eject_value());
306        assert!(candidate.is_private());
307
308        let (_, candidate) = Field::<Circuit>::parse("5_field.private").unwrap();
309        assert_eq!(Primitive::from_str("5field").unwrap(), candidate.eject_value());
310        assert!(candidate.is_private());
311
312        let (_, candidate) = Field::<Circuit>::parse("1_5_field.private").unwrap();
313        assert_eq!(Primitive::from_str("15field").unwrap(), candidate.eject_value());
314        assert!(candidate.is_private());
315
316        // Random
317
318        let mut rng = TestRng::default();
319
320        for mode in [Mode::Constant, Mode::Public, Mode::Private] {
321            for _ in 0..ITERATIONS {
322                let value = Uniform::rand(&mut rng);
323                let expected = Field::<Circuit>::new(mode, value);
324
325                let (_, candidate) = Field::<Circuit>::parse(&format!("{expected}")).unwrap();
326                assert_eq!(expected.eject_value(), candidate.eject_value());
327                assert_eq!(mode, candidate.eject_mode());
328            }
329        }
330    }
331}