commit.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 include!("../helpers/macros.rs"); 17 18 use crate::helpers::sample::{sample_finalize_registers, sample_registers}; 19 20 use deltavm_synthesizer_process::{Process, Stack}; 21 use deltavm_synthesizer_program::{ 22 CommitBHP256, 23 CommitBHP512, 24 CommitBHP768, 25 CommitBHP1024, 26 CommitInstruction, 27 CommitPED64, 28 CommitPED128, 29 Opcode, 30 Operand, 31 Program, 32 RegistersCircuit as _, 33 RegistersTrait as _, 34 }; 35 use circuit::{AlphaV0, Eject}; 36 use console::{ 37 network::MainnetV0, 38 prelude::*, 39 program::{Identifier, Literal, LiteralType, Plaintext, Register, Value}, 40 }; 41 42 type CurrentNetwork = MainnetV0; 43 type CurrentAlpha = AlphaV0; 44 45 const ITERATIONS: usize = 25; 46 47 /// **Attention**: When changing this, also update in `src/logic/instruction/commit.rs`. 48 fn valid_destination_types() -> &'static [LiteralType] { 49 &[LiteralType::Address, LiteralType::Field, LiteralType::Group] 50 } 51 52 /// Samples the stack. Note: Do not replicate this for real program use, it is insecure. 53 #[allow(clippy::type_complexity)] 54 fn sample_stack( 55 opcode: Opcode, 56 type_a: LiteralType, 57 type_b: LiteralType, 58 mode_a: circuit::Mode, 59 mode_b: circuit::Mode, 60 destination_type: LiteralType, 61 ) -> Result<(Stack<CurrentNetwork>, Vec<Operand<CurrentNetwork>>, Register<CurrentNetwork>)> { 62 // Initialize the opcode. 63 let opcode = opcode.to_string(); 64 65 // Initialize the function name. 66 let function_name = Identifier::<CurrentNetwork>::from_str("run")?; 67 68 // Initialize the registers. 69 let r0 = Register::Locator(0); 70 let r1 = Register::Locator(1); 71 let r2 = Register::Locator(2); 72 73 // Initialize the program. 74 let program = Program::from_str(&format!( 75 "program testing.delta; 76 function {function_name}: 77 input {r0} as {type_a}.{mode_a}; 78 input {r1} as {type_b}.{mode_b}; 79 {opcode} {r0} {r1} into {r2} as {destination_type}; 80 async {function_name} {r0} {r1} into r3; 81 output r3 as testing.delta/{function_name}.future; 82 83 finalize {function_name}: 84 input {r0} as {type_a}.public; 85 input {r1} as {type_b}.public; 86 {opcode} {r0} {r1} into {r2} as {destination_type}; 87 " 88 ))?; 89 90 // Initialize the operands. 91 let operand_a = Operand::Register(r0); 92 let operand_b = Operand::Register(r1); 93 let operands = vec![operand_a, operand_b]; 94 95 // Initialize the stack. 96 let stack = Stack::new(&Process::load()?, &program)?; 97 98 Ok((stack, operands, r2)) 99 } 100 101 fn check_commit<const VARIANT: u8>( 102 operation: impl FnOnce( 103 Vec<Operand<CurrentNetwork>>, 104 Register<CurrentNetwork>, 105 LiteralType, 106 ) -> CommitInstruction<CurrentNetwork, VARIANT>, 107 opcode: Opcode, 108 literal_a: &Literal<CurrentNetwork>, 109 literal_b: &Literal<CurrentNetwork>, 110 mode_a: &circuit::Mode, 111 mode_b: &circuit::Mode, 112 destination_type: LiteralType, 113 ) { 114 println!("Checking '{opcode}' for '{literal_a}.{mode_a}' and '{literal_b}.{mode_b}'"); 115 116 // Initialize the types. 117 let type_a = literal_a.to_type(); 118 let type_b = literal_b.to_type(); 119 120 // Initialize the stack. 121 let (stack, operands, destination) = 122 sample_stack(opcode, type_a, type_b, *mode_a, *mode_b, destination_type).unwrap(); 123 // Initialize the operation. 124 let operation = operation(operands, destination.clone(), destination_type); 125 // Initialize the function name. 126 let function_name = Identifier::from_str("run").unwrap(); 127 // Initialize a destination operand. 128 let destination_operand = Operand::Register(destination); 129 130 // Attempt to evaluate the valid operand case. 131 let values = 132 [(Value::Plaintext(Plaintext::from(literal_a)), None), (Value::Plaintext(Plaintext::from(literal_b)), None)]; 133 let mut evaluate_registers = sample_registers(&stack, &function_name, &values).unwrap(); 134 let result_a = operation.evaluate(&stack, &mut evaluate_registers); 135 136 // Attempt to execute the valid operand case. 137 let values = [ 138 (Value::Plaintext(Plaintext::from(literal_a)), Some(*mode_a)), 139 (Value::Plaintext(Plaintext::from(literal_b)), Some(*mode_b)), 140 ]; 141 let mut execute_registers = sample_registers(&stack, &function_name, &values).unwrap(); 142 let result_b = operation.execute::<CurrentAlpha>(&stack, &mut execute_registers); 143 144 // Attempt to finalize the valid operand case. 145 let mut finalize_registers = 146 sample_finalize_registers(&stack, &function_name, &[Plaintext::from(literal_a), Plaintext::from(literal_b)]) 147 .unwrap(); 148 let result_c = operation.finalize(&stack, &mut finalize_registers); 149 150 // Check that either all operations failed, or all operations succeeded. 151 let all_failed = result_a.is_err() && result_b.is_err() && result_c.is_err(); 152 let all_succeeded = result_a.is_ok() && result_b.is_ok() && result_c.is_ok(); 153 assert!( 154 all_failed || all_succeeded, 155 "The results of the evaluation, execution, and finalization should either all succeed or all fail" 156 ); 157 158 // If all operations succeeded, check that the outputs are consistent. 159 if all_succeeded { 160 // Retrieve the output of evaluation. 161 let output_a = evaluate_registers.load(&stack, &destination_operand).unwrap(); 162 163 // Retrieve the output of execution. 164 let output_b = execute_registers.load_circuit(&stack, &destination_operand).unwrap(); 165 166 // Retrieve the output of finalization. 167 let output_c = finalize_registers.load(&stack, &destination_operand).unwrap(); 168 169 // Check that the outputs are consistent. 170 assert_eq!(output_a, output_b.eject_value(), "The results of the evaluation and execution are inconsistent"); 171 assert_eq!(output_a, output_c, "The results of the evaluation and finalization are inconsistent"); 172 173 // Check that the output type is consistent with the declared type. 174 match output_a { 175 Value::Plaintext(Plaintext::Literal(literal, _)) => { 176 assert_eq!( 177 literal.to_type(), 178 destination_type, 179 "The output type is inconsistent with the declared type" 180 ); 181 } 182 _ => unreachable!("The output type is inconsistent with the declared type"), 183 } 184 } 185 186 // Reset the circuit. 187 <CurrentAlpha as circuit::Environment>::reset(); 188 } 189 190 macro_rules! test_commit { 191 ($name: tt, $commit:ident) => { 192 paste::paste! { 193 #[test] 194 fn [<test _ $name _ is _ consistent>]() { 195 // Initialize the operation. 196 let operation = |operands, destination, destination_type| $commit::<CurrentNetwork>::new(operands, destination, destination_type).unwrap(); 197 // Initialize the opcode. 198 let opcode = $commit::<CurrentNetwork>::opcode(); 199 200 // Prepare the rng. 201 let mut rng = TestRng::default(); 202 203 // Prepare the test. 204 let modes_a = [circuit::Mode::Public, circuit::Mode::Private]; 205 let modes_b = [circuit::Mode::Public, circuit::Mode::Private]; 206 207 for _ in 0..ITERATIONS { 208 let literals_a = sample_literals!(CurrentNetwork, &mut rng); 209 let literals_b = vec![console::program::Literal::Scalar(console::types::Scalar::rand(&mut rng))]; 210 211 for literal_a in &literals_a { 212 for literal_b in &literals_b { 213 for mode_a in &modes_a { 214 for mode_b in &modes_b { 215 for destination_type in valid_destination_types() { 216 check_commit(operation, opcode, literal_a, literal_b, mode_a, mode_b, *destination_type); 217 } 218 } 219 } 220 } 221 } 222 } 223 } 224 } 225 }; 226 } 227 228 test_commit!(commit_bhp256, CommitBHP256); 229 test_commit!(commit_bhp512, CommitBHP512); 230 test_commit!(commit_bhp768, CommitBHP768); 231 test_commit!(commit_bhp1024, CommitBHP1024); 232 233 // Note this test must be explicitly written, instead of using the macro, because CommitPED64 and CommitToGroupPED64 fails on certain input types. 234 #[test] 235 fn test_commit_ped64_is_consistent() { 236 // Prepare the rng. 237 let mut rng = TestRng::default(); 238 239 // Prepare the test. 240 let modes_a = [circuit::Mode::Public, circuit::Mode::Private]; 241 let modes_b = [circuit::Mode::Public, circuit::Mode::Private]; 242 243 macro_rules! check_commit { 244 ($operation:tt) => { 245 for _ in 0..ITERATIONS { 246 let literals_a = [ 247 Literal::Boolean(console::types::Boolean::rand(&mut rng)), 248 Literal::I8(console::types::I8::rand(&mut rng)), 249 Literal::I16(console::types::I16::rand(&mut rng)), 250 Literal::I32(console::types::I32::rand(&mut rng)), 251 Literal::U8(console::types::U8::rand(&mut rng)), 252 Literal::U16(console::types::U16::rand(&mut rng)), 253 Literal::U32(console::types::U32::rand(&mut rng)), 254 ]; 255 let literals_b = vec![Literal::Scalar(console::types::Scalar::rand(&mut rng))]; 256 for literal_a in &literals_a { 257 for literal_b in &literals_b { 258 for mode_a in &modes_a { 259 for mode_b in &modes_b { 260 for destination_type in valid_destination_types() { 261 check_commit( 262 |operands, destination, destination_type| { 263 $operation::<CurrentNetwork>::new(operands, destination, destination_type) 264 .unwrap() 265 }, 266 $operation::<CurrentNetwork>::opcode(), 267 literal_a, 268 literal_b, 269 mode_a, 270 mode_b, 271 *destination_type, 272 ); 273 } 274 } 275 } 276 } 277 } 278 } 279 }; 280 } 281 check_commit!(CommitPED64); 282 } 283 284 // Note this test must be explicitly written, instead of using the macro, because CommitPED128 and CommitToGroupPED64 fails on certain input types. 285 #[test] 286 fn test_commit_ped128_is_consistent() { 287 // Prepare the rng. 288 let mut rng = TestRng::default(); 289 290 // Prepare the test. 291 let modes_a = [circuit::Mode::Public, circuit::Mode::Private]; 292 let modes_b = [circuit::Mode::Public, circuit::Mode::Private]; 293 294 macro_rules! check_commit { 295 ($operation:tt) => { 296 for _ in 0..ITERATIONS { 297 let literals_a = [ 298 Literal::Boolean(console::types::Boolean::rand(&mut rng)), 299 Literal::I8(console::types::I8::rand(&mut rng)), 300 Literal::I16(console::types::I16::rand(&mut rng)), 301 Literal::I32(console::types::I32::rand(&mut rng)), 302 Literal::I64(console::types::I64::rand(&mut rng)), 303 Literal::U8(console::types::U8::rand(&mut rng)), 304 Literal::U16(console::types::U16::rand(&mut rng)), 305 Literal::U32(console::types::U32::rand(&mut rng)), 306 Literal::U64(console::types::U64::rand(&mut rng)), 307 ]; 308 let literals_b = vec![Literal::Scalar(console::types::Scalar::rand(&mut rng))]; 309 for literal_a in &literals_a { 310 for literal_b in &literals_b { 311 for mode_a in &modes_a { 312 for mode_b in &modes_b { 313 for destination_type in valid_destination_types() { 314 check_commit( 315 |operands, destination, destination_type| { 316 $operation::<CurrentNetwork>::new(operands, destination, destination_type) 317 .unwrap() 318 }, 319 $operation::<CurrentNetwork>::opcode(), 320 literal_a, 321 literal_b, 322 mode_a, 323 mode_b, 324 *destination_type, 325 ); 326 } 327 } 328 } 329 } 330 } 331 } 332 }; 333 } 334 check_commit!(CommitPED128); 335 }