snarkvm_circuit_program/data/record/
equal.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
16use super::*;
17
18impl<A: Aleo, Private: Visibility<A>> Equal<Self> for Record<A, Private> {
19    type Output = Boolean<A>;
20
21    /// Returns `true` if `self` and `other` are equal.
22    ///
23    /// Note: This method does **not** check the `nonce` equality.
24    fn is_equal(&self, other: &Self) -> Self::Output {
25        // Ensure the `data` are the same length.
26        if self.data.len() != other.data.len() {
27            return Boolean::constant(false);
28        }
29        // Recursively check each entry for equality.
30        let mut equal = Boolean::constant(true);
31        for ((name_a, entry_a), (name_b, entry_b)) in self.data.iter().zip_eq(other.data.iter()) {
32            equal = equal & name_a.is_equal(name_b) & entry_a.is_equal(entry_b);
33        }
34
35        // Check the `owner`, `data`, and `nonce`.
36        self.owner.is_equal(&other.owner) & equal & self.nonce.is_equal(&other.nonce)
37    }
38
39    /// Returns `true` if `self` and `other` are *not* equal.
40    ///
41    /// Note: This method does **not** check the `nonce` equality.
42    fn is_not_equal(&self, other: &Self) -> Self::Output {
43        // Check the `data` lengths.
44        if self.data.len() != other.data.len() {
45            return Boolean::constant(true);
46        }
47        // Recursively check each entry for inequality.
48        let mut not_equal = Boolean::constant(false);
49        for ((name_a, entry_a), (name_b, entry_b)) in self.data.iter().zip_eq(other.data.iter()) {
50            not_equal = not_equal | name_a.is_not_equal(name_b) | entry_a.is_not_equal(entry_b);
51        }
52
53        // Check the `owner`, `data`, and `nonce`.
54        self.owner.is_not_equal(&other.owner) | not_equal | self.nonce.is_not_equal(&other.nonce)
55    }
56}
57
58#[cfg(test)]
59mod tests {
60    use super::*;
61    use crate::Circuit;
62
63    fn sample_record(mode: Mode) -> Record<Circuit, Plaintext<Circuit>> {
64        let record = console::Record::<
65            <Circuit as Environment>::Network,
66            console::Plaintext<<Circuit as Environment>::Network>,
67        >::from_str(
68            r"{
69    owner: aleo14tlamssdmg3d0p5zmljma573jghe2q9n6wz29qf36re2glcedcpqfg4add.private,
70    a: true.private,
71    b: 123456789field.public,
72    c: 0group.private,
73    d: {
74        e: true.private,
75        f: 123456789field.private,
76        g: 0group.private
77    },
78    _nonce: 0group.public
79}",
80        )
81        .unwrap();
82        Record::new(mode, record)
83    }
84
85    fn sample_mismatched_record(mode: Mode) -> Record<Circuit, Plaintext<Circuit>> {
86        let record = console::Record::<
87            <Circuit as Environment>::Network,
88            console::Plaintext<<Circuit as Environment>::Network>,
89        >::from_str(
90            r"{
91    owner: aleo14tlamssdmg3d0p5zmljma573jghe2q9n6wz29qf36re2glcedcpqfg4add.private,
92    a: true.public,
93    b: 123456789field.public,
94    c: 0group.private,
95    d: {
96        e: true.private,
97        f: 123456789field.private,
98        g: 0group.private
99    },
100    _nonce: 0group.public
101}",
102        )
103        .unwrap();
104        Record::new(mode, record)
105    }
106
107    fn check_is_equal(
108        mode: Mode,
109        num_constants: u64,
110        num_public: u64,
111        num_private: u64,
112        num_constraints: u64,
113    ) -> Result<()> {
114        // Sample the record.
115        let record = sample_record(mode);
116        let mismatched_record = sample_mismatched_record(mode);
117
118        Circuit::scope(format!("{mode}"), || {
119            let candidate = record.is_equal(&record);
120            assert!(candidate.eject_value());
121            assert_scope!(num_constants, num_public, num_private, num_constraints);
122        });
123
124        Circuit::scope(format!("{mode}"), || {
125            let candidate = record.is_equal(&mismatched_record);
126            assert!(!candidate.eject_value());
127        });
128
129        Circuit::reset();
130        Ok(())
131    }
132
133    fn check_is_not_equal(
134        mode: Mode,
135        num_constants: u64,
136        num_public: u64,
137        num_private: u64,
138        num_constraints: u64,
139    ) -> Result<()> {
140        // Sample the record.
141        let record = sample_record(mode);
142        let mismatched_record = sample_mismatched_record(mode);
143
144        Circuit::scope(format!("{mode}"), || {
145            let candidate = record.is_not_equal(&mismatched_record);
146            assert!(candidate.eject_value());
147            assert_scope!(num_constants, num_public, num_private, num_constraints);
148        });
149
150        Circuit::scope(format!("{mode}"), || {
151            let candidate = record.is_not_equal(&record);
152            assert!(!candidate.eject_value());
153        });
154
155        Circuit::reset();
156        Ok(())
157    }
158
159    #[test]
160    fn test_is_equal_constant() -> Result<()> {
161        // Note: This is correct. At this (high) level of a program, we override the default mode in the `Record` case,
162        // based on the user-defined visibility in the record type. Thus, we have nonzero private and constraint values.
163        check_is_equal(Mode::Constant, 17, 0, 23, 33)
164    }
165
166    #[test]
167    fn test_is_equal_public() -> Result<()> {
168        check_is_equal(Mode::Public, 17, 0, 23, 33)
169    }
170
171    #[test]
172    fn test_is_equal_private() -> Result<()> {
173        check_is_equal(Mode::Private, 17, 0, 23, 33)
174    }
175
176    #[test]
177    fn test_is_not_equal_constant() -> Result<()> {
178        // Note: This is correct. At this (high) level of a program, we override the default mode in the `Record` case,
179        // based on the user-defined visibility in the record type. Thus, we have nonzero private and constraint values.
180        check_is_not_equal(Mode::Constant, 7, 0, 27, 27)
181    }
182
183    #[test]
184    fn test_is_not_equal_public() -> Result<()> {
185        check_is_not_equal(Mode::Public, 7, 0, 27, 27)
186    }
187
188    #[test]
189    fn test_is_not_equal_private() -> Result<()> {
190        check_is_not_equal(Mode::Private, 7, 0, 27, 27)
191    }
192}