snarkvm_synthesizer_program/logic/command/
mod.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
16mod await_;
17pub use await_::*;
18
19mod branch;
20pub use branch::*;
21
22mod contains;
23pub use contains::*;
24
25mod get;
26pub use get::*;
27
28mod get_or_use;
29pub use get_or_use::*;
30
31mod rand_chacha;
32pub use crate::command::rand_chacha::*;
33
34mod remove;
35pub use remove::*;
36
37mod position;
38pub use position::*;
39
40mod set;
41pub use set::*;
42
43use crate::{
44    CastType,
45    FinalizeOperation,
46    FinalizeRegistersState,
47    FinalizeStoreTrait,
48    Instruction,
49    Operand,
50    StackTrait,
51};
52use console::{
53    network::{error, prelude::*},
54    program::{Identifier, Register},
55};
56
57#[derive(Clone, PartialEq, Eq, Hash)]
58pub enum Command<N: Network> {
59    /// Evaluates the instruction.
60    Instruction(Instruction<N>),
61    /// Awaits the result of a future.
62    Await(Await<N>),
63    /// Returns true if the `key` operand is present in `mapping`, and stores the result into `destination`.
64    Contains(Contains<N>),
65    /// Gets the value stored at the `key` operand in `mapping` and stores the result into `destination`.
66    Get(Get<N>),
67    /// Gets the value stored at the `key` operand in `mapping` and stores the result into `destination`.
68    /// If the key is not present, `default` is stored `destination`.
69    GetOrUse(GetOrUse<N>),
70    /// Generates a random value using the `rand.chacha` command and stores the result into `destination`.
71    RandChaCha(RandChaCha<N>),
72    /// Removes the (`key`, `value`) entry from the `mapping`.
73    Remove(Remove<N>),
74    /// Sets the value stored at the `key` operand in the `mapping` to `value`.
75    Set(Set<N>),
76    /// Jumps to the `position`, if `first` equals `second`.
77    BranchEq(BranchEq<N>),
78    /// Jumps to the `position`, if `first` does **not** equal `second`.
79    BranchNeq(BranchNeq<N>),
80    /// Indicates a position to which the program can branch to.
81    Position(Position<N>),
82}
83
84impl<N: Network> Command<N> {
85    /// Returns `true` if the command is an async instruction.
86    pub fn is_async(&self) -> bool {
87        matches!(self, Command::Instruction(Instruction::Async(_)))
88    }
89
90    /// Returns `true` if the command is an await command.
91    #[inline]
92    pub fn is_await(&self) -> bool {
93        matches!(self, Command::Await(_))
94    }
95
96    /// Returns `true` if the command is a call instruction.
97    pub fn is_call(&self) -> bool {
98        matches!(self, Command::Instruction(Instruction::Call(_)))
99    }
100
101    /// Returns `true` if the command is a cast to record instruction.
102    pub fn is_cast_to_record(&self) -> bool {
103        matches!(self, Command::Instruction(Instruction::Cast(cast)) if matches!(cast.cast_type(), CastType::Record(_) | CastType::ExternalRecord(_)))
104    }
105
106    /// Returns `true` if the command is a write operation.
107    pub fn is_write(&self) -> bool {
108        matches!(self, Command::Set(_) | Command::Remove(_))
109    }
110
111    /// Returns the branch target, if the command is a branch command.
112    /// Otherwise, returns `None`.
113    pub fn branch_to(&self) -> Option<&Identifier<N>> {
114        match self {
115            Command::BranchEq(branch_eq) => Some(branch_eq.position()),
116            Command::BranchNeq(branch_neq) => Some(branch_neq.position()),
117            _ => None,
118        }
119    }
120
121    /// Returns the position name, if the command is a position command.
122    /// Otherwise, returns `None`.
123    pub fn position(&self) -> Option<&Identifier<N>> {
124        match self {
125            Command::Position(position) => Some(position.name()),
126            _ => None,
127        }
128    }
129
130    /// Returns the destination registers of the command.
131    pub fn destinations(&self) -> Vec<Register<N>> {
132        match self {
133            Command::Instruction(instruction) => instruction.destinations(),
134            Command::Contains(contains) => vec![contains.destination().clone()],
135            Command::Get(get) => vec![get.destination().clone()],
136            Command::GetOrUse(get_or_use) => vec![get_or_use.destination().clone()],
137            Command::RandChaCha(rand_chacha) => vec![rand_chacha.destination().clone()],
138            Command::Await(_)
139            | Command::BranchEq(_)
140            | Command::BranchNeq(_)
141            | Command::Position(_)
142            | Command::Remove(_)
143            | Command::Set(_) => vec![],
144        }
145    }
146
147    /// Returns the operands of the command.
148    #[inline]
149    pub fn operands(&self) -> &[Operand<N>] {
150        match self {
151            Command::Instruction(c) => c.operands(),
152            Command::Await(c) => c.operands(),
153            Command::Contains(c) => c.operands(),
154            Command::Get(c) => c.operands(),
155            Command::GetOrUse(c) => c.operands(),
156            Command::RandChaCha(c) => c.operands(),
157            Command::Remove(c) => c.operands(),
158            Command::Set(c) => c.operands(),
159            Command::BranchEq(c) => c.operands(),
160            Command::BranchNeq(c) => c.operands(),
161            Command::Position(_) => Default::default(),
162        }
163    }
164
165    /// Finalizes the command.
166    pub fn finalize(
167        &self,
168        stack: &impl StackTrait<N>,
169        store: &impl FinalizeStoreTrait<N>,
170        registers: &mut impl FinalizeRegistersState<N>,
171    ) -> Result<Option<FinalizeOperation<N>>> {
172        match self {
173            // Finalize the instruction, and return no finalize operation.
174            Command::Instruction(instruction) => instruction.finalize(stack, registers).map(|_| None),
175            // `await` commands are processed by the caller of this method.
176            Command::Await(_) => bail!("`await` commands cannot be finalized directly."),
177            // Finalize the 'contains' command, and return no finalize operation.
178            Command::Contains(contains) => contains.finalize(stack, store, registers).map(|_| None),
179            // Finalize the 'get' command, and return no finalize operation.
180            Command::Get(get) => get.finalize(stack, store, registers).map(|_| None),
181            // Finalize the 'get.or_use' command, and return no finalize operation.
182            Command::GetOrUse(get_or_use) => get_or_use.finalize(stack, store, registers).map(|_| None),
183            // Finalize the `rand.chacha` command, and return no finalize operation.
184            Command::RandChaCha(rand_chacha) => rand_chacha.finalize(stack, registers).map(|_| None),
185            // Finalize the 'remove' command, and return the finalize operation.
186            Command::Remove(remove) => remove.finalize(stack, store, registers),
187            // Finalize the 'set' command, and return the finalize operation.
188            Command::Set(set) => set.finalize(stack, store, registers).map(Some),
189            // 'branch.eq' and 'branch.neq' commands are processed by the caller of this method.
190            Command::BranchEq(_) | Command::BranchNeq(_) => {
191                bail!("`branch` commands cannot be finalized directly.")
192            }
193            // Finalize the `position` command, and return no finalize operation.
194            Command::Position(position) => position.finalize().map(|_| None),
195        }
196    }
197}
198
199impl<N: Network> FromBytes for Command<N> {
200    /// Reads the command from a buffer.
201    fn read_le<R: Read>(mut reader: R) -> IoResult<Self> {
202        // Read the variant.
203        let variant = u8::read_le(&mut reader)?;
204        match variant {
205            // Read the instruction.
206            0 => Ok(Self::Instruction(Instruction::read_le(&mut reader)?)),
207            // Read the `await` operation.
208            1 => Ok(Self::Await(Await::read_le(&mut reader)?)),
209            // Read the `contains` operation.
210            2 => Ok(Self::Contains(Contains::read_le(&mut reader)?)),
211            // Read the `get` operation.
212            3 => Ok(Self::Get(Get::read_le(&mut reader)?)),
213            // Read the `get.or_use` operation.
214            4 => Ok(Self::GetOrUse(GetOrUse::read_le(&mut reader)?)),
215            // Read the `rand.chacha` operation.
216            5 => Ok(Self::RandChaCha(RandChaCha::read_le(&mut reader)?)),
217            // Read the `remove` operation.
218            6 => Ok(Self::Remove(Remove::read_le(&mut reader)?)),
219            // Read the `set` operation.
220            7 => Ok(Self::Set(Set::read_le(&mut reader)?)),
221            // Read the `branch.eq` command.
222            8 => Ok(Self::BranchEq(BranchEq::read_le(&mut reader)?)),
223            // Read the `branch.neq` command.
224            9 => Ok(Self::BranchNeq(BranchNeq::read_le(&mut reader)?)),
225            // Read the `position` command.
226            10 => Ok(Self::Position(Position::read_le(&mut reader)?)),
227            // Invalid variant.
228            11.. => Err(error(format!("Invalid command variant: {variant}"))),
229        }
230    }
231}
232
233impl<N: Network> ToBytes for Command<N> {
234    /// Writes the command to a buffer.
235    fn write_le<W: Write>(&self, mut writer: W) -> IoResult<()> {
236        match self {
237            Self::Instruction(instruction) => {
238                // Write the variant.
239                0u8.write_le(&mut writer)?;
240                // Write the instruction.
241                instruction.write_le(&mut writer)
242            }
243            Self::Await(await_) => {
244                // Write the variant.
245                1u8.write_le(&mut writer)?;
246                // Write the `await` operation.
247                await_.write_le(&mut writer)
248            }
249            Self::Contains(contains) => {
250                // Write the variant.
251                2u8.write_le(&mut writer)?;
252                // Write the `contains` operation.
253                contains.write_le(&mut writer)
254            }
255            Self::Get(get) => {
256                // Write the variant.
257                3u8.write_le(&mut writer)?;
258                // Write the `get` operation.
259                get.write_le(&mut writer)
260            }
261            Self::GetOrUse(get_or_use) => {
262                // Write the variant.
263                4u8.write_le(&mut writer)?;
264                // Write the defaulting `get` operation.
265                get_or_use.write_le(&mut writer)
266            }
267            Self::RandChaCha(rand_chacha) => {
268                // Write the variant.
269                5u8.write_le(&mut writer)?;
270                // Write the `rand.chacha` operation.
271                rand_chacha.write_le(&mut writer)
272            }
273            Self::Remove(remove) => {
274                // Write the variant.
275                6u8.write_le(&mut writer)?;
276                // Write the remove.
277                remove.write_le(&mut writer)
278            }
279            Self::Set(set) => {
280                // Write the variant.
281                7u8.write_le(&mut writer)?;
282                // Write the set.
283                set.write_le(&mut writer)
284            }
285            Self::BranchEq(branch_eq) => {
286                // Write the variant.
287                8u8.write_le(&mut writer)?;
288                // Write the `branch.eq` command.
289                branch_eq.write_le(&mut writer)
290            }
291            Self::BranchNeq(branch_neq) => {
292                // Write the variant.
293                9u8.write_le(&mut writer)?;
294                // Write the `branch.neq` command.
295                branch_neq.write_le(&mut writer)
296            }
297            Self::Position(position) => {
298                // Write the variant.
299                10u8.write_le(&mut writer)?;
300                // Write the position command.
301                position.write_le(&mut writer)
302            }
303        }
304    }
305}
306
307impl<N: Network> Parser for Command<N> {
308    /// Parses the string into the command.
309    #[inline]
310    fn parse(string: &str) -> ParserResult<Self> {
311        // Parse the command.
312        // Note that the order of the parsers is important.
313        alt((
314            map(Await::parse, |await_| Self::Await(await_)),
315            map(Contains::parse, |contains| Self::Contains(contains)),
316            map(GetOrUse::parse, |get_or_use| Self::GetOrUse(get_or_use)),
317            map(Get::parse, |get| Self::Get(get)),
318            map(RandChaCha::parse, |rand_chacha| Self::RandChaCha(rand_chacha)),
319            map(Remove::parse, |remove| Self::Remove(remove)),
320            map(Set::parse, |set| Self::Set(set)),
321            map(BranchEq::parse, |branch_eq| Self::BranchEq(branch_eq)),
322            map(BranchNeq::parse, |branch_neq| Self::BranchNeq(branch_neq)),
323            map(Position::parse, |position| Self::Position(position)),
324            map(Instruction::parse, |instruction| Self::Instruction(instruction)),
325        ))(string)
326    }
327}
328
329impl<N: Network> FromStr for Command<N> {
330    type Err = Error;
331
332    /// Parses the string into the command.
333    #[inline]
334    fn from_str(string: &str) -> Result<Self> {
335        match Self::parse(string) {
336            Ok((remainder, object)) => {
337                // Ensure the remainder is empty.
338                ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
339                // Return the object.
340                Ok(object)
341            }
342            Err(error) => bail!("Failed to parse string. {error}"),
343        }
344    }
345}
346
347impl<N: Network> Debug for Command<N> {
348    /// Prints the command as a string.
349    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
350        Display::fmt(self, f)
351    }
352}
353
354impl<N: Network> Display for Command<N> {
355    /// Prints the command as a string.
356    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
357        match self {
358            Self::Instruction(instruction) => Display::fmt(instruction, f),
359            Self::Await(await_) => Display::fmt(await_, f),
360            Self::Contains(contains) => Display::fmt(contains, f),
361            Self::Get(get) => Display::fmt(get, f),
362            Self::GetOrUse(get_or_use) => Display::fmt(get_or_use, f),
363            Self::RandChaCha(rand_chacha) => Display::fmt(rand_chacha, f),
364            Self::Remove(remove) => Display::fmt(remove, f),
365            Self::Set(set) => Display::fmt(set, f),
366            Self::BranchEq(branch_eq) => Display::fmt(branch_eq, f),
367            Self::BranchNeq(branch_neq) => Display::fmt(branch_neq, f),
368            Self::Position(position) => Display::fmt(position, f),
369        }
370    }
371}
372
373#[cfg(test)]
374mod tests {
375    use super::*;
376    use console::network::MainnetV0;
377
378    type CurrentNetwork = MainnetV0;
379
380    #[test]
381    fn test_command_bytes() {
382        // Decrement
383        let expected = "decrement object[r0] by r1;";
384        Command::<CurrentNetwork>::parse(expected).unwrap_err();
385
386        // Instruction
387        let expected = "add r0 r1 into r2;";
388        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
389        let bytes = command.to_bytes_le().unwrap();
390        assert_eq!(command, Command::from_bytes_le(&bytes).unwrap());
391
392        // Increment
393        let expected = "increment object[r0] by r1;";
394        Command::<CurrentNetwork>::parse(expected).unwrap_err();
395
396        // Await
397        let expected = "await r1;";
398        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
399        let bytes = command.to_bytes_le().unwrap();
400        assert_eq!(command, Command::from_bytes_le(&bytes).unwrap());
401
402        // Contains
403        let expected = "contains object[r0] into r1;";
404        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
405        let bytes = command.to_bytes_le().unwrap();
406        assert_eq!(command, Command::from_bytes_le(&bytes).unwrap());
407
408        // Get
409        let expected = "get object[r0] into r1;";
410        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
411        let bytes = command.to_bytes_le().unwrap();
412        assert_eq!(command, Command::from_bytes_le(&bytes).unwrap());
413
414        // GetOr
415        let expected = "get.or_use object[r0] r1 into r2;";
416        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
417        let bytes = command.to_bytes_le().unwrap();
418        assert_eq!(command, Command::from_bytes_le(&bytes).unwrap());
419
420        // RandChaCha
421        let expected = "rand.chacha into r1 as field;";
422        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
423        let bytes = command.to_bytes_le().unwrap();
424        assert_eq!(command, Command::from_bytes_le(&bytes).unwrap());
425
426        // RandChaCha
427        let expected = "rand.chacha r0 r1 into r2 as group;";
428        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
429        let bytes = command.to_bytes_le().unwrap();
430        assert_eq!(command, Command::from_bytes_le(&bytes).unwrap());
431
432        // Remove
433        let expected = "remove object[r0];";
434        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
435        let bytes = command.to_bytes_le().unwrap();
436        assert_eq!(command, Command::from_bytes_le(&bytes).unwrap());
437
438        // Set
439        let expected = "set r0 into object[r1];";
440        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
441        let bytes = command.to_bytes_le().unwrap();
442        assert_eq!(command, Command::from_bytes_le(&bytes).unwrap());
443
444        // BranchEq
445        let expected = "branch.eq r0 r1 to exit;";
446        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
447        let bytes = command.to_bytes_le().unwrap();
448        assert_eq!(command, Command::from_bytes_le(&bytes).unwrap());
449
450        // BranchNeq
451        let expected = "branch.neq r2 r3 to start;";
452        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
453        let bytes = command.to_bytes_le().unwrap();
454        assert_eq!(command, Command::from_bytes_le(&bytes).unwrap());
455
456        // Position
457        let expected = "position exit;";
458        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
459        let bytes = command.to_bytes_le().unwrap();
460        assert_eq!(command, Command::from_bytes_le(&bytes).unwrap());
461    }
462
463    #[test]
464    fn test_command_parse() {
465        // Decrement
466        let expected = "decrement object[r0] by r1;";
467        Command::<CurrentNetwork>::parse(expected).unwrap_err();
468
469        // Instruction
470        let expected = "add r0 r1 into r2;";
471        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
472        assert_eq!(Command::Instruction(Instruction::from_str(expected).unwrap()), command);
473        assert_eq!(expected, command.to_string());
474
475        // Increment
476        let expected = "increment object[r0] by r1;";
477        Command::<CurrentNetwork>::parse(expected).unwrap_err();
478
479        // Contains
480        let expected = "contains object[r0] into r1;";
481        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
482        assert_eq!(Command::Contains(Contains::from_str(expected).unwrap()), command);
483        assert_eq!(expected, command.to_string());
484
485        // Get
486        let expected = "get object[r0] into r1;";
487        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
488        assert_eq!(Command::Get(Get::from_str(expected).unwrap()), command);
489        assert_eq!(expected, command.to_string());
490
491        // GetOr
492        let expected = "get.or_use object[r0] r1 into r2;";
493        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
494        assert_eq!(Command::GetOrUse(GetOrUse::from_str(expected).unwrap()), command);
495        assert_eq!(expected, command.to_string());
496
497        // RandChaCha
498        let expected = "rand.chacha into r1 as field;";
499        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
500        assert_eq!(Command::RandChaCha(RandChaCha::from_str(expected).unwrap()), command);
501        assert_eq!(expected, command.to_string());
502
503        // RandChaCha
504        let expected = "rand.chacha r0 r1 into r2 as group;";
505        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
506        assert_eq!(Command::RandChaCha(RandChaCha::from_str(expected).unwrap()), command);
507        assert_eq!(expected, command.to_string());
508
509        // Remove
510        let expected = "remove object[r0];";
511        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
512        assert_eq!(Command::Remove(Remove::from_str(expected).unwrap()), command);
513        assert_eq!(expected, command.to_string());
514
515        // Set
516        let expected = "set r0 into object[r1];";
517        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
518        assert_eq!(Command::Set(Set::from_str(expected).unwrap()), command);
519        assert_eq!(expected, command.to_string());
520
521        // BranchEq
522        let expected = "branch.eq r0 r1 to exit;";
523        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
524        assert_eq!(Command::BranchEq(BranchEq::from_str(expected).unwrap()), command);
525        assert_eq!(expected, command.to_string());
526
527        // BranchNeq
528        let expected = "branch.neq r2 r3 to start;";
529        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
530        assert_eq!(Command::BranchNeq(BranchNeq::from_str(expected).unwrap()), command);
531        assert_eq!(expected, command.to_string());
532
533        // Position
534        let expected = "position exit;";
535        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
536        assert_eq!(Command::Position(Position::from_str(expected).unwrap()), command);
537        assert_eq!(expected, command.to_string());
538    }
539}