equal.rs
1 // Copyright (c) 2019-2025 Alpha-Delta Network Inc. 2 // This file is part of the deltavm 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 use super::*; 17 18 impl<A: Alpha, 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)] 65 mod 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: dx14tlamssdmg3d0p5zmljma573jghe2q9n6wz29qf36re2glcedcpqfg4add.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: dx14tlamssdmg3d0p5zmljma573jghe2q9n6wz29qf36re2glcedcpqfg4add.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 }