use super::*;
#[derive(Clone, Debug)]
pub enum Argument<N: Network> {
Plaintext(Plaintext<N>),
Future(Future<N>),
DynamicFuture(DynamicFuture<N>),
}
impl<N: Network> Equal<Self> for Argument<N> {
type Output = Boolean<N>;
fn is_equal(&self, other: &Self) -> Self::Output {
match (self, other) {
(Self::Plaintext(a), Self::Plaintext(b)) => a.is_equal(b),
(Self::Future(a), Self::Future(b)) => a.is_equal(b),
(Self::DynamicFuture(a), Self::DynamicFuture(b)) => a.is_equal(b),
(Self::Plaintext(..), _) | (Self::Future(..), _) | (Self::DynamicFuture(..), _) => Boolean::new(false),
}
}
fn is_not_equal(&self, other: &Self) -> Self::Output {
match (self, other) {
(Self::Plaintext(a), Self::Plaintext(b)) => a.is_not_equal(b),
(Self::Future(a), Self::Future(b)) => a.is_not_equal(b),
(Self::DynamicFuture(a), Self::DynamicFuture(b)) => a.is_not_equal(b),
(Self::Plaintext(..), _) | (Self::Future(..), _) | (Self::DynamicFuture(..), _) => Boolean::new(true),
}
}
}
impl<N: Network> ToBits for Argument<N> {
#[inline]
fn write_bits_le(&self, vec: &mut Vec<bool>) {
match self {
Self::Plaintext(plaintext) => {
vec.push(false);
plaintext.write_bits_le(vec);
}
Self::Future(future) => {
vec.push(true);
future.write_bits_le(vec);
}
Self::DynamicFuture(dynamic_future) => {
vec.push(true);
vec.extend(std::iter::repeat_n(false, 12));
dynamic_future.write_bits_le(vec);
}
}
}
#[inline]
fn write_bits_be(&self, vec: &mut Vec<bool>) {
match self {
Self::Plaintext(plaintext) => {
vec.push(false);
plaintext.write_bits_be(vec);
}
Self::Future(future) => {
vec.push(true);
future.write_bits_be(vec);
}
Self::DynamicFuture(dynamic_future) => {
vec.push(true);
vec.extend(std::iter::repeat_n(false, 12));
dynamic_future.write_bits_be(vec);
}
}
}
}
impl<N: Network> ToFields for Argument<N> {
type Field = Field<N>;
fn to_fields(&self) -> Result<Vec<Self::Field>> {
let mut bits_le = self.to_bits_le();
bits_le.push(true);
let fields = bits_le
.chunks(Field::<N>::size_in_data_bits())
.map(Field::<N>::from_bits_le)
.collect::<Result<Vec<_>>>()?;
match fields.len() <= N::MAX_DATA_SIZE_IN_FIELDS as usize {
true => Ok(fields),
false => bail!("Argument exceeds maximum allowed size"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use snarkvm_console_network::MainnetV0;
use core::str::FromStr;
type CurrentNetwork = MainnetV0;
#[test]
fn test_plaintext_argument_bit_encoding() {
let plaintext = Plaintext::<CurrentNetwork>::from_str("42u64").unwrap();
let argument = Argument::Plaintext(plaintext);
let bits = argument.to_bits_le();
assert!(!bits[0], "Plaintext argument should start with false tag bit");
}
#[test]
fn test_future_argument_bit_encoding() {
let future = Future::<CurrentNetwork>::new(
ProgramID::from_str("test.aleo").unwrap(),
Identifier::from_str("foo").unwrap(),
vec![],
);
let argument = Argument::Future(future);
let bits = argument.to_bits_le();
assert!(bits[0], "Future argument should start with true tag bit");
let first_byte_bits = &bits[1..9];
assert!(first_byte_bits.iter().any(|&b| b), "Static future's program ID should not start with a zero byte");
}
#[test]
fn test_dynamic_future_argument_bit_encoding() {
let future = Future::<CurrentNetwork>::new(
ProgramID::from_str("test.aleo").unwrap(),
Identifier::from_str("foo").unwrap(),
vec![Argument::Plaintext(Plaintext::from_str("100u64").unwrap())],
);
let dynamic_future = DynamicFuture::from_future(&future).unwrap();
let argument = Argument::DynamicFuture(dynamic_future);
let bits = argument.to_bits_le();
assert!(bits[0], "DynamicFuture argument should start with true tag bit");
for (i, &bit) in bits[1..13].iter().enumerate() {
assert!(!bit, "DynamicFuture should have false bit at position {} (1-indexed: {})", i, i + 1);
}
}
#[test]
fn test_dynamic_future_distinguishable_from_static_future() {
let static_future = Future::<CurrentNetwork>::new(
ProgramID::from_str("test.aleo").unwrap(),
Identifier::from_str("foo").unwrap(),
vec![],
);
let static_argument = Argument::Future(static_future.clone());
let dynamic_future = DynamicFuture::from_future(&static_future).unwrap();
let dynamic_argument = Argument::DynamicFuture(dynamic_future);
let static_bits = static_argument.to_bits_le();
let dynamic_bits = dynamic_argument.to_bits_le();
assert!(static_bits[0], "Static future should start with true tag bit");
assert!(dynamic_bits[0], "Dynamic future should start with true tag bit");
let static_first_12 = &static_bits[1..13];
let dynamic_first_12 = &dynamic_bits[1..13];
assert!(
static_first_12[..8].iter().any(|&b| b),
"Static future should have non-zero first byte (program ID identifier)"
);
assert!(dynamic_first_12.iter().all(|&b| !b), "Dynamic future should have all-zero disambiguation prefix");
}
fn check_equality(
same1: &Argument<CurrentNetwork>,
same2: &Argument<CurrentNetwork>,
different: &Argument<CurrentNetwork>,
) {
assert!(*same1.is_equal(same2));
assert!(!*same1.is_not_equal(same2));
assert!(!*same1.is_equal(different));
assert!(*same1.is_not_equal(different));
}
#[test]
fn test_argument_equality() {
let p1 = Argument::Plaintext(Plaintext::from_str("42u64").unwrap());
let p2 = Argument::Plaintext(Plaintext::from_str("42u64").unwrap());
let p3 = Argument::Plaintext(Plaintext::from_str("100u64").unwrap());
check_equality(&p1, &p2, &p3);
let make_future = |name: &str| {
Future::<CurrentNetwork>::new(
ProgramID::from_str("test.aleo").unwrap(),
Identifier::from_str(name).unwrap(),
vec![Argument::Plaintext(Plaintext::from_str("100u64").unwrap())],
)
};
let f1 = Argument::Future(make_future("foo"));
let f2 = Argument::Future(make_future("foo"));
let f3 = Argument::Future(make_future("bar"));
check_equality(&f1, &f2, &f3);
let make_dynamic = |program: &str| {
let future = Future::<CurrentNetwork>::new(
ProgramID::from_str(program).unwrap(),
Identifier::from_str("foo").unwrap(),
vec![Argument::Plaintext(Plaintext::from_str("100u64").unwrap())],
);
DynamicFuture::from_future(&future).unwrap()
};
let d1 = Argument::DynamicFuture(make_dynamic("test.aleo"));
let d2 = Argument::DynamicFuture(make_dynamic("test.aleo"));
let d3 = Argument::DynamicFuture(make_dynamic("other.aleo"));
check_equality(&d1, &d2, &d3);
}
#[test]
fn test_argument_equality_cross_variant() {
let plaintext = Plaintext::<CurrentNetwork>::from_str("42u64").unwrap();
let future = Future::<CurrentNetwork>::new(
ProgramID::from_str("test.aleo").unwrap(),
Identifier::from_str("foo").unwrap(),
vec![],
);
let dynamic_future = DynamicFuture::from_future(&future).unwrap();
let arg_plaintext = Argument::Plaintext(plaintext);
let arg_future = Argument::Future(future);
let arg_dynamic = Argument::DynamicFuture(dynamic_future);
assert!(!*arg_plaintext.is_equal(&arg_future));
assert!(!*arg_plaintext.is_equal(&arg_dynamic));
assert!(!*arg_future.is_equal(&arg_plaintext));
assert!(!*arg_future.is_equal(&arg_dynamic));
assert!(!*arg_dynamic.is_equal(&arg_plaintext));
assert!(!*arg_dynamic.is_equal(&arg_future));
assert!(*arg_plaintext.is_not_equal(&arg_future));
assert!(*arg_plaintext.is_not_equal(&arg_dynamic));
assert!(*arg_future.is_not_equal(&arg_plaintext));
assert!(*arg_future.is_not_equal(&arg_dynamic));
assert!(*arg_dynamic.is_not_equal(&arg_plaintext));
assert!(*arg_dynamic.is_not_equal(&arg_future));
}
}