snarkvm_synthesizer_program/logic/command/
get.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
16use crate::{
17    CallOperator,
18    Opcode,
19    Operand,
20    traits::{FinalizeStoreTrait, RegistersLoad, RegistersStore, StackMatches, StackProgram},
21};
22use console::{
23    network::prelude::*,
24    program::{Register, Value},
25};
26
27/// A get command, e.g. `get accounts[r0] into r1;`.
28/// Gets the value stored at `operand` in `mapping` and stores the result in `destination`.
29#[derive(Clone)]
30pub struct Get<N: Network> {
31    /// The mapping.
32    mapping: CallOperator<N>,
33    /// The key to access the mapping.
34    key: Operand<N>,
35    /// The destination register.
36    destination: Register<N>,
37}
38
39impl<N: Network> PartialEq for Get<N> {
40    /// Returns true if the two objects are equal.
41    #[inline]
42    fn eq(&self, other: &Self) -> bool {
43        self.mapping == other.mapping && self.key == other.key && self.destination == other.destination
44    }
45}
46
47impl<N: Network> Eq for Get<N> {}
48
49impl<N: Network> std::hash::Hash for Get<N> {
50    /// Returns the hash of the object.
51    #[inline]
52    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
53        self.mapping.hash(state);
54        self.key.hash(state);
55        self.destination.hash(state);
56    }
57}
58
59impl<N: Network> Get<N> {
60    /// Returns the opcode.
61    #[inline]
62    pub const fn opcode() -> Opcode {
63        Opcode::Command("get")
64    }
65
66    /// Returns the operands in the operation.
67    #[inline]
68    pub fn operands(&self) -> Vec<Operand<N>> {
69        vec![self.key.clone()]
70    }
71
72    /// Returns the mapping.
73    #[inline]
74    pub const fn mapping(&self) -> &CallOperator<N> {
75        &self.mapping
76    }
77
78    /// Returns the operand containing the key.
79    #[inline]
80    pub const fn key(&self) -> &Operand<N> {
81        &self.key
82    }
83
84    /// Returns the destination register.
85    #[inline]
86    pub const fn destination(&self) -> &Register<N> {
87        &self.destination
88    }
89}
90
91impl<N: Network> Get<N> {
92    /// Finalizes the command.
93    #[inline]
94    pub fn finalize(
95        &self,
96        stack: &(impl StackMatches<N> + StackProgram<N>),
97        store: &impl FinalizeStoreTrait<N>,
98        registers: &mut (impl RegistersLoad<N> + RegistersStore<N>),
99    ) -> Result<()> {
100        // Determine the program ID and mapping name.
101        let (program_id, mapping_name) = match self.mapping {
102            CallOperator::Locator(locator) => (*locator.program_id(), *locator.resource()),
103            CallOperator::Resource(mapping_name) => (*stack.program_id(), mapping_name),
104        };
105
106        // Ensure the mapping exists in storage.
107        if !store.contains_mapping_confirmed(&program_id, &mapping_name)? {
108            bail!("Mapping '{program_id}/{mapping_name}' does not exist in storage");
109        }
110
111        // Load the operand as a plaintext.
112        let key = registers.load_plaintext(stack, &self.key)?;
113
114        // Retrieve the value from storage as a literal.
115        let value = match store.get_value_speculative(program_id, mapping_name, &key)? {
116            Some(Value::Plaintext(plaintext)) => Value::Plaintext(plaintext),
117            Some(Value::Record(..)) => bail!("Cannot 'get' a 'record'"),
118            Some(Value::Future(..)) => bail!("Cannot 'get' a 'future'",),
119            // If a key does not exist, then bail.
120            None => bail!("Key '{key}' does not exist in mapping '{program_id}/{mapping_name}'"),
121        };
122
123        // Assign the value to the destination register.
124        registers.store(stack, &self.destination, value)?;
125
126        Ok(())
127    }
128}
129
130impl<N: Network> Parser for Get<N> {
131    /// Parses a string into an operation.
132    #[inline]
133    fn parse(string: &str) -> ParserResult<Self> {
134        // Parse the whitespace and comments from the string.
135        let (string, _) = Sanitizer::parse(string)?;
136        // Parse the opcode from the string.
137        let (string, _) = tag(*Self::opcode())(string)?;
138        // Parse the whitespace from the string.
139        let (string, _) = Sanitizer::parse_whitespaces(string)?;
140
141        // Parse the mapping name from the string.
142        let (string, mapping) = CallOperator::parse(string)?;
143        // Parse the "[" from the string.
144        let (string, _) = tag("[")(string)?;
145        // Parse the whitespace from the string.
146        let (string, _) = Sanitizer::parse_whitespaces(string)?;
147        // Parse the key operand from the string.
148        let (string, key) = Operand::parse(string)?;
149        // Parse the whitespace from the string.
150        let (string, _) = Sanitizer::parse_whitespaces(string)?;
151        // Parse the "]" from the string.
152        let (string, _) = tag("]")(string)?;
153
154        // Parse the whitespace from the string.
155        let (string, _) = Sanitizer::parse_whitespaces(string)?;
156        // Parse the "into" keyword from the string.
157        let (string, _) = tag("into")(string)?;
158        // Parse the whitespace from the string.
159        let (string, _) = Sanitizer::parse_whitespaces(string)?;
160        // Parse the destination register from the string.
161        let (string, destination) = Register::parse(string)?;
162
163        // Parse the whitespace from the string.
164        let (string, _) = Sanitizer::parse_whitespaces(string)?;
165        // Parse the ";" from the string.
166        let (string, _) = tag(";")(string)?;
167
168        Ok((string, Self { mapping, key, destination }))
169    }
170}
171
172impl<N: Network> FromStr for Get<N> {
173    type Err = Error;
174
175    /// Parses a string into the command.
176    #[inline]
177    fn from_str(string: &str) -> Result<Self> {
178        match Self::parse(string) {
179            Ok((remainder, object)) => {
180                // Ensure the remainder is empty.
181                ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
182                // Return the object.
183                Ok(object)
184            }
185            Err(error) => bail!("Failed to parse string. {error}"),
186        }
187    }
188}
189
190impl<N: Network> Debug for Get<N> {
191    /// Prints the command as a string.
192    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
193        Display::fmt(self, f)
194    }
195}
196
197impl<N: Network> Display for Get<N> {
198    /// Prints the command to a string.
199    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
200        // Print the command.
201        write!(f, "{} ", Self::opcode())?;
202        // Print the mapping and key operand.
203        write!(f, "{}[{}] into ", self.mapping, self.key)?;
204        // Print the destination register.
205        write!(f, "{};", self.destination)
206    }
207}
208
209impl<N: Network> FromBytes for Get<N> {
210    /// Reads the command from a buffer.
211    fn read_le<R: Read>(mut reader: R) -> IoResult<Self> {
212        // Read the mapping name.
213        let mapping = CallOperator::read_le(&mut reader)?;
214        // Read the key operand.
215        let key = Operand::read_le(&mut reader)?;
216        // Read the destination register.
217        let destination = Register::read_le(&mut reader)?;
218        // Return the command.
219        Ok(Self { mapping, key, destination })
220    }
221}
222
223impl<N: Network> ToBytes for Get<N> {
224    /// Writes the command to a buffer.
225    fn write_le<W: Write>(&self, mut writer: W) -> IoResult<()> {
226        // Write the mapping name.
227        self.mapping.write_le(&mut writer)?;
228        // Write the key operand.
229        self.key.write_le(&mut writer)?;
230        // Write the destination register.
231        self.destination.write_le(&mut writer)
232    }
233}
234
235#[cfg(test)]
236mod tests {
237    use super::*;
238    use console::{network::MainnetV0, program::Register};
239
240    type CurrentNetwork = MainnetV0;
241
242    #[test]
243    fn test_parse() {
244        let (string, get) = Get::<CurrentNetwork>::parse("get account[r0] into r1;").unwrap();
245        assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
246        assert_eq!(get.mapping, CallOperator::from_str("account").unwrap());
247        assert_eq!(get.operands().len(), 1, "The number of operands is incorrect");
248        assert_eq!(get.key, Operand::Register(Register::Locator(0)), "The first operand is incorrect");
249        assert_eq!(get.destination, Register::Locator(1), "The second operand is incorrect");
250
251        let (string, get) = Get::<CurrentNetwork>::parse("get token.aleo/balances[r0] into r1;").unwrap();
252        assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
253        assert_eq!(get.mapping, CallOperator::from_str("token.aleo/balances").unwrap());
254        assert_eq!(get.operands().len(), 1, "The number of operands is incorrect");
255        assert_eq!(get.key, Operand::Register(Register::Locator(0)), "The first operand is incorrect");
256        assert_eq!(get.destination, Register::Locator(1), "The second operand is incorrect");
257    }
258
259    #[test]
260    fn test_from_bytes() {
261        let (string, get) = Get::<CurrentNetwork>::parse("get account[r0] into r1;").unwrap();
262        assert!(string.is_empty());
263        let bytes_le = get.to_bytes_le().unwrap();
264        let result = Get::<CurrentNetwork>::from_bytes_le(&bytes_le[..]);
265        assert!(result.is_ok())
266    }
267}