snarkvm_synthesizer_program/logic/command/get/
dynamic.rs1use crate::{FinalizeStoreTrait, Opcode, Operand, RegistersTrait, StackTrait};
17use console::{
18 network::prelude::*,
19 program::{Identifier, Literal, Plaintext, PlaintextType, ProgramID, Register, Value},
20};
21
22#[derive(Clone, PartialEq, Eq, Hash)]
25pub struct GetDynamic<N: Network> {
26 operands: [Operand<N>; 4],
28 destination: Register<N>,
30 destination_type: PlaintextType<N>,
32}
33
34impl<N: Network> GetDynamic<N> {
35 #[inline]
37 pub const fn opcode() -> Opcode {
38 Opcode::Command("get.dynamic")
39 }
40
41 #[inline]
43 pub fn operands(&self) -> &[Operand<N>] {
44 &self.operands
45 }
46
47 #[inline]
49 pub const fn program_name_operand(&self) -> &Operand<N> {
50 &self.operands[0]
51 }
52
53 #[inline]
55 pub const fn program_network_operand(&self) -> &Operand<N> {
56 &self.operands[1]
57 }
58
59 #[inline]
61 pub const fn mapping_name_operand(&self) -> &Operand<N> {
62 &self.operands[2]
63 }
64
65 #[inline]
67 pub const fn key_operand(&self) -> &Operand<N> {
68 &self.operands[3]
69 }
70
71 #[inline]
73 pub const fn destination(&self) -> &Register<N> {
74 &self.destination
75 }
76
77 #[inline]
79 pub const fn destination_type(&self) -> &PlaintextType<N> {
80 &self.destination_type
81 }
82}
83
84impl<N: Network> GetDynamic<N> {
85 #[inline]
87 pub fn finalize(
88 &self,
89 stack: &impl StackTrait<N>,
90 store: &impl FinalizeStoreTrait<N>,
91 registers: &mut impl RegistersTrait<N>,
92 ) -> Result<()> {
93 let program_name = match registers.load(stack, self.program_name_operand())? {
95 Value::Plaintext(Plaintext::Literal(Literal::Field(field), _)) => Identifier::from_field(&field)?,
96 Value::Plaintext(Plaintext::Literal(Literal::Identifier(id_lit), _)) => {
97 Identifier::from_field(&id_lit.to_field()?)?
98 }
99 _ => bail!("Expected the first operand of `get.dynamic` to be a field or identifier literal."),
100 };
101
102 let program_network = match registers.load(stack, self.program_network_operand())? {
104 Value::Plaintext(Plaintext::Literal(Literal::Field(field), _)) => Identifier::from_field(&field)?,
105 Value::Plaintext(Plaintext::Literal(Literal::Identifier(id_lit), _)) => {
106 Identifier::from_field(&id_lit.to_field()?)?
107 }
108 _ => bail!("Expected the second operand of `get.dynamic` to be a field or identifier literal."),
109 };
110
111 let program_id = ProgramID::try_from((program_name, program_network))?;
113
114 let mapping_name = match registers.load(stack, self.mapping_name_operand())? {
116 Value::Plaintext(Plaintext::Literal(Literal::Field(field), _)) => Identifier::from_field(&field)?,
117 Value::Plaintext(Plaintext::Literal(Literal::Identifier(id_lit), _)) => {
118 Identifier::from_field(&id_lit.to_field()?)?
119 }
120 _ => bail!("Expected the third operand of `get.dynamic` to be a field or identifier literal."),
121 };
122
123 if !store.contains_mapping_speculative(&program_id, &mapping_name)? {
125 bail!("Mapping '{program_id}/{mapping_name}' does not exist");
126 }
127
128 let key = registers.load_plaintext(stack, self.key_operand())?;
130
131 let mapping = stack.get_stack_global(&program_id)?.program().get_mapping(&mapping_name)?;
133 let mapping_key_type = mapping.key().plaintext_type();
135 ensure!(
137 stack.matches_plaintext(&key, mapping_key_type).is_ok(),
138 "Expected the key to be of type '{mapping_key_type}', found '{key}'."
139 );
140 let mapping_value_type = mapping.value().plaintext_type();
142 ensure!(
144 &self.destination_type == mapping_value_type,
145 "Expected the destination type to be '{mapping_value_type}', found '{}'.",
146 self.destination_type
147 );
148
149 let value = match store.get_value_speculative(program_id, mapping_name, &key)? {
151 Some(Value::Plaintext(plaintext)) => Value::Plaintext(plaintext),
152 Some(Value::Record(..)) => bail!("Cannot 'get.dynamic' a 'record'"),
153 Some(Value::Future(..)) => bail!("Cannot 'get.dynamic' a 'future'"),
154 Some(Value::DynamicRecord(..)) => bail!("Cannot 'get.dynamic' a 'dynamic.record'"),
155 Some(Value::DynamicFuture(..)) => bail!("Cannot 'get.dynamic' a 'dynamic.future'"),
156 None => bail!("Key '{key}' does not exist in mapping '{program_id}/{mapping_name}'"),
158 };
159
160 registers.store(stack, &self.destination, value)?;
162
163 Ok(())
164 }
165}
166
167impl<N: Network> Parser for GetDynamic<N> {
168 #[inline]
170 fn parse(string: &str) -> ParserResult<Self> {
171 let (string, _) = Sanitizer::parse(string)?;
173 let (string, _) = tag(*Self::opcode())(string)?;
175 let (string, _) = Sanitizer::parse_whitespaces(string)?;
177
178 let (string, program_name) = Operand::parse(string)?;
180 let (string, _) = Sanitizer::parse_whitespaces(string)?;
182 let (string, program_network) = Operand::parse(string)?;
184 let (string, _) = Sanitizer::parse_whitespaces(string)?;
186 let (string, mapping_name) = Operand::parse(string)?;
188 let (string, _) = Sanitizer::parse_whitespaces(string)?;
190
191 let (string, _) = tag("[")(string)?;
193 let (string, _) = Sanitizer::parse_whitespaces(string)?;
195 let (string, key) = Operand::parse(string)?;
197 let (string, _) = Sanitizer::parse_whitespaces(string)?;
199 let (string, _) = tag("]")(string)?;
201
202 let (string, _) = Sanitizer::parse_whitespaces(string)?;
204 let (string, _) = tag("into")(string)?;
206 let (string, _) = Sanitizer::parse_whitespaces(string)?;
208 let (string, destination) = Register::parse(string)?;
210
211 let (string, _) = Sanitizer::parse_whitespaces(string)?;
213 let (string, _) = tag("as")(string)?;
215 let (string, _) = Sanitizer::parse_whitespaces(string)?;
217 let (string, destination_type) = PlaintextType::parse(string)?;
219
220 let (string, _) = Sanitizer::parse_whitespaces(string)?;
222 let (string, _) = tag(";")(string)?;
224
225 Ok((string, Self {
226 operands: [program_name, program_network, mapping_name, key],
227 destination,
228 destination_type,
229 }))
230 }
231}
232
233impl<N: Network> FromStr for GetDynamic<N> {
234 type Err = Error;
235
236 #[inline]
238 fn from_str(string: &str) -> Result<Self> {
239 match Self::parse(string) {
240 Ok((remainder, object)) => {
241 ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
243 Ok(object)
245 }
246 Err(error) => bail!("Failed to parse string. {error}"),
247 }
248 }
249}
250
251impl<N: Network> Debug for GetDynamic<N> {
252 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
254 Display::fmt(self, f)
255 }
256}
257
258impl<N: Network> Display for GetDynamic<N> {
259 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
261 write!(f, "{} ", Self::opcode())?;
263 write!(
265 f,
266 "{} {} {}[{}] into ",
267 self.program_name_operand(),
268 self.program_network_operand(),
269 self.mapping_name_operand(),
270 self.key_operand()
271 )?;
272 write!(f, "{} as {};", self.destination, self.destination_type())
274 }
275}
276
277impl<N: Network> FromBytes for GetDynamic<N> {
278 fn read_le<R: Read>(mut reader: R) -> IoResult<Self> {
280 let program_name = Operand::read_le(&mut reader)?;
282 let program_network = Operand::read_le(&mut reader)?;
284 let mapping_name = Operand::read_le(&mut reader)?;
286 let key = Operand::read_le(&mut reader)?;
288 let destination = Register::read_le(&mut reader)?;
290 let destination_type = PlaintextType::read_le(&mut reader)?;
292 Ok(Self { operands: [program_name, program_network, mapping_name, key], destination, destination_type })
294 }
295}
296
297impl<N: Network> ToBytes for GetDynamic<N> {
298 fn write_le<W: Write>(&self, mut writer: W) -> IoResult<()> {
300 self.program_name_operand().write_le(&mut writer)?;
302 self.program_network_operand().write_le(&mut writer)?;
304 self.mapping_name_operand().write_le(&mut writer)?;
306 self.key_operand().write_le(&mut writer)?;
308 self.destination.write_le(&mut writer)?;
310 self.destination_type.write_le(&mut writer)
312 }
313}
314
315#[cfg(test)]
316mod tests {
317 use super::*;
318 use console::{network::MainnetV0, program::Register};
319
320 type CurrentNetwork = MainnetV0;
321
322 #[test]
323 fn test_parse() {
324 let (string, get) = GetDynamic::<CurrentNetwork>::parse("get.dynamic r0 r1 r2[r3] into r4 as Baz;").unwrap();
325 assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
326 assert_eq!(get.operands().len(), 4, "The number of operands is incorrect");
327 assert_eq!(
328 get.program_name_operand(),
329 &Operand::Register(Register::Locator(0)),
330 "The first operand is incorrect"
331 );
332 assert_eq!(
333 get.program_network_operand(),
334 &Operand::Register(Register::Locator(1)),
335 "The second operand is incorrect"
336 );
337 assert_eq!(
338 get.mapping_name_operand(),
339 &Operand::Register(Register::Locator(2)),
340 "The third operand is incorrect"
341 );
342 assert_eq!(get.key_operand(), &Operand::Register(Register::Locator(3)), "The fourth operand is incorrect");
343 assert_eq!(get.destination, Register::Locator(4), "The destination register is incorrect");
344 assert_eq!(
345 get.destination_type,
346 PlaintextType::Struct(Identifier::from_str("Baz").unwrap()),
347 "The destination type is incorrect"
348 );
349 }
350
351 #[test]
352 fn test_from_bytes() {
353 let (string, get) = GetDynamic::<CurrentNetwork>::parse("get.dynamic r0 r1 r2[r3] into r4 as u8;").unwrap();
354 assert!(string.is_empty());
355 let bytes_le = get.to_bytes_le().unwrap();
356 let result = GetDynamic::<CurrentNetwork>::from_bytes_le(&bytes_le[..]);
357 assert!(result.is_ok())
358 }
359
360 #[test]
361 fn test_display_parse_roundtrip() {
362 let input = "get.dynamic r0 r1 r2[r3] into r4 as u8;";
363 let (string, original) = GetDynamic::<CurrentNetwork>::parse(input).unwrap();
364 assert!(string.is_empty());
365 let displayed = format!("{original}");
366 let (remainder, reparsed) = GetDynamic::<CurrentNetwork>::parse(&displayed).unwrap();
367 assert!(remainder.is_empty());
368 assert_eq!(original, reparsed);
369 }
370}