From 5c176d2f9635bc93cb73a4aa3839b6e9410b6620 Mon Sep 17 00:00:00 2001 From: Henry de Valence Date: Mon, 17 Aug 2020 02:06:38 -0700 Subject: [PATCH] chain: move JoinSplit to sprout --- zebra-chain/src/sapling.rs | 6 +- zebra-chain/src/sapling/tests.rs | 2 +- zebra-chain/src/sapling/tests/arbitrary.rs | 3 +- zebra-chain/src/sprout.rs | 8 ++ zebra-chain/src/sprout/joinsplit.rs | 118 ++++++++++++++++++ zebra-chain/src/sprout/tests.rs | 1 + zebra-chain/src/sprout/tests/arbitrary.rs | 57 +++++++++ zebra-chain/src/transaction.rs | 2 +- zebra-chain/src/transaction/joinsplit.rs | 62 +-------- zebra-chain/src/transaction/serialize.rs | 54 +------- .../src/transaction/tests/arbitrary.rs | 60 +-------- 11 files changed, 199 insertions(+), 174 deletions(-) create mode 100644 zebra-chain/src/sprout/joinsplit.rs create mode 100644 zebra-chain/src/sprout/tests.rs create mode 100644 zebra-chain/src/sprout/tests/arbitrary.rs diff --git a/zebra-chain/src/sapling.rs b/zebra-chain/src/sapling.rs index 9aca98fe..09d6359f 100644 --- a/zebra-chain/src/sapling.rs +++ b/zebra-chain/src/sapling.rs @@ -1,10 +1,10 @@ //! Sapling-related functionality. -mod spend; mod output; +mod spend; -pub use spend::Spend; pub use output::Output; +pub use spend::Spend; // XXX clean up these modules @@ -15,4 +15,4 @@ pub mod note; pub mod tree; #[cfg(test)] -mod tests; \ No newline at end of file +mod tests; diff --git a/zebra-chain/src/sapling/tests.rs b/zebra-chain/src/sapling/tests.rs index 03dce05a..be9770e9 100644 --- a/zebra-chain/src/sapling/tests.rs +++ b/zebra-chain/src/sapling/tests.rs @@ -1 +1 @@ -mod arbitrary; \ No newline at end of file +mod arbitrary; diff --git a/zebra-chain/src/sapling/tests/arbitrary.rs b/zebra-chain/src/sapling/tests/arbitrary.rs index 64a64a17..09d45043 100644 --- a/zebra-chain/src/sapling/tests/arbitrary.rs +++ b/zebra-chain/src/sapling/tests/arbitrary.rs @@ -1,9 +1,8 @@ - use proptest::{arbitrary::any, array, collection::vec, prelude::*}; use crate::primitives::Groth16Proof; -use super::super::{Spend, Output, keys, tree, commitment, note}; +use super::super::{commitment, keys, note, tree, Output, Spend}; impl Arbitrary for Spend { type Parameters = (); diff --git a/zebra-chain/src/sprout.rs b/zebra-chain/src/sprout.rs index c64c4cbc..5840a411 100644 --- a/zebra-chain/src/sprout.rs +++ b/zebra-chain/src/sprout.rs @@ -1,7 +1,15 @@ //! Sprout-related functionality. +mod joinsplit; +pub use joinsplit::JoinSplit; + +// XXX clean up these modules + pub mod address; pub mod commitment; pub mod keys; pub mod note; pub mod tree; + +#[cfg(test)] +mod tests; diff --git a/zebra-chain/src/sprout/joinsplit.rs b/zebra-chain/src/sprout/joinsplit.rs new file mode 100644 index 00000000..77fc7c74 --- /dev/null +++ b/zebra-chain/src/sprout/joinsplit.rs @@ -0,0 +1,118 @@ +use std::{convert::TryInto, io}; + +use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; +use serde::{Deserialize, Serialize}; + +use crate::{ + amount::{Amount, NonNegative}, + primitives::{x25519, ZkSnarkProof}, + serialization::{ + ReadZcashExt, SerializationError, WriteZcashExt, ZcashDeserialize, ZcashSerialize, + }, +}; + +use super::{commitment, note, tree}; + +/// A _JoinSplit Description_, as described in [protocol specification §7.2][ps]. +/// +/// [ps]: https://zips.z.cash/protocol/protocol.pdf#joinsplitencoding +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct JoinSplit { + /// A value that the JoinSplit transfer removes from the transparent value + /// pool. + pub vpub_old: Amount, + /// A value that the JoinSplit transfer inserts into the transparent value + /// pool. + /// + pub vpub_new: Amount, + /// A root of the Sprout note commitment tree at some block height in the + /// past, or the root produced by a previous JoinSplit transfer in this + /// transaction. + pub anchor: tree::NoteTreeRootHash, + /// A nullifier for the input notes. + pub nullifiers: [note::Nullifier; 2], + /// A note commitment for this output note. + pub commitments: [commitment::NoteCommitment; 2], + /// An X25519 public key. + pub ephemeral_key: x25519::PublicKey, + /// A 256-bit seed that must be chosen independently at random for each + /// JoinSplit description. + pub random_seed: [u8; 32], + /// A message authentication tag. + pub vmacs: [note::MAC; 2], + /// A ZK JoinSplit proof, either a + /// [`Groth16Proof`](crate::primitives::Groth16Proof) or a + /// [`Bctv14Proof`](crate::primitives::Bctv14Proof). + #[serde(bound(serialize = "P: ZkSnarkProof", deserialize = "P: ZkSnarkProof"))] + pub zkproof: P, + /// A ciphertext component for this output note. + pub enc_ciphertexts: [note::EncryptedCiphertext; 2], +} + +// Because x25519_dalek::PublicKey does not impl PartialEq +impl PartialEq for JoinSplit

{ + fn eq(&self, other: &Self) -> bool { + self.vpub_old == other.vpub_old + && self.vpub_new == other.vpub_new + && self.anchor == other.anchor + && self.nullifiers == other.nullifiers + && self.commitments == other.commitments + && self.ephemeral_key.as_bytes() == other.ephemeral_key.as_bytes() + && self.random_seed == other.random_seed + && self.vmacs == other.vmacs + && self.zkproof == other.zkproof + && self.enc_ciphertexts == other.enc_ciphertexts + } +} + +// Because x25519_dalek::PublicKey does not impl Eq +impl Eq for JoinSplit

{} + +impl ZcashSerialize for JoinSplit

{ + fn zcash_serialize(&self, mut writer: W) -> Result<(), io::Error> { + writer.write_u64::(self.vpub_old.into())?; + writer.write_u64::(self.vpub_new.into())?; + writer.write_32_bytes(&self.anchor.into())?; + writer.write_32_bytes(&self.nullifiers[0].into())?; + writer.write_32_bytes(&self.nullifiers[1].into())?; + writer.write_32_bytes(&self.commitments[0].into())?; + writer.write_32_bytes(&self.commitments[1].into())?; + writer.write_all(&self.ephemeral_key.as_bytes()[..])?; + writer.write_all(&self.random_seed[..])?; + self.vmacs[0].zcash_serialize(&mut writer)?; + self.vmacs[1].zcash_serialize(&mut writer)?; + self.zkproof.zcash_serialize(&mut writer)?; + self.enc_ciphertexts[0].zcash_serialize(&mut writer)?; + self.enc_ciphertexts[1].zcash_serialize(&mut writer)?; + Ok(()) + } +} + +impl ZcashDeserialize for JoinSplit

{ + fn zcash_deserialize(mut reader: R) -> Result { + Ok(JoinSplit::

{ + vpub_old: reader.read_u64::()?.try_into()?, + vpub_new: reader.read_u64::()?.try_into()?, + anchor: tree::NoteTreeRootHash::from(reader.read_32_bytes()?), + nullifiers: [ + reader.read_32_bytes()?.into(), + reader.read_32_bytes()?.into(), + ], + commitments: [ + commitment::NoteCommitment::from(reader.read_32_bytes()?), + commitment::NoteCommitment::from(reader.read_32_bytes()?), + ], + ephemeral_key: x25519_dalek::PublicKey::from(reader.read_32_bytes()?), + random_seed: reader.read_32_bytes()?, + vmacs: [ + note::MAC::zcash_deserialize(&mut reader)?, + note::MAC::zcash_deserialize(&mut reader)?, + ], + zkproof: P::zcash_deserialize(&mut reader)?, + enc_ciphertexts: [ + note::EncryptedCiphertext::zcash_deserialize(&mut reader)?, + note::EncryptedCiphertext::zcash_deserialize(&mut reader)?, + ], + }) + } +} diff --git a/zebra-chain/src/sprout/tests.rs b/zebra-chain/src/sprout/tests.rs new file mode 100644 index 00000000..be9770e9 --- /dev/null +++ b/zebra-chain/src/sprout/tests.rs @@ -0,0 +1 @@ +mod arbitrary; diff --git a/zebra-chain/src/sprout/tests/arbitrary.rs b/zebra-chain/src/sprout/tests/arbitrary.rs new file mode 100644 index 00000000..465d9dbc --- /dev/null +++ b/zebra-chain/src/sprout/tests/arbitrary.rs @@ -0,0 +1,57 @@ +use proptest::{arbitrary::any, array, prelude::*}; + +use crate::{ + amount::{Amount, NonNegative}, + primitives::ZkSnarkProof, +}; + +use super::super::{commitment, note, tree, JoinSplit}; + +impl Arbitrary for JoinSplit

{ + type Parameters = (); + + fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { + ( + any::>(), + any::>(), + any::(), + array::uniform2(any::()), + array::uniform2(any::()), + array::uniform32(any::()), + array::uniform32(any::()), + array::uniform2(any::()), + any::

(), + array::uniform2(any::()), + ) + .prop_map( + |( + vpub_old, + vpub_new, + anchor, + nullifiers, + commitments, + ephemeral_key_bytes, + random_seed, + vmacs, + zkproof, + enc_ciphertexts, + )| { + Self { + vpub_old, + vpub_new, + anchor, + nullifiers, + commitments, + ephemeral_key: x25519_dalek::PublicKey::from(ephemeral_key_bytes), + random_seed, + vmacs, + zkproof, + enc_ciphertexts, + } + }, + ) + .boxed() + } + + type Strategy = BoxedStrategy; +} diff --git a/zebra-chain/src/transaction.rs b/zebra-chain/src/transaction.rs index c70d0468..8fa40a51 100644 --- a/zebra-chain/src/transaction.rs +++ b/zebra-chain/src/transaction.rs @@ -13,7 +13,7 @@ mod shielded_data; mod tests; pub use hash::TransactionHash; -pub use joinsplit::{JoinSplit, JoinSplitData}; +pub use joinsplit::JoinSplitData; pub use lock_time::LockTime; pub use memo::Memo; pub use shielded_data::ShieldedData; diff --git a/zebra-chain/src/transaction/joinsplit.rs b/zebra-chain/src/transaction/joinsplit.rs index 1d59834a..b7c02502 100644 --- a/zebra-chain/src/transaction/joinsplit.rs +++ b/zebra-chain/src/transaction/joinsplit.rs @@ -1,66 +1,10 @@ use serde::{Deserialize, Serialize}; use crate::{ - amount::{Amount, NonNegative}, - primitives::{ed25519, x25519, ZkSnarkProof}, - sprout, + primitives::{ed25519, ZkSnarkProof}, + sprout::JoinSplit, }; -/// A _JoinSplit Description_, as described in [protocol specification §7.2][ps]. -/// -/// [ps]: https://zips.z.cash/protocol/protocol.pdf#joinsplitencoding -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct JoinSplit { - /// A value that the JoinSplit transfer removes from the transparent value - /// pool. - pub vpub_old: Amount, - /// A value that the JoinSplit transfer inserts into the transparent value - /// pool. - /// - pub vpub_new: Amount, - /// A root of the Sprout note commitment tree at some block height in the - /// past, or the root produced by a previous JoinSplit transfer in this - /// transaction. - pub anchor: sprout::tree::NoteTreeRootHash, - /// A nullifier for the input notes. - pub nullifiers: [sprout::note::Nullifier; 2], - /// A note commitment for this output note. - pub commitments: [sprout::commitment::NoteCommitment; 2], - /// An X25519 public key. - pub ephemeral_key: x25519::PublicKey, - /// A 256-bit seed that must be chosen independently at random for each - /// JoinSplit description. - pub random_seed: [u8; 32], - /// A message authentication tag. - pub vmacs: [sprout::note::MAC; 2], - /// A ZK JoinSplit proof, either a - /// [`Groth16Proof`](crate::primitives::Groth16Proof) or a - /// [`Bctv14Proof`](crate::primitives::Bctv14Proof). - #[serde(bound(serialize = "P: ZkSnarkProof", deserialize = "P: ZkSnarkProof"))] - pub zkproof: P, - /// A ciphertext component for this output note. - pub enc_ciphertexts: [sprout::note::EncryptedCiphertext; 2], -} - -// Because x25519_dalek::PublicKey does not impl PartialEq -impl PartialEq for JoinSplit

{ - fn eq(&self, other: &Self) -> bool { - self.vpub_old == other.vpub_old - && self.vpub_new == other.vpub_new - && self.anchor == other.anchor - && self.nullifiers == other.nullifiers - && self.commitments == other.commitments - && self.ephemeral_key.as_bytes() == other.ephemeral_key.as_bytes() - && self.random_seed == other.random_seed - && self.vmacs == other.vmacs - && self.zkproof == other.zkproof - && self.enc_ciphertexts == other.enc_ciphertexts - } -} - -// Because x25519_dalek::PublicKey does not impl Eq -impl Eq for JoinSplit

{} - /// A bundle of JoinSplit descriptions and signature data. #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct JoinSplitData { @@ -68,7 +12,7 @@ pub struct JoinSplitData { /// /// Storing this separately from `rest` ensures that it is impossible /// to construct an invalid `JoinSplitData` with no `JoinSplit`s. - /// + ///` /// However, it's not necessary to access or process `first` and `rest` /// separately, as the [`JoinSplitData::joinsplits`] method provides an /// iterator over all of the `JoinSplit`s. diff --git a/zebra-chain/src/transaction/serialize.rs b/zebra-chain/src/transaction/serialize.rs index 4221b0ea..acba1274 100644 --- a/zebra-chain/src/transaction/serialize.rs +++ b/zebra-chain/src/transaction/serialize.rs @@ -31,56 +31,6 @@ impl ZcashDeserialize for jubjub::Fq { } } } - -impl ZcashSerialize for JoinSplit

{ - fn zcash_serialize(&self, mut writer: W) -> Result<(), io::Error> { - writer.write_u64::(self.vpub_old.into())?; - writer.write_u64::(self.vpub_new.into())?; - writer.write_32_bytes(&self.anchor.into())?; - writer.write_32_bytes(&self.nullifiers[0].into())?; - writer.write_32_bytes(&self.nullifiers[1].into())?; - writer.write_32_bytes(&self.commitments[0].into())?; - writer.write_32_bytes(&self.commitments[1].into())?; - writer.write_all(&self.ephemeral_key.as_bytes()[..])?; - writer.write_all(&self.random_seed[..])?; - self.vmacs[0].zcash_serialize(&mut writer)?; - self.vmacs[1].zcash_serialize(&mut writer)?; - self.zkproof.zcash_serialize(&mut writer)?; - self.enc_ciphertexts[0].zcash_serialize(&mut writer)?; - self.enc_ciphertexts[1].zcash_serialize(&mut writer)?; - Ok(()) - } -} - -impl ZcashDeserialize for JoinSplit

{ - fn zcash_deserialize(mut reader: R) -> Result { - Ok(JoinSplit::

{ - vpub_old: reader.read_u64::()?.try_into()?, - vpub_new: reader.read_u64::()?.try_into()?, - anchor: sprout::tree::NoteTreeRootHash::from(reader.read_32_bytes()?), - nullifiers: [ - reader.read_32_bytes()?.into(), - reader.read_32_bytes()?.into(), - ], - commitments: [ - sprout::commitment::NoteCommitment::from(reader.read_32_bytes()?), - sprout::commitment::NoteCommitment::from(reader.read_32_bytes()?), - ], - ephemeral_key: x25519_dalek::PublicKey::from(reader.read_32_bytes()?), - random_seed: reader.read_32_bytes()?, - vmacs: [ - sprout::note::MAC::zcash_deserialize(&mut reader)?, - sprout::note::MAC::zcash_deserialize(&mut reader)?, - ], - zkproof: P::zcash_deserialize(&mut reader)?, - enc_ciphertexts: [ - sprout::note::EncryptedCiphertext::zcash_deserialize(&mut reader)?, - sprout::note::EncryptedCiphertext::zcash_deserialize(&mut reader)?, - ], - }) - } -} - impl ZcashSerialize for JoinSplitData

{ fn zcash_serialize(&self, mut writer: W) -> Result<(), io::Error> { writer.write_compactsize(self.joinsplits().count() as u64)?; @@ -99,10 +49,10 @@ impl ZcashDeserialize for Option> { match num_joinsplits { 0 => Ok(None), n => { - let first = JoinSplit::zcash_deserialize(&mut reader)?; + let first = sprout::JoinSplit::zcash_deserialize(&mut reader)?; let mut rest = Vec::with_capacity((n - 1) as usize); for _ in 0..(n - 1) { - rest.push(JoinSplit::zcash_deserialize(&mut reader)?); + rest.push(sprout::JoinSplit::zcash_deserialize(&mut reader)?); } let pub_key = reader.read_32_bytes()?.into(); let sig = reader.read_64_bytes()?.into(); diff --git a/zebra-chain/src/transaction/tests/arbitrary.rs b/zebra-chain/src/transaction/tests/arbitrary.rs index 935bfde1..6660fec2 100644 --- a/zebra-chain/src/transaction/tests/arbitrary.rs +++ b/zebra-chain/src/transaction/tests/arbitrary.rs @@ -3,15 +3,13 @@ use futures::future::Either; use proptest::{arbitrary::any, array, collection::vec, option, prelude::*}; use crate::{ - amount::{Amount, NonNegative}, + amount::Amount, block, primitives::{Bctv14Proof, Groth16Proof, ZkSnarkProof}, sapling, sprout, transparent, }; -use super::super::{ - JoinSplit, JoinSplitData, LockTime, Memo, ShieldedData, Transaction, -}; +use super::super::{JoinSplitData, LockTime, Memo, ShieldedData, Transaction}; impl Transaction { pub fn v1_strategy() -> impl Strategy { @@ -131,62 +129,13 @@ impl Arbitrary for LockTime { type Strategy = BoxedStrategy; } -impl Arbitrary for JoinSplit

{ - type Parameters = (); - - fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { - ( - any::>(), - any::>(), - any::(), - array::uniform2(any::()), - array::uniform2(any::()), - array::uniform32(any::()), - array::uniform32(any::()), - array::uniform2(any::()), - any::

(), - array::uniform2(any::()), - ) - .prop_map( - |( - vpub_old, - vpub_new, - anchor, - nullifiers, - commitments, - ephemeral_key_bytes, - random_seed, - vmacs, - zkproof, - enc_ciphertexts, - )| { - Self { - vpub_old, - vpub_new, - anchor, - nullifiers, - commitments, - ephemeral_key: x25519_dalek::PublicKey::from(ephemeral_key_bytes), - random_seed, - vmacs, - zkproof, - enc_ciphertexts, - } - }, - ) - .boxed() - } - - type Strategy = BoxedStrategy; -} - impl Arbitrary for JoinSplitData

{ type Parameters = (); fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { ( - any::>(), - vec(any::>(), 0..10), + any::>(), + vec(any::>(), 0..10), array::uniform32(any::()), vec(any::(), 64), ) @@ -235,7 +184,6 @@ impl Arbitrary for ShieldedData { type Strategy = BoxedStrategy; } - impl Arbitrary for Transaction { type Parameters = ();