1use crate::{Opcode, Operand, RegistersCircuit, RegistersTrait, StackTrait};
17use console::{
18 network::prelude::*,
19 program::{Identifier, Locator, Register, RegisterType, ValueType},
20};
21
22#[derive(Clone, PartialEq, Eq, Hash)]
24pub enum CallOperator<N: Network> {
25 Locator(Locator<N>),
27 Resource(Identifier<N>),
29}
30
31impl<N: Network> Parser for CallOperator<N> {
32 #[inline]
34 fn parse(string: &str) -> ParserResult<Self> {
35 alt((map(Locator::parse, CallOperator::Locator), map(Identifier::parse, CallOperator::Resource)))(string)
36 }
37}
38
39impl<N: Network> FromStr for CallOperator<N> {
40 type Err = Error;
41
42 #[inline]
44 fn from_str(string: &str) -> Result<Self> {
45 match Self::parse(string) {
46 Ok((remainder, object)) => {
47 ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
49 Ok(object)
51 }
52 Err(error) => bail!("Failed to parse string. {error}"),
53 }
54 }
55}
56
57impl<N: Network> Debug for CallOperator<N> {
58 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
60 Display::fmt(self, f)
61 }
62}
63
64impl<N: Network> Display for CallOperator<N> {
65 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
67 match self {
68 CallOperator::Locator(locator) => Display::fmt(locator, f),
69 CallOperator::Resource(resource) => Display::fmt(resource, f),
70 }
71 }
72}
73
74impl<N: Network> FromBytes for CallOperator<N> {
75 fn read_le<R: Read>(mut reader: R) -> IoResult<Self> {
77 let variant = u8::read_le(&mut reader)?;
79 match variant {
81 0 => Ok(CallOperator::Locator(Locator::read_le(&mut reader)?)),
82 1 => Ok(CallOperator::Resource(Identifier::read_le(&mut reader)?)),
83 _ => Err(error("Failed to read CallOperator. Invalid variant.")),
84 }
85 }
86}
87
88impl<N: Network> ToBytes for CallOperator<N> {
89 fn write_le<W: Write>(&self, mut writer: W) -> IoResult<()> {
91 match self {
92 CallOperator::Locator(locator) => {
93 0u8.write_le(&mut writer)?;
95 locator.write_le(&mut writer)
97 }
98 CallOperator::Resource(resource) => {
99 1u8.write_le(&mut writer)?;
101 resource.write_le(&mut writer)
103 }
104 }
105 }
106}
107
108#[derive(Clone, PartialEq, Eq, Hash)]
111pub struct Call<N: Network> {
112 operator: CallOperator<N>,
114 operands: Vec<Operand<N>>,
116 destinations: Vec<Register<N>>,
118}
119
120impl<N: Network> Call<N> {
121 #[inline]
123 pub const fn opcode() -> Opcode {
124 Opcode::Call
125 }
126
127 #[inline]
129 pub const fn operator(&self) -> &CallOperator<N> {
130 &self.operator
131 }
132
133 #[inline]
135 pub fn operands(&self) -> &[Operand<N>] {
136 &self.operands
137 }
138
139 #[inline]
141 pub fn destinations(&self) -> Vec<Register<N>> {
142 self.destinations.clone()
143 }
144
145 #[inline]
147 pub fn is_function_call(&self, stack: &impl StackTrait<N>) -> Result<bool> {
148 match self.operator() {
149 CallOperator::Locator(locator) => {
151 let external_stack = stack.get_external_stack(locator.program_id())?;
153 let program = external_stack.program();
155 Ok(program.contains_function(locator.resource()))
157 }
158 CallOperator::Resource(resource) => Ok(stack.program().contains_function(resource)),
160 }
161 }
162
163 pub fn evaluate(&self, _stack: &impl StackTrait<N>, _registers: &mut impl RegistersTrait<N>) -> Result<()> {
165 bail!("Forbidden operation: Evaluate cannot invoke a 'call' directly. Use 'call' in 'Stack' instead.")
166 }
167
168 pub fn execute<A: circuit::Aleo<Network = N>>(
170 &self,
171 _stack: &impl StackTrait<N>,
172 _registers: &mut impl RegistersCircuit<N, A>,
173 ) -> Result<()> {
174 bail!("Forbidden operation: Execute cannot invoke a 'call' directly. Use 'call' in 'Stack' instead.")
175 }
176
177 #[inline]
179 pub fn finalize(&self, _stack: &impl StackTrait<N>, _registers: &mut impl RegistersTrait<N>) -> Result<()> {
180 bail!("Forbidden operation: Finalize cannot invoke a 'call' directly. Use 'call' in 'Stack' instead.")
181 }
182
183 #[inline]
185 pub fn output_types(
186 &self,
187 stack: &impl StackTrait<N>,
188 input_types: &[RegisterType<N>],
189 ) -> Result<Vec<RegisterType<N>>> {
190 let (external_stack, resource) = match &self.operator {
192 CallOperator::Locator(locator) => {
193 (Some(stack.get_external_stack(locator.program_id())?), locator.resource())
194 }
195 CallOperator::Resource(resource) => {
196 if stack.program().contains_function(resource) {
200 bail!("Cannot call '{resource}'. Use a closure ('closure {resource}:') instead.")
201 }
202 (None, resource)
203 }
204 };
205 let (is_external, program) = match &external_stack {
207 Some(external_stack) => (true, external_stack.program()),
208 None => (false, stack.program()),
209 };
210 if let Ok(closure) = program.get_closure(resource) {
212 if closure.inputs().len() != self.operands.len() {
214 bail!("Expected {} inputs, found {}", closure.inputs().len(), self.operands.len())
215 }
216 if closure.inputs().len() != input_types.len() {
218 bail!("Expected {} input types, found {}", closure.inputs().len(), input_types.len())
219 }
220 if closure.outputs().len() != self.destinations.len() {
222 bail!("Expected {} outputs, found {}", closure.outputs().len(), self.destinations.len())
223 }
224 Ok(closure.outputs().iter().map(|output| output.register_type()).cloned().collect())
226 }
227 else if let Ok(function) = program.get_function(resource) {
229 if function.inputs().len() != self.operands.len() {
231 bail!("Expected {} inputs, found {}", function.inputs().len(), self.operands.len())
232 }
233 if function.inputs().len() != input_types.len() {
235 bail!("Expected {} input types, found {}", function.inputs().len(), input_types.len())
236 }
237 if function.outputs().len() != self.destinations.len() {
239 bail!("Expected {} outputs, found {}", function.outputs().len(), self.destinations.len())
240 }
241 function
243 .output_types()
244 .into_iter()
245 .map(|output_type| match (is_external, output_type) {
246 (true, ValueType::Record(record_name)) => Ok(RegisterType::ExternalRecord(Locator::from_str(
248 &format!("{}/{}", program.id(), record_name),
249 )?)),
250 (_, output_type) => Ok(RegisterType::from(output_type)),
252 })
253 .collect::<Result<Vec<_>>>()
254 }
255 else {
257 bail!("Call operator '{}' is invalid or unsupported.", self.operator)
258 }
259 }
260}
261
262impl<N: Network> Parser for Call<N> {
263 #[inline]
265 fn parse(string: &str) -> ParserResult<Self> {
266 fn parse_operand<N: Network>(string: &str) -> ParserResult<Operand<N>> {
268 let (string, _) = Sanitizer::parse_whitespaces(string)?;
270 Operand::parse(string)
272 }
273
274 fn parse_destination<N: Network>(string: &str) -> ParserResult<Register<N>> {
276 let (string, _) = Sanitizer::parse_whitespaces(string)?;
278 Register::parse(string)
280 }
281
282 let (string, _) = tag(*Self::opcode())(string)?;
284 let (string, _) = Sanitizer::parse_whitespaces(string)?;
286 let (string, operator) = CallOperator::parse(string)?;
288 let (string, _) = Sanitizer::parse_whitespaces(string)?;
290 let (string, operands) = map_res(many0(complete(parse_operand)), |operands: Vec<Operand<N>>| {
292 match operands.len() <= N::MAX_OPERANDS {
294 true => Ok(operands),
295 false => Err(error("Failed to parse 'call' opcode: too many operands")),
296 }
297 })(string)?;
298 let (string, _) = Sanitizer::parse_whitespaces(string)?;
300
301 let (string, destinations) = match opt(tag("into"))(string)? {
303 (string, None) => (string, vec![]),
305 (string, Some(_)) => {
307 let (string, _) = Sanitizer::parse_whitespaces(string)?;
309 let (string, destinations) =
311 map_res(many1(complete(parse_destination)), |destinations: Vec<Register<N>>| {
312 match destinations.len() <= N::MAX_OPERANDS {
314 true => Ok(destinations),
315 false => Err(error("Failed to parse 'call' opcode: too many destinations")),
316 }
317 })(string)?;
318 (string, destinations)
320 }
321 };
322
323 Ok((string, Self { operator, operands, destinations }))
324 }
325}
326
327impl<N: Network> FromStr for Call<N> {
328 type Err = Error;
329
330 #[inline]
332 fn from_str(string: &str) -> Result<Self> {
333 match Self::parse(string) {
334 Ok((remainder, object)) => {
335 ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
337 Ok(object)
339 }
340 Err(error) => bail!("Failed to parse string. {error}"),
341 }
342 }
343}
344
345impl<N: Network> Debug for Call<N> {
346 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
348 Display::fmt(self, f)
349 }
350}
351
352impl<N: Network> Display for Call<N> {
353 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
355 if self.operands.len() > N::MAX_OPERANDS {
357 return Err(fmt::Error);
358 }
359 if self.destinations.len() > N::MAX_OPERANDS {
361 return Err(fmt::Error);
362 }
363 write!(f, "{} {}", Self::opcode(), self.operator)?;
365 self.operands.iter().try_for_each(|operand| write!(f, " {operand}"))?;
366 if !self.destinations.is_empty() {
367 write!(f, " into")?;
368 self.destinations.iter().try_for_each(|destination| write!(f, " {destination}"))?;
369 }
370 Ok(())
371 }
372}
373
374impl<N: Network> FromBytes for Call<N> {
375 fn read_le<R: Read>(mut reader: R) -> IoResult<Self> {
377 let operator = CallOperator::read_le(&mut reader)?;
379
380 let num_operands = u8::read_le(&mut reader)? as usize;
382 if num_operands > N::MAX_OPERANDS {
384 return Err(error(format!("The number of operands must be <= {}", N::MAX_OPERANDS)));
385 }
386
387 let mut operands = Vec::with_capacity(num_operands);
389 for _ in 0..num_operands {
391 operands.push(Operand::read_le(&mut reader)?);
392 }
393
394 let num_destinations = u8::read_le(&mut reader)? as usize;
396 if num_destinations > N::MAX_OPERANDS {
398 return Err(error(format!("The number of destinations must be <= {}", N::MAX_OPERANDS)));
399 }
400
401 let mut destinations = Vec::with_capacity(num_destinations);
403 for _ in 0..num_destinations {
405 destinations.push(Register::read_le(&mut reader)?);
406 }
407
408 Ok(Self { operator, operands, destinations })
410 }
411}
412
413impl<N: Network> ToBytes for Call<N> {
414 fn write_le<W: Write>(&self, mut writer: W) -> IoResult<()> {
416 if self.operands.len() > N::MAX_OPERANDS {
418 return Err(error(format!("The number of operands must be <= {}", N::MAX_OPERANDS)));
419 }
420 if self.destinations.len() > N::MAX_OPERANDS {
422 return Err(error(format!("The number of destinations must be <= {}", N::MAX_OPERANDS)));
423 }
424
425 self.operator.write_le(&mut writer)?;
427 u8::try_from(self.operands.len()).map_err(|e| error(e.to_string()))?.write_le(&mut writer)?;
429 self.operands.iter().try_for_each(|operand| operand.write_le(&mut writer))?;
431 u8::try_from(self.destinations.len()).map_err(|e| error(e.to_string()))?.write_le(&mut writer)?;
433 self.destinations.iter().try_for_each(|destination| destination.write_le(&mut writer))
435 }
436}
437
438#[cfg(test)]
439mod tests {
440 use super::*;
441 use console::{
442 network::MainnetV0,
443 program::{Access, Address, Identifier, Literal, U64},
444 };
445
446 type CurrentNetwork = MainnetV0;
447
448 const TEST_CASES: &[&str] = &[
449 "call foo",
450 "call foo r0",
451 "call foo r0.owner",
452 "call foo r0 r1",
453 "call foo into r0",
454 "call foo into r0 r1",
455 "call foo into r0 r1 r2",
456 "call foo r0 into r1",
457 "call foo r0 r1 into r2",
458 "call foo r0 r1 into r2 r3",
459 "call foo r0 r1 r2 into r3 r4",
460 "call foo r0 r1 r2 into r3 r4 r5",
461 ];
462
463 fn check_parser(
464 string: &str,
465 expected_operator: CallOperator<CurrentNetwork>,
466 expected_operands: Vec<Operand<CurrentNetwork>>,
467 expected_destinations: Vec<Register<CurrentNetwork>>,
468 ) {
469 let (string, call) = Call::<CurrentNetwork>::parse(string).unwrap();
471
472 assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
474
475 assert_eq!(call.operator, expected_operator, "The call operator is incorrect");
477
478 assert_eq!(call.operands.len(), expected_operands.len(), "The number of operands is incorrect");
480 for (i, (given, expected)) in call.operands.iter().zip(expected_operands.iter()).enumerate() {
481 assert_eq!(given, expected, "The {i}-th operand is incorrect");
482 }
483
484 assert_eq!(call.destinations.len(), expected_destinations.len(), "The number of destinations is incorrect");
486 for (i, (given, expected)) in call.destinations.iter().zip(expected_destinations.iter()).enumerate() {
487 assert_eq!(given, expected, "The {i}-th destination is incorrect");
488 }
489 }
490
491 #[test]
492 fn test_parse() {
493 check_parser(
494 "call transfer r0.owner r0.token_amount into r1 r2 r3",
495 CallOperator::from_str("transfer").unwrap(),
496 vec![
497 Operand::Register(Register::Access(0, vec![Access::from(Identifier::from_str("owner").unwrap())])),
498 Operand::Register(Register::Access(0, vec![Access::from(
499 Identifier::from_str("token_amount").unwrap(),
500 )])),
501 ],
502 vec![Register::Locator(1), Register::Locator(2), Register::Locator(3)],
503 );
504
505 check_parser(
506 "call mint_public aleo1wfyyj2uvwuqw0c0dqa5x70wrawnlkkvuepn4y08xyaqfqqwweqys39jayw 100u64",
507 CallOperator::from_str("mint_public").unwrap(),
508 vec![
509 Operand::Literal(Literal::Address(
510 Address::from_str("aleo1wfyyj2uvwuqw0c0dqa5x70wrawnlkkvuepn4y08xyaqfqqwweqys39jayw").unwrap(),
511 )),
512 Operand::Literal(Literal::U64(U64::from_str("100u64").unwrap())),
513 ],
514 vec![],
515 );
516
517 check_parser(
518 "call get_magic_number into r0",
519 CallOperator::from_str("get_magic_number").unwrap(),
520 vec![],
521 vec![Register::Locator(0)],
522 );
523
524 check_parser("call noop", CallOperator::from_str("noop").unwrap(), vec![], vec![])
525 }
526
527 #[test]
528 fn test_display() {
529 for expected in TEST_CASES {
530 assert_eq!(Call::<CurrentNetwork>::from_str(expected).unwrap().to_string(), *expected);
531 }
532 }
533
534 #[test]
535 fn test_bytes() {
536 for case in TEST_CASES {
537 let expected = Call::<CurrentNetwork>::from_str(case).unwrap();
538
539 let expected_bytes = expected.to_bytes_le().unwrap();
541 assert_eq!(expected, Call::read_le(&expected_bytes[..]).unwrap());
542 }
543 }
544}