mod bytes;
mod serialize;
mod string;
use console::{
network::prelude::*,
program::{Ciphertext, Plaintext, Record},
types::{Field, Group},
};
type Variant = u16;
#[derive(Clone, PartialEq, Eq)]
pub enum Output<N: Network> {
Constant(Field<N>, Option<Plaintext<N>>),
Public(Field<N>, Option<Plaintext<N>>),
Private(Field<N>, Option<Ciphertext<N>>),
Record(Field<N>, Field<N>, Option<Record<N, Ciphertext<N>>>),
ExternalRecord(Field<N>),
}
impl<N: Network> Output<N> {
pub const fn variant(&self) -> Variant {
match self {
Output::Constant(_, _) => 0,
Output::Public(_, _) => 1,
Output::Private(_, _) => 2,
Output::Record(_, _, _) => 3,
Output::ExternalRecord(_) => 4,
}
}
pub const fn id(&self) -> &Field<N> {
match self {
Output::Constant(id, ..) => id,
Output::Public(id, ..) => id,
Output::Private(id, ..) => id,
Output::Record(commitment, ..) => commitment,
Output::ExternalRecord(id) => id,
}
}
#[allow(clippy::type_complexity)]
pub const fn record(&self) -> Option<(&Field<N>, &Record<N, Ciphertext<N>>)> {
match self {
Output::Record(commitment, _, Some(record)) => Some((commitment, record)),
_ => None,
}
}
#[allow(clippy::type_complexity)]
pub fn into_record(self) -> Option<(Field<N>, Record<N, Ciphertext<N>>)> {
match self {
Output::Record(commitment, _, Some(record)) => Some((commitment, record)),
_ => None,
}
}
pub const fn commitment(&self) -> Option<&Field<N>> {
match self {
Output::Record(commitment, ..) => Some(commitment),
_ => None,
}
}
pub fn into_commitment(self) -> Option<Field<N>> {
match self {
Output::Record(commitment, ..) => Some(commitment),
_ => None,
}
}
pub const fn nonce(&self) -> Option<&Group<N>> {
match self {
Output::Record(_, _, Some(record)) => Some(record.nonce()),
_ => None,
}
}
pub fn into_nonce(self) -> Option<Group<N>> {
match self {
Output::Record(_, _, Some(record)) => Some(record.into_nonce()),
_ => None,
}
}
pub const fn checksum(&self) -> Option<&Field<N>> {
match self {
Output::Record(_, checksum, ..) => Some(checksum),
_ => None,
}
}
pub fn into_checksum(self) -> Option<Field<N>> {
match self {
Output::Record(_, checksum, ..) => Some(checksum),
_ => None,
}
}
pub fn verifier_inputs(&self) -> impl '_ + Iterator<Item = N::Field> {
[**self.id()].into_iter()
.chain([self.checksum().map(|sum| **sum)].into_iter().flatten())
}
pub fn verify(&self, tcm: &Field<N>, index: usize) -> bool {
let result = match self {
Output::Constant(hash, Some(output)) => {
match output.to_fields() {
Ok(fields) => {
let index = Field::from_u16(index as u16);
let mut preimage = fields;
preimage.push(*tcm);
preimage.push(index);
match N::hash_psd8(&preimage) {
Ok(candidate_hash) => Ok(hash == &candidate_hash),
Err(error) => Err(error),
}
}
Err(error) => Err(error),
}
}
Output::Public(hash, Some(output)) => {
match output.to_fields() {
Ok(fields) => {
let index = Field::from_u16(index as u16);
let mut preimage = fields;
preimage.push(*tcm);
preimage.push(index);
match N::hash_psd8(&preimage) {
Ok(candidate_hash) => Ok(hash == &candidate_hash),
Err(error) => Err(error),
}
}
Err(error) => Err(error),
}
}
Output::Private(hash, Some(value)) => {
match value.to_fields() {
Ok(fields) => match N::hash_psd8(&fields) {
Ok(candidate_hash) => Ok(hash == &candidate_hash),
Err(error) => Err(error),
},
Err(error) => Err(error),
}
}
Output::Record(_, checksum, Some(value)) => match N::hash_bhp1024(&value.to_bits_le()) {
Ok(candidate_hash) => Ok(checksum == &candidate_hash),
Err(error) => Err(error),
},
_ => Ok(true),
};
match result {
Ok(is_hash_valid) => is_hash_valid,
Err(error) => {
eprintln!("{error}");
false
}
}
}
}
#[cfg(test)]
pub(crate) mod test_helpers {
use super::*;
use console::{network::Testnet3, program::Literal};
type CurrentNetwork = Testnet3;
pub(crate) fn sample_outputs() -> Vec<(<CurrentNetwork as Network>::TransitionID, Output<CurrentNetwork>)> {
let transaction = crate::ledger::vm::test_helpers::sample_execution_transaction();
let transition = transaction.transitions().next().unwrap();
let transition_id = *transition.id();
let input = transition.outputs().iter().next().unwrap().clone();
let rng = &mut test_crypto_rng();
let plaintext = Plaintext::Literal(Literal::Field(Uniform::rand(rng)), Default::default());
let plaintext_hash = CurrentNetwork::hash_bhp1024(&plaintext.to_bits_le()).unwrap();
let ciphertext = Ciphertext::from_fields(&vec![Uniform::rand(rng); 10]).unwrap();
let ciphertext_hash = CurrentNetwork::hash_bhp1024(&ciphertext.to_bits_le()).unwrap();
let randomizer = Uniform::rand(rng);
let nonce = CurrentNetwork::g_scalar_multiply(&randomizer);
let record = Record::<CurrentNetwork, Plaintext<CurrentNetwork>>::from_str(
&format!("{{ owner: aleo1d5hg2z3ma00382pngntdp68e74zv54jdxy249qhaujhks9c72yrs33ddah.private, gates: 5u64.private, token_amount: 100u64.private, _nonce: {nonce}.public }}"),
).unwrap();
let record_ciphertext = record.encrypt(randomizer).unwrap();
let record_checksum = CurrentNetwork::hash_bhp1024(&record_ciphertext.to_bits_le()).unwrap();
vec![
(transition_id, input),
(Uniform::rand(rng), Output::Constant(Uniform::rand(rng), None)),
(Uniform::rand(rng), Output::Constant(plaintext_hash, Some(plaintext.clone()))),
(Uniform::rand(rng), Output::Public(Uniform::rand(rng), None)),
(Uniform::rand(rng), Output::Public(plaintext_hash, Some(plaintext))),
(Uniform::rand(rng), Output::Private(Uniform::rand(rng), None)),
(Uniform::rand(rng), Output::Private(ciphertext_hash, Some(ciphertext))),
(Uniform::rand(rng), Output::Record(Uniform::rand(rng), Uniform::rand(rng), None)),
(Uniform::rand(rng), Output::Record(Uniform::rand(rng), record_checksum, Some(record_ciphertext))),
(Uniform::rand(rng), Output::ExternalRecord(Uniform::rand(rng))),
]
}
}