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  }