snarkvm_synthesizer_program/logic/command/contains/
dynamic.rs1use crate::{FinalizeStoreTrait, Opcode, Operand, RegistersTrait, StackTrait};
17use console::{
18 network::prelude::*,
19 program::{Identifier, Literal, Plaintext, ProgramID, Register, Value},
20 types::Boolean,
21};
22
23#[derive(Clone, PartialEq, Eq, Hash)]
26pub struct ContainsDynamic<N: Network> {
27 operands: [Operand<N>; 4],
29 destination: Register<N>,
31}
32
33impl<N: Network> ContainsDynamic<N> {
34 #[inline]
36 pub const fn opcode() -> Opcode {
37 Opcode::Command("contains.dynamic")
38 }
39
40 #[inline]
42 pub fn operands(&self) -> &[Operand<N>] {
43 &self.operands
44 }
45
46 #[inline]
48 pub const fn program_name_operand(&self) -> &Operand<N> {
49 &self.operands[0]
50 }
51
52 #[inline]
54 pub const fn program_network_operand(&self) -> &Operand<N> {
55 &self.operands[1]
56 }
57
58 #[inline]
60 pub const fn mapping_name_operand(&self) -> &Operand<N> {
61 &self.operands[2]
62 }
63
64 #[inline]
66 pub const fn key_operand(&self) -> &Operand<N> {
67 &self.operands[3]
68 }
69
70 #[inline]
72 pub const fn destination(&self) -> &Register<N> {
73 &self.destination
74 }
75}
76
77impl<N: Network> ContainsDynamic<N> {
78 pub fn finalize(
80 &self,
81 stack: &impl StackTrait<N>,
82 store: &impl FinalizeStoreTrait<N>,
83 registers: &mut impl RegistersTrait<N>,
84 ) -> Result<()> {
85 let program_name = match registers.load(stack, self.program_name_operand())? {
87 Value::Plaintext(Plaintext::Literal(Literal::Field(field), _)) => Identifier::from_field(&field)?,
88 Value::Plaintext(Plaintext::Literal(Literal::Identifier(id_lit), _)) => {
89 Identifier::from_field(&id_lit.to_field()?)?
90 }
91 _ => bail!("Expected the first operand of `contains.dynamic` to be a field or identifier literal."),
92 };
93
94 let program_network = match registers.load(stack, self.program_network_operand())? {
96 Value::Plaintext(Plaintext::Literal(Literal::Field(field), _)) => Identifier::from_field(&field)?,
97 Value::Plaintext(Plaintext::Literal(Literal::Identifier(id_lit), _)) => {
98 Identifier::from_field(&id_lit.to_field()?)?
99 }
100 _ => bail!("Expected the second operand of `contains.dynamic` to be a field or identifier literal."),
101 };
102
103 let program_id = ProgramID::try_from((program_name, program_network))?;
105
106 let mapping_name = match registers.load(stack, self.mapping_name_operand())? {
108 Value::Plaintext(Plaintext::Literal(Literal::Field(field), _)) => Identifier::from_field(&field)?,
109 Value::Plaintext(Plaintext::Literal(Literal::Identifier(id_lit), _)) => {
110 Identifier::from_field(&id_lit.to_field()?)?
111 }
112 _ => bail!("Expected the third operand of `contains.dynamic` to be a field or identifier literal."),
113 };
114
115 if !store.contains_mapping_speculative(&program_id, &mapping_name)? {
117 bail!("Mapping '{program_id}/{mapping_name}' does not exist");
118 }
119
120 let key = registers.load_plaintext(stack, self.key_operand())?;
122
123 let mapping = stack.get_stack_global(&program_id)?.program().get_mapping(&mapping_name)?;
125 let mapping_key_type = mapping.key().plaintext_type();
127 ensure!(
129 stack.matches_plaintext(&key, mapping_key_type).is_ok(),
130 "Expected the key to be of type '{mapping_key_type}', found '{key}'."
131 );
132
133 let contains_key = store.contains_key_speculative(program_id, mapping_name, &key)?;
135
136 registers.store(stack, &self.destination, Value::from(Literal::Boolean(Boolean::new(contains_key))))?;
138
139 Ok(())
140 }
141}
142
143impl<N: Network> Parser for ContainsDynamic<N> {
144 #[inline]
146 fn parse(string: &str) -> ParserResult<Self> {
147 let (string, _) = Sanitizer::parse(string)?;
149 let (string, _) = tag(*Self::opcode())(string)?;
151 let (string, _) = Sanitizer::parse_whitespaces(string)?;
153
154 let (string, program_name) = Operand::parse(string)?;
156 let (string, _) = Sanitizer::parse_whitespaces(string)?;
158 let (string, program_network) = Operand::parse(string)?;
160 let (string, _) = Sanitizer::parse_whitespaces(string)?;
162 let (string, mapping_name) = Operand::parse(string)?;
164 let (string, _) = Sanitizer::parse_whitespaces(string)?;
166
167 let (string, _) = tag("[")(string)?;
169 let (string, _) = Sanitizer::parse_whitespaces(string)?;
171 let (string, key) = Operand::parse(string)?;
173 let (string, _) = Sanitizer::parse_whitespaces(string)?;
175 let (string, _) = tag("]")(string)?;
177
178 let (string, _) = Sanitizer::parse_whitespaces(string)?;
180 let (string, _) = tag("into")(string)?;
182 let (string, _) = Sanitizer::parse_whitespaces(string)?;
184 let (string, destination) = Register::parse(string)?;
186
187 let (string, _) = Sanitizer::parse_whitespaces(string)?;
189 let (string, _) = tag(";")(string)?;
191
192 Ok((string, Self { operands: [program_name, program_network, mapping_name, key], destination }))
193 }
194}
195
196impl<N: Network> FromStr for ContainsDynamic<N> {
197 type Err = Error;
198
199 #[inline]
201 fn from_str(string: &str) -> Result<Self> {
202 match Self::parse(string) {
203 Ok((remainder, object)) => {
204 ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
206 Ok(object)
208 }
209 Err(error) => bail!("Failed to parse string. {error}"),
210 }
211 }
212}
213
214impl<N: Network> Debug for ContainsDynamic<N> {
215 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
217 Display::fmt(self, f)
218 }
219}
220
221impl<N: Network> Display for ContainsDynamic<N> {
222 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
224 write!(f, "{} ", Self::opcode())?;
226 write!(
228 f,
229 "{} {} {}[{}] into ",
230 self.program_name_operand(),
231 self.program_network_operand(),
232 self.mapping_name_operand(),
233 self.key_operand()
234 )?;
235 write!(f, "{};", self.destination)
237 }
238}
239
240impl<N: Network> FromBytes for ContainsDynamic<N> {
241 fn read_le<R: Read>(mut reader: R) -> IoResult<Self> {
243 let program_name = Operand::read_le(&mut reader)?;
245 let program_network = Operand::read_le(&mut reader)?;
247 let mapping_name = Operand::read_le(&mut reader)?;
249 let key = Operand::read_le(&mut reader)?;
251 let destination = Register::read_le(&mut reader)?;
253 Ok(Self { operands: [program_name, program_network, mapping_name, key], destination })
255 }
256}
257
258impl<N: Network> ToBytes for ContainsDynamic<N> {
259 fn write_le<W: Write>(&self, mut writer: W) -> IoResult<()> {
261 self.program_name_operand().write_le(&mut writer)?;
263 self.program_network_operand().write_le(&mut writer)?;
265 self.mapping_name_operand().write_le(&mut writer)?;
267 self.key_operand().write_le(&mut writer)?;
269 self.destination.write_le(&mut writer)
271 }
272}
273
274#[cfg(test)]
275mod tests {
276 use super::*;
277 use console::{network::MainnetV0, program::Register};
278
279 type CurrentNetwork = MainnetV0;
280
281 #[test]
282 fn test_parse() {
283 let (string, contains) =
284 ContainsDynamic::<CurrentNetwork>::parse("contains.dynamic r0 r1 r2[r3] into r4;").unwrap();
285 assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
286 assert_eq!(contains.operands().len(), 4, "The number of operands is incorrect");
287 assert_eq!(
288 contains.program_name_operand(),
289 &Operand::Register(Register::Locator(0)),
290 "The first operand is incorrect"
291 );
292 assert_eq!(
293 contains.program_network_operand(),
294 &Operand::Register(Register::Locator(1)),
295 "The second operand is incorrect"
296 );
297 assert_eq!(
298 contains.mapping_name_operand(),
299 &Operand::Register(Register::Locator(2)),
300 "The third operand is incorrect"
301 );
302 assert_eq!(contains.key_operand(), &Operand::Register(Register::Locator(3)), "The fourth operand is incorrect");
303 assert_eq!(contains.destination, Register::Locator(4), "The destination register is incorrect");
304 }
305
306 #[test]
307 fn test_from_bytes() {
308 let (string, contains) =
309 ContainsDynamic::<CurrentNetwork>::parse("contains.dynamic r0 r1 r2[r3] into r4;").unwrap();
310 assert!(string.is_empty());
311 let bytes_le = contains.to_bytes_le().unwrap();
312 let result = ContainsDynamic::<CurrentNetwork>::from_bytes_le(&bytes_le[..]);
313 assert!(result.is_ok())
314 }
315
316 #[test]
317 fn test_display_parse_roundtrip() {
318 let input = "contains.dynamic r0 r1 r2[r3] into r4;";
319 let (string, original) = ContainsDynamic::<CurrentNetwork>::parse(input).unwrap();
320 assert!(string.is_empty());
321 let displayed = format!("{original}");
322 let (remainder, reparsed) = ContainsDynamic::<CurrentNetwork>::parse(&displayed).unwrap();
323 assert!(remainder.is_empty());
324 assert_eq!(original, reparsed);
325 }
326}