Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Memo type, existing EncryptedCiphertext types for Sprout and Sapling #249

Merged
merged 12 commits into from Feb 13, 2020
1 change: 1 addition & 0 deletions zebra-chain/src/lib.rs
Expand Up @@ -7,6 +7,7 @@ mod sha256d_writer;
pub mod block;
pub mod equihash_solution;
pub mod note_commitment_tree;
pub mod note_encryption;
pub mod proofs;
pub mod serialization;
pub mod transaction;
Expand Down
90 changes: 90 additions & 0 deletions zebra-chain/src/note_encryption.rs
@@ -0,0 +1,90 @@
//! Note encryption types.

use std::{
convert::{AsRef, From},
fmt,
};

mod sapling;
mod sprout;

/// A 512-byte _Memo_ field associated with a note, as described in
/// [protocol specification §5.5][ps].
///
/// The usage of the memo field is by agreement between the sender and
/// recipient of the note.
dconnolly marked this conversation as resolved.
Show resolved Hide resolved
///
/// [ps]: https://zips.z.cash/protocol/protocol.pdf#spendencoding
dconnolly marked this conversation as resolved.
Show resolved Hide resolved
#[derive(Clone, Copy)]
pub struct Memo([u8; 512]);

impl<T: AsRef<[u8]>> From<T> for Memo {
fn from(input: T) -> Self {
let input_bytes: &[u8] = input.as_ref();

let mut full_bytes = [0; 512];

if input_bytes.len() < 512 {
full_bytes[0..input_bytes.len()].copy_from_slice(input_bytes);
Memo(full_bytes)
} else if input_bytes.len() == 512 {
full_bytes[..].copy_from_slice(input_bytes);
Memo(full_bytes)
} else {
// Because this is a From impl, we truncate your input
// rather than return any Memo at all.
dconnolly marked this conversation as resolved.
Show resolved Hide resolved
full_bytes.copy_from_slice(&input_bytes[0..512]);
Memo(full_bytes)
}
}
}

impl fmt::Debug for Memo {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let output: String;

// This saves work but if the 'valid utf8 string' is just a
// bunch of numbers, it prints them out like
// 'Memo("\u{0}\u{0}..")', so. ¯\_(ツ)_/¯
match String::from_utf8(self.0.to_vec()) {
dconnolly marked this conversation as resolved.
Show resolved Hide resolved
Ok(memo) => output = memo,
_ => output = hex::encode(&self.0[..]),
}

f.debug_tuple("Memo").field(&output).finish()
}
}

#[test]
fn memo_fmt() {
let memo = Memo([0u8; 512]);

println!("{:?}", memo);

let memo2 = Memo(
*b"thiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiis \
iiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiis \
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa \
veeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeryyyyyyyyyyyyyyyyyyyyyyyyyy \
looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong \
meeeeeeeeeeeeeeeeeeemooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo \
but it's just short enough",
);

println!("{:?}", memo2);

let mut some_bytes = [0u8; 512];
some_bytes[0] = 0xF6;

println!("{:?}", Memo(some_bytes));
}

#[test]
fn memo_from_string() {
let memo = Memo::from("foo bar baz");

let mut bytes = [0; 512];
bytes[0..11].copy_from_slice(&[102, 111, 111, 32, 98, 97, 114, 32, 98, 97, 122]);

assert!(memo.0.iter().eq(bytes.iter()));
}
160 changes: 160 additions & 0 deletions 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<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
writer.write_all(&self.0[..])?;
Ok(())
}
}

impl ZcashDeserialize for EncryptedCiphertext {
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
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::<u8>(), 580))
.prop_map(|v| {
let mut bytes = [0; 580];
bytes.copy_from_slice(v.as_slice());
return Self(bytes);
})
.boxed()
}

type Strategy = BoxedStrategy<Self>;
}

/// 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<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
writer.write_all(&self.0[..])?;
Ok(())
}
}

impl ZcashDeserialize for OutCiphertext {
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
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::<u8>(), 80))
.prop_map(|v| {
let mut bytes = [0; 80];
bytes.copy_from_slice(v.as_slice());
return Self(bytes);
})
.boxed()
}

type Strategy = BoxedStrategy<Self>;
}

#[cfg(test)]
proptest! {

#[test]
fn encrypted_ciphertext_roundtrip(ec in any::<EncryptedCiphertext>()) {

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::<OutCiphertext>()) {

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];
}
}
88 changes: 88 additions & 0 deletions 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<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
writer.write_all(&self.0[..])?;
Ok(())
}
}

impl ZcashDeserialize for EncryptedCiphertext {
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
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::<u8>(), 601))
.prop_map(|v| {
let mut bytes = [0; 601];
bytes.copy_from_slice(v.as_slice());
return Self(bytes);
})
.boxed()
}

type Strategy = BoxedStrategy<Self>;
}

#[cfg(test)]
proptest! {

#[test]
fn encrypted_ciphertext_roundtrip(ec in any::<EncryptedCiphertext>()) {

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];
}
}
Empty file.
Empty file.