Move note encryption types around (#362)

* Move around and dedupe note *Cipertext types

* Bump code coverage test timeout to 5 minutes

* Try uploading coverage to codecov.io
This commit is contained in:
Deirdre Connolly 2020-04-19 14:45:25 -04:00 committed by GitHub
parent 21eca164d8
commit df7ed7ae81
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 29 additions and 267 deletions

View File

@ -58,9 +58,12 @@ jobs:
uses: actions-rs/cargo@v1 uses: actions-rs/cargo@v1
with: with:
command: tarpaulin command: tarpaulin
args: --verbose --count --workspace --timeout 180 --out Html args: --verbose --count --workspace --timeout 300 --out Xml
- name: Archive code coverage results - name: Archive code coverage results
uses: actions/upload-artifact@v1 uses: actions/upload-artifact@v1
with: with:
name: code-coverage-report name: code-coverage-report
path: tarpaulin-report.html path: cobertura.xml
- uses: codecov/codecov-action@v1

View File

@ -15,7 +15,7 @@ pub mod block;
pub mod equihash_solution; pub mod equihash_solution;
pub mod keys; pub mod keys;
pub mod note_commitment_tree; pub mod note_commitment_tree;
pub mod note_encryption; pub mod notes;
pub mod proofs; pub mod proofs;
pub mod serialization; pub mod serialization;
pub mod transaction; pub mod transaction;

View File

@ -1,7 +1,7 @@
//! Note encryption types. //! Note encryption types.
mod memo; mod memo;
mod sapling; pub mod sapling;
mod sprout; pub mod sprout;
/// The randomness used in the Pedersen Hash for note commitment. /// The randomness used in the Pedersen Hash for note commitment.
#[derive(Copy, Clone, Debug, PartialEq)] #[derive(Copy, Clone, Debug, PartialEq)]

View File

@ -1,3 +1,5 @@
//!
use std::{fmt, io}; use std::{fmt, io};
#[cfg(test)] #[cfg(test)]
@ -15,6 +17,7 @@ use super::*;
#[derive(Copy, Clone, Debug, PartialEq)] #[derive(Copy, Clone, Debug, PartialEq)]
pub struct Diversifier(pub [u8; 11]); pub struct Diversifier(pub [u8; 11]);
///
pub struct Note { pub struct Note {
diversifier: Diversifier, diversifier: Diversifier,
// TODO: refine as a type, derived from a scalar mult of the // TODO: refine as a type, derived from a scalar mult of the

View File

@ -1,3 +1,5 @@
//!
use std::{ use std::{
fmt, fmt,
io::{self}, io::{self},
@ -8,8 +10,9 @@ use proptest::{collection::vec, prelude::*};
use crate::serialization::{SerializationError, ZcashDeserialize, ZcashSerialize}; use crate::serialization::{SerializationError, ZcashDeserialize, ZcashSerialize};
use super::*; use super::{memo::Memo, *};
///
pub struct Note { pub struct Note {
// TODO: refine type as a SHA-256d output derived from a spending key. // TODO: refine type as a SHA-256d output derived from a spending key.
paying_key: [u8; 32], paying_key: [u8; 32],
@ -26,7 +29,7 @@ pub struct NotePlaintext {
rho: [u8; 32], rho: [u8; 32],
// TODO: refine as jub-jub appropriate in the base field. // TODO: refine as jub-jub appropriate in the base field.
note_commitment_randomness: NoteCommitmentRandomness, note_commitment_randomness: NoteCommitmentRandomness,
memo: memo::Memo, memo: Memo,
} }
/// A ciphertext component for encrypted output notes. /// A ciphertext component for encrypted output notes.

View File

@ -1,15 +1,7 @@
use std::{
fmt,
io::{self},
};
#[cfg(test)] #[cfg(test)]
use proptest::{array, collection::vec, prelude::*}; use proptest::{array, collection::vec, prelude::*};
use crate::{ use crate::{notes::sprout, proofs::ZkSnarkProof};
proofs::ZkSnarkProof,
serialization::{SerializationError, ZcashDeserialize, ZcashSerialize},
};
/// A _JoinSplit Description_, as described in [protocol specification §7.2][ps]. /// A _JoinSplit Description_, as described in [protocol specification §7.2][ps].
/// ///
@ -54,7 +46,7 @@ pub struct JoinSplit<P: ZkSnarkProof> {
/// [`Bctv14Proof`](crate::proofs::Bctv14Proof). /// [`Bctv14Proof`](crate::proofs::Bctv14Proof).
pub zkproof: P, pub zkproof: P,
/// A ciphertext component for this output note. /// A ciphertext component for this output note.
pub enc_ciphertexts: [EncryptedCiphertext; 2], pub enc_ciphertexts: [sprout::EncryptedCiphertext; 2],
} }
// Because x25519_dalek::PublicKey does not impl PartialEq // Because x25519_dalek::PublicKey does not impl PartialEq
@ -91,7 +83,7 @@ impl<P: ZkSnarkProof + Arbitrary + 'static> Arbitrary for JoinSplit<P> {
array::uniform32(any::<u8>()), array::uniform32(any::<u8>()),
array::uniform2(array::uniform32(any::<u8>())), array::uniform2(array::uniform32(any::<u8>())),
any::<P>(), any::<P>(),
array::uniform2(any::<EncryptedCiphertext>()), array::uniform2(any::<sprout::EncryptedCiphertext>()),
) )
.prop_map( .prop_map(
|( |(
@ -184,83 +176,3 @@ impl<P: ZkSnarkProof + Arbitrary + 'static> Arbitrary for JoinSplitData<P> {
type Strategy = BoxedStrategy<Self>; type Strategy = BoxedStrategy<Self>;
} }
/// A ciphertext component for encrypted output notes.
// XXX move as part of #181 (note encryption implementation)
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];
}
}

View File

@ -4,6 +4,7 @@
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use std::io::{self, Read}; use std::io::{self, Read};
use crate::notes;
use crate::proofs::ZkSnarkProof; use crate::proofs::ZkSnarkProof;
use crate::serialization::{ use crate::serialization::{
ReadZcashExt, SerializationError, WriteZcashExt, ZcashDeserialize, ZcashSerialize, ReadZcashExt, SerializationError, WriteZcashExt, ZcashDeserialize, ZcashSerialize,
@ -259,8 +260,8 @@ impl<P: ZkSnarkProof> ZcashDeserialize for JoinSplit<P> {
vmacs: [reader.read_32_bytes()?, reader.read_32_bytes()?], vmacs: [reader.read_32_bytes()?, reader.read_32_bytes()?],
zkproof: P::zcash_deserialize(&mut reader)?, zkproof: P::zcash_deserialize(&mut reader)?,
enc_ciphertexts: [ enc_ciphertexts: [
joinsplit::EncryptedCiphertext::zcash_deserialize(&mut reader)?, notes::sprout::EncryptedCiphertext::zcash_deserialize(&mut reader)?,
joinsplit::EncryptedCiphertext::zcash_deserialize(&mut reader)?, notes::sprout::EncryptedCiphertext::zcash_deserialize(&mut reader)?,
], ],
}) })
} }
@ -346,8 +347,8 @@ impl ZcashDeserialize for Output {
cv: reader.read_32_bytes()?, cv: reader.read_32_bytes()?,
cmu: reader.read_32_bytes()?, cmu: reader.read_32_bytes()?,
ephemeral_key: jubjub::AffinePoint::from_bytes(reader.read_32_bytes()?).unwrap(), ephemeral_key: jubjub::AffinePoint::from_bytes(reader.read_32_bytes()?).unwrap(),
enc_ciphertext: shielded_data::EncryptedCiphertext::zcash_deserialize(&mut reader)?, enc_ciphertext: notes::sapling::EncryptedCiphertext::zcash_deserialize(&mut reader)?,
out_ciphertext: shielded_data::OutCiphertext::zcash_deserialize(&mut reader)?, out_ciphertext: notes::sapling::OutCiphertext::zcash_deserialize(&mut reader)?,
zkproof: Groth16Proof::zcash_deserialize(&mut reader)?, zkproof: Groth16Proof::zcash_deserialize(&mut reader)?,
}) })
} }

View File

@ -1,18 +1,13 @@
use std::{fmt, io};
use futures::future::Either; use futures::future::Either;
#[cfg(test)] #[cfg(test)]
use proptest::{arbitrary::Arbitrary, array, collection::vec, prelude::*}; use proptest::{arbitrary::Arbitrary, array, collection::vec, prelude::*};
#[cfg(test)]
use proptest_derive::Arbitrary;
// XXX this name seems too long? // XXX this name seems too long?
use crate::note_commitment_tree::SaplingNoteTreeRootHash; use crate::note_commitment_tree::SaplingNoteTreeRootHash;
use crate::notes::sapling;
use crate::proofs::Groth16Proof; use crate::proofs::Groth16Proof;
use crate::redjubjub::{self, Binding, SpendAuth}; use crate::redjubjub::{self, Binding, SpendAuth};
use crate::serialization::{SerializationError, ZcashDeserialize, ZcashSerialize};
/// A _Spend Description_, as described in [protocol specification §7.3][ps]. /// A _Spend Description_, as described in [protocol specification §7.3][ps].
/// ///
@ -88,9 +83,9 @@ pub struct Output {
/// An encoding of an ephemeral Jubjub public key. /// An encoding of an ephemeral Jubjub public key.
pub ephemeral_key: jubjub::AffinePoint, pub ephemeral_key: jubjub::AffinePoint,
/// A ciphertext component for the encrypted output note. /// A ciphertext component for the encrypted output note.
pub enc_ciphertext: EncryptedCiphertext, pub enc_ciphertext: sapling::EncryptedCiphertext,
/// A ciphertext component for the encrypted output note. /// A ciphertext component for the encrypted output note.
pub out_ciphertext: OutCiphertext, pub out_ciphertext: sapling::OutCiphertext,
/// The ZK output proof. /// The ZK output proof.
pub zkproof: Groth16Proof, pub zkproof: Groth16Proof,
} }
@ -108,8 +103,8 @@ impl Arbitrary for Output {
array::uniform32(any::<u8>()).prop_filter("Valid jubjub::AffinePoint", |b| { array::uniform32(any::<u8>()).prop_filter("Valid jubjub::AffinePoint", |b| {
jubjub::AffinePoint::from_bytes(*b).is_some().unwrap_u8() == 1 jubjub::AffinePoint::from_bytes(*b).is_some().unwrap_u8() == 1
}), }),
any::<EncryptedCiphertext>(), any::<sapling::EncryptedCiphertext>(),
any::<OutCiphertext>(), any::<sapling::OutCiphertext>(),
any::<Groth16Proof>(), any::<Groth16Proof>(),
) )
.prop_map( .prop_map(
@ -236,158 +231,3 @@ impl Arbitrary for ShieldedData {
type Strategy = BoxedStrategy<Self>; type Strategy = BoxedStrategy<Self>;
} }
/// A ciphertext component for encrypted output notes.
// XXX move as part of #181 (note encryption implementation)
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];
}
}