deserialize.rs
1 // Copyright (c) 2025 ADnet Contributors 2 // This file is part of the AlphaVM 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 crate::helpers::sample::{sample_finalize_registers, sample_registers}; 17 18 use alphavm_synthesizer_process::{Process, Stack}; 19 use alphavm_synthesizer_program::{ 20 DeserializeBits, 21 DeserializeBitsRaw, 22 DeserializeInstruction, 23 DeserializeVariant, 24 Opcode, 25 Operand, 26 Program, 27 RegistersCircuit as _, 28 RegistersTrait as _, 29 }; 30 use circuit::{AleoV0, Eject}; 31 use console::{ 32 network::MainnetV0, 33 prelude::*, 34 program::{ArrayType, Identifier, LiteralType, Plaintext, PlaintextType, Register, U32, Value}, 35 }; 36 37 type CurrentNetwork = MainnetV0; 38 type CurrentAleo = AleoV0; 39 40 const ITERATIONS: usize = 25; 41 42 /// Samples the stack. Note: Do not replicate this for real program use, it is insecure. 43 #[allow(clippy::type_complexity)] 44 fn sample_stack( 45 opcode: Opcode, 46 type_: &PlaintextType<CurrentNetwork>, 47 bits: &ArrayType<CurrentNetwork>, 48 mode: circuit::Mode, 49 ) -> Result<(Stack<CurrentNetwork>, Vec<Operand<CurrentNetwork>>, Register<CurrentNetwork>)> { 50 // Initialize the opcode. 51 let opcode = opcode.to_string(); 52 53 // Initialize the function name. 54 let function_name = Identifier::<CurrentNetwork>::from_str("run")?; 55 56 // Initialize the registers. 57 let r0 = Register::Locator(0); 58 let r1 = Register::Locator(1); 59 60 // Initialize the program. 61 let program = Program::from_str(&format!( 62 "program testing.alpha; 63 function {function_name}: 64 input {r0} as {bits}.{mode}; 65 {opcode} {r0} ({bits}) into {r1} ({type_}); 66 async {function_name} {r0} into r2; 67 output r2 as testing.alpha/{function_name}.future; 68 finalize {function_name}: 69 input {r0} as {bits}.public; 70 {opcode} {r0} ({bits}) into {r1} ({type_}); 71 " 72 ))?; 73 74 // Initialize the operands. 75 let operands = vec![Operand::Register(r0)]; 76 77 // Initialize the stack. 78 let stack = Stack::new(&Process::load()?, &program)?; 79 80 Ok((stack, operands, r1)) 81 } 82 83 fn check_deserialize<const VARIANT: u8>( 84 operation: impl FnOnce( 85 Vec<Operand<CurrentNetwork>>, 86 ArrayType<CurrentNetwork>, 87 Register<CurrentNetwork>, 88 PlaintextType<CurrentNetwork>, 89 ) -> DeserializeInstruction<CurrentNetwork, VARIANT>, 90 opcode: Opcode, 91 type_: &PlaintextType<CurrentNetwork>, 92 mode: &circuit::Mode, 93 iterations: usize, 94 ) { 95 // Initalize an RNG. 96 let rng = &mut TestRng::default(); 97 98 // Struct definitions are not supported. 99 let fail_get_struct = |_: &Identifier<CurrentNetwork>| bail!("structs are not supported"); 100 101 // Get the size in bits. 102 let size_in_bits = match VARIANT { 103 0 => type_.size_in_bits(&fail_get_struct).unwrap(), 104 1 => type_.size_in_bits_raw(&fail_get_struct).unwrap(), 105 _ => panic!("Invalid 'deserialize' variant"), 106 }; 107 let size_in_bits = u32::try_from(size_in_bits).unwrap(); 108 109 println!("Checking '{opcode}' for '{type_}.{mode}' to [boolean; {size_in_bits}u32]"); 110 111 // Construct the array type. 112 let bits_type = ArrayType::new(PlaintextType::Literal(LiteralType::Boolean), vec![U32::new(size_in_bits)]).unwrap(); 113 114 // Initialize the stack. 115 let (stack, operands, destination) = sample_stack(opcode, type_, &bits_type, *mode).unwrap(); 116 117 // Initialize the operation. 118 let operation = operation(operands, bits_type, destination.clone(), type_.clone()); 119 // Initialize the function name. 120 let function_name = Identifier::from_str("run").unwrap(); 121 // Initialize a destination operand. 122 let destination_operand = Operand::Register(destination); 123 124 // Run the test for a desired number of iterations. 125 for _ in 0..iterations { 126 // Sample the plaintext. 127 let plaintext = stack.sample_plaintext(type_, rng).unwrap(); 128 129 // Get the bits of the plaintext. 130 let bits = match VARIANT { 131 0 => plaintext.to_bits_le(), 132 1 => plaintext.to_bits_raw_le(), 133 _ => panic!("Invalid 'deserialize' variant"), 134 }; 135 136 // Check that the number of bits matches. 137 assert_eq!(bits.len(), size_in_bits as usize, "The number of bits does not match the expected size"); 138 139 // Construct the bit array input. 140 let bit_array = Plaintext::from_bit_array(bits, size_in_bits).unwrap(); 141 142 // Attempt to evaluate the valid operand case. 143 let mut evaluate_registers = 144 sample_registers(&stack, &function_name, &[(Value::Plaintext(bit_array.clone()), None)]).unwrap(); 145 let result_a = operation.evaluate(&stack, &mut evaluate_registers); 146 147 // Attempt to execute the valid operand case. 148 let mut execute_registers = 149 sample_registers(&stack, &function_name, &[(Value::Plaintext(bit_array.clone()), Some(*mode))]).unwrap(); 150 let result_b = operation.execute::<CurrentAleo>(&stack, &mut execute_registers); 151 152 // Attempt to finalize the valid operand case. 153 let mut finalize_registers = sample_finalize_registers(&stack, &function_name, &[bit_array]).unwrap(); 154 let result_c = operation.finalize(&stack, &mut finalize_registers); 155 156 // Check that either all operations failed, or all operations succeeded. 157 let all_failed = result_a.is_err() && result_b.is_err() && result_c.is_err(); 158 let all_succeeded = result_a.is_ok() && result_b.is_ok() && result_c.is_ok(); 159 assert!( 160 all_failed || all_succeeded, 161 "The results of the evaluation, execution, and finalization should either all succeed or all fail" 162 ); 163 164 // If all operations succeeded, check that the outputs are consistent. 165 if all_succeeded { 166 // Retrieve the output of evaluation. 167 let output_a = evaluate_registers.load(&stack, &destination_operand).unwrap(); 168 169 // Retrieve the output of execution. 170 let output_b = execute_registers.load_circuit(&stack, &destination_operand).unwrap(); 171 172 // Retrieve the output of finalization. 173 let output_c = finalize_registers.load(&stack, &destination_operand).unwrap(); 174 175 // Check that the outputs are consistent. 176 assert_eq!( 177 output_a, 178 output_b.eject_value(), 179 "The results of the evaluation and execution are inconsistent" 180 ); 181 assert_eq!(output_a, output_c, "The results of the evaluation and finalization are inconsistent"); 182 183 // Check that the output type is consistent with the declared type. 184 match output_a { 185 Value::Plaintext(output_plaintext) => { 186 // Check that the output plaintext matches the input. 187 assert_eq!(output_plaintext, plaintext, "The output value does not match the input type"); 188 } 189 _ => unreachable!("The output type is inconsistent with the declared type"), 190 } 191 } 192 // Reset the circuit. 193 <CurrentAleo as circuit::Environment>::reset(); 194 } 195 } 196 197 // Get the types to be tested. 198 fn test_types(variant: DeserializeVariant) -> Vec<PlaintextType<CurrentNetwork>> { 199 let mut types = vec![ 200 PlaintextType::Literal(LiteralType::Address), 201 PlaintextType::Literal(LiteralType::Boolean), 202 PlaintextType::Literal(LiteralType::Field), 203 PlaintextType::Literal(LiteralType::Group), 204 PlaintextType::Literal(LiteralType::I8), 205 PlaintextType::Literal(LiteralType::I16), 206 PlaintextType::Literal(LiteralType::I32), 207 PlaintextType::Literal(LiteralType::I64), 208 PlaintextType::Literal(LiteralType::I128), 209 PlaintextType::Literal(LiteralType::U8), 210 PlaintextType::Literal(LiteralType::U16), 211 PlaintextType::Literal(LiteralType::U32), 212 PlaintextType::Literal(LiteralType::U64), 213 PlaintextType::Literal(LiteralType::U128), 214 PlaintextType::Literal(LiteralType::Scalar), 215 PlaintextType::Array(ArrayType::new(PlaintextType::Literal(LiteralType::U8), vec![U32::new(8)]).unwrap()), 216 ]; 217 218 // Add additional types for the raw variant. 219 if variant == DeserializeVariant::FromBitsRaw { 220 types.push(PlaintextType::Array( 221 ArrayType::new(PlaintextType::Literal(LiteralType::U8), vec![U32::new(32)]).unwrap(), 222 )) 223 } 224 225 types 226 } 227 228 macro_rules! test_deserialize { 229 ($name: tt, $deserialize:ident, $variant:ident, $iterations:expr) => { 230 paste::paste! { 231 #[test] 232 fn [<test _ $name _ is _ consistent>]() { 233 // Initialize the operation. 234 let operation = |operands, operand_type, destination, destination_type| $deserialize::<CurrentNetwork>::new(operands, operand_type, destination, destination_type).unwrap(); 235 // Initialize the opcode. 236 let opcode = $deserialize::<CurrentNetwork>::opcode(); 237 238 // Prepare the test. 239 let modes = [circuit::Mode::Public, circuit::Mode::Private]; 240 241 for mode in modes.iter() { 242 for type_ in test_types(DeserializeVariant::$variant).iter() { 243 check_deserialize( 244 operation, 245 opcode, 246 type_, 247 mode, 248 $iterations 249 ); 250 } 251 } 252 } 253 } 254 }; 255 } 256 257 test_deserialize!(deserialize_bits, DeserializeBits, FromBits, ITERATIONS); 258 test_deserialize!(deserialize_bits_raw, DeserializeBitsRaw, FromBitsRaw, ITERATIONS);