1use crate::{
17 FinalizeRegistersState,
18 FinalizeStoreTrait,
19 Opcode,
20 Operand,
21 RegistersCircuit,
22 RegistersTrait,
23 StackTrait,
24 register_types_equivalent,
25};
26use console::{
27 network::prelude::*,
28 program::{FinalizeType, Identifier, Locator, Register, RegisterType, Value},
29};
30
31#[derive(Clone, PartialEq, Eq, Hash)]
33pub enum CallOperator<N: Network> {
34 Locator(Locator<N>),
36 Resource(Identifier<N>),
38}
39
40impl<N: Network> Parser for CallOperator<N> {
41 #[inline]
43 fn parse(string: &str) -> ParserResult<Self> {
44 alt((map(Locator::parse, CallOperator::Locator), map(Identifier::parse, CallOperator::Resource)))(string)
45 }
46}
47
48impl<N: Network> FromStr for CallOperator<N> {
49 type Err = Error;
50
51 #[inline]
53 fn from_str(string: &str) -> Result<Self> {
54 match Self::parse(string) {
55 Ok((remainder, object)) => {
56 ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
58 Ok(object)
60 }
61 Err(error) => bail!("Failed to parse string. {error}"),
62 }
63 }
64}
65
66impl<N: Network> Debug for CallOperator<N> {
67 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
69 Display::fmt(self, f)
70 }
71}
72
73impl<N: Network> Display for CallOperator<N> {
74 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
76 match self {
77 CallOperator::Locator(locator) => Display::fmt(locator, f),
78 CallOperator::Resource(resource) => Display::fmt(resource, f),
79 }
80 }
81}
82
83impl<N: Network> FromBytes for CallOperator<N> {
84 fn read_le<R: Read>(mut reader: R) -> IoResult<Self> {
86 let variant = u8::read_le(&mut reader)?;
88 match variant {
90 0 => Ok(CallOperator::Locator(Locator::read_le(&mut reader)?)),
91 1 => Ok(CallOperator::Resource(Identifier::read_le(&mut reader)?)),
92 _ => Err(error("Failed to read CallOperator. Invalid variant.")),
93 }
94 }
95}
96
97impl<N: Network> ToBytes for CallOperator<N> {
98 fn write_le<W: Write>(&self, mut writer: W) -> IoResult<()> {
100 match self {
101 CallOperator::Locator(locator) => {
102 0u8.write_le(&mut writer)?;
104 locator.write_le(&mut writer)
106 }
107 CallOperator::Resource(resource) => {
108 1u8.write_le(&mut writer)?;
110 resource.write_le(&mut writer)
112 }
113 }
114 }
115}
116
117#[derive(Clone, PartialEq, Eq, Hash)]
120pub struct Call<N: Network> {
121 operator: CallOperator<N>,
123 operands: Vec<Operand<N>>,
125 destinations: Vec<Register<N>>,
127}
128
129impl<N: Network> Call<N> {
130 #[inline]
132 pub const fn opcode() -> Opcode {
133 Opcode::Call("call")
134 }
135
136 #[inline]
138 pub const fn operator(&self) -> &CallOperator<N> {
139 &self.operator
140 }
141
142 #[inline]
144 pub fn operands(&self) -> &[Operand<N>] {
145 &self.operands
146 }
147
148 #[inline]
150 pub fn destinations(&self) -> Vec<Register<N>> {
151 self.destinations.clone()
152 }
153
154 #[inline]
156 pub fn contains_external_struct(&self) -> bool {
157 false
158 }
159
160 #[inline]
162 pub fn is_function_call(&self, stack: &impl StackTrait<N>) -> Result<bool> {
163 match self.operator() {
164 CallOperator::Locator(locator) => {
166 let external_stack = stack.get_external_stack(locator.program_id())?;
168 let program = external_stack.program();
170 Ok(program.contains_function(locator.resource()))
172 }
173 CallOperator::Resource(resource) => Ok(stack.program().contains_function(resource)),
175 }
176 }
177
178 pub fn evaluate(&self, _stack: &impl StackTrait<N>, _registers: &mut impl RegistersTrait<N>) -> Result<()> {
180 bail!("Forbidden operation: Evaluate cannot invoke a 'call' directly. Use 'call' in 'Stack' instead.")
181 }
182
183 pub fn execute<A: circuit::Aleo<Network = N>>(
185 &self,
186 _stack: &impl StackTrait<N>,
187 _registers: &mut impl RegistersCircuit<N, A>,
188 ) -> Result<()> {
189 bail!("Forbidden operation: Execute cannot invoke a 'call' directly. Use 'call' in 'Stack' instead.")
190 }
191
192 #[inline]
200 pub fn finalize(
201 &self,
202 stack: &impl StackTrait<N>,
203 store: Option<&dyn FinalizeStoreTrait<N>>,
204 caller_registers: &mut impl FinalizeRegistersState<N>,
205 ) -> Result<()> {
206 let store = store.ok_or_else(|| anyhow!("`call` from a finalize context requires a finalize store"))?;
209
210 let inputs: Vec<Value<N>> =
212 self.operands.iter().map(|op| caller_registers.load(stack, op)).collect::<Result<_>>()?;
213
214 let state = *caller_registers.state();
216
217 let outputs = match &self.operator {
222 CallOperator::Locator(locator) => {
223 let external_stack = stack.get_external_stack(locator.program_id())?;
224 external_stack.evaluate_view(state, store, locator.resource(), inputs)?
225 }
226 CallOperator::Resource(name) => stack.evaluate_view(state, store, name, inputs)?,
227 };
228
229 ensure!(
231 self.destinations.len() == outputs.len(),
232 "View returned {} outputs but the call expects {}",
233 outputs.len(),
234 self.destinations.len(),
235 );
236
237 for (dest, value) in self.destinations.iter().zip_eq(outputs) {
239 caller_registers.store(stack, dest, value)?;
240 }
241 Ok(())
242 }
243
244 pub fn output_types(
246 &self,
247 stack: &impl StackTrait<N>,
248 input_types: &[RegisterType<N>],
249 ) -> Result<Vec<RegisterType<N>>> {
250 let stack_value;
252 let (is_external, program, name) = match &self.operator {
253 CallOperator::Locator(locator) => {
254 let program_name = locator.program_id();
255 stack_value = Some(stack.get_external_stack(program_name)?);
256 (true, stack_value.as_ref().unwrap().program(), locator.resource())
257 }
258 CallOperator::Resource(resource) => {
259 if stack.program().contains_function(resource) {
263 bail!("Cannot call '{resource}'. Use a closure ('closure {resource}:') instead.")
264 }
265 (false, stack.program(), resource)
266 }
267 };
268
269 if let Ok(view) = program.get_view_ref(name) {
274 if view.inputs().len() != self.operands.len() {
275 bail!("Expected {} inputs, found {}", view.inputs().len(), self.operands.len())
276 }
277 if view.inputs().len() != input_types.len() {
278 bail!("Expected {} input types, found {}", view.inputs().len(), input_types.len())
279 }
280 if view.outputs().len() != self.destinations.len() {
281 bail!("Expected {} outputs, found {}", view.outputs().len(), self.destinations.len())
282 }
283
284 for (index, (operand_type, input)) in input_types.iter().zip(view.inputs().iter()).enumerate() {
288 let plaintext_type = match input.finalize_type() {
289 FinalizeType::Plaintext(plaintext_type) => plaintext_type.clone(),
290 FinalizeType::Future(_) | FinalizeType::DynamicFuture => {
291 bail!("View '{name}' input '{index}' must be a plaintext type")
292 }
293 };
294 let mut expected_type = RegisterType::Plaintext(plaintext_type);
295 if is_external {
296 expected_type = expected_type.qualify(*program.id());
297 }
298 if !register_types_equivalent(stack, &expected_type, stack, operand_type)? {
299 bail!("Input '{index}' of view '{name}' expects '{expected_type}', found '{operand_type}'");
300 }
301 }
302
303 view.outputs()
304 .iter()
305 .map(|output| match output.finalize_type() {
306 FinalizeType::Plaintext(plaintext_type) => Ok(RegisterType::Plaintext(plaintext_type.clone())),
307 FinalizeType::Future(_) | FinalizeType::DynamicFuture => {
308 bail!("View '{name}' output must be a plaintext type")
309 }
310 })
311 .map(|result| {
312 result.map(
313 |register_type| {
314 if is_external { register_type.qualify(*program.id()) } else { register_type }
315 },
316 )
317 })
318 .collect::<Result<Vec<_>>>()
319 }
320 else if let Ok(closure) = program.get_closure(name) {
322 if closure.inputs().len() != self.operands.len() {
324 bail!("Expected {} inputs, found {}", closure.inputs().len(), self.operands.len())
325 }
326 if closure.inputs().len() != input_types.len() {
328 bail!("Expected {} input types, found {}", closure.inputs().len(), input_types.len())
329 }
330 if closure.outputs().len() != self.destinations.len() {
332 bail!("Expected {} outputs, found {}", closure.outputs().len(), self.destinations.len())
333 }
334 Ok(closure
336 .output_types()
337 .into_iter()
338 .map(|output_type| if is_external { output_type.qualify(*program.id()) } else { output_type })
340 .collect::<Vec<_>>())
341 }
342 else if let Ok(function) = program.get_function(name) {
344 if function.inputs().len() != self.operands.len() {
346 bail!("Expected {} inputs, found {}", function.inputs().len(), self.operands.len())
347 }
348 if function.inputs().len() != input_types.len() {
350 bail!("Expected {} input types, found {}", function.inputs().len(), input_types.len())
351 }
352 if function.outputs().len() != self.destinations.len() {
354 bail!("Expected {} outputs, found {}", function.outputs().len(), self.destinations.len())
355 }
356 Ok(function
358 .output_types()
359 .into_iter()
360 .map(RegisterType::from)
361 .map(|register_type| if is_external { register_type.qualify(*program.id()) } else { register_type })
364 .collect::<Vec<_>>())
365 }
366 else {
368 bail!("Call operator '{}' is invalid or unsupported.", self.operator)
369 }
370 }
371}
372
373impl<N: Network> Parser for Call<N> {
374 #[inline]
376 fn parse(string: &str) -> ParserResult<Self> {
377 fn parse_operand<N: Network>(string: &str) -> ParserResult<Operand<N>> {
379 let (string, _) = Sanitizer::parse_whitespaces(string)?;
381 Operand::parse(string)
383 }
384
385 fn parse_destination<N: Network>(string: &str) -> ParserResult<Register<N>> {
387 let (string, _) = Sanitizer::parse_whitespaces(string)?;
389 Register::parse(string)
391 }
392
393 let (string, _) = tag(*Self::opcode())(string)?;
395 let (string, _) = Sanitizer::parse_whitespaces(string)?;
397 let (string, operator) = CallOperator::parse(string)?;
399 let (string, _) = Sanitizer::parse_whitespaces(string)?;
401 let (string, operands) = map_res(many0(complete(parse_operand)), |operands: Vec<Operand<N>>| {
403 match operands.len() <= N::MAX_OPERANDS {
405 true => Ok(operands),
406 false => Err(error("Failed to parse 'call' opcode: too many operands")),
407 }
408 })(string)?;
409 let (string, _) = Sanitizer::parse_whitespaces(string)?;
411
412 let (string, destinations) = match opt(tag("into"))(string)? {
414 (string, None) => (string, vec![]),
416 (string, Some(_)) => {
418 let (string, _) = Sanitizer::parse_whitespaces(string)?;
420 let (string, destinations) =
422 map_res(many1(complete(parse_destination)), |destinations: Vec<Register<N>>| {
423 match destinations.len() <= N::MAX_OPERANDS {
425 true => Ok(destinations),
426 false => Err(error("Failed to parse 'call' opcode: too many destinations")),
427 }
428 })(string)?;
429 (string, destinations)
431 }
432 };
433
434 Ok((string, Self { operator, operands, destinations }))
435 }
436}
437
438impl<N: Network> FromStr for Call<N> {
439 type Err = Error;
440
441 #[inline]
443 fn from_str(string: &str) -> Result<Self> {
444 match Self::parse(string) {
445 Ok((remainder, object)) => {
446 ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
448 Ok(object)
450 }
451 Err(error) => bail!("Failed to parse string. {error}"),
452 }
453 }
454}
455
456impl<N: Network> Debug for Call<N> {
457 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
459 Display::fmt(self, f)
460 }
461}
462
463impl<N: Network> Display for Call<N> {
464 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
466 if self.operands.len() > N::MAX_OPERANDS {
468 return Err(fmt::Error);
469 }
470 if self.destinations.len() > N::MAX_OPERANDS {
472 return Err(fmt::Error);
473 }
474 write!(f, "{} {}", Self::opcode(), self.operator)?;
476 self.operands.iter().try_for_each(|operand| write!(f, " {operand}"))?;
477 if !self.destinations.is_empty() {
478 write!(f, " into")?;
479 self.destinations.iter().try_for_each(|destination| write!(f, " {destination}"))?;
480 }
481 Ok(())
482 }
483}
484
485impl<N: Network> FromBytes for Call<N> {
486 fn read_le<R: Read>(mut reader: R) -> IoResult<Self> {
488 let operator = CallOperator::read_le(&mut reader)?;
490
491 let num_operands = u8::read_le(&mut reader)? as usize;
493 if num_operands > N::MAX_OPERANDS {
495 return Err(error(format!("The number of operands must be <= {}", N::MAX_OPERANDS)));
496 }
497
498 let mut operands = Vec::with_capacity(num_operands);
500 for _ in 0..num_operands {
502 operands.push(Operand::read_le(&mut reader)?);
503 }
504
505 let num_destinations = u8::read_le(&mut reader)? as usize;
507 if num_destinations > N::MAX_OPERANDS {
509 return Err(error(format!("The number of destinations must be <= {}", N::MAX_OPERANDS)));
510 }
511
512 let mut destinations = Vec::with_capacity(num_destinations);
514 for _ in 0..num_destinations {
516 destinations.push(Register::read_le(&mut reader)?);
517 }
518
519 Ok(Self { operator, operands, destinations })
521 }
522}
523
524impl<N: Network> ToBytes for Call<N> {
525 fn write_le<W: Write>(&self, mut writer: W) -> IoResult<()> {
527 if self.operands.len() > N::MAX_OPERANDS {
529 return Err(error(format!("The number of operands must be <= {}", N::MAX_OPERANDS)));
530 }
531 if self.destinations.len() > N::MAX_OPERANDS {
533 return Err(error(format!("The number of destinations must be <= {}", N::MAX_OPERANDS)));
534 }
535
536 self.operator.write_le(&mut writer)?;
538 u8::try_from(self.operands.len()).map_err(|e| error(e.to_string()))?.write_le(&mut writer)?;
540 self.operands.iter().try_for_each(|operand| operand.write_le(&mut writer))?;
542 u8::try_from(self.destinations.len()).map_err(|e| error(e.to_string()))?.write_le(&mut writer)?;
544 self.destinations.iter().try_for_each(|destination| destination.write_le(&mut writer))
546 }
547}
548
549#[cfg(test)]
550mod tests {
551 use super::*;
552 use console::{
553 network::MainnetV0,
554 program::{Access, Address, Identifier, Literal, U64},
555 };
556
557 type CurrentNetwork = MainnetV0;
558
559 const TEST_CASES: &[&str] = &[
560 "call foo",
561 "call foo r0",
562 "call foo r0.owner",
563 "call foo r0 r1",
564 "call foo into r0",
565 "call foo into r0 r1",
566 "call foo into r0 r1 r2",
567 "call foo r0 into r1",
568 "call foo r0 r1 into r2",
569 "call foo r0 r1 into r2 r3",
570 "call foo r0 r1 r2 into r3 r4",
571 "call foo r0 r1 r2 into r3 r4 r5",
572 ];
573
574 fn check_parser(
575 string: &str,
576 expected_operator: CallOperator<CurrentNetwork>,
577 expected_operands: Vec<Operand<CurrentNetwork>>,
578 expected_destinations: Vec<Register<CurrentNetwork>>,
579 ) {
580 let (string, call) = Call::<CurrentNetwork>::parse(string).unwrap();
582
583 assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
585
586 assert_eq!(call.operator, expected_operator, "The call operator is incorrect");
588
589 assert_eq!(call.operands.len(), expected_operands.len(), "The number of operands is incorrect");
591 for (i, (given, expected)) in call.operands.iter().zip(expected_operands.iter()).enumerate() {
592 assert_eq!(given, expected, "The {i}-th operand is incorrect");
593 }
594
595 assert_eq!(call.destinations.len(), expected_destinations.len(), "The number of destinations is incorrect");
597 for (i, (given, expected)) in call.destinations.iter().zip(expected_destinations.iter()).enumerate() {
598 assert_eq!(given, expected, "The {i}-th destination is incorrect");
599 }
600 }
601
602 #[test]
603 fn test_parse() {
604 check_parser(
605 "call transfer r0.owner r0.token_amount into r1 r2 r3",
606 CallOperator::from_str("transfer").unwrap(),
607 vec![
608 Operand::Register(Register::Access(0, vec![Access::from(Identifier::from_str("owner").unwrap())])),
609 Operand::Register(Register::Access(0, vec![Access::from(
610 Identifier::from_str("token_amount").unwrap(),
611 )])),
612 ],
613 vec![Register::Locator(1), Register::Locator(2), Register::Locator(3)],
614 );
615
616 check_parser(
617 "call mint_public aleo1wfyyj2uvwuqw0c0dqa5x70wrawnlkkvuepn4y08xyaqfqqwweqys39jayw 100u64",
618 CallOperator::from_str("mint_public").unwrap(),
619 vec![
620 Operand::Literal(Literal::Address(
621 Address::from_str("aleo1wfyyj2uvwuqw0c0dqa5x70wrawnlkkvuepn4y08xyaqfqqwweqys39jayw").unwrap(),
622 )),
623 Operand::Literal(Literal::U64(U64::from_str("100u64").unwrap())),
624 ],
625 vec![],
626 );
627
628 check_parser(
629 "call get_magic_number into r0",
630 CallOperator::from_str("get_magic_number").unwrap(),
631 vec![],
632 vec![Register::Locator(0)],
633 );
634
635 check_parser("call noop", CallOperator::from_str("noop").unwrap(), vec![], vec![])
636 }
637
638 #[test]
639 fn test_display() {
640 for expected in TEST_CASES {
641 assert_eq!(Call::<CurrentNetwork>::from_str(expected).unwrap().to_string(), *expected);
642 }
643 }
644
645 #[test]
646 fn test_bytes() {
647 for case in TEST_CASES {
648 let expected = Call::<CurrentNetwork>::from_str(case).unwrap();
649
650 let expected_bytes = expected.to_bytes_le().unwrap();
652 assert_eq!(expected, Call::read_le(&expected_bytes[..]).unwrap());
653 }
654 }
655}