1use crate::{
17 Opcode,
18 Operand,
19 traits::{
20 RegistersLoad,
21 RegistersLoadCircuit,
22 RegistersSigner,
23 RegistersSignerCircuit,
24 RegistersStore,
25 RegistersStoreCircuit,
26 StackMatches,
27 StackProgram,
28 },
29};
30use console::{
31 network::prelude::*,
32 program::{
33 ArrayType,
34 Entry,
35 EntryType,
36 Identifier,
37 Literal,
38 LiteralType,
39 Locator,
40 Owner,
41 Plaintext,
42 PlaintextType,
43 Record,
44 Register,
45 RegisterType,
46 Value,
47 ValueType,
48 },
49 types::Field,
50};
51
52use indexmap::IndexMap;
53
54#[derive(Clone, PartialEq, Eq, Hash)]
55pub enum CastType<N: Network> {
57 GroupXCoordinate,
58 GroupYCoordinate,
59 Plaintext(PlaintextType<N>),
60 Record(Identifier<N>),
61 ExternalRecord(Locator<N>),
62}
63
64impl<N: Network> Parser for CastType<N> {
65 fn parse(string: &str) -> ParserResult<Self> {
66 alt((
68 map(tag("group.x"), |_| Self::GroupXCoordinate),
69 map(tag("group.y"), |_| Self::GroupYCoordinate),
70 map(pair(Locator::parse, tag(".record")), |(locator, _)| Self::ExternalRecord(locator)),
71 map(pair(Identifier::parse, tag(".record")), |(identifier, _)| Self::Record(identifier)),
72 map(PlaintextType::parse, Self::Plaintext),
73 ))(string)
74 }
75}
76
77impl<N: Network> Display for CastType<N> {
78 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
79 match self {
80 Self::GroupXCoordinate => write!(f, "group.x"),
81 Self::GroupYCoordinate => write!(f, "group.y"),
82 Self::Plaintext(plaintext_type) => write!(f, "{}", plaintext_type),
83 Self::Record(identifier) => write!(f, "{}.record", identifier),
84 Self::ExternalRecord(locator) => write!(f, "{}.record", locator),
85 }
86 }
87}
88
89impl<N: Network> Debug for CastType<N> {
90 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
92 Display::fmt(self, f)
93 }
94}
95
96impl<N: Network> FromStr for CastType<N> {
97 type Err = Error;
98
99 fn from_str(string: &str) -> Result<Self> {
101 match Self::parse(string) {
102 Ok((remainder, object)) => {
103 ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
105 Ok(object)
107 }
108 Err(error) => bail!("Failed to parse string. {error}"),
109 }
110 }
111}
112
113impl<N: Network> ToBytes for CastType<N> {
114 fn write_le<W: Write>(&self, mut writer: W) -> IoResult<()> {
116 match self {
117 Self::GroupXCoordinate => 0u8.write_le(&mut writer),
118 Self::GroupYCoordinate => 1u8.write_le(&mut writer),
119 CastType::Plaintext(plaintext_type) => {
120 2u8.write_le(&mut writer)?;
121 plaintext_type.write_le(&mut writer)
122 }
123 CastType::Record(identifier) => {
124 3u8.write_le(&mut writer)?;
125 identifier.write_le(&mut writer)
126 }
127 CastType::ExternalRecord(locator) => {
128 4u8.write_le(&mut writer)?;
129 locator.write_le(&mut writer)
130 }
131 }
132 }
133}
134
135impl<N: Network> FromBytes for CastType<N> {
136 fn read_le<R: Read>(mut reader: R) -> IoResult<Self> {
138 let variant = u8::read_le(&mut reader)?;
139 match variant {
140 0 => Ok(Self::GroupXCoordinate),
141 1 => Ok(Self::GroupYCoordinate),
142 2 => Ok(Self::Plaintext(PlaintextType::read_le(&mut reader)?)),
143 3 => Ok(Self::Record(Identifier::read_le(&mut reader)?)),
144 4 => Ok(Self::ExternalRecord(Locator::read_le(&mut reader)?)),
145 5.. => Err(error(format!("Failed to deserialize cast type variant {variant}"))),
146 }
147 }
148}
149
150pub type Cast<N> = CastOperation<N, { CastVariant::Cast as u8 }>;
152pub type CastLossy<N> = CastOperation<N, { CastVariant::CastLossy as u8 }>;
154
155enum CastVariant {
157 Cast,
158 CastLossy,
159}
160
161#[derive(Clone, PartialEq, Eq, Hash)]
163pub struct CastOperation<N: Network, const VARIANT: u8> {
164 operands: Vec<Operand<N>>,
166 destination: Register<N>,
168 cast_type: CastType<N>,
170}
171
172impl<N: Network, const VARIANT: u8> CastOperation<N, VARIANT> {
173 #[inline]
175 pub const fn opcode() -> Opcode {
176 Opcode::Cast(match VARIANT {
177 0 => "cast",
178 1 => "cast.lossy",
179 2.. => panic!("Invalid cast variant"),
180 })
181 }
182
183 #[inline]
185 pub fn operands(&self) -> &[Operand<N>] {
186 &self.operands
187 }
188
189 #[inline]
191 pub fn destinations(&self) -> Vec<Register<N>> {
192 vec![self.destination.clone()]
193 }
194
195 #[inline]
197 pub const fn cast_type(&self) -> &CastType<N> {
198 &self.cast_type
199 }
200}
201
202impl<N: Network, const VARIANT: u8> CastOperation<N, VARIANT> {
203 #[inline]
205 pub fn evaluate(
206 &self,
207 stack: &(impl StackMatches<N> + StackProgram<N>),
208 registers: &mut (impl RegistersSigner<N> + RegistersLoad<N> + RegistersStore<N>),
209 ) -> Result<()> {
210 if VARIANT == CastVariant::CastLossy as u8 {
212 ensure!(
213 matches!(self.cast_type, CastType::Plaintext(PlaintextType::Literal(..))),
214 "`cast.lossy` is only supported for casting to a literal type"
215 )
216 }
217
218 let inputs: Vec<_> = self.operands.iter().map(|operand| registers.load(stack, operand)).try_collect()?;
220
221 match &self.cast_type {
222 CastType::GroupXCoordinate => {
223 ensure!(inputs.len() == 1, "Casting to a group x-coordinate requires exactly 1 operand");
224 let field = match &inputs[0] {
225 Value::Plaintext(Plaintext::Literal(Literal::Group(group), ..)) => group.to_x_coordinate(),
226 _ => bail!("Casting to a group x-coordinate requires a group element"),
227 };
228 registers.store(stack, &self.destination, Value::Plaintext(Plaintext::from(Literal::Field(field))))
229 }
230 CastType::GroupYCoordinate => {
231 ensure!(inputs.len() == 1, "Casting to a group y-coordinate requires exactly 1 operand");
232 let field = match &inputs[0] {
233 Value::Plaintext(Plaintext::Literal(Literal::Group(group), ..)) => group.to_y_coordinate(),
234 _ => bail!("Casting to a group y-coordinate requires a group element"),
235 };
236 registers.store(stack, &self.destination, Value::Plaintext(Plaintext::from(Literal::Field(field))))
237 }
238 CastType::Plaintext(PlaintextType::Literal(literal_type)) => {
239 ensure!(inputs.len() == 1, "Casting to a literal requires exactly 1 operand");
240 let value = match &inputs[0] {
241 Value::Plaintext(Plaintext::Literal(literal, ..)) => match VARIANT {
242 0 => literal.cast(*literal_type)?,
243 1 => literal.cast_lossy(*literal_type)?,
244 2.. => unreachable!("Invalid cast variant"),
245 },
246 _ => bail!("Casting to a literal requires a literal"),
247 };
248 registers.store(stack, &self.destination, Value::Plaintext(Plaintext::from(value)))
249 }
250 CastType::Plaintext(PlaintextType::Struct(struct_name)) => {
251 self.cast_to_struct(stack, registers, *struct_name, inputs)
252 }
253 CastType::Plaintext(PlaintextType::Array(array_type)) => {
254 self.cast_to_array(stack, registers, array_type, inputs)
255 }
256 CastType::Record(record_name) => {
257 if inputs.len() < N::MIN_RECORD_ENTRIES {
259 bail!("Casting to a record requires at least {} operand", N::MIN_RECORD_ENTRIES)
260 }
261
262 let record_type = stack.program().get_record(record_name)?;
264
265 if inputs.len() != record_type.entries().len() + 1 {
267 bail!(
268 "Casting to the record {} requires {} operands, but {} were provided",
269 record_type.name(),
270 record_type.entries().len() + 1,
271 inputs.len()
272 )
273 }
274
275 let owner: Owner<N, Plaintext<N>> = match &inputs[0] {
277 Value::Plaintext(Plaintext::Literal(Literal::Address(owner), ..)) => {
279 match record_type.owner().is_public() {
280 true => Owner::Public(*owner),
281 false => Owner::Private(Plaintext::Literal(Literal::Address(*owner), Default::default())),
282 }
283 }
284 _ => bail!("Invalid record 'owner'"),
285 };
286
287 let mut entries = IndexMap::new();
289 for (entry, (entry_name, entry_type)) in
290 inputs.iter().skip(N::MIN_RECORD_ENTRIES).zip_eq(record_type.entries())
291 {
292 let plaintext_type = entry_type.plaintext_type();
294 let plaintext = match entry {
296 Value::Plaintext(plaintext) => {
297 stack.matches_plaintext(plaintext, plaintext_type)?;
299 plaintext.clone()
301 }
302 Value::Record(..) => bail!("Casting a record into a record entry is illegal"),
304 Value::Future(..) => bail!("Casting a future into a record entry is illegal"),
306 };
307 match entry_type {
309 EntryType::Constant(..) => entries.insert(*entry_name, Entry::Constant(plaintext)),
310 EntryType::Public(..) => entries.insert(*entry_name, Entry::Public(plaintext)),
311 EntryType::Private(..) => entries.insert(*entry_name, Entry::Private(plaintext)),
312 };
313 }
314
315 let index = Field::from_u64(self.destination.locator());
317 let randomizer = N::hash_to_scalar_psd2(&[registers.tvk()?, index])?;
319 let nonce = N::g_scalar_multiply(&randomizer);
321
322 let version = console::program::U8::one();
325
326 let record = Record::<N, Plaintext<N>>::from_plaintext(owner, entries, nonce, version)?;
328 registers.store(stack, &self.destination, Value::Record(record))
330 }
331 CastType::ExternalRecord(_locator) => {
332 bail!("Illegal operation: Cannot cast to an external record.")
333 }
334 }
335 }
336
337 #[inline]
339 pub fn execute<A: circuit::Aleo<Network = N>>(
340 &self,
341 stack: &(impl StackMatches<N> + StackProgram<N>),
342 registers: &mut (impl RegistersSignerCircuit<N, A> + RegistersLoadCircuit<N, A> + RegistersStoreCircuit<N, A>),
343 ) -> Result<()> {
344 if VARIANT == CastVariant::CastLossy as u8 {
346 ensure!(
347 matches!(self.cast_type, CastType::Plaintext(PlaintextType::Literal(..))),
348 "`cast.lossy` is only supported for casting to a literal type"
349 )
350 }
351
352 use circuit::{Eject, Inject};
353
354 let inputs: Vec<_> =
356 self.operands.iter().map(|operand| registers.load_circuit(stack, operand)).try_collect()?;
357
358 match &self.cast_type {
359 CastType::GroupXCoordinate => {
360 ensure!(inputs.len() == 1, "Casting to a group x-coordinate requires exactly 1 operand");
361 let field = match &inputs[0] {
362 circuit::Value::Plaintext(circuit::Plaintext::Literal(circuit::Literal::Group(group), ..)) => {
363 group.to_x_coordinate()
364 }
365 _ => bail!("Casting to a group x-coordinate requires a group element"),
366 };
367 registers.store_circuit(
368 stack,
369 &self.destination,
370 circuit::Value::Plaintext(circuit::Plaintext::from(circuit::Literal::Field(field))),
371 )
372 }
373 CastType::GroupYCoordinate => {
374 ensure!(inputs.len() == 1, "Casting to a group y-coordinate requires exactly 1 operand");
375 let field = match &inputs[0] {
376 circuit::Value::Plaintext(circuit::Plaintext::Literal(circuit::Literal::Group(group), ..)) => {
377 group.to_y_coordinate()
378 }
379 _ => bail!("Casting to a group y-coordinate requires a group element"),
380 };
381 registers.store_circuit(
382 stack,
383 &self.destination,
384 circuit::Value::Plaintext(circuit::Plaintext::from(circuit::Literal::Field(field))),
385 )
386 }
387 CastType::Plaintext(PlaintextType::Literal(literal_type)) => {
388 ensure!(inputs.len() == 1, "Casting to a literal requires exactly 1 operand");
389 let value = match &inputs[0] {
390 circuit::Value::Plaintext(circuit::Plaintext::Literal(literal, ..)) => match VARIANT {
391 0 => literal.cast(*literal_type)?,
392 1 => literal.cast_lossy(*literal_type)?,
393 2.. => unreachable!("Invalid cast variant"),
394 },
395 _ => bail!("Casting to a literal requires a literal"),
396 };
397 registers.store_circuit(
398 stack,
399 &self.destination,
400 circuit::Value::Plaintext(circuit::Plaintext::from(value)),
401 )
402 }
403 CastType::Plaintext(PlaintextType::Struct(struct_)) => {
404 if inputs.len() < N::MIN_STRUCT_ENTRIES {
406 bail!("Casting to a struct requires at least {} operand(s)", N::MIN_STRUCT_ENTRIES)
407 }
408 if inputs.len() > N::MAX_STRUCT_ENTRIES {
410 bail!("Casting to struct '{struct_}' cannot exceed {} members", N::MAX_STRUCT_ENTRIES)
411 }
412
413 let struct_ = stack.program().get_struct(struct_)?;
415
416 if inputs.len() != struct_.members().len() {
418 bail!(
419 "Casting to the struct {} requires {} operands, but {} were provided",
420 struct_.name(),
421 struct_.members().len(),
422 inputs.len()
423 )
424 }
425
426 let mut members = IndexMap::new();
428 for (member, (member_name, member_type)) in inputs.iter().zip_eq(struct_.members()) {
429 let plaintext = match member {
431 circuit::Value::Plaintext(plaintext) => {
432 stack.matches_plaintext(&plaintext.eject_value(), member_type)?;
434 plaintext.clone()
436 }
437 circuit::Value::Record(..) => {
439 bail!("Casting a record into a struct member is illegal")
440 }
441 circuit::Value::Future(..) => {
443 bail!("Casting a future into a struct member is illegal")
444 }
445 };
446 members.insert(circuit::Identifier::constant(*member_name), plaintext);
448 }
449
450 let struct_ = circuit::Plaintext::Struct(members, Default::default());
452 registers.store_circuit(stack, &self.destination, circuit::Value::Plaintext(struct_))
454 }
455 CastType::Plaintext(PlaintextType::Array(array_type)) => {
456 if inputs.len() < N::MIN_ARRAY_ELEMENTS {
458 bail!("Casting to an array requires at least {} operand(s)", N::MIN_ARRAY_ELEMENTS)
459 }
460 if inputs.len() > N::MAX_ARRAY_ELEMENTS {
462 bail!("Casting to array '{array_type}' cannot exceed {} elements", N::MAX_ARRAY_ELEMENTS)
463 }
464
465 if inputs.len() != **array_type.length() as usize {
467 bail!(
468 "Casting to the array {} requires {} operands, but {} were provided",
469 array_type,
470 array_type.length(),
471 inputs.len()
472 )
473 }
474
475 let mut elements = Vec::with_capacity(inputs.len());
477 for element in inputs.iter() {
478 let plaintext = match element {
480 circuit::Value::Plaintext(plaintext) => {
481 stack.matches_plaintext(&plaintext.eject_value(), array_type.next_element_type())?;
483 plaintext.clone()
485 }
486 circuit::Value::Record(..) => bail!("Casting a record into an array element is illegal"),
488 circuit::Value::Future(..) => bail!("Casting a future into an array element is illegal"),
490 };
491 elements.push(plaintext);
493 }
494
495 let array = circuit::Plaintext::Array(elements, Default::default());
497 registers.store_circuit(stack, &self.destination, circuit::Value::Plaintext(array))
499 }
500 CastType::Record(record_name) => {
501 if inputs.len() < N::MIN_RECORD_ENTRIES {
503 bail!("Casting to a record requires at least {} operand(s)", N::MIN_RECORD_ENTRIES)
504 }
505 if inputs.len() > N::MAX_RECORD_ENTRIES {
507 bail!("Casting to record '{record_name}' cannot exceed {} members", N::MAX_RECORD_ENTRIES)
508 }
509
510 let record_type = stack.program().get_record(record_name)?;
512
513 if inputs.len() != record_type.entries().len() + 1 {
515 bail!(
516 "Casting to the record {} requires {} operands, but {} were provided",
517 record_type.name(),
518 record_type.entries().len() + 1,
519 inputs.len()
520 )
521 }
522
523 let owner: circuit::Owner<A, circuit::Plaintext<A>> = match &inputs[0] {
525 circuit::Value::Plaintext(circuit::Plaintext::Literal(circuit::Literal::Address(owner), ..)) => {
527 match record_type.owner().is_public() {
528 true => circuit::Owner::Public(owner.clone()),
529 false => circuit::Owner::Private(circuit::Plaintext::Literal(
530 circuit::Literal::Address(owner.clone()),
531 Default::default(),
532 )),
533 }
534 }
535 _ => bail!("Invalid record 'owner'"),
536 };
537
538 let mut entries = IndexMap::new();
540 for (entry, (entry_name, entry_type)) in
541 inputs.iter().skip(N::MIN_RECORD_ENTRIES).zip_eq(record_type.entries())
542 {
543 let register_type = RegisterType::from(ValueType::from(entry_type.clone()));
545 let plaintext = match entry {
547 circuit::Value::Plaintext(plaintext) => {
548 stack.matches_register_type(
550 &circuit::Value::Plaintext(plaintext.clone()).eject_value(),
551 ®ister_type,
552 )?;
553 plaintext.clone()
555 }
556 circuit::Value::Record(..) => bail!("Casting a record into a record entry is illegal"),
558 circuit::Value::Future(..) => bail!("Casting a future into a record entry is illegal"),
560 };
561 let entry_name = circuit::Identifier::constant(*entry_name);
563 match entry_type {
565 EntryType::Constant(..) => entries.insert(entry_name, circuit::Entry::Constant(plaintext)),
566 EntryType::Public(..) => entries.insert(entry_name, circuit::Entry::Public(plaintext)),
567 EntryType::Private(..) => entries.insert(entry_name, circuit::Entry::Private(plaintext)),
568 };
569 }
570
571 let index = circuit::Field::constant(Field::from_u64(self.destination.locator()));
573 let randomizer = A::hash_to_scalar_psd2(&[registers.tvk_circuit()?, index]);
575 let nonce = A::g_scalar_multiply(&randomizer);
577
578 let version = circuit::U8::new(circuit::Mode::Private, console::program::U8::one());
584
585 let record =
587 circuit::Record::<A, circuit::Plaintext<A>>::from_plaintext(owner, entries, nonce, version)?;
588 registers.store_circuit(stack, &self.destination, circuit::Value::Record(record))
590 }
591 CastType::ExternalRecord(_locator) => {
592 bail!("Illegal operation: Cannot cast to an external record.")
593 }
594 }
595 }
596
597 #[inline]
599 pub fn finalize(
600 &self,
601 stack: &(impl StackMatches<N> + StackProgram<N>),
602 registers: &mut (impl RegistersLoad<N> + RegistersStore<N>),
603 ) -> Result<()> {
604 if VARIANT == CastVariant::CastLossy as u8 {
606 ensure!(
607 matches!(self.cast_type, CastType::Plaintext(PlaintextType::Literal(..))),
608 "`cast.lossy` is only supported for casting to a literal type"
609 )
610 }
611
612 let inputs: Vec<_> = self.operands.iter().map(|operand| registers.load(stack, operand)).try_collect()?;
614
615 match &self.cast_type {
616 CastType::GroupXCoordinate => {
617 ensure!(inputs.len() == 1, "Casting to a group x-coordinate requires exactly 1 operand");
618 let field = match &inputs[0] {
619 Value::Plaintext(Plaintext::Literal(Literal::Group(group), ..)) => group.to_x_coordinate(),
620 _ => bail!("Casting to a group x-coordinate requires a group element"),
621 };
622 registers.store(stack, &self.destination, Value::Plaintext(Plaintext::from(Literal::Field(field))))
623 }
624 CastType::GroupYCoordinate => {
625 ensure!(inputs.len() == 1, "Casting to a group y-coordinate requires exactly 1 operand");
626 let field = match &inputs[0] {
627 Value::Plaintext(Plaintext::Literal(Literal::Group(group), ..)) => group.to_y_coordinate(),
628 _ => bail!("Casting to a group y-coordinate requires a group element"),
629 };
630 registers.store(stack, &self.destination, Value::Plaintext(Plaintext::from(Literal::Field(field))))
631 }
632 CastType::Plaintext(PlaintextType::Literal(literal_type)) => {
633 ensure!(inputs.len() == 1, "Casting to a literal requires exactly 1 operand");
634 let value = match &inputs[0] {
635 Value::Plaintext(Plaintext::Literal(literal, ..)) => match VARIANT {
636 0 => literal.cast(*literal_type)?,
637 1 => literal.cast_lossy(*literal_type)?,
638 2.. => unreachable!("Invalid cast variant"),
639 },
640 _ => bail!("Casting to a literal requires a literal"),
641 };
642 registers.store(stack, &self.destination, Value::Plaintext(Plaintext::from(value)))
643 }
644 CastType::Plaintext(PlaintextType::Struct(struct_name)) => {
645 self.cast_to_struct(stack, registers, *struct_name, inputs)
646 }
647 CastType::Plaintext(PlaintextType::Array(array_type)) => {
648 self.cast_to_array(stack, registers, array_type, inputs)
649 }
650 CastType::Record(_record_name) => {
651 bail!("Illegal operation: Cannot cast to a record in a finalize block.")
652 }
653 CastType::ExternalRecord(_locator) => {
654 bail!("Illegal operation: Cannot cast to an external record.")
655 }
656 }
657 }
658
659 #[inline]
661 pub fn output_types(
662 &self,
663 stack: &impl StackProgram<N>,
664 input_types: &[RegisterType<N>],
665 ) -> Result<Vec<RegisterType<N>>> {
666 if VARIANT == CastVariant::CastLossy as u8 {
668 ensure!(
669 matches!(self.cast_type, CastType::Plaintext(PlaintextType::Literal(..))),
670 "`cast.lossy` is only supported for casting to a literal type"
671 )
672 }
673
674 ensure!(
676 input_types.len() == self.operands.len(),
677 "Instruction '{}' expects {} operands, found {} operands",
678 Self::opcode(),
679 input_types.len(),
680 self.operands.len(),
681 );
682
683 match &self.cast_type {
685 CastType::GroupXCoordinate | CastType::GroupYCoordinate => {
686 ensure!(input_types.len() == 1, "Casting to a group coordinate requires exactly 1 operand");
687 ensure!(
688 matches!(input_types[0], RegisterType::Plaintext(PlaintextType::Literal(LiteralType::Group))),
689 "Type mismatch: expected 'group', found '{}'",
690 input_types[0]
691 );
692 }
693 CastType::Plaintext(PlaintextType::Literal(..)) => {
694 ensure!(input_types.len() == 1, "Casting to a literal requires exactly 1 operand");
695 }
696 CastType::Plaintext(PlaintextType::Struct(struct_name)) => {
697 let struct_ = stack.program().get_struct(struct_name)?;
699
700 if input_types.len() < N::MIN_STRUCT_ENTRIES {
702 bail!("Casting to a struct requires at least {} operand(s)", N::MIN_STRUCT_ENTRIES)
703 }
704 if input_types.len() > N::MAX_STRUCT_ENTRIES {
706 bail!("Casting to struct '{struct_}' cannot exceed {} members", N::MAX_STRUCT_ENTRIES)
707 }
708
709 ensure!(
711 input_types.len() == struct_.members().len(),
712 "Casting to the struct {} requires {} operands, but {} were provided",
713 struct_.name(),
714 struct_.members().len(),
715 input_types.len()
716 );
717 for ((_, member_type), input_type) in struct_.members().iter().zip_eq(input_types) {
719 match input_type {
720 RegisterType::Plaintext(plaintext_type) => {
722 ensure!(
723 member_type == plaintext_type,
724 "Struct '{struct_name}' member type mismatch: expected '{member_type}', found '{plaintext_type}'"
725 )
726 }
727 RegisterType::Record(record_name) => bail!(
729 "Struct '{struct_name}' member type mismatch: expected '{member_type}', found record '{record_name}'"
730 ),
731 RegisterType::ExternalRecord(locator) => bail!(
733 "Struct '{struct_name}' member type mismatch: expected '{member_type}', found external record '{locator}'"
734 ),
735 RegisterType::Future(..) => {
737 bail!("Struct '{struct_name}' member type mismatch: expected '{member_type}', found future")
738 }
739 }
740 }
741 }
742 CastType::Plaintext(PlaintextType::Array(array_type)) => {
743 if input_types.len() < N::MIN_ARRAY_ELEMENTS {
745 bail!("Casting to an array requires at least {} operand(s)", N::MIN_ARRAY_ELEMENTS)
746 }
747 if input_types.len() > N::MAX_ARRAY_ELEMENTS {
749 bail!("Casting to array '{array_type}' cannot exceed {} elements", N::MAX_ARRAY_ELEMENTS)
750 }
751
752 if input_types.len() != **array_type.length() as usize {
754 bail!(
755 "Casting to the array {} requires {} operands, but {} were provided",
756 array_type,
757 array_type.length(),
758 input_types.len()
759 )
760 }
761
762 for input_type in input_types {
764 match input_type {
765 RegisterType::Plaintext(plaintext_type) => {
767 ensure!(
768 plaintext_type == array_type.next_element_type(),
769 "Array element type mismatch: expected '{}', found '{plaintext_type}'",
770 array_type.next_element_type()
771 )
772 }
773 RegisterType::Record(record_name) => bail!(
775 "Array element type mismatch: expected '{}', found record '{record_name}'",
776 array_type.next_element_type()
777 ),
778 RegisterType::ExternalRecord(locator) => bail!(
780 "Array element type mismatch: expected '{}', found external record '{locator}'",
781 array_type.next_element_type()
782 ),
783 RegisterType::Future(..) => bail!(
785 "Array element type mismatch: expected '{}', found future",
786 array_type.next_element_type()
787 ),
788 }
789 }
790 }
791 CastType::Record(record_name) => {
792 let record = stack.program().get_record(record_name)?;
794
795 if input_types.len() < N::MIN_RECORD_ENTRIES {
797 bail!("Casting to a record requires at least {} operand(s)", N::MIN_RECORD_ENTRIES)
798 }
799 if input_types.len() > N::MAX_RECORD_ENTRIES {
801 bail!("Casting to record '{record_name}' cannot exceed {} members", N::MAX_RECORD_ENTRIES)
802 }
803
804 ensure!(
806 input_types.len() == record.entries().len() + 1,
807 "Casting to the record {} requires {} operands, but {} were provided",
808 record.name(),
809 record.entries().len() + 1,
810 input_types.len()
811 );
812 ensure!(
814 input_types[0] == RegisterType::Plaintext(PlaintextType::Literal(LiteralType::Address)),
815 "Casting to a record requires the first operand to be an address"
816 );
817
818 for (input_type, (_, entry_type)) in
820 input_types.iter().skip(N::MIN_RECORD_ENTRIES).zip_eq(record.entries())
821 {
822 match input_type {
823 RegisterType::Plaintext(plaintext_type) => match entry_type {
825 EntryType::Constant(entry_type)
826 | EntryType::Public(entry_type)
827 | EntryType::Private(entry_type) => {
828 ensure!(
829 entry_type == plaintext_type,
830 "Record '{record_name}' entry type mismatch: expected '{entry_type}', found '{plaintext_type}'"
831 )
832 }
833 },
834 RegisterType::Record(record_name) => bail!(
836 "Record '{record_name}' entry type mismatch: expected '{entry_type}', found record '{record_name}'"
837 ),
838 RegisterType::ExternalRecord(locator) => bail!(
840 "Record '{record_name}' entry type mismatch: expected '{entry_type}', found external record '{locator}'"
841 ),
842 RegisterType::Future(..) => {
844 bail!("Record '{record_name}' entry type mismatch: expected '{entry_type}', found future",)
845 }
846 }
847 }
848 }
849 CastType::ExternalRecord(_locator) => {
850 bail!("Illegal operation: Cannot cast to an external record.")
851 }
852 }
853
854 Ok(vec![match &self.cast_type {
855 CastType::GroupXCoordinate => RegisterType::Plaintext(PlaintextType::Literal(LiteralType::Field)),
856 CastType::GroupYCoordinate => RegisterType::Plaintext(PlaintextType::Literal(LiteralType::Field)),
857 CastType::Plaintext(plaintext_type) => RegisterType::Plaintext(plaintext_type.clone()),
858 CastType::Record(identifier) => RegisterType::Record(*identifier),
859 CastType::ExternalRecord(locator) => RegisterType::ExternalRecord(*locator),
860 }])
861 }
862}
863
864impl<N: Network, const VARIANT: u8> CastOperation<N, VARIANT> {
865 fn cast_to_struct(
867 &self,
868 stack: &(impl StackMatches<N> + StackProgram<N>),
869 registers: &mut impl RegistersStore<N>,
870 struct_name: Identifier<N>,
871 inputs: Vec<Value<N>>,
872 ) -> Result<()> {
873 if inputs.len() < N::MIN_STRUCT_ENTRIES {
875 bail!("Casting to a struct requires at least {} operand", N::MIN_STRUCT_ENTRIES)
876 }
877
878 let struct_ = stack.program().get_struct(&struct_name)?;
880
881 if inputs.len() != struct_.members().len() {
883 bail!(
884 "Casting to the struct {} requires {} operands, but {} were provided",
885 struct_.name(),
886 struct_.members().len(),
887 inputs.len()
888 )
889 }
890
891 let mut members = IndexMap::new();
893 for (member, (member_name, member_type)) in inputs.iter().zip_eq(struct_.members()) {
894 let plaintext = match member {
896 Value::Plaintext(plaintext) => {
897 stack.matches_plaintext(plaintext, member_type)?;
899 plaintext.clone()
901 }
902 Value::Record(..) => bail!("Casting a record into a struct member is illegal"),
904 Value::Future(..) => bail!("Casting a future into a struct member is illegal"),
906 };
907 members.insert(*member_name, plaintext);
909 }
910
911 let struct_ = Plaintext::Struct(members, Default::default());
913 registers.store(stack, &self.destination, Value::Plaintext(struct_))
915 }
916
917 fn cast_to_array(
919 &self,
920 stack: &(impl StackMatches<N> + StackProgram<N>),
921 registers: &mut impl RegistersStore<N>,
922 array_type: &ArrayType<N>,
923 inputs: Vec<Value<N>>,
924 ) -> Result<()> {
925 if inputs.len() < N::MIN_ARRAY_ELEMENTS {
927 bail!("Casting to an array requires at least {} operand", N::MIN_ARRAY_ELEMENTS)
928 }
929
930 if inputs.len() != **array_type.length() as usize {
932 bail!(
933 "Casting to the array {} requires {} operands, but {} were provided",
934 array_type,
935 array_type.length(),
936 inputs.len()
937 )
938 }
939
940 let mut elements = Vec::with_capacity(inputs.len());
942 for element in inputs.iter() {
943 let plaintext = match element {
945 Value::Plaintext(plaintext) => {
946 stack.matches_plaintext(plaintext, array_type.next_element_type())?;
948 plaintext.clone()
950 }
951 Value::Record(..) => bail!("Casting a record into an array element is illegal"),
953 Value::Future(..) => bail!("Casting a future into an array element is illegal"),
955 };
956 elements.push(plaintext);
958 }
959
960 let array = Plaintext::Array(elements, Default::default());
962 registers.store(stack, &self.destination, Value::Plaintext(array))
964 }
965}
966
967impl<N: Network, const VARIANT: u8> Parser for CastOperation<N, VARIANT> {
968 #[inline]
970 fn parse(string: &str) -> ParserResult<Self> {
971 fn parse_operand<N: Network>(string: &str) -> ParserResult<Operand<N>> {
973 let (string, _) = Sanitizer::parse_whitespaces(string)?;
975 Operand::parse(string)
977 }
978
979 let (string, _) = tag(*Self::opcode())(string)?;
981 let (string, operands) = many1(parse_operand)(string)?;
983 let (string, _) = Sanitizer::parse_whitespaces(string)?;
985 let (string, _) = tag("into")(string)?;
987 let (string, _) = Sanitizer::parse_whitespaces(string)?;
989 let (string, destination) = Register::parse(string)?;
991 let (string, _) = Sanitizer::parse_whitespaces(string)?;
993 let (string, _) = tag("as")(string)?;
995 let (string, _) = Sanitizer::parse_whitespaces(string)?;
997 let (string, cast_type) = CastType::parse(string)?;
999 let max_operands = match cast_type {
1001 CastType::GroupXCoordinate
1002 | CastType::GroupYCoordinate
1003 | CastType::Plaintext(PlaintextType::Literal(_)) => 1,
1004 CastType::Plaintext(PlaintextType::Struct(_)) => N::MAX_STRUCT_ENTRIES,
1005 CastType::Plaintext(PlaintextType::Array(_)) => N::MAX_ARRAY_ELEMENTS,
1006 CastType::Record(_) | CastType::ExternalRecord(_) => N::MAX_RECORD_ENTRIES,
1007 };
1008 match !operands.is_empty() && (operands.len() <= max_operands) {
1009 true => Ok((string, Self { operands, destination, cast_type })),
1010 false => {
1011 map_res(fail, |_: ParserResult<Self>| Err(error("Failed to parse 'cast' opcode: too many operands")))(
1012 string,
1013 )
1014 }
1015 }
1016 }
1017}
1018
1019impl<N: Network, const VARIANT: u8> FromStr for CastOperation<N, VARIANT> {
1020 type Err = Error;
1021
1022 #[inline]
1024 fn from_str(string: &str) -> Result<Self> {
1025 match Self::parse(string) {
1026 Ok((remainder, object)) => {
1027 ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
1029 Ok(object)
1031 }
1032 Err(error) => bail!("Failed to parse string. {error}"),
1033 }
1034 }
1035}
1036
1037impl<N: Network, const VARIANT: u8> Debug for CastOperation<N, VARIANT> {
1038 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
1040 Display::fmt(self, f)
1041 }
1042}
1043
1044impl<N: Network, const VARIANT: u8> Display for CastOperation<N, VARIANT> {
1045 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
1047 let max_operands = match self.cast_type {
1049 CastType::GroupYCoordinate
1050 | CastType::GroupXCoordinate
1051 | CastType::Plaintext(PlaintextType::Literal(_)) => 1,
1052 CastType::Plaintext(PlaintextType::Struct(_)) => N::MAX_STRUCT_ENTRIES,
1053 CastType::Plaintext(PlaintextType::Array(_)) => N::MAX_ARRAY_ELEMENTS,
1054 CastType::Record(_) | CastType::ExternalRecord(_) => N::MAX_RECORD_ENTRIES,
1055 };
1056 if self.operands.is_empty() || self.operands.len() > max_operands {
1057 return Err(fmt::Error);
1058 }
1059 write!(f, "{} ", Self::opcode())?;
1061 self.operands.iter().try_for_each(|operand| write!(f, "{operand} "))?;
1062 write!(f, "into {} as {}", self.destination, self.cast_type)
1063 }
1064}
1065
1066impl<N: Network, const VARIANT: u8> FromBytes for CastOperation<N, VARIANT> {
1067 fn read_le<R: Read>(mut reader: R) -> IoResult<Self> {
1069 let num_operands = u8::read_le(&mut reader)? as usize;
1071
1072 if num_operands.is_zero() || num_operands > N::MAX_RECORD_ENTRIES {
1076 return Err(error(format!("The number of operands must be nonzero and <= {}", N::MAX_RECORD_ENTRIES)));
1077 }
1078
1079 let mut operands = Vec::with_capacity(num_operands);
1081 for _ in 0..num_operands {
1083 operands.push(Operand::read_le(&mut reader)?);
1084 }
1085
1086 let destination = Register::read_le(&mut reader)?;
1088
1089 let cast_type = CastType::read_le(&mut reader)?;
1091
1092 let max_operands = match cast_type {
1094 CastType::GroupYCoordinate
1095 | CastType::GroupXCoordinate
1096 | CastType::Plaintext(PlaintextType::Literal(_)) => 1,
1097 CastType::Plaintext(PlaintextType::Struct(_)) => N::MAX_STRUCT_ENTRIES,
1098 CastType::Plaintext(PlaintextType::Array(_)) => N::MAX_ARRAY_ELEMENTS,
1099 CastType::Record(_) | CastType::ExternalRecord(_) => N::MAX_RECORD_ENTRIES,
1100 };
1101 if num_operands.is_zero() || num_operands > max_operands {
1102 return Err(error(format!("The number of operands must be nonzero and <= {max_operands}")));
1103 }
1104
1105 Ok(Self { operands, destination, cast_type })
1107 }
1108}
1109
1110impl<N: Network, const VARIANT: u8> ToBytes for CastOperation<N, VARIANT> {
1111 fn write_le<W: Write>(&self, mut writer: W) -> IoResult<()> {
1113 let max_operands = match self.cast_type {
1115 CastType::GroupYCoordinate
1116 | CastType::GroupXCoordinate
1117 | CastType::Plaintext(PlaintextType::Literal(_)) => 1,
1118 CastType::Plaintext(PlaintextType::Struct(_)) => N::MAX_STRUCT_ENTRIES,
1119 CastType::Plaintext(PlaintextType::Array(_)) => N::MAX_ARRAY_ELEMENTS,
1120 CastType::Record(_) | CastType::ExternalRecord(_) => N::MAX_RECORD_ENTRIES,
1121 };
1122 if self.operands.is_empty() || self.operands.len() > max_operands {
1123 return Err(error(format!("The number of operands must be nonzero and <= {max_operands}")));
1124 }
1125
1126 u8::try_from(self.operands.len()).map_err(|e| error(e.to_string()))?.write_le(&mut writer)?;
1128 self.operands.iter().try_for_each(|operand| operand.write_le(&mut writer))?;
1130 self.destination.write_le(&mut writer)?;
1132 self.cast_type.write_le(&mut writer)
1134 }
1135}
1136
1137#[cfg(test)]
1138mod tests {
1139 use super::*;
1140 use console::{
1141 network::MainnetV0,
1142 program::{Access, Identifier},
1143 };
1144
1145 type CurrentNetwork = MainnetV0;
1146
1147 #[test]
1148 fn test_parse() {
1149 let (string, cast) =
1150 Cast::<CurrentNetwork>::parse("cast r0.owner r0.token_amount into r1 as token.record").unwrap();
1151 assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
1152 assert_eq!(cast.operands.len(), 2, "The number of operands is incorrect");
1153 assert_eq!(
1154 cast.operands[0],
1155 Operand::Register(Register::Access(0, vec![Access::from(Identifier::from_str("owner").unwrap())])),
1156 "The first operand is incorrect"
1157 );
1158 assert_eq!(
1159 cast.operands[1],
1160 Operand::Register(Register::Access(0, vec![Access::from(Identifier::from_str("token_amount").unwrap())])),
1161 "The second operand is incorrect"
1162 );
1163 assert_eq!(cast.destination, Register::Locator(1), "The destination register is incorrect");
1164 assert_eq!(
1165 cast.cast_type,
1166 CastType::Record(Identifier::from_str("token").unwrap()),
1167 "The value type is incorrect"
1168 );
1169 }
1170
1171 #[test]
1172 fn test_parse_cast_into_plaintext_max_operands() {
1173 let mut string = "cast ".to_string();
1174 let mut operands = Vec::with_capacity(CurrentNetwork::MAX_STRUCT_ENTRIES);
1175 for i in 0..CurrentNetwork::MAX_STRUCT_ENTRIES {
1176 string.push_str(&format!("r{i} "));
1177 operands.push(Operand::Register(Register::Locator(i as u64)));
1178 }
1179 string.push_str(&format!("into r{} as foo", CurrentNetwork::MAX_STRUCT_ENTRIES));
1180 let (string, cast) = Cast::<CurrentNetwork>::parse(&string).unwrap();
1181 assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
1182 assert_eq!(cast.operands.len(), CurrentNetwork::MAX_STRUCT_ENTRIES, "The number of operands is incorrect");
1183 assert_eq!(cast.operands, operands, "The operands are incorrect");
1184 assert_eq!(
1185 cast.destination,
1186 Register::Locator(CurrentNetwork::MAX_STRUCT_ENTRIES as u64),
1187 "The destination register is incorrect"
1188 );
1189 assert_eq!(
1190 cast.cast_type,
1191 CastType::Plaintext(PlaintextType::Struct(Identifier::from_str("foo").unwrap())),
1192 "The value type is incorrect"
1193 );
1194 }
1195
1196 #[test]
1197 fn test_parse_cast_into_record_max_operands() {
1198 let mut string = "cast ".to_string();
1199 let mut operands = Vec::with_capacity(CurrentNetwork::MAX_RECORD_ENTRIES);
1200 for i in 0..CurrentNetwork::MAX_RECORD_ENTRIES {
1201 string.push_str(&format!("r{i} "));
1202 operands.push(Operand::Register(Register::Locator(i as u64)));
1203 }
1204 string.push_str(&format!("into r{} as token.record", CurrentNetwork::MAX_RECORD_ENTRIES));
1205 let (string, cast) = Cast::<CurrentNetwork>::parse(&string).unwrap();
1206 assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
1207 assert_eq!(cast.operands.len(), CurrentNetwork::MAX_RECORD_ENTRIES, "The number of operands is incorrect");
1208 assert_eq!(cast.operands, operands, "The operands are incorrect");
1209 assert_eq!(
1210 cast.destination,
1211 Register::Locator((CurrentNetwork::MAX_RECORD_ENTRIES) as u64),
1212 "The destination register is incorrect"
1213 );
1214 assert_eq!(
1215 cast.cast_type,
1216 CastType::Record(Identifier::from_str("token").unwrap()),
1217 "The value type is incorrect"
1218 );
1219 }
1220
1221 #[test]
1222 fn test_parse_cast_into_record_too_many_operands() {
1223 let mut string = "cast ".to_string();
1224 for i in 0..=CurrentNetwork::MAX_RECORD_ENTRIES {
1225 string.push_str(&format!("r{i} "));
1226 }
1227 string.push_str(&format!("into r{} as token.record", CurrentNetwork::MAX_RECORD_ENTRIES + 1));
1228 assert!(Cast::<CurrentNetwork>::parse(&string).is_err(), "Parser did not error");
1229 }
1230
1231 #[test]
1232 fn test_parse_cast_into_plaintext_too_many_operands() {
1233 let mut string = "cast ".to_string();
1234 for i in 0..=CurrentNetwork::MAX_STRUCT_ENTRIES {
1235 string.push_str(&format!("r{i} "));
1236 }
1237 string.push_str(&format!("into r{} as foo", CurrentNetwork::MAX_STRUCT_ENTRIES + 1));
1238 assert!(Cast::<CurrentNetwork>::parse(&string).is_err(), "Parser did not error");
1239 }
1240}