Skip to main content

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    /// Returns whether this commands refers to an external struct.
199    pub fn contains_external_struct(&self) -> bool {
200        match self {
201            Command::Instruction(c) => c.contains_external_struct(),
202            Command::Await(c) => c.contains_external_struct(),
203            Command::Contains(c) => c.contains_external_struct(),
204            Command::Get(c) => c.contains_external_struct(),
205            Command::GetOrUse(c) => c.contains_external_struct(),
206            Command::RandChaCha(c) => c.contains_external_struct(),
207            Command::Remove(c) => c.contains_external_struct(),
208            Command::Set(c) => c.contains_external_struct(),
209            Command::BranchEq(c) => c.contains_external_struct(),
210            Command::BranchNeq(c) => c.contains_external_struct(),
211            Command::Position(c) => c.contains_external_struct(),
212        }
213    }
214
215    /// Returns `true` if the command contains a string type.
216    pub fn contains_string_type(&self) -> bool {
217        self.operands().iter().any(|operand| operand.contains_string_type())
218    }
219
220    /// Returns `true` if the command contains an array type with a size that exceeds the given maximum.
221    pub fn exceeds_max_array_size(&self, max_array_size: u32) -> bool {
222        match self {
223            Command::Instruction(instruction) => instruction.exceeds_max_array_size(max_array_size),
224            _ => false,
225        }
226    }
227}
228
229impl<N: Network> FromBytes for Command<N> {
230    /// Reads the command from a buffer.
231    fn read_le<R: Read>(mut reader: R) -> IoResult<Self> {
232        // Read the variant.
233        let variant = u8::read_le(&mut reader)?;
234        match variant {
235            // Read the instruction.
236            0 => Ok(Self::Instruction(Instruction::read_le(&mut reader)?)),
237            // Read the `await` operation.
238            1 => Ok(Self::Await(Await::read_le(&mut reader)?)),
239            // Read the `contains` operation.
240            2 => Ok(Self::Contains(Contains::read_le(&mut reader)?)),
241            // Read the `get` operation.
242            3 => Ok(Self::Get(Get::read_le(&mut reader)?)),
243            // Read the `get.or_use` operation.
244            4 => Ok(Self::GetOrUse(GetOrUse::read_le(&mut reader)?)),
245            // Read the `rand.chacha` operation.
246            5 => Ok(Self::RandChaCha(RandChaCha::read_le(&mut reader)?)),
247            // Read the `remove` operation.
248            6 => Ok(Self::Remove(Remove::read_le(&mut reader)?)),
249            // Read the `set` operation.
250            7 => Ok(Self::Set(Set::read_le(&mut reader)?)),
251            // Read the `branch.eq` command.
252            8 => Ok(Self::BranchEq(BranchEq::read_le(&mut reader)?)),
253            // Read the `branch.neq` command.
254            9 => Ok(Self::BranchNeq(BranchNeq::read_le(&mut reader)?)),
255            // Read the `position` command.
256            10 => Ok(Self::Position(Position::read_le(&mut reader)?)),
257            // Invalid variant.
258            11.. => Err(error(format!("Invalid command variant: {variant}"))),
259        }
260    }
261}
262
263impl<N: Network> ToBytes for Command<N> {
264    /// Writes the command to a buffer.
265    fn write_le<W: Write>(&self, mut writer: W) -> IoResult<()> {
266        match self {
267            Self::Instruction(instruction) => {
268                // Write the variant.
269                0u8.write_le(&mut writer)?;
270                // Write the instruction.
271                instruction.write_le(&mut writer)
272            }
273            Self::Await(await_) => {
274                // Write the variant.
275                1u8.write_le(&mut writer)?;
276                // Write the `await` operation.
277                await_.write_le(&mut writer)
278            }
279            Self::Contains(contains) => {
280                // Write the variant.
281                2u8.write_le(&mut writer)?;
282                // Write the `contains` operation.
283                contains.write_le(&mut writer)
284            }
285            Self::Get(get) => {
286                // Write the variant.
287                3u8.write_le(&mut writer)?;
288                // Write the `get` operation.
289                get.write_le(&mut writer)
290            }
291            Self::GetOrUse(get_or_use) => {
292                // Write the variant.
293                4u8.write_le(&mut writer)?;
294                // Write the defaulting `get` operation.
295                get_or_use.write_le(&mut writer)
296            }
297            Self::RandChaCha(rand_chacha) => {
298                // Write the variant.
299                5u8.write_le(&mut writer)?;
300                // Write the `rand.chacha` operation.
301                rand_chacha.write_le(&mut writer)
302            }
303            Self::Remove(remove) => {
304                // Write the variant.
305                6u8.write_le(&mut writer)?;
306                // Write the remove.
307                remove.write_le(&mut writer)
308            }
309            Self::Set(set) => {
310                // Write the variant.
311                7u8.write_le(&mut writer)?;
312                // Write the set.
313                set.write_le(&mut writer)
314            }
315            Self::BranchEq(branch_eq) => {
316                // Write the variant.
317                8u8.write_le(&mut writer)?;
318                // Write the `branch.eq` command.
319                branch_eq.write_le(&mut writer)
320            }
321            Self::BranchNeq(branch_neq) => {
322                // Write the variant.
323                9u8.write_le(&mut writer)?;
324                // Write the `branch.neq` command.
325                branch_neq.write_le(&mut writer)
326            }
327            Self::Position(position) => {
328                // Write the variant.
329                10u8.write_le(&mut writer)?;
330                // Write the position command.
331                position.write_le(&mut writer)
332            }
333        }
334    }
335}
336
337impl<N: Network> Parser for Command<N> {
338    /// Parses the string into the command.
339    #[inline]
340    fn parse(string: &str) -> ParserResult<Self> {
341        // Parse the command.
342        // Note that the order of the parsers is important.
343        alt((
344            map(Await::parse, |await_| Self::Await(await_)),
345            map(Contains::parse, |contains| Self::Contains(contains)),
346            map(GetOrUse::parse, |get_or_use| Self::GetOrUse(get_or_use)),
347            map(Get::parse, |get| Self::Get(get)),
348            map(RandChaCha::parse, |rand_chacha| Self::RandChaCha(rand_chacha)),
349            map(Remove::parse, |remove| Self::Remove(remove)),
350            map(Set::parse, |set| Self::Set(set)),
351            map(BranchEq::parse, |branch_eq| Self::BranchEq(branch_eq)),
352            map(BranchNeq::parse, |branch_neq| Self::BranchNeq(branch_neq)),
353            map(Position::parse, |position| Self::Position(position)),
354            map(Instruction::parse, |instruction| Self::Instruction(instruction)),
355        ))(string)
356    }
357}
358
359impl<N: Network> FromStr for Command<N> {
360    type Err = Error;
361
362    /// Parses the string into the command.
363    #[inline]
364    fn from_str(string: &str) -> Result<Self> {
365        match Self::parse(string) {
366            Ok((remainder, object)) => {
367                // Ensure the remainder is empty.
368                ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
369                // Return the object.
370                Ok(object)
371            }
372            Err(error) => bail!("Failed to parse string. {error}"),
373        }
374    }
375}
376
377impl<N: Network> Debug for Command<N> {
378    /// Prints the command as a string.
379    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
380        Display::fmt(self, f)
381    }
382}
383
384impl<N: Network> Display for Command<N> {
385    /// Prints the command as a string.
386    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
387        match self {
388            Self::Instruction(instruction) => Display::fmt(instruction, f),
389            Self::Await(await_) => Display::fmt(await_, f),
390            Self::Contains(contains) => Display::fmt(contains, f),
391            Self::Get(get) => Display::fmt(get, f),
392            Self::GetOrUse(get_or_use) => Display::fmt(get_or_use, f),
393            Self::RandChaCha(rand_chacha) => Display::fmt(rand_chacha, f),
394            Self::Remove(remove) => Display::fmt(remove, f),
395            Self::Set(set) => Display::fmt(set, f),
396            Self::BranchEq(branch_eq) => Display::fmt(branch_eq, f),
397            Self::BranchNeq(branch_neq) => Display::fmt(branch_neq, f),
398            Self::Position(position) => Display::fmt(position, f),
399        }
400    }
401}
402
403#[cfg(test)]
404mod tests {
405    use super::*;
406    use console::network::MainnetV0;
407
408    type CurrentNetwork = MainnetV0;
409
410    #[test]
411    fn test_command_bytes() {
412        // Decrement
413        let expected = "decrement object[r0] by r1;";
414        Command::<CurrentNetwork>::parse(expected).unwrap_err();
415
416        // Instruction
417        let expected = "add r0 r1 into r2;";
418        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
419        let bytes = command.to_bytes_le().unwrap();
420        assert_eq!(command, Command::from_bytes_le(&bytes).unwrap());
421
422        // Increment
423        let expected = "increment object[r0] by r1;";
424        Command::<CurrentNetwork>::parse(expected).unwrap_err();
425
426        // Await
427        let expected = "await r1;";
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        // Contains
433        let expected = "contains object[r0] into r1;";
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        // Get
439        let expected = "get object[r0] into 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        // GetOr
445        let expected = "get.or_use object[r0] r1 into r2;";
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        // RandChaCha
451        let expected = "rand.chacha into r1 as field;";
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        // RandChaCha
457        let expected = "rand.chacha r0 r1 into r2 as group;";
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        // Remove
463        let expected = "remove object[r0];";
464        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
465        let bytes = command.to_bytes_le().unwrap();
466        assert_eq!(command, Command::from_bytes_le(&bytes).unwrap());
467
468        // Set
469        let expected = "set r0 into object[r1];";
470        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
471        let bytes = command.to_bytes_le().unwrap();
472        assert_eq!(command, Command::from_bytes_le(&bytes).unwrap());
473
474        // BranchEq
475        let expected = "branch.eq r0 r1 to exit;";
476        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
477        let bytes = command.to_bytes_le().unwrap();
478        assert_eq!(command, Command::from_bytes_le(&bytes).unwrap());
479
480        // BranchNeq
481        let expected = "branch.neq r2 r3 to start;";
482        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
483        let bytes = command.to_bytes_le().unwrap();
484        assert_eq!(command, Command::from_bytes_le(&bytes).unwrap());
485
486        // Position
487        let expected = "position exit;";
488        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
489        let bytes = command.to_bytes_le().unwrap();
490        assert_eq!(command, Command::from_bytes_le(&bytes).unwrap());
491    }
492
493    #[test]
494    fn test_command_parse() {
495        // Decrement
496        let expected = "decrement object[r0] by r1;";
497        Command::<CurrentNetwork>::parse(expected).unwrap_err();
498
499        // Instruction
500        let expected = "add r0 r1 into r2;";
501        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
502        assert_eq!(Command::Instruction(Instruction::from_str(expected).unwrap()), command);
503        assert_eq!(expected, command.to_string());
504
505        // Increment
506        let expected = "increment object[r0] by r1;";
507        Command::<CurrentNetwork>::parse(expected).unwrap_err();
508
509        // Contains
510        let expected = "contains object[r0] into r1;";
511        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
512        assert_eq!(Command::Contains(Contains::from_str(expected).unwrap()), command);
513        assert_eq!(expected, command.to_string());
514
515        // Get
516        let expected = "get object[r0] into r1;";
517        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
518        assert_eq!(Command::Get(Get::from_str(expected).unwrap()), command);
519        assert_eq!(expected, command.to_string());
520
521        // GetOr
522        let expected = "get.or_use object[r0] r1 into r2;";
523        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
524        assert_eq!(Command::GetOrUse(GetOrUse::from_str(expected).unwrap()), command);
525        assert_eq!(expected, command.to_string());
526
527        // RandChaCha
528        let expected = "rand.chacha into r1 as field;";
529        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
530        assert_eq!(Command::RandChaCha(RandChaCha::from_str(expected).unwrap()), command);
531        assert_eq!(expected, command.to_string());
532
533        // RandChaCha
534        let expected = "rand.chacha r0 r1 into r2 as group;";
535        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
536        assert_eq!(Command::RandChaCha(RandChaCha::from_str(expected).unwrap()), command);
537        assert_eq!(expected, command.to_string());
538
539        // Remove
540        let expected = "remove object[r0];";
541        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
542        assert_eq!(Command::Remove(Remove::from_str(expected).unwrap()), command);
543        assert_eq!(expected, command.to_string());
544
545        // Set
546        let expected = "set r0 into object[r1];";
547        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
548        assert_eq!(Command::Set(Set::from_str(expected).unwrap()), command);
549        assert_eq!(expected, command.to_string());
550
551        // BranchEq
552        let expected = "branch.eq r0 r1 to exit;";
553        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
554        assert_eq!(Command::BranchEq(BranchEq::from_str(expected).unwrap()), command);
555        assert_eq!(expected, command.to_string());
556
557        // BranchNeq
558        let expected = "branch.neq r2 r3 to start;";
559        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
560        assert_eq!(Command::BranchNeq(BranchNeq::from_str(expected).unwrap()), command);
561        assert_eq!(expected, command.to_string());
562
563        // Position
564        let expected = "position exit;";
565        let command = Command::<CurrentNetwork>::parse(expected).unwrap().1;
566        assert_eq!(Command::Position(Position::from_str(expected).unwrap()), command);
567        assert_eq!(expected, command.to_string());
568    }
569}