use super::*;
impl<N: Network> FromBytes for Future<N> {
fn read_le<R: Read>(mut reader: R) -> IoResult<Self> {
Self::read_le_internal(&mut reader, 0)
}
}
impl<N: Network> Future<N> {
fn read_le_internal<R: Read>(mut reader: R, depth: usize) -> IoResult<Self> {
if depth > N::MAX_DATA_DEPTH {
return Err(error(format!(
"Failed to deserialize plaintext: Depth exceeds maximum limit: {}",
N::MAX_DATA_DEPTH
)));
}
let program_id = ProgramID::read_le(&mut reader)?;
let function_name = Identifier::<N>::read_le(&mut reader)?;
let num_arguments = u8::read_le(&mut reader)? as usize;
if num_arguments > N::MAX_INPUTS {
return Err(error("Failed to read future: too many arguments"));
};
let mut arguments = Vec::with_capacity(num_arguments);
for _ in 0..num_arguments {
let num_bytes = u16::read_le(&mut reader)?;
let mut bytes = Vec::new();
(&mut reader).take(num_bytes as u64).read_to_end(&mut bytes)?;
let entry = Argument::read_le_internal(&mut bytes.as_slice(), depth)?;
arguments.push(entry);
}
Ok(Self::new(program_id, function_name, arguments))
}
}
impl<N: Network> ToBytes for Future<N> {
fn write_le<W: Write>(&self, mut writer: W) -> IoResult<()> {
self.program_id.write_le(&mut writer)?;
self.function_name.write_le(&mut writer)?;
if self.arguments.len() > N::MAX_INPUTS {
return Err(error("Failed to write future: too many arguments"));
};
u8::try_from(self.arguments.len()).map_err(error)?.write_le(&mut writer)?;
for argument in &self.arguments {
let bytes = argument.to_bytes_le().map_err(error)?;
u16::try_from(bytes.len()).map_err(error)?.write_le(&mut writer)?;
bytes.write_le(&mut writer)?;
}
Ok(())
}
}
impl<N: Network> Argument<N> {
fn read_le_internal<R: Read>(mut reader: R, depth: usize) -> IoResult<Self>
where
Self: Sized,
{
let index = u8::read_le(&mut reader)?;
let argument = match index {
0 => Self::Plaintext(Plaintext::read_le(&mut reader)?),
1 => Self::Future(Future::read_le_internal(&mut reader, depth + 1)?),
2 => Self::DynamicFuture(DynamicFuture::read_le(&mut reader)?),
3.. => return Err(error(format!("Failed to decode future argument {index}"))),
};
Ok(argument)
}
}
impl<N: Network> ToBytes for Argument<N> {
fn write_le<W: Write>(&self, mut writer: W) -> IoResult<()> {
match self {
Self::Plaintext(plaintext) => {
0u8.write_le(&mut writer)?;
plaintext.write_le(&mut writer)
}
Self::Future(future) => {
1u8.write_le(&mut writer)?;
future.write_le(&mut writer)
}
Self::DynamicFuture(dynamic_future) => {
2u8.write_le(&mut writer)?;
dynamic_future.write_le(&mut writer)
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use snarkvm_console_network::MainnetV0;
use core::str::FromStr;
type CurrentNetwork = MainnetV0;
#[test]
fn test_bytes() {
let expected =
Future::<CurrentNetwork>::from_str("{ program_id: credits.aleo, function_name: transfer, arguments: [] }")
.unwrap();
let expected_bytes = expected.to_bytes_le().unwrap();
assert_eq!(expected, Future::read_le(&expected_bytes[..]).unwrap());
}
#[test]
fn test_bytes_with_plaintext_arguments() {
let expected = Future::<CurrentNetwork>::new(
ProgramID::from_str("test.aleo").unwrap(),
Identifier::from_str("foo").unwrap(),
vec![
Argument::Plaintext(Plaintext::from_str("100u64").unwrap()),
Argument::Plaintext(Plaintext::from_str("true").unwrap()),
],
);
let expected_bytes = expected.to_bytes_le().unwrap();
let candidate = Future::read_le(&expected_bytes[..]).unwrap();
assert_eq!(expected, candidate);
}
#[test]
fn test_bytes_with_dynamic_future_argument() {
let inner = Future::<CurrentNetwork>::new(
ProgramID::from_str("inner.aleo").unwrap(),
Identifier::from_str("bar").unwrap(),
vec![Argument::Plaintext(Plaintext::from_str("42u64").unwrap())],
);
let dynamic_inner = DynamicFuture::from_future(&inner).unwrap();
let expected = Future::<CurrentNetwork>::new(
ProgramID::from_str("outer.aleo").unwrap(),
Identifier::from_str("baz").unwrap(),
vec![Argument::DynamicFuture(dynamic_inner)],
);
let expected_bytes = expected.to_bytes_le().unwrap();
let candidate = Future::read_le(&expected_bytes[..]).unwrap();
assert_eq!(expected.program_id(), candidate.program_id());
assert_eq!(expected.function_name(), candidate.function_name());
assert_eq!(expected.arguments().len(), candidate.arguments().len());
match (&expected.arguments()[0], &candidate.arguments()[0]) {
(Argument::DynamicFuture(e), Argument::DynamicFuture(c)) => {
assert_eq!(e.program_name(), c.program_name());
assert_eq!(e.program_network(), c.program_network());
assert_eq!(e.function_name(), c.function_name());
assert_eq!(e.checksum(), c.checksum());
}
_ => panic!("Expected DynamicFuture argument"),
}
}
#[test]
fn test_bytes_with_nested_future_argument() {
let inner = Future::<CurrentNetwork>::new(
ProgramID::from_str("inner.aleo").unwrap(),
Identifier::from_str("bar").unwrap(),
vec![Argument::Plaintext(Plaintext::from_str("42u64").unwrap())],
);
let expected = Future::<CurrentNetwork>::new(
ProgramID::from_str("outer.aleo").unwrap(),
Identifier::from_str("baz").unwrap(),
vec![Argument::Future(inner)],
);
let expected_bytes = expected.to_bytes_le().unwrap();
let candidate = Future::read_le(&expected_bytes[..]).unwrap();
assert_eq!(expected, candidate);
}
#[test]
fn test_bytes_with_mixed_arguments() {
let inner = Future::<CurrentNetwork>::new(
ProgramID::from_str("inner.aleo").unwrap(),
Identifier::from_str("bar").unwrap(),
vec![],
);
let dynamic_inner = DynamicFuture::from_future(&inner).unwrap();
let expected = Future::<CurrentNetwork>::new(
ProgramID::from_str("test.aleo").unwrap(),
Identifier::from_str("mixed").unwrap(),
vec![
Argument::Plaintext(Plaintext::from_str("100u64").unwrap()),
Argument::Future(inner),
Argument::DynamicFuture(dynamic_inner),
],
);
let expected_bytes = expected.to_bytes_le().unwrap();
let candidate = Future::read_le(&expected_bytes[..]).unwrap();
assert_eq!(expected.program_id(), candidate.program_id());
assert_eq!(expected.function_name(), candidate.function_name());
assert_eq!(expected.arguments().len(), candidate.arguments().len());
}
fn get_depth<N: Network>(future: &Future<N>) -> usize {
future
.arguments
.iter()
.map(|arg| match arg {
Argument::Plaintext(_) => 0,
Argument::Future(future) => 1 + get_depth(future),
Argument::DynamicFuture(_) => panic!("Dynamic futures are not used in this test"),
})
.sum()
}
#[test]
fn test_deeply_nested_future() {
fn create_nested_future(depth: usize) -> Vec<u8> {
let mut result = Future::<CurrentNetwork>::from_str(
r"{
program_id: foo.aleo,
function_name: bar,
arguments: []
}",
)
.unwrap()
.to_bytes_le()
.unwrap();
result.reverse();
for _ in 0..depth {
let mut variant = 1u8.to_bytes_le().unwrap();
variant.reverse();
result.extend(variant);
let mut length = (u16::try_from(result.len()).unwrap()).to_bytes_le().unwrap();
length.reverse();
result.extend(length);
let mut num_elements = 1u8.to_bytes_le().unwrap();
num_elements.reverse();
result.extend(num_elements);
let mut function_name = Identifier::<CurrentNetwork>::from_str("bar").unwrap().to_bytes_le().unwrap();
function_name.reverse();
result.extend(function_name);
let mut program_id = ProgramID::<CurrentNetwork>::from_str("foo.aleo").unwrap().to_bytes_le().unwrap();
program_id.reverse();
result.extend(program_id);
}
result.reverse();
result
}
fn run_test(expected_depth: usize, input: Vec<u8>, expected_error: bool) {
let result = Future::<CurrentNetwork>::read_le(&*input);
match expected_error {
true => {
assert!(result.is_err());
return;
}
false => assert!(result.is_ok()),
};
let candidate = result.unwrap();
assert_eq!(input, candidate.to_bytes_le().unwrap());
assert_eq!(get_depth(&candidate), expected_depth);
}
let mut depths = (0usize..100).collect_vec();
depths.extend((100..3900).step_by(100));
for i in depths.iter().copied() {
run_test(i, create_nested_future(i), i > CurrentNetwork::MAX_DATA_DEPTH);
run_test(i, create_nested_future(i), i > CurrentNetwork::MAX_DATA_DEPTH);
run_test(i, create_nested_future(i), i > CurrentNetwork::MAX_DATA_DEPTH);
run_test(i, create_nested_future(i), i > CurrentNetwork::MAX_DATA_DEPTH);
}
}
}