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