snarkvm_synthesizer_program/logic/instruction/operation/
macros.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
16/// Creates a new `struct` that implements the `Operation` trait.
17///
18/// # Examples
19/// ```ignore
20/// operation!(
21///     pub struct AddOperation<AddOperator, "add"> {
22///         (Field, Field) => Field,
23///         (Group, Group) => Group,
24///         (I8, I8) => I8,
25///         (I16, I16) => I16,
26///         (I32, I32) => I32,
27///         (I64, I64) => I64,
28///         (I128, I128) => I128,
29///         (U8, U8) => U8,
30///         (U16, U16) => U16,
31///         (U32, U32) => U32,
32///         (U64, U64) => U64,
33///         (U128, U128) => U128,
34///         (Scalar, Scalar) => Scalar,
35///     }
36/// );
37/// ```
38#[macro_export]
39macro_rules! operation {
40    // Unary operation.
41    ($vis:vis struct $name:ident<$operator:path, $circuit_operator:path, $operate:ident, $opcode:tt> { $( $input:ident => $output:ident $( ($($condition:tt),+) )?, )+ }) => {
42        $crate::operation!($vis struct $name<$operator, $circuit_operator, $operate, $opcode, 1> { $( ($input) => $output $( ( $($condition),+ ) )?, )+ });
43    };
44    // Unary operation with question mark (?).
45    ($vis:vis struct $name:ident<$operator:path, $circuit_operator:path, $operate:ident?, $opcode:tt> { $( $input:ident => $output:ident $( ($($condition:tt),+) )?, )+ }) => {
46        $crate::operation!($vis struct $name<$operator, $circuit_operator, $operate?, $opcode, 1> { $( ($input) => $output $( ( $($condition),+ ) )?, )+ });
47    };
48    // Binary operation.
49    ($vis:vis struct $name:ident<$operator:path, $circuit_operator:path, $operate:ident, $opcode:tt> { $( ($input_a:ident, $input_b:ident) => $output:ident $( ($($condition:tt),+) )?, )+ }) => {
50        $crate::operation!($vis struct $name<$operator, $circuit_operator, $operate, $opcode, 2> { $( ($input_a, $input_b) => $output $( ( $($condition),+ ) )?, )+ });
51    };
52    // Ternary operation.
53    ($vis:vis struct $name:ident<$operator:path, $circuit_operator:path, $operate:ident, $opcode:tt> { $( ($input_a:ident, $input_b:ident, $input_c:ident) => $output:ident $( ($($condition:tt),+) )?, )+ }) => {
54        $crate::operation!($vis struct $name<$operator, $circuit_operator, $operate, $opcode, 3> { $( ($input_a, $input_b, $input_c) => $output $( ( $($condition),+ ) )?, )+ });
55    };
56    // K-ary operation.
57    ($vis:vis struct $name:ident<$operator:path, $circuit_operator:path, $operate:ident, $opcode:tt, $num_inputs:tt> { $( ( $($input:ident),+ ) => $output:ident $( ($($condition:tt),+) )?, )+ }) => {
58        /// The implementation of the binary operation.
59        #[derive(Clone, PartialEq, Eq, Hash)]
60        $vis struct $name<N: Network>(core::marker::PhantomData<N>);
61
62        impl<N: Network> $crate::Operation<N, console::program::Literal<N>, console::program::LiteralType, $num_inputs> for $name<N> {
63            /// The opcode of the operation.
64            const OPCODE: $crate::Opcode = Opcode::Literal($opcode);
65
66            /// Returns the result of evaluating the operation on the given inputs.
67            #[inline]
68            fn evaluate(inputs: &[console::program::Literal<N>; $num_inputs]) -> Result<console::program::Literal<N>> {
69                // Prepare the operator.
70                use $operator as Operator;
71                // Compute the output.
72                Ok($crate::evaluate!(match Operator::$operate(inputs) { $( ( $($input),+ ) => $output, )+ }))
73            }
74
75            /// Returns the result of executing the operation on the given circuit inputs.
76            #[inline]
77            fn execute<A: circuit::Aleo<Network = N>>(inputs: &[circuit::Literal<A>; $num_inputs]) -> Result<circuit::Literal<A>> {
78                // Prepare the circuit operator.
79                use $circuit_operator as Operator;
80                // Compute the output.
81                Ok($crate::execute!(match Operator::$operate(inputs) { $( ( $($input),+ ) => $output, )+ }))
82            }
83
84            /// Returns the output type from the given input types.
85            #[inline]
86            fn output_type(inputs: &[console::program::LiteralType; $num_inputs]) -> Result<console::program::LiteralType> {
87                // Compute the output type.
88                Ok($crate::output_type!(match inputs { $( ( $($input),+ ) => $output, )+ }))
89            }
90        }
91
92        paste::paste! {
93            #[cfg(test)]
94            mod [<test _ $operate>] {
95                use super::*;
96                use console::types::*;
97
98                // Prepare the environment.
99                type CurrentNetwork = console::network::MainnetV0;
100                type CurrentAleo = circuit::network::AleoV0;
101
102                // Prepare the operator.
103                use $operator as Operator;
104                // Prepare the operation.
105                type Operation = $name::<CurrentNetwork>;
106                // Execute the test cases for the operation.
107                $crate::test_execute!(Operator::$operate == Operation::execute { $( ( $($input),+ ) => $output $( ($($condition),+) )?, )+ });
108            }
109        }
110    };
111    // K-ary operation with question mark (?).
112    ($vis:vis struct $name:ident<$operator:path, $circuit_operator:path, $operate:ident?, $opcode:tt, $num_inputs:tt> { $( ( $($input:ident),+ ) => $output:ident $( ($($condition:tt),+) )?, )+ }) => {
113        /// The implementation of the binary operation.
114        #[derive(Clone, PartialEq, Eq, Hash)]
115        $vis struct $name<N: Network>(core::marker::PhantomData<N>);
116
117        impl<N: Network> $crate::Operation<N, console::program::Literal<N>, console::program::LiteralType, $num_inputs> for $name<N> {
118            /// The opcode of the operation.
119            const OPCODE: $crate::Opcode = Opcode::Literal($opcode);
120
121            /// Returns the result of evaluating the operation on the given inputs.
122            #[inline]
123            fn evaluate(inputs: &[console::program::Literal<N>; $num_inputs]) -> Result<console::program::Literal<N>> {
124                // Prepare the operator.
125                use $operator as Operator;
126                // Compute the output.
127                Ok($crate::evaluate!(match Operator::$operate(inputs)? { $( ( $($input),+ ) => $output, )+ }))
128            }
129
130            /// Returns the result of executing the operation on the given circuit inputs.
131            #[inline]
132            fn execute<A: circuit::Aleo<Network = N>>(inputs: &[circuit::Literal<A>; $num_inputs]) -> Result<circuit::Literal<A>> {
133                // Prepare the circuit operator.
134                use $circuit_operator as Operator;
135                // Compute the output.
136                Ok($crate::execute!(match Operator::$operate(inputs) { $( ( $($input),+ ) => $output, )+ }))
137            }
138
139            /// Returns the output type from the given input types.
140            #[inline]
141            fn output_type(inputs: &[console::program::LiteralType; $num_inputs]) -> Result<console::program::LiteralType> {
142                // Compute the output type.
143                Ok($crate::output_type!(match inputs { $( ( $($input),+ ) => $output, )+ }))
144            }
145        }
146
147        paste::paste! {
148            #[cfg(test)]
149            mod [<test _ $operate>] {
150                use super::*;
151                use console::types::*;
152
153                // Prepare the environment.
154                type CurrentNetwork = console::network::MainnetV0;
155                type CurrentAleo = circuit::network::AleoV0;
156
157                // Prepare the operator.
158                use $operator as Operator;
159                // Prepare the operation.
160                type Operation = $name::<CurrentNetwork>;
161                // Execute the test cases for the operation.
162                $crate::test_execute!(Operator::$operate == Operation::execute? { $( ( $($input),+ ) => $output $( ($($condition),+) )?, )+ });
163            }
164        }
165    };
166}
167
168/// Creates a match statement that evaluates the operation.
169///
170/// ## Example
171/// ```ignore
172/// evaluate!(
173///     match Operator::add(inputs) {
174///         (I8, I8) => I8,
175///         (I16, I16) => I16,
176///         (I32, I32) => I32,
177///         (I64, I64) => I64,
178///         (I128, I128) => I128,
179///         (U8, U8) => U8,
180///         (U16, U16) => U16,
181///         (U32, U32) => U32,
182///         (U64, U64) => U64,
183///         (U128, U128) => U128,
184///     }
185/// )
186/// ```
187#[macro_export]
188macro_rules! evaluate {
189    // Unary operation.
190    (match $operator:tt::$operate:tt($inputs:expr) { $( ($input:ident) => $output:ident, )+ }) => {{
191        // Retrieve the operand.
192        let [first] = $inputs;
193        // Compute the output.
194        match first {
195            $(console::program::Literal::$input(first) => console::program::Literal::$output(first.$operate()),)+
196            _ => bail!("Invalid operand for the '{}' instruction", Self::OPCODE),
197        }
198    }};
199    // Unary operation with question mark (?).
200    (match $operator:tt::$operate:tt($inputs:expr)? { $( ( $input:ident ) => $output:ident, )+ }) => {{
201        // Retrieve the operand.
202        let [first] = $inputs;
203        // Compute the output.
204        match first {
205            $(console::program::Literal::$input(first) => console::program::Literal::$output(first.$operate()?),)+
206            _ => bail!("Invalid operand for the '{}' instruction", Self::OPCODE),
207        }
208    }};
209    // Binary operation.
210    (match $operator:tt::$operate:tt($inputs:expr) { $( ($input_a:ident, $input_b:ident) => $output:ident, )+ }) => {{
211        // Retrieve the operands.
212        let [first, second] = $inputs;
213        // Compute the output.
214        match (first, second) {
215            $((console::program::Literal::$input_a(first), console::program::Literal::$input_b(second)) => console::program::Literal::$output(first.$operate(second)),)+
216            _ => bail!("Invalid operands for the '{}' instruction", Self::OPCODE),
217        }
218    }};
219    // Ternary operation.
220    (match $operator:tt::$operate:tt($inputs:expr) { $( ($input_a:ident, $input_b:ident, $input_c:ident) => $output:ident, )+ }) => {{
221        // Retrieve the operands.
222        let [first, second, third] = $inputs;
223        // Compute the output.
224        match (first, second, third) {
225            $((console::program::Literal::$input_a(first), console::program::Literal::$input_b(second), console::program::Literal::$input_c(third)) => console::program::Literal::$output($operator::$operate(first, second, third)),)+
226            _ => bail!("Invalid operands for the '{}' instruction", Self::OPCODE),
227        }
228    }};
229}
230
231/// Creates a match statement that executes the operation.
232///
233/// ## Example
234/// ```ignore
235/// execute!(
236///     match Operator::add(inputs) {
237///         (I8, I8) => I8,
238///         (I16, I16) => I16,
239///         (I32, I32) => I32,
240///         (I64, I64) => I64,
241///         (I128, I128) => I128,
242///         (U8, U8) => U8,
243///         (U16, U16) => U16,
244///         (U32, U32) => U32,
245///         (U64, U64) => U64,
246///         (U128, U128) => U128,
247///     }
248/// )
249/// ```
250#[macro_export]
251macro_rules! execute {
252    // Unary operation.
253    (match $operator:tt::$operate:tt($inputs:expr) { $( ($input:ident) => $output:ident, )+ }) => {{
254        // Retrieve the operand.
255        let [first] = $inputs.to_owned();
256        // Compute the output.
257        match first {
258            $(circuit::Literal::$input(first) => circuit::Literal::$output(first.$operate()),)+
259            _ => bail!("Invalid operand for the '{}' instruction", Self::OPCODE),
260        }
261    }};
262    // Binary operation.
263    (match $operator:tt::$operate:tt($inputs:expr) { $( ($input_a:ident, $input_b:ident) => $output:ident, )+ }) => {{
264        // Retrieve the operands.
265        let [first, second] = $inputs.to_owned();
266        // Compute the output.
267        match (first, second) {
268            $((circuit::Literal::$input_a(first), circuit::Literal::$input_b(second)) => circuit::Literal::$output(first.$operate(&second)),)+
269            _ => bail!("Invalid operands for the '{}' instruction", Self::OPCODE),
270        }
271    }};
272    // Ternary operation.
273    (match $operator:tt::$operate:tt($inputs:expr) { $( ($input_a:ident, $input_b:ident, $input_c:ident) => $output:ident, )+ }) => {{
274        // Retrieve the operands.
275        let [first, second, third] = $inputs.to_owned();
276        // Compute the output.
277        match (first, second, third) {
278            $((circuit::Literal::$input_a(first), circuit::Literal::$input_b(second), circuit::Literal::$input_c(third)) => circuit::Literal::$output($operator::$operate(&first, &second, &third)),)+
279            _ => bail!("Invalid operands for the '{}' instruction", Self::OPCODE),
280        }
281    }};
282}
283
284/// Creates a match statement that returns the output type given the input types.
285///
286/// ## Example
287/// ```ignore
288/// output_type!(
289///     match (first, second) {
290///         (I8, I8) => I8,
291///         (I16, I16) => I16,
292///         (I32, I32) => I32,
293///         (I64, I64) => I64,
294///         (I128, I128) => I128,
295///         (U8, U8) => U8,
296///         (U16, U16) => U16,
297///         (U32, U32) => U32,
298///         (U64, U64) => U64,
299///         (U128, U128) => U128,
300///     }
301/// )
302/// ```
303#[macro_export]
304macro_rules! output_type {
305    // Unary operation.
306    (match $inputs:ident { $( ($input:ident) => $output:ident, )+ }) => {{
307        // Retrieve the operand.
308        let [first] = $inputs;
309        // Compute the output type.
310        match first {
311            $(console::program::LiteralType::$input => console::program::LiteralType::$output,)+
312            _ => bail!("Invalid operand types for the '{}' instruction", Self::OPCODE),
313        }
314    }};
315    // Binary operation.
316    (match $inputs:ident { $( ($input_a:ident, $input_b:ident) => $output:ident, )+ }) => {{
317        // Retrieve the operands.
318        let [first, second] = $inputs;
319        // Compute the output type.
320        match (first, second) {
321            $((console::program::LiteralType::$input_a, console::program::LiteralType::$input_b) => console::program::LiteralType::$output,)+
322            _ => bail!("Invalid operand types for the '{}' instruction", Self::OPCODE),
323        }
324    }};
325    // Ternary operation.
326    (match $inputs:ident { $( ($input_a:ident, $input_b:ident, $input_c:ident) => $output:ident, )+ }) => {{
327        // Retrieve the operands.
328        let [first, second, third] = $inputs;
329        // Compute the output type.
330        match (first, second, third) {
331            $((console::program::LiteralType::$input_a, console::program::LiteralType::$input_b, console::program::LiteralType::$input_c) => console::program::LiteralType::$output,)+
332            _ => bail!("Invalid operand types for the '{}' instruction", Self::OPCODE),
333        }
334    }};
335}
336
337#[cfg(test)]
338mod tests {
339    /// Samples a random value for each literal type.
340    #[macro_export]
341    macro_rules! sample_literals {
342        ($network:ident, $rng:expr) => {
343            [
344                console::program::Literal::<$network>::Address(console::types::Address::rand($rng)),
345                console::program::Literal::Boolean(console::types::Boolean::rand($rng)),
346                console::program::Literal::Field(console::types::Field::rand($rng)),
347                console::program::Literal::Group(console::types::Group::rand($rng)),
348                console::program::Literal::I8(console::types::I8::rand($rng)),
349                console::program::Literal::I16(console::types::I16::rand($rng)),
350                console::program::Literal::I32(console::types::I32::rand($rng)),
351                console::program::Literal::I64(console::types::I64::rand($rng)),
352                console::program::Literal::I128(console::types::I128::rand($rng)),
353                console::program::Literal::U8(console::types::U8::rand($rng)),
354                console::program::Literal::U16(console::types::U16::rand($rng)),
355                console::program::Literal::U32(console::types::U32::rand($rng)),
356                console::program::Literal::U64(console::types::U64::rand($rng)),
357                console::program::Literal::U128(console::types::U128::rand($rng)),
358                console::program::Literal::Scalar(console::types::Scalar::rand($rng)),
359                console::program::Literal::sample(console::program::LiteralType::Signature, $rng),
360                console::program::Literal::String(console::types::StringType::rand($rng)),
361            ]
362        };
363    }
364
365    ///
366    /// Creates a test of the given operation for each declared case.
367    ///
368    /// For each declared case, this macro samples random values and checks that
369    /// the output of the operator (LHS) matches the output of the operation (RHS).
370    /// In addition, this macro ensures all combinations of literal types that
371    /// do **not** match these declared cases fail on evaluation.
372    ///
373    /// ## Example
374    /// ```ignore
375    /// ```text
376    ///     test_execute!(
377    ///         Operator::add == AddOp::execute {
378    ///             (Field, Field) => Field,
379    ///             (Group, Group) => Group,
380    ///             (I8, I8) => I8,
381    ///             (I16, I16) => I16,
382    ///             (I32, I32) => I32,
383    ///             (I64, I64) => I64,
384    ///             (I128, I128) => I128,
385    ///             (U8, U8) => U8,
386    ///             (U16, U16) => U16,
387    ///             (U32, U32) => U32,
388    ///             (U64, U64) => U64,
389    ///             (U128, U128) => U128,
390    ///             (Scalar, Scalar) => Scalar,
391    ///         }
392    ///     );
393    /// ```
394    #[macro_export]
395    macro_rules! test_execute {
396        // Case 0: Unary operation.
397        ($operator:tt::$operate:tt == $operation:tt::$execute:tt { $( ($input:ident) => $output:ident $( ($($condition:tt),+) )?, )+ }) => {
398            // For each given case of inputs and outputs, invoke `Case 0-A` or `Case 0-B` (see below).
399            $( $crate::test_execute!{$operator::$operate == $operation::$execute for $input => $output $( ($($condition),+) )?} )+
400            // For each non-existent case of inputs and outputs, invoke the following test to ensure the operation **fails**.
401            paste::paste! {
402                #[test]
403                fn [<test _ $operate _ fails _ on _ invalid _ operands>]() -> Result<()> {
404                    // Prepare the rng.
405                    let mut rng = TestRng::default();
406
407                    for i in 0..8 {
408                        for literal_a in $crate::sample_literals!(CurrentNetwork, &mut rng).iter() {
409                            for mode_a in &[circuit::Mode::Constant, circuit::Mode::Public, circuit::Mode::Private] {
410                                // Skip this iteration, if this is **not** an invalid operand case.
411                                $(if literal_a.to_type() == console::program::LiteralType::$input {
412                                    continue;
413                                })+
414
415                                // Attempt to compute the invalid operand case.
416                                let result_a = <$operation as $crate::Operation<_, _, _, 1>>::evaluate(&[literal_a.clone()]);
417                                // Ensure the computation failed.
418                                assert!(result_a.is_err(), "An invalid operand case (on iteration {i}) did not fail (console): {literal_a}");
419
420                                // Attempt to compute the invalid operand case.
421                                let result_b = <$operation as $crate::Operation<_, _, _, 1>>::$execute::<CurrentAleo>(&[
422                                    circuit::program::Literal::from_str(&format!("{literal_a}.{mode_a}"))?,
423                                ]);
424                                // Ensure the computation failed.
425                                assert!(result_b.is_err(), "An invalid operand case (on iteration {i}) did not fail (circuit): {literal_a}");
426                                // Reset the circuit.
427                                <CurrentAleo as circuit::Environment>::reset();
428                            }
429                        }
430                    }
431                    Ok(())
432                }
433            }
434        };
435
436        // Case 0Q: Unary operation with question mark (?).
437        ($operator:tt::$operate:tt == $operation:tt::$execute:tt? { $( ($input:ident) => $output:ident $( ($($condition:tt),+) )?, )+ }) => {
438            // For each given case of inputs and outputs, invoke `Case 0Q-A` or `Case 0Q-B` (see below).
439            $( $crate::test_execute!{$operator::$operate == $operation::$execute (.unwrap()) for $input => $output $( ($($condition),+) )?} )+
440            // For each non-existent case of inputs and outputs, invoke the following test to ensure the operation **fails**.
441            paste::paste! {
442                #[test]
443                fn [<test _ $operate _ fails _ on _ invalid _ operands>]() -> Result<()> {
444                    // Prepare the rng.
445                    let mut rng = TestRng::default();
446
447                    for i in 0..8 {
448                        for literal_a in $crate::sample_literals!(CurrentNetwork, &mut rng).iter() {
449                            for mode_a in &[circuit::Mode::Constant, circuit::Mode::Public, circuit::Mode::Private] {
450                                // Skip this iteration, if this is **not** an invalid operand case.
451                                $(if literal_a.to_type() == console::program::LiteralType::$input {
452                                    continue;
453                                })+
454
455                                // Attempt to compute the invalid operand case.
456                                let result_a = <$operation as $crate::Operation<_, _, _, 1>>::evaluate(&[literal_a.clone()]);
457                                // Ensure the computation failed.
458                                assert!(result_a.is_err(), "An invalid operand case (on iteration {i}) did not fail (console): {literal_a}");
459
460                                // Attempt to compute the invalid operand case.
461                                let result_b = <$operation as $crate::Operation<_, _, _, 1>>::$execute::<CurrentAleo>(&[
462                                    circuit::program::Literal::from_str(&format!("{literal_a}.{mode_a}"))?,
463                                ]);
464                                // Ensure the computation failed.
465                                assert!(result_b.is_err(), "An invalid operand case (on iteration {i}) did not fail (circuit): {literal_a}");
466                                // Reset the circuit.
467                                <CurrentAleo as circuit::Environment>::reset();
468                            }
469                        }
470                    }
471                    Ok(())
472                }
473            }
474        };
475
476        // Case 1: Binary operation.
477        ($operator:tt::$operate:tt == $operation:tt::$execute:tt { $( ($input_a:ident, $input_b:ident) => $output:ident $( ($($condition:tt),+) )?, )+ }) => {
478            // For each given case of inputs and outputs, invoke `Case 1-A` or `Case 1-B` (see below).
479            $( $crate::test_execute!{$operator::$operate == $operation::$execute for ($input_a, $input_b) => $output $( ($($condition),+) )?} )+
480
481            // For each non-existent case of inputs and outputs, invoke the following test to ensure the operation **fails**.
482            paste::paste! {
483                #[test]
484                fn [<test _ $operate _ fails _ on _ invalid _ operands>]() -> Result<()> {
485                    // Prepare the rng.
486                    let mut rng = TestRng::default();
487
488                    for i in 0..8 {
489                        for literal_a in $crate::sample_literals!(CurrentNetwork, &mut rng).iter() {
490                            for literal_b in $crate::sample_literals!(CurrentNetwork, &mut rng).iter() {
491                                for mode_a in &[circuit::Mode::Constant, circuit::Mode::Public, circuit::Mode::Private] {
492                                    for mode_b in &[circuit::Mode::Constant, circuit::Mode::Public, circuit::Mode::Private] {
493                                        // Skip this iteration, if this is **not** an invalid operand case.
494                                        $(if literal_a.to_type() == console::program::LiteralType::$input_a
495                                          && literal_b.to_type() == console::program::LiteralType::$input_b {
496                                            continue;
497                                        })+
498
499                                        // Attempt to compute the invalid operand case.
500                                        let result_a = <$operation as $crate::Operation<_, _, _, 2>>::evaluate(&[literal_a.clone(), literal_b.clone()]);
501                                        // Ensure the computation failed.
502                                        assert!(result_a.is_err(), "An invalid operands case (on iteration {i}) did not fail (console): {literal_a} {literal_b}");
503
504                                        // Attempt to compute the invalid operand case.
505                                        let result_b = <$operation as $crate::Operation<_, _, _, 2>>::$execute::<CurrentAleo>(&[
506                                            circuit::program::Literal::from_str(&format!("{literal_a}.{mode_a}"))?,
507                                            circuit::program::Literal::from_str(&format!("{literal_b}.{mode_b}"))?,
508                                        ]);
509                                        // Ensure the computation failed.
510                                        assert!(result_b.is_err(), "An invalid operands case (on iteration {i}) did not fail (circuit): {literal_a} {literal_b}");
511                                        // Reset the circuit.
512                                        <CurrentAleo as circuit::Environment>::reset();
513                                    }
514                                }
515                            }
516                        }
517                    }
518                    Ok(())
519                }
520            }
521        };
522
523        // Case 2: Ternary operation.
524        ($operator:tt::$operate:tt == $operation:tt::$execute:tt { $( ($input_a:ident, $input_b:ident, $input_c:ident) => $output:ident $( ($($condition:tt),+) )?, )+ }) => {
525            // For each given case of inputs and outputs, invoke `Case 2-A` or `Case 2-B` (see below).
526            $( $crate::test_execute!{$operator::$operate == $operation::$execute for ($input_a, $input_b, $input_c) => $output $( ($($condition),+) )?} )+
527
528            // For each non-existent case of inputs and outputs, invoke the following test to ensure the operation **fails**.
529            paste::paste! {
530                #[test]
531                fn [<test _ $operate _ fails _ on _ invalid _ operands>]() -> Result<()> {
532                    // Prepare the rng.
533                    let mut rng = TestRng::default();
534
535                    for literal_a in $crate::sample_literals!(CurrentNetwork, &mut rng).iter() {
536                        for literal_b in $crate::sample_literals!(CurrentNetwork, &mut rng).iter() {
537                            for literal_c in $crate::sample_literals!(CurrentNetwork, &mut rng).iter() {
538                                for mode_a in &[circuit::Mode::Constant, circuit::Mode::Public, circuit::Mode::Private] {
539                                    for mode_b in &[circuit::Mode::Constant, circuit::Mode::Public, circuit::Mode::Private] {
540                                        for mode_c in &[circuit::Mode::Constant, circuit::Mode::Public, circuit::Mode::Private] {
541                                            // Skip this iteration, if this is **not** an invalid operand case.
542                                            $(if literal_a.to_type() == console::program::LiteralType::$input_a
543                                              && literal_b.to_type() == console::program::LiteralType::$input_b
544                                              && literal_c.to_type() == console::program::LiteralType::$input_c {
545                                                continue;
546                                            })+
547
548                                            // Attempt to compute the invalid operand case.
549                                            let result_a = <$operation as $crate::Operation<_, _, _, 3>>::evaluate(&[literal_a.clone(), literal_b.clone(), literal_c.clone()]);
550                                            // Ensure the computation failed.
551                                            assert!(result_a.is_err(), "An invalid operands case did not fail (console): {literal_a} {literal_b}");
552
553                                            // Attempt to compute the invalid operand case.
554                                            let result_b = <$operation as $crate::Operation<_, _, _, 3>>::$execute::<CurrentAleo>(&[
555                                                circuit::program::Literal::from_str(&format!("{literal_a}.{mode_a}"))?,
556                                                circuit::program::Literal::from_str(&format!("{literal_b}.{mode_b}"))?,
557                                                circuit::program::Literal::from_str(&format!("{literal_c}.{mode_c}"))?,
558                                            ]);
559                                            // Ensure the computation failed.
560                                            assert!(result_b.is_err(), "An invalid operands case did not fail (circuit): {literal_a} {literal_b} {literal_c}");
561                                            // Reset the circuit.
562                                            <CurrentAleo as circuit::Environment>::reset();
563                                        }
564                                    }
565                                }
566                            }
567                        }
568                    }
569                    Ok(())
570                }
571            }
572        };
573
574        ////////////////////
575        // Private Macros //
576        ////////////////////
577
578        // Case 0-A: Unary operation.
579        // Case 0-B: Unary operation, where:
580        //   1. "ensure overflow halts"
581        //     - If the sampled values overflow on evaluation, ensure it halts.
582        //     - If the sampled values **do not** overflow on evaluation, ensure it succeeds.
583        ($operator:tt::$operate:tt == $operation:tt::$execute:tt $((.$unwrap:tt()))? for $input:ident => $output:ident $( ($($condition:tt),+) )?) => {
584            paste::paste! {
585                #[test]
586                fn [<test _ $operate _ $input:lower _ into _ $output:lower>]() -> Result<()> {
587                    // Prepare the rng.
588                    let mut rng = TestRng::default();
589
590                    // Ensure the expected output type is correct.
591                    assert_eq!(
592                        console::program::LiteralType::$output,
593                        <$operation as $crate::Operation<_, _, _, 1>>::output_type(&[console::program::LiteralType::$input.into()])?
594                    );
595
596                    // Check the operation on randomly-sampled values.
597                    for i in 0..150u64 {
598                        // Sample the first and second value.
599                        #[allow(deprecated)]
600                        let a = match i {
601                            0 => $input::zero(),
602                            1.. => $input::<CurrentNetwork>::rand(&mut rng)
603                        };
604
605                        // Initialize an indicator whether the operation should succeed or not.
606                        #[allow(unused_mut)]
607                        let mut should_succeed = true;
608                        /// A helper macro to check the conditions.
609                        #[allow(unused_macros)]
610                        macro_rules! check_condition {
611                            ("ensure overflows halt") => {
612                                match *<$operation as $crate::Operation<_, _, _, 1>>::OPCODE {
613                                    "abs" => should_succeed &= (*a).checked_abs().is_some(),
614                                    "neg" => should_succeed &= (*a).checked_neg().is_some(),
615                                    _ => panic!("Unsupported test enforcement for '{}'", <$operation as $crate::Operation<_, _, _, 1>>::OPCODE),
616                                }
617                            };
618                            ("ensure inverse of zero halts") => {
619                                should_succeed &= !(*a).is_zero()
620                            };
621                            ("ensure quadratic nonresidues halt") => {
622                                should_succeed &= (*a).sqrt().is_some()
623                            };
624                        }
625                        // Check the conditions.
626                        $( $( check_condition!($condition); )+ )?
627
628                        // If `should_succeed` is `true`, compute the expected output.
629                        let expected = match should_succeed {
630                            true => Some(console::program::Literal::$output(a.$operate()$(.$unwrap())?)),
631                            false => None
632                        };
633
634                        for mode_a in &[circuit::Mode::Constant, circuit::Mode::Public, circuit::Mode::Private] {
635                            // Initialize the operands.
636                            let a = console::program::Literal::from_str(&format!("{a}"))?;
637
638                            // Initialize the operands.
639                            let first = circuit::program::Literal::from_str(&format!("{a}.{mode_a}"))?;
640
641                            // If this iteration should succeed, ensure the evaluated and executed outputs match the expected output.
642                            if should_succeed {
643                                // Compute the evaluated output.
644                                let candidate_a = <$operation as $crate::Operation<_, _, _, 1>>::evaluate(&[a])?;
645                                // Compute the executed output.
646                                let candidate_b = <$operation as $crate::Operation<_, _, _, 1>>::$execute::<CurrentAleo>(&[first])?;
647
648                                // Ensure the outputs match.
649                                assert_eq!(expected, Some(candidate_a));
650                                // Ensure the outputs match.
651                                assert_eq!(expected, Some(circuit::Eject::eject_value(&candidate_b)));
652
653                            }
654                            // If the sampled values overflow on evaluation, ensure it halts.
655                            else {
656                                // Halt the evaluation.
657                                let result_a = std::panic::catch_unwind(|| <$operation as $crate::Operation<_, _, _, 1>>::evaluate(&[a.clone()]).unwrap());
658                                // Ensure the evaluation halted.
659                                assert!(result_a.is_err(), "Failure case (on iteration {i}) did not halt (console): {a}");
660
661                                // Halt the execution.
662                                if mode_a.is_constant() {
663                                    // Attempt to execute a failure case.
664                                    let result_b = std::panic::catch_unwind(|| <$operation as $crate::Operation<_, _, _, 1>>::$execute::<CurrentAleo>(&[first]).unwrap());
665                                    // Ensure the execution halted.
666                                    assert!(result_b.is_err(), "Failure case (on iteration {i}) did not halt (circuit): {a}");
667                                } else {
668                                    // Attempt to execute a failure case.
669                                    let _result_b = <$operation as $crate::Operation<_, _, _, 1>>::$execute::<CurrentAleo>(&[first])?;
670                                    // Ensure the execution halted.
671                                    assert!(!<CurrentAleo as circuit::Environment>::is_satisfied(), "Failure case (on iteration {i}) should not be satisfied (circuit): {a}");
672                                }
673                            }
674                            // Reset the circuit.
675                            <CurrentAleo as circuit::Environment>::reset();
676                        }
677                    }
678
679                    Ok(())
680                }
681            }
682        };
683
684        // Case 1-A: Binary operation.
685        // Case 1-B: Binary operation, where:
686        //   1. "ensure overflow halts" | "ensure exponentiation overflow halts" | "ensure shifting past boundary halts"
687        //     - If the sampled values overflow or underflow on evaluation, ensure it halts.
688        //     - If the sampled values **do not** overflow or underflow on evaluation, ensure it succeeds.
689        //   2. "ensure divide by zero halts"
690        //     - If the sampled divisor is zero, ensure it halts.
691        //     - If the sampled divisor is **not** zero, ensure it succeeds.
692        ($operator:tt::$operate:tt == $operation:tt::$execute:tt for ($input_a:ident, $input_b:ident) => $output:ident $( ($($condition:tt),+) )?) => {
693            paste::paste! {
694                #[test]
695                fn [<test _ $operate _ $input_a:lower _ $input_b:lower _ into _ $output:lower>]() -> Result<()> {
696                    // Prepare the rng.
697                    let mut rng = TestRng::default();
698
699                    // Ensure the expected output type is correct.
700                    assert_eq!(
701                        console::program::LiteralType::$output,
702                        <$operation as $crate::Operation<_, _, _, 2>>::output_type(&[console::program::LiteralType::$input_a.into(), console::program::LiteralType::$input_b.into()])?
703                    );
704
705                    // Determine the number of iterations to run, based on the opcode.
706                    let num_iterations: u64 = match *<$operation as $crate::Operation<_, _, _, 2>>::OPCODE {
707                        "pow" | "pow.w" => 10,
708                        _ => 100
709                    };
710
711                    // Check the operation on randomly-sampled values.
712                    for i in 0..num_iterations {
713                        macro_rules! sample_value {
714                            (I8, I8) => { sample_value!(I128, I128) };
715                            (I16, I16) => { sample_value!(I128, I128) };
716                            (I32, I32) => { sample_value!(I128, I128) };
717                            (I64, I64) => { sample_value!(I128, I128) };
718                            (I128, I128) => {
719                                match i {
720                                    0 => ($input_a::zero(), $input_b::zero()),
721                                    1 => ($input_a::<CurrentNetwork>::rand(&mut rng), $input_b::zero()),
722                                    2 => ($input_a::zero(), $input_b::<CurrentNetwork>::rand(&mut rng)),
723                                    3 => ($input_a::MIN, $input_b::zero() - $input_b::one()),
724                                    4.. => ($input_b::<CurrentNetwork>::rand(&mut rng), $input_b::<CurrentNetwork>::rand(&mut rng))
725                                }
726                            };
727                            ($lhs:ident, $rhs:ident) => {
728                                match i {
729                                    0 => ($lhs::zero(), $rhs::zero()),
730                                    1 => ($lhs::<CurrentNetwork>::rand(&mut rng), $rhs::zero()),
731                                    2 => ($lhs::zero(), $rhs::<CurrentNetwork>::rand(&mut rng)),
732                                    3.. => ($lhs::<CurrentNetwork>::rand(&mut rng), $rhs::<CurrentNetwork>::rand(&mut rng))
733                                }
734                            }
735                        }
736                        // Sample the first and second value.
737                        #[allow(deprecated)]
738                        let (a, b) = sample_value!($input_a, $input_b);
739
740                        // This flag is used to determine halting conditions.
741                        #[allow(deprecated)]
742                        let is_rhs_zero = (*b) == *$input_b::<CurrentNetwork>::zero();
743
744                        // Initialize an indicator whether the operation should succeed or not.
745                        #[allow(unused_mut)]
746                        let mut should_succeed = true;
747                        #[allow(unused_mut)]
748                        let mut is_shift_operator = false;
749                        #[allow(unused_mut)]
750                        let mut shift_exceeds_bitwidth = false;
751                        #[allow(unused_mut)]
752                        let mut is_division_operator = false;
753                        /// A helper macro to check the conditions.
754                        #[allow(unused_macros)]
755                        macro_rules! check_condition {
756                            ("ensure overflows halt") => {
757                                match *<$operation as $crate::Operation<_, _, _, 2>>::OPCODE {
758                                    "add" => should_succeed &= (*a).checked_add(*b).is_some(),
759                                    "div" => should_succeed &= (*a).checked_div(*b).is_some(),
760                                    "mul" => should_succeed &= (*a).checked_mul(*b).is_some(),
761                                    "rem" => should_succeed &= (*a).checked_rem(*b).is_some(),
762                                    "sub" => should_succeed &= (*a).checked_sub(*b).is_some(),
763                                    _ => panic!("Unsupported test enforcement for '{}'", <$operation as $crate::Operation<_, _, _, 2>>::OPCODE),
764                                }
765                            };
766                            ("ensure exponentiation overflows halt") => {
767                                should_succeed &= (*a).checked_pow((*b) as u32).is_some()
768                            };
769                            ("ensure shifting past boundary halts") => {
770                                match *<$operation as $crate::Operation<_, _, _, 2>>::OPCODE {
771                                    // Note that this case needs special handling, since the desired behavior of `checked_shl` deviates from Rust semantics.
772                                    "shl" => should_succeed &= console::prelude::CheckedShl::checked_shl(&*a, &(*b as u32)).is_some(),
773                                    "shr" => should_succeed &= (*a).checked_shr(*b as u32).is_some(),
774                                    _ => panic!("Unsupported test enforcement for '{}'", <$operation as $crate::Operation<_, _, _, 2>>::OPCODE),
775                                }
776                                // These indicators are later used in the for-loops below.
777                                is_shift_operator |= true;
778                                let input_a_size_in_bits = u32::try_from($input_a::<CurrentNetwork>::size_in_bits()).expect("Input size in bits exceeded u32::MAX");
779                                shift_exceeds_bitwidth |= ((*b as u32) >= input_a_size_in_bits);
780                            };
781                            ("ensure divide by zero halts") => {
782                                should_succeed &= (*b) != *$input_b::<CurrentNetwork>::zero();
783                                // This indicator is later used in the for-loops below.
784                                is_division_operator |= true;
785                            };
786                        }
787                        // Check the conditions.
788                        $( $( check_condition!($condition); )+ )?
789
790                        // If `should_succeed` is `true`, compute the expected output.
791                        let expected = match should_succeed {
792                            true => Some(console::program::Literal::$output(a.$operate(&b))),
793                            false => None
794                        };
795
796                        for mode_a in &[circuit::Mode::Constant, circuit::Mode::Public, circuit::Mode::Private] {
797                            for mode_b in &[circuit::Mode::Constant, circuit::Mode::Public, circuit::Mode::Private] {
798                                // Initialize the operands.
799                                let a = console::program::Literal::from_str(&format!("{a}"))?;
800                                let b = console::program::Literal::from_str(&format!("{b}"))?;
801
802                                // Initialize the operands.
803                                let first = circuit::program::Literal::from_str(&format!("{a}.{mode_a}"))?;
804                                let second = circuit::program::Literal::from_str(&format!("{b}.{mode_b}"))?;
805
806                                // This indicator bit is used to check that a case panics on halt,
807                                // instead of checking that the circuit is not satisfied (i.e. for `Public|Private && Constant`).
808                                let mut should_panic_on_halt = false;
809                                // If the operation is a shift operator, check if the mode of the RHS is a constant and if the shift amount exceeds the bitwidth.
810                                should_panic_on_halt |= is_shift_operator && shift_exceeds_bitwidth && mode_b.is_constant();
811                                // If the operation is a division operator, check if both operands are constant or if the RHS is a constant and zero.
812                                should_panic_on_halt |= is_division_operator && (
813                                    mode_b.is_constant() && (mode_a.is_constant() || is_rhs_zero)
814                                );
815
816                                // If this iteration should succeed, ensure the evaluated and executed outputs match the expected output.
817                                if should_succeed {
818                                    // Compute the evaluated output.
819                                    let candidate_a = <$operation as $crate::Operation<_, _, _, 2>>::evaluate(&[a, b])?;
820                                    // Compute the executed output.
821                                    let candidate_b = <$operation as $crate::Operation<_, _, _, 2>>::$execute::<CurrentAleo>(&[first, second])?;
822
823                                    // Ensure the outputs match.
824                                    assert_eq!(expected, Some(candidate_a));
825                                    // Ensure the outputs match.
826                                    assert_eq!(expected, Some(circuit::Eject::eject_value(&candidate_b)));
827
828                                }
829                                // If the sampled values overflow on evaluation, ensure it halts.
830                                else {
831                                    // Halt the evaluation.
832                                    let result_a = std::panic::catch_unwind(|| <$operation as $crate::Operation<_, _, _, 2>>::evaluate(&[a.clone(), b.clone()]).unwrap());
833                                    // Ensure the evaluation halted.
834                                    assert!(result_a.is_err(), "Failure case (on iteration {i}) did not halt (console): {a} {b}");
835
836                                    // Halt the execution.
837                                    if (mode_a.is_constant() && mode_b.is_constant()) || should_panic_on_halt {
838                                        // Attempt to execute a failure case.
839                                        let result_b = std::panic::catch_unwind(|| <$operation as $crate::Operation<_, _, _, 2>>::$execute::<CurrentAleo>(&[first, second]).unwrap());
840                                        // Ensure the execution halted.
841                                        assert!(result_b.is_err(), "Failure case (on iteration {i}) did not halt (circuit): {a} {b}");
842                                    } else {
843                                        // Attempt to execute a failure case.
844                                        let _result_b = <$operation as $crate::Operation<_, _, _, 2>>::$execute::<CurrentAleo>(&[first, second])?;
845                                        // Ensure the execution halted.
846                                        assert!(!<CurrentAleo as circuit::Environment>::is_satisfied(), "Failure case (on iteration {i}) should not be satisfied (circuit): {a} {b}");
847                                    }
848                                }
849                                // Reset the circuit.
850                                <CurrentAleo as circuit::Environment>::reset();
851                            }
852                        }
853                    }
854
855                    Ok(())
856                }
857            }
858        };
859
860        // Case 2-A: Ternary operation.
861        // Case 2-B: Ternary operation, where:
862        //   1. "ensure overflow halts" | "ensure exponentiation overflow halts" | "ensure shifting past boundary halts"
863        //     - If the sampled values overflow or underflow on evaluation, ensure it halts.
864        //     - If the sampled values **do not** overflow or underflow on evaluation, ensure it succeeds.
865        //   2. "ensure divide by zero halts"
866        //     - If the sampled divisor is zero, ensure it halts.
867        //     - If the sampled divisor is **not** zero, ensure it succeeds.
868        ($operator:tt::$operate:tt == $operation:tt::$execute:tt for ($input_a:ident, $input_b:ident, $input_c:ident) => $output:ident $( ($($condition:tt),+) )?) => {
869            paste::paste! {
870                #[test]
871                fn [<test _ $operate _ $input_a:lower _ $input_b:lower _ $input_c:lower _ into _ $output:lower>]() -> Result<()> {
872                    // Prepare the rng.
873                    let mut rng = TestRng::default();
874
875                    // Ensure the expected output type is correct.
876                    assert_eq!(
877                        console::program::LiteralType::$output,
878                        <$operation as $crate::Operation<_, _, _, 3>>::output_type(&[console::program::LiteralType::$input_a.into(), console::program::LiteralType::$input_b.into(), console::program::LiteralType::$input_c.into()])?
879                    );
880
881                    // Determine the number of iterations to run, based on the opcode.
882                    let num_iterations: u64 = match *<$operation as $crate::Operation<_, _, _, 3>>::OPCODE {
883                        _ => 100
884                    };
885
886                    // Check the operation on randomly-sampled values.
887                    for i in 0..num_iterations {
888                        // Sample the first, second, and third values.
889                        #[allow(deprecated)]
890                        let (a, b, c) = match i {
891                            0 => ($input_a::zero(), $input_b::zero(), $input_c::zero()),
892                            1 => ($input_a::<CurrentNetwork>::rand(&mut rng), $input_b::<CurrentNetwork>::rand(&mut rng), $input_c::zero()),
893                            2 => ($input_a::<CurrentNetwork>::rand(&mut rng), $input_b::zero(), $input_c::<CurrentNetwork>::rand(&mut rng)),
894                            3 => ($input_a::<CurrentNetwork>::rand(&mut rng), $input_b::zero(), $input_c::zero()),
895                            4 => ($input_a::zero(), $input_b::<CurrentNetwork>::rand(&mut rng), $input_c::<CurrentNetwork>::rand(&mut rng)),
896                            5 => ($input_a::zero(), $input_b::<CurrentNetwork>::rand(&mut rng), $input_c::<CurrentNetwork>::zero()),
897                            6 => ($input_a::zero(), $input_b::zero(), $input_c::<CurrentNetwork>::rand(&mut rng)),
898                            7.. => ($input_a::<CurrentNetwork>::rand(&mut rng), $input_b::<CurrentNetwork>::rand(&mut rng), $input_c::<CurrentNetwork>::rand(&mut rng))
899                        };
900
901                        // Initialize an indicator whether the operation should succeed or not.
902                        #[allow(unused_mut)]
903                        let mut should_succeed = true;
904
905                        // If `should_succeed` is `true`, compute the expected output.
906                        let expected = match should_succeed {
907                            true => Some(console::program::Literal::from_str(&format!("{}", $operator::$operate(&a, &b, &c)))?),
908                            false => None
909                        };
910
911                        for mode_a in &[circuit::Mode::Constant, circuit::Mode::Public, circuit::Mode::Private] {
912                            for mode_b in &[circuit::Mode::Constant, circuit::Mode::Public, circuit::Mode::Private] {
913                                for mode_c in &[circuit::Mode::Constant, circuit::Mode::Public, circuit::Mode::Private] {
914                                    // Initialize the operands.
915                                    let a = console::program::Literal::from_str(&format!("{a}"))?;
916                                    let b = console::program::Literal::from_str(&format!("{b}"))?;
917                                    let c = console::program::Literal::from_str(&format!("{c}"))?;
918
919                                    // Initialize the operands.
920                                    let first = circuit::program::Literal::from_str(&format!("{a}.{mode_a}"))?;
921                                    let second = circuit::program::Literal::from_str(&format!("{b}.{mode_b}"))?;
922                                    let third = circuit::program::Literal::from_str(&format!("{c}.{mode_c}"))?;
923
924                                    // If this iteration should succeed, ensure the evaluated and executed outputs match the expected output.
925                                    if should_succeed {
926                                        // Compute the evaluated output.
927                                        let candidate_a = <$operation as $crate::Operation<_, _, _, 3>>::evaluate(&[a, b, c])?;
928                                        // Compute the executed output.
929                                        let candidate_b = <$operation as $crate::Operation<_, _, _, 3>>::$execute::<CurrentAleo>(&[first, second, third])?;
930
931                                        // Ensure the outputs match.
932                                        assert_eq!(expected, Some(candidate_a));
933                                        // Ensure the outputs match.
934                                        assert_eq!(expected, Some(circuit::Eject::eject_value(&candidate_b)));
935                                    }
936                                    // If the sampled values overflow on evaluation, ensure it halts.
937                                    else {
938                                        // Halt the evaluation.
939                                        let result_a = std::panic::catch_unwind(|| <$operation as $crate::Operation<_, _, _, 3>>::evaluate(&[a.clone(), b.clone(), c.clone()]).unwrap());
940                                        // Ensure the evaluation halted.
941                                        assert!(result_a.is_err(), "Failure case (on iteration {i}) did not halt (console): {a} {b} {c}");
942
943                                        // Halt the execution.
944                                        if (mode_a.is_constant() && mode_b.is_constant() && mode_c.is_constant()) {
945                                            // Attempt to execute a failure case.
946                                            let result_b = std::panic::catch_unwind(|| <$operation as $crate::Operation<_, _, _, 3>>::$execute::<CurrentAleo>(&[first, second, third]).unwrap());
947                                            // Ensure the execution halted.
948                                            assert!(result_b.is_err(), "Failure case (on iteration {i}) did not halt (circuit): {a} {b} {c}");
949                                        } else {
950                                            // Attempt to execute a failure case.
951                                            let _result_b = <$operation as $crate::Operation<_, _, _, 3>>::$execute::<CurrentAleo>(&[first, second, third])?;
952                                            // Ensure the execution halted.
953                                            assert!(!<CurrentAleo as circuit::Environment>::is_satisfied(), "Failure case (on iteration {i}) should not be satisfied (circuit): {a} {b} {c}");
954                                        }
955                                    }
956                                    // Reset the circuit.
957                                    <CurrentAleo as circuit::Environment>::reset();
958                                }
959                            }
960                        }
961                    }
962
963                    Ok(())
964                }
965            }
966        };
967    }
968}