1use crate::{
17 Opcode,
18 Operand,
19 traits::{RegistersLoad, RegistersLoadCircuit, StackMatches, StackProgram},
20};
21use console::{
22 network::prelude::*,
23 program::{Identifier, Locator, Register, RegisterType, ValueType},
24};
25
26#[derive(Clone, PartialEq, Eq, Hash)]
28pub enum CallOperator<N: Network> {
29 Locator(Locator<N>),
31 Resource(Identifier<N>),
33}
34
35impl<N: Network> Parser for CallOperator<N> {
36 #[inline]
38 fn parse(string: &str) -> ParserResult<Self> {
39 alt((map(Locator::parse, CallOperator::Locator), map(Identifier::parse, CallOperator::Resource)))(string)
40 }
41}
42
43impl<N: Network> FromStr for CallOperator<N> {
44 type Err = Error;
45
46 #[inline]
48 fn from_str(string: &str) -> Result<Self> {
49 match Self::parse(string) {
50 Ok((remainder, object)) => {
51 ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
53 Ok(object)
55 }
56 Err(error) => bail!("Failed to parse string. {error}"),
57 }
58 }
59}
60
61impl<N: Network> Debug for CallOperator<N> {
62 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
64 Display::fmt(self, f)
65 }
66}
67
68impl<N: Network> Display for CallOperator<N> {
69 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
71 match self {
72 CallOperator::Locator(locator) => Display::fmt(locator, f),
73 CallOperator::Resource(resource) => Display::fmt(resource, f),
74 }
75 }
76}
77
78impl<N: Network> FromBytes for CallOperator<N> {
79 fn read_le<R: Read>(mut reader: R) -> IoResult<Self> {
81 let variant = u8::read_le(&mut reader)?;
83 match variant {
85 0 => Ok(CallOperator::Locator(Locator::read_le(&mut reader)?)),
86 1 => Ok(CallOperator::Resource(Identifier::read_le(&mut reader)?)),
87 _ => Err(error("Failed to read CallOperator. Invalid variant.")),
88 }
89 }
90}
91
92impl<N: Network> ToBytes for CallOperator<N> {
93 fn write_le<W: Write>(&self, mut writer: W) -> IoResult<()> {
95 match self {
96 CallOperator::Locator(locator) => {
97 0u8.write_le(&mut writer)?;
99 locator.write_le(&mut writer)
101 }
102 CallOperator::Resource(resource) => {
103 1u8.write_le(&mut writer)?;
105 resource.write_le(&mut writer)
107 }
108 }
109 }
110}
111
112#[derive(Clone, PartialEq, Eq, Hash)]
115pub struct Call<N: Network> {
116 operator: CallOperator<N>,
118 operands: Vec<Operand<N>>,
120 destinations: Vec<Register<N>>,
122}
123
124impl<N: Network> Call<N> {
125 #[inline]
127 pub const fn opcode() -> Opcode {
128 Opcode::Call
129 }
130
131 #[inline]
133 pub const fn operator(&self) -> &CallOperator<N> {
134 &self.operator
135 }
136
137 #[inline]
139 pub fn operands(&self) -> &[Operand<N>] {
140 &self.operands
141 }
142
143 #[inline]
145 pub fn destinations(&self) -> Vec<Register<N>> {
146 self.destinations.clone()
147 }
148}
149
150impl<N: Network> Call<N> {
151 #[inline]
153 pub fn is_function_call(&self, stack: &impl StackProgram<N>) -> Result<bool> {
154 match self.operator() {
155 CallOperator::Locator(locator) => {
157 let external_stack = stack.get_external_stack(locator.program_id())?;
159 let program = external_stack.program();
161 Ok(program.contains_function(locator.resource()))
163 }
164 CallOperator::Resource(resource) => Ok(stack.program().contains_function(resource)),
166 }
167 }
168
169 pub fn evaluate(&self, _stack: &impl StackProgram<N>, _registers: &mut impl RegistersLoad<N>) -> Result<()> {
171 bail!("Forbidden operation: Evaluate cannot invoke a 'call' directly. Use 'call' in 'Stack' instead.")
172 }
173
174 pub fn execute<A: circuit::Aleo<Network = N>>(
176 &self,
177 _stack: &impl StackProgram<N>,
178 _registers: &mut impl RegistersLoadCircuit<N, A>,
179 ) -> Result<()> {
180 bail!("Forbidden operation: Execute cannot invoke a 'call' directly. Use 'call' in 'Stack' instead.")
181 }
182
183 #[inline]
185 pub fn finalize(
186 &self,
187 _stack: &(impl StackMatches<N> + StackProgram<N>),
188 _registers: &mut impl RegistersLoad<N>,
189 ) -> Result<()> {
190 bail!("Forbidden operation: Finalize cannot invoke a 'call' directly. Use 'call' in 'Stack' instead.")
191 }
192
193 #[inline]
195 pub fn output_types(
196 &self,
197 stack: &impl StackProgram<N>,
198 input_types: &[RegisterType<N>],
199 ) -> Result<Vec<RegisterType<N>>> {
200 let (external_stack, resource) = match &self.operator {
202 CallOperator::Locator(locator) => {
203 (Some(stack.get_external_stack(locator.program_id())?), locator.resource())
204 }
205 CallOperator::Resource(resource) => {
206 if stack.program().contains_function(resource) {
210 bail!("Cannot call '{resource}'. Use a closure ('closure {resource}:') instead.")
211 }
212 (None, resource)
213 }
214 };
215 let (is_external, program) = match &external_stack {
217 Some(external_stack) => (true, external_stack.program()),
218 None => (false, stack.program()),
219 };
220 if let Ok(closure) = program.get_closure(resource) {
222 if closure.inputs().len() != self.operands.len() {
224 bail!("Expected {} inputs, found {}", closure.inputs().len(), self.operands.len())
225 }
226 if closure.inputs().len() != input_types.len() {
228 bail!("Expected {} input types, found {}", closure.inputs().len(), input_types.len())
229 }
230 if closure.outputs().len() != self.destinations.len() {
232 bail!("Expected {} outputs, found {}", closure.outputs().len(), self.destinations.len())
233 }
234 Ok(closure.outputs().iter().map(|output| output.register_type()).cloned().collect())
236 }
237 else if let Ok(function) = program.get_function(resource) {
239 if function.inputs().len() != self.operands.len() {
241 bail!("Expected {} inputs, found {}", function.inputs().len(), self.operands.len())
242 }
243 if function.inputs().len() != input_types.len() {
245 bail!("Expected {} input types, found {}", function.inputs().len(), input_types.len())
246 }
247 if function.outputs().len() != self.destinations.len() {
249 bail!("Expected {} outputs, found {}", function.outputs().len(), self.destinations.len())
250 }
251 function
253 .output_types()
254 .into_iter()
255 .map(|output_type| match (is_external, output_type) {
256 (true, ValueType::Record(record_name)) => Ok(RegisterType::ExternalRecord(Locator::from_str(
258 &format!("{}/{}", program.id(), record_name),
259 )?)),
260 (_, output_type) => Ok(RegisterType::from(output_type)),
262 })
263 .collect::<Result<Vec<_>>>()
264 }
265 else {
267 bail!("Call operator '{}' is invalid or unsupported.", self.operator)
268 }
269 }
270}
271
272impl<N: Network> Parser for Call<N> {
273 #[inline]
275 fn parse(string: &str) -> ParserResult<Self> {
276 fn parse_operand<N: Network>(string: &str) -> ParserResult<Operand<N>> {
278 let (string, _) = Sanitizer::parse_whitespaces(string)?;
280 Operand::parse(string)
282 }
283
284 fn parse_destination<N: Network>(string: &str) -> ParserResult<Register<N>> {
286 let (string, _) = Sanitizer::parse_whitespaces(string)?;
288 Register::parse(string)
290 }
291
292 let (string, _) = tag(*Self::opcode())(string)?;
294 let (string, _) = Sanitizer::parse_whitespaces(string)?;
296 let (string, operator) = CallOperator::parse(string)?;
298 let (string, _) = Sanitizer::parse_whitespaces(string)?;
300 let (string, operands) = map_res(many0(complete(parse_operand)), |operands: Vec<Operand<N>>| {
302 match operands.len() <= N::MAX_OPERANDS {
304 true => Ok(operands),
305 false => Err(error("Failed to parse 'call' opcode: too many operands")),
306 }
307 })(string)?;
308 let (string, _) = Sanitizer::parse_whitespaces(string)?;
310
311 let (string, destinations) = match opt(tag("into"))(string)? {
313 (string, None) => (string, vec![]),
315 (string, Some(_)) => {
317 let (string, _) = Sanitizer::parse_whitespaces(string)?;
319 let (string, destinations) =
321 map_res(many1(complete(parse_destination)), |destinations: Vec<Register<N>>| {
322 match destinations.len() <= N::MAX_OPERANDS {
324 true => Ok(destinations),
325 false => Err(error("Failed to parse 'call' opcode: too many destinations")),
326 }
327 })(string)?;
328 (string, destinations)
330 }
331 };
332
333 Ok((string, Self { operator, operands, destinations }))
334 }
335}
336
337impl<N: Network> FromStr for Call<N> {
338 type Err = Error;
339
340 #[inline]
342 fn from_str(string: &str) -> Result<Self> {
343 match Self::parse(string) {
344 Ok((remainder, object)) => {
345 ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
347 Ok(object)
349 }
350 Err(error) => bail!("Failed to parse string. {error}"),
351 }
352 }
353}
354
355impl<N: Network> Debug for Call<N> {
356 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
358 Display::fmt(self, f)
359 }
360}
361
362impl<N: Network> Display for Call<N> {
363 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
365 if self.operands.len() > N::MAX_OPERANDS {
367 return Err(fmt::Error);
368 }
369 if self.destinations.len() > N::MAX_OPERANDS {
371 return Err(fmt::Error);
372 }
373 write!(f, "{} {}", Self::opcode(), self.operator)?;
375 self.operands.iter().try_for_each(|operand| write!(f, " {operand}"))?;
376 if !self.destinations.is_empty() {
377 write!(f, " into")?;
378 self.destinations.iter().try_for_each(|destination| write!(f, " {destination}"))?;
379 }
380 Ok(())
381 }
382}
383
384impl<N: Network> FromBytes for Call<N> {
385 fn read_le<R: Read>(mut reader: R) -> IoResult<Self> {
387 let operator = CallOperator::read_le(&mut reader)?;
389
390 let num_operands = u8::read_le(&mut reader)? as usize;
392 if num_operands > N::MAX_OPERANDS {
394 return Err(error(format!("The number of operands must be <= {}", N::MAX_OPERANDS)));
395 }
396
397 let mut operands = Vec::with_capacity(num_operands);
399 for _ in 0..num_operands {
401 operands.push(Operand::read_le(&mut reader)?);
402 }
403
404 let num_destinations = u8::read_le(&mut reader)? as usize;
406 if num_destinations > N::MAX_OPERANDS {
408 return Err(error(format!("The number of destinations must be <= {}", N::MAX_OPERANDS)));
409 }
410
411 let mut destinations = Vec::with_capacity(num_destinations);
413 for _ in 0..num_destinations {
415 destinations.push(Register::read_le(&mut reader)?);
416 }
417
418 Ok(Self { operator, operands, destinations })
420 }
421}
422
423impl<N: Network> ToBytes for Call<N> {
424 fn write_le<W: Write>(&self, mut writer: W) -> IoResult<()> {
426 if self.operands.len() > N::MAX_OPERANDS {
428 return Err(error(format!("The number of operands must be <= {}", N::MAX_OPERANDS)));
429 }
430 if self.destinations.len() > N::MAX_OPERANDS {
432 return Err(error(format!("The number of destinations must be <= {}", N::MAX_OPERANDS)));
433 }
434
435 self.operator.write_le(&mut writer)?;
437 u8::try_from(self.operands.len()).map_err(|e| error(e.to_string()))?.write_le(&mut writer)?;
439 self.operands.iter().try_for_each(|operand| operand.write_le(&mut writer))?;
441 u8::try_from(self.destinations.len()).map_err(|e| error(e.to_string()))?.write_le(&mut writer)?;
443 self.destinations.iter().try_for_each(|destination| destination.write_le(&mut writer))
445 }
446}
447
448#[cfg(test)]
449mod tests {
450 use super::*;
451 use console::{
452 network::MainnetV0,
453 program::{Access, Address, Identifier, Literal, U64},
454 };
455
456 type CurrentNetwork = MainnetV0;
457
458 const TEST_CASES: &[&str] = &[
459 "call foo",
460 "call foo r0",
461 "call foo r0.owner",
462 "call foo r0 r1",
463 "call foo into r0",
464 "call foo into r0 r1",
465 "call foo into r0 r1 r2",
466 "call foo r0 into r1",
467 "call foo r0 r1 into r2",
468 "call foo r0 r1 into r2 r3",
469 "call foo r0 r1 r2 into r3 r4",
470 "call foo r0 r1 r2 into r3 r4 r5",
471 ];
472
473 fn check_parser(
474 string: &str,
475 expected_operator: CallOperator<CurrentNetwork>,
476 expected_operands: Vec<Operand<CurrentNetwork>>,
477 expected_destinations: Vec<Register<CurrentNetwork>>,
478 ) {
479 let (string, call) = Call::<CurrentNetwork>::parse(string).unwrap();
481
482 assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
484
485 assert_eq!(call.operator, expected_operator, "The call operator is incorrect");
487
488 assert_eq!(call.operands.len(), expected_operands.len(), "The number of operands is incorrect");
490 for (i, (given, expected)) in call.operands.iter().zip(expected_operands.iter()).enumerate() {
491 assert_eq!(given, expected, "The {i}-th operand is incorrect");
492 }
493
494 assert_eq!(call.destinations.len(), expected_destinations.len(), "The number of destinations is incorrect");
496 for (i, (given, expected)) in call.destinations.iter().zip(expected_destinations.iter()).enumerate() {
497 assert_eq!(given, expected, "The {i}-th destination is incorrect");
498 }
499 }
500
501 #[test]
502 fn test_parse() {
503 check_parser(
504 "call transfer r0.owner r0.token_amount into r1 r2 r3",
505 CallOperator::from_str("transfer").unwrap(),
506 vec![
507 Operand::Register(Register::Access(0, vec![Access::from(Identifier::from_str("owner").unwrap())])),
508 Operand::Register(Register::Access(0, vec![Access::from(
509 Identifier::from_str("token_amount").unwrap(),
510 )])),
511 ],
512 vec![Register::Locator(1), Register::Locator(2), Register::Locator(3)],
513 );
514
515 check_parser(
516 "call mint_public aleo1wfyyj2uvwuqw0c0dqa5x70wrawnlkkvuepn4y08xyaqfqqwweqys39jayw 100u64",
517 CallOperator::from_str("mint_public").unwrap(),
518 vec![
519 Operand::Literal(Literal::Address(
520 Address::from_str("aleo1wfyyj2uvwuqw0c0dqa5x70wrawnlkkvuepn4y08xyaqfqqwweqys39jayw").unwrap(),
521 )),
522 Operand::Literal(Literal::U64(U64::from_str("100u64").unwrap())),
523 ],
524 vec![],
525 );
526
527 check_parser(
528 "call get_magic_number into r0",
529 CallOperator::from_str("get_magic_number").unwrap(),
530 vec![],
531 vec![Register::Locator(0)],
532 );
533
534 check_parser("call noop", CallOperator::from_str("noop").unwrap(), vec![], vec![])
535 }
536
537 #[test]
538 fn test_display() {
539 for expected in TEST_CASES {
540 assert_eq!(Call::<CurrentNetwork>::from_str(expected).unwrap().to_string(), *expected);
541 }
542 }
543
544 #[test]
545 fn test_bytes() {
546 for case in TEST_CASES {
547 let expected = Call::<CurrentNetwork>::from_str(case).unwrap();
548
549 let expected_bytes = expected.to_bytes_le().unwrap();
551 assert_eq!(expected, Call::read_le(&expected_bytes[..]).unwrap());
552 }
553 }
554}