use super::*;
#[allow(clippy::type_complexity)]
fn read_record_fields<N: Network, R: Read>(
reader: &mut R,
) -> IoResult<(Field<N>, Field<N>, Option<Record<N, Ciphertext<N>>>, Option<Field<N>>)> {
let commitment = FromBytes::read_le(&mut *reader)?;
let checksum = FromBytes::read_le(&mut *reader)?;
let record_ciphertext_exists: bool = FromBytes::read_le(&mut *reader)?;
let record_ciphertext: Option<Record<N, _>> = match record_ciphertext_exists {
true => Some(FromBytes::read_le(&mut *reader)?),
false => None,
};
let sender_ciphertext = match &record_ciphertext {
Some(record) => match record.version().is_zero() {
true => None,
false => {
let sender_ciphertext_version: u8 = FromBytes::read_le(&mut *reader)?;
if sender_ciphertext_version != 0 {
return Err(error(format!(
"Failed to decode sender ciphertext version {sender_ciphertext_version}"
)));
}
Some(FromBytes::read_le(&mut *reader)?)
}
},
None => None,
};
Ok((commitment, checksum, record_ciphertext, sender_ciphertext))
}
fn write_record_fields<N: Network, W: Write>(
writer: &mut W,
commitment: &Field<N>,
checksum: &Field<N>,
record_ciphertext: &Option<Record<N, Ciphertext<N>>>,
sender_ciphertext: &Option<Field<N>>,
) -> IoResult<()> {
commitment.write_le(&mut *writer)?;
checksum.write_le(&mut *writer)?;
match record_ciphertext {
Some(record) => {
true.write_le(&mut *writer)?;
record.write_le(&mut *writer)?;
if !record.version().is_zero() {
0u8.write_le(&mut *writer)?;
match sender_ciphertext {
Some(sender) => sender.write_le(&mut *writer)?,
None => {
return Err(error("Failed to encode sender ciphertext for non-zero version record"));
}
}
}
}
None => false.write_le(&mut *writer)?,
}
Ok(())
}
impl<N: Network> FromBytes for Output<N> {
fn read_le<R: Read>(mut reader: R) -> IoResult<Self> {
let index = Variant::read_le(&mut reader)?;
let literal = match index {
0 => {
let plaintext_hash: Field<N> = FromBytes::read_le(&mut reader)?;
let plaintext_exists: bool = FromBytes::read_le(&mut reader)?;
let plaintext = match plaintext_exists {
true => Some(FromBytes::read_le(&mut reader)?),
false => None,
};
Self::Constant(plaintext_hash, plaintext)
}
1 => {
let plaintext_hash: Field<N> = FromBytes::read_le(&mut reader)?;
let plaintext_exists: bool = FromBytes::read_le(&mut reader)?;
let plaintext = match plaintext_exists {
true => Some(FromBytes::read_le(&mut reader)?),
false => None,
};
Self::Public(plaintext_hash, plaintext)
}
2 => {
let ciphertext_hash: Field<N> = FromBytes::read_le(&mut reader)?;
let ciphertext_exists: bool = FromBytes::read_le(&mut reader)?;
let ciphertext = match ciphertext_exists {
true => Some(FromBytes::read_le(&mut reader)?),
false => None,
};
Self::Private(ciphertext_hash, ciphertext)
}
3 => {
let (commitment, checksum, record_ciphertext, sender_ciphertext) = read_record_fields(&mut reader)?;
Self::Record(commitment, checksum, record_ciphertext, sender_ciphertext)
}
4 => {
let commitment = FromBytes::read_le(&mut reader)?;
Self::ExternalRecord(commitment)
}
5 => {
let future_hash: Field<N> = FromBytes::read_le(&mut reader)?;
let future_exists: bool = FromBytes::read_le(&mut reader)?;
let future = match future_exists {
true => Some(FromBytes::read_le(&mut reader)?),
false => None,
};
Self::Future(future_hash, future)
}
6 => {
let commitment = FromBytes::read_le(&mut reader)?;
Self::DynamicRecord(commitment)
}
7 => {
let (commitment, checksum, record_ciphertext, sender_ciphertext) = read_record_fields(&mut reader)?;
let dynamic_id: Field<N> = FromBytes::read_le(&mut reader)?;
Self::RecordWithDynamicID(commitment, checksum, record_ciphertext, sender_ciphertext, dynamic_id)
}
8 => {
let external_hash: Field<N> = FromBytes::read_le(&mut reader)?;
let dynamic_id: Field<N> = FromBytes::read_le(&mut reader)?;
Self::ExternalRecordWithDynamicID(external_hash, dynamic_id)
}
9.. => return Err(error(format!("Failed to decode output variant {index}"))),
};
Ok(literal)
}
}
impl<N: Network> ToBytes for Output<N> {
fn write_le<W: Write>(&self, mut writer: W) -> IoResult<()> {
match self {
Self::Constant(plaintext_hash, plaintext) => {
(0 as Variant).write_le(&mut writer)?;
plaintext_hash.write_le(&mut writer)?;
match plaintext {
Some(plaintext) => {
true.write_le(&mut writer)?;
plaintext.write_le(&mut writer)
}
None => false.write_le(&mut writer),
}
}
Self::Public(plaintext_hash, plaintext) => {
(1 as Variant).write_le(&mut writer)?;
plaintext_hash.write_le(&mut writer)?;
match plaintext {
Some(plaintext) => {
true.write_le(&mut writer)?;
plaintext.write_le(&mut writer)
}
None => false.write_le(&mut writer),
}
}
Self::Private(ciphertext_hash, ciphertext) => {
(2 as Variant).write_le(&mut writer)?;
ciphertext_hash.write_le(&mut writer)?;
match ciphertext {
Some(ciphertext) => {
true.write_le(&mut writer)?;
ciphertext.write_le(&mut writer)
}
None => false.write_le(&mut writer),
}
}
Self::Record(commitment, checksum, record_ciphertext, sender_ciphertext) => {
(3 as Variant).write_le(&mut writer)?;
write_record_fields(&mut writer, commitment, checksum, record_ciphertext, sender_ciphertext)
}
Self::ExternalRecord(commitment) => {
(4 as Variant).write_le(&mut writer)?;
commitment.write_le(&mut writer)
}
Self::Future(future_hash, future) => {
(5 as Variant).write_le(&mut writer)?;
future_hash.write_le(&mut writer)?;
match future {
Some(future) => {
true.write_le(&mut writer)?;
future.write_le(&mut writer)
}
None => false.write_le(&mut writer),
}
}
Self::DynamicRecord(commitment) => {
(6 as Variant).write_le(&mut writer)?;
commitment.write_le(&mut writer)
}
Self::RecordWithDynamicID(commitment, checksum, record_ciphertext, sender_ciphertext, dynamic_id) => {
(7 as Variant).write_le(&mut writer)?;
write_record_fields(&mut writer, commitment, checksum, record_ciphertext, sender_ciphertext)?;
dynamic_id.write_le(&mut writer)
}
Self::ExternalRecordWithDynamicID(external_hash, dynamic_id) => {
(8 as Variant).write_le(&mut writer)?;
external_hash.write_le(&mut writer)?;
dynamic_id.write_le(&mut writer)
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_bytes() {
for (_, expected) in crate::transition::output::test_helpers::sample_outputs() {
let expected_bytes = expected.to_bytes_le().unwrap();
assert_eq!(expected, Output::read_le(&expected_bytes[..]).unwrap());
}
}
}