snarkvm_circuit_program/data/record/
equal.rs

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