diff --git a/zebra-chain/src/note_encryption.rs b/zebra-chain/src/note_encryption.rs index e9ee5794..5902cfbb 100644 --- a/zebra-chain/src/note_encryption.rs +++ b/zebra-chain/src/note_encryption.rs @@ -5,6 +5,9 @@ use std::{ fmt, }; +mod sapling; +mod sprout; + /// A 512-byte _Memo_ field associated with a note, as described in /// [protocol specification ยง5.5][ps]. /// diff --git a/zebra-chain/src/note_encryption/sapling.rs b/zebra-chain/src/note_encryption/sapling.rs new file mode 100644 index 00000000..9fcb5efe --- /dev/null +++ b/zebra-chain/src/note_encryption/sapling.rs @@ -0,0 +1,160 @@ +use std::{fmt, io}; + +#[cfg(test)] +use proptest::{arbitrary::Arbitrary, collection::vec, prelude::*}; + +use crate::serialization::{SerializationError, ZcashDeserialize, ZcashSerialize}; + +/// A ciphertext component for encrypted output notes. +pub struct EncryptedCiphertext(pub [u8; 580]); + +impl fmt::Debug for EncryptedCiphertext { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_tuple("EncryptedCiphertext") + .field(&hex::encode(&self.0[..])) + .finish() + } +} + +// These impls all only exist because of array length restrictions. + +impl Copy for EncryptedCiphertext {} + +impl Clone for EncryptedCiphertext { + fn clone(&self) -> Self { + let mut bytes = [0; 580]; + bytes[..].copy_from_slice(&self.0[..]); + Self(bytes) + } +} + +impl PartialEq for EncryptedCiphertext { + fn eq(&self, other: &Self) -> bool { + self.0[..] == other.0[..] + } +} + +impl Eq for EncryptedCiphertext {} + +impl ZcashSerialize for EncryptedCiphertext { + fn zcash_serialize(&self, mut writer: W) -> Result<(), io::Error> { + writer.write_all(&self.0[..])?; + Ok(()) + } +} + +impl ZcashDeserialize for EncryptedCiphertext { + fn zcash_deserialize(mut reader: R) -> Result { + let mut bytes = [0; 580]; + reader.read_exact(&mut bytes[..])?; + Ok(Self(bytes)) + } +} + +#[cfg(test)] +impl Arbitrary for EncryptedCiphertext { + type Parameters = (); + + fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { + (vec(any::(), 580)) + .prop_map(|v| { + let mut bytes = [0; 580]; + bytes.copy_from_slice(v.as_slice()); + return Self(bytes); + }) + .boxed() + } + + type Strategy = BoxedStrategy; +} + +/// A ciphertext component for encrypted output notes. +pub struct OutCiphertext(pub [u8; 80]); + +impl fmt::Debug for OutCiphertext { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_tuple("OutCiphertext") + .field(&hex::encode(&self.0[..])) + .finish() + } +} + +// These impls all only exist because of array length restrictions. + +impl Copy for OutCiphertext {} + +impl Clone for OutCiphertext { + fn clone(&self) -> Self { + let mut bytes = [0; 80]; + bytes[..].copy_from_slice(&self.0[..]); + Self(bytes) + } +} + +impl PartialEq for OutCiphertext { + fn eq(&self, other: &Self) -> bool { + self.0[..] == other.0[..] + } +} + +impl Eq for OutCiphertext {} + +impl ZcashSerialize for OutCiphertext { + fn zcash_serialize(&self, mut writer: W) -> Result<(), io::Error> { + writer.write_all(&self.0[..])?; + Ok(()) + } +} + +impl ZcashDeserialize for OutCiphertext { + fn zcash_deserialize(mut reader: R) -> Result { + let mut bytes = [0; 80]; + reader.read_exact(&mut bytes[..])?; + Ok(Self(bytes)) + } +} + +#[cfg(test)] +impl Arbitrary for OutCiphertext { + type Parameters = (); + + fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { + (vec(any::(), 80)) + .prop_map(|v| { + let mut bytes = [0; 80]; + bytes.copy_from_slice(v.as_slice()); + return Self(bytes); + }) + .boxed() + } + + type Strategy = BoxedStrategy; +} + +#[cfg(test)] +proptest! { + + #[test] + fn encrypted_ciphertext_roundtrip(ec in any::()) { + + let mut data = Vec::new(); + + ec.zcash_serialize(&mut data).expect("EncryptedCiphertext should serialize"); + + let ec2 = EncryptedCiphertext::zcash_deserialize(&data[..]).expect("randomized EncryptedCiphertext should deserialize"); + + prop_assert_eq![ec, ec2]; + } + + #[test] + fn out_ciphertext_roundtrip(oc in any::()) { + + let mut data = Vec::new(); + + oc.zcash_serialize(&mut data).expect("OutCiphertext should serialize"); + + let oc2 = OutCiphertext::zcash_deserialize(&data[..]).expect("randomized OutCiphertext should deserialize"); + + prop_assert_eq![oc, oc2]; + } +} diff --git a/zebra-chain/src/note_encryption/sprout.rs b/zebra-chain/src/note_encryption/sprout.rs new file mode 100644 index 00000000..cc3b12dd --- /dev/null +++ b/zebra-chain/src/note_encryption/sprout.rs @@ -0,0 +1,88 @@ +use std::{ + fmt, + io::{self}, +}; + +#[cfg(test)] +use proptest::{collection::vec, prelude::*}; + +use crate::serialization::{SerializationError, ZcashDeserialize, ZcashSerialize}; + +/// A ciphertext component for encrypted output notes. +pub struct EncryptedCiphertext(pub [u8; 601]); + +impl fmt::Debug for EncryptedCiphertext { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_tuple("EncryptedCiphertext") + .field(&hex::encode(&self.0[..])) + .finish() + } +} + +// These impls all only exist because of array length restrictions. + +impl Copy for EncryptedCiphertext {} + +impl Clone for EncryptedCiphertext { + fn clone(&self) -> Self { + let mut bytes = [0; 601]; + bytes[..].copy_from_slice(&self.0[..]); + Self(bytes) + } +} + +impl PartialEq for EncryptedCiphertext { + fn eq(&self, other: &Self) -> bool { + self.0[..] == other.0[..] + } +} + +impl Eq for EncryptedCiphertext {} + +impl ZcashSerialize for EncryptedCiphertext { + fn zcash_serialize(&self, mut writer: W) -> Result<(), io::Error> { + writer.write_all(&self.0[..])?; + Ok(()) + } +} + +impl ZcashDeserialize for EncryptedCiphertext { + fn zcash_deserialize(mut reader: R) -> Result { + let mut bytes = [0; 601]; + reader.read_exact(&mut bytes[..])?; + Ok(Self(bytes)) + } +} + +#[cfg(test)] +impl Arbitrary for EncryptedCiphertext { + type Parameters = (); + + fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { + (vec(any::(), 601)) + .prop_map(|v| { + let mut bytes = [0; 601]; + bytes.copy_from_slice(v.as_slice()); + return Self(bytes); + }) + .boxed() + } + + type Strategy = BoxedStrategy; +} + +#[cfg(test)] +proptest! { + + #[test] + fn encrypted_ciphertext_roundtrip(ec in any::()) { + + let mut data = Vec::new(); + + ec.zcash_serialize(&mut data).expect("EncryptedCiphertext should serialize"); + + let ec2 = EncryptedCiphertext::zcash_deserialize(&data[..]).expect("randomized EncryptedCiphertext should deserialize"); + + prop_assert_eq![ec, ec2]; + } +}