chain: move Spend, Output into sapling

The ShieldedData container for the spend and output descriptions of a
transaction does *not* move, because it's encoding transaction
structure.  A good sign that this is the right factoring is that the
transaction module now only needs to pull in sapling::{Spend, Output}
and not all the internals.
This commit is contained in:
Henry de Valence 2020-08-17 01:48:47 -07:00
parent d945cd28e8
commit 1fc859d0c5
9 changed files with 200 additions and 169 deletions

View File

@ -1,7 +1,18 @@
//! Sapling-related functionality.
mod spend;
mod output;
pub use spend::Spend;
pub use output::Output;
// XXX clean up these modules
pub mod address;
pub mod commitment;
pub mod keys;
pub mod note;
pub mod tree;
#[cfg(test)]
mod tests;

View File

@ -0,0 +1,53 @@
use std::io;
use crate::{
primitives::Groth16Proof,
serialization::{serde_helpers, SerializationError, ZcashDeserialize, ZcashSerialize},
};
use super::{commitment, keys, note};
/// A _Output Description_, as described in [protocol specification §7.4][ps].
///
/// [ps]: https://zips.z.cash/protocol/protocol.pdf#outputencoding
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Output {
/// A value commitment to the value of the input note.
pub cv: commitment::ValueCommitment,
/// The u-coordinate of the note commitment for the output note.
#[serde(with = "serde_helpers::Fq")]
pub cm_u: jubjub::Fq,
/// An encoding of an ephemeral Jubjub public key.
pub ephemeral_key: keys::EphemeralPublicKey,
/// A ciphertext component for the encrypted output note.
pub enc_ciphertext: note::EncryptedCiphertext,
/// A ciphertext component for the encrypted output note.
pub out_ciphertext: note::OutCiphertext,
/// The ZK output proof.
pub zkproof: Groth16Proof,
}
impl ZcashSerialize for Output {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
self.cv.zcash_serialize(&mut writer)?;
writer.write_all(&self.cm_u.to_bytes())?;
self.ephemeral_key.zcash_serialize(&mut writer)?;
self.enc_ciphertext.zcash_serialize(&mut writer)?;
self.out_ciphertext.zcash_serialize(&mut writer)?;
self.zkproof.zcash_serialize(&mut writer)?;
Ok(())
}
}
impl ZcashDeserialize for Output {
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
Ok(Output {
cv: commitment::ValueCommitment::zcash_deserialize(&mut reader)?,
cm_u: jubjub::Fq::zcash_deserialize(&mut reader)?,
ephemeral_key: keys::EphemeralPublicKey::zcash_deserialize(&mut reader)?,
enc_ciphertext: note::EncryptedCiphertext::zcash_deserialize(&mut reader)?,
out_ciphertext: note::OutCiphertext::zcash_deserialize(&mut reader)?,
zkproof: Groth16Proof::zcash_deserialize(&mut reader)?,
})
}
}

View File

@ -0,0 +1,60 @@
use std::io;
use crate::{
primitives::{
redjubjub::{self, SpendAuth},
Groth16Proof,
},
serialization::{
ReadZcashExt, SerializationError, WriteZcashExt, ZcashDeserialize, ZcashSerialize,
},
};
use super::{commitment, note, tree};
/// A _Spend Description_, as described in [protocol specification §7.3][ps].
///
/// [ps]: https://zips.z.cash/protocol/protocol.pdf#spendencoding
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Spend {
/// A value commitment to the value of the input note.
pub cv: commitment::ValueCommitment,
/// A root of the Sapling note commitment tree at some block height in the past.
pub anchor: tree::SaplingNoteTreeRootHash,
/// The nullifier of the input note.
pub nullifier: note::Nullifier,
/// The randomized public key for `spend_auth_sig`.
pub rk: redjubjub::VerificationKeyBytes<SpendAuth>,
/// The ZK spend proof.
pub zkproof: Groth16Proof,
/// A signature authorizing this spend.
pub spend_auth_sig: redjubjub::Signature<SpendAuth>,
}
impl ZcashSerialize for Spend {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
self.cv.zcash_serialize(&mut writer)?;
writer.write_all(&self.anchor.0[..])?;
writer.write_32_bytes(&self.nullifier.into())?;
writer.write_all(&<[u8; 32]>::from(self.rk)[..])?;
self.zkproof.zcash_serialize(&mut writer)?;
writer.write_all(&<[u8; 64]>::from(self.spend_auth_sig)[..])?;
Ok(())
}
}
impl ZcashDeserialize for Spend {
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
use crate::sapling::{
commitment::ValueCommitment, note::Nullifier, tree::SaplingNoteTreeRootHash,
};
Ok(Spend {
cv: ValueCommitment::zcash_deserialize(&mut reader)?,
anchor: SaplingNoteTreeRootHash(reader.read_32_bytes()?),
nullifier: Nullifier::from(reader.read_32_bytes()?),
rk: reader.read_32_bytes()?.into(),
zkproof: Groth16Proof::zcash_deserialize(&mut reader)?,
spend_auth_sig: reader.read_64_bytes()?.into(),
})
}
}

View File

@ -0,0 +1 @@
mod arbitrary;

View File

@ -0,0 +1,66 @@
use proptest::{arbitrary::any, array, collection::vec, prelude::*};
use crate::primitives::Groth16Proof;
use super::super::{Spend, Output, keys, tree, commitment, note};
impl Arbitrary for Spend {
type Parameters = ();
fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
(
any::<tree::SaplingNoteTreeRootHash>(),
any::<commitment::ValueCommitment>(),
any::<note::Nullifier>(),
array::uniform32(any::<u8>()),
any::<Groth16Proof>(),
vec(any::<u8>(), 64),
)
.prop_map(
|(anchor, cv, nullifier, rpk_bytes, proof, sig_bytes)| Self {
anchor,
cv,
nullifier,
rk: redjubjub::VerificationKeyBytes::from(rpk_bytes),
zkproof: proof,
spend_auth_sig: redjubjub::Signature::from({
let mut b = [0u8; 64];
b.copy_from_slice(sig_bytes.as_slice());
b
}),
},
)
.boxed()
}
type Strategy = BoxedStrategy<Self>;
}
impl Arbitrary for Output {
type Parameters = ();
fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
(
any::<commitment::ValueCommitment>(),
any::<commitment::NoteCommitment>(),
any::<keys::EphemeralPublicKey>(),
any::<note::EncryptedCiphertext>(),
any::<note::OutCiphertext>(),
any::<Groth16Proof>(),
)
.prop_map(
|(cv, cm, ephemeral_key, enc_ciphertext, out_ciphertext, zkproof)| Self {
cv,
cm_u: cm.extract_u(),
ephemeral_key,
enc_ciphertext,
out_ciphertext,
zkproof,
},
)
.boxed()
}
type Strategy = BoxedStrategy<Self>;
}

View File

@ -16,7 +16,7 @@ pub use hash::TransactionHash;
pub use joinsplit::{JoinSplit, JoinSplitData};
pub use lock_time::LockTime;
pub use memo::Memo;
pub use shielded_data::{Output, ShieldedData, Spend};
pub use shielded_data::ShieldedData;
use crate::{
amount::Amount,

View File

@ -117,63 +117,6 @@ impl<P: ZkSnarkProof> ZcashDeserialize for Option<JoinSplitData<P>> {
}
}
impl ZcashSerialize for Spend {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
self.cv.zcash_serialize(&mut writer)?;
writer.write_all(&self.anchor.0[..])?;
writer.write_32_bytes(&self.nullifier.into())?;
writer.write_all(&<[u8; 32]>::from(self.rk)[..])?;
self.zkproof.zcash_serialize(&mut writer)?;
writer.write_all(&<[u8; 64]>::from(self.spend_auth_sig)[..])?;
Ok(())
}
}
impl ZcashDeserialize for Spend {
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
use crate::sapling::{
commitment::ValueCommitment, note::Nullifier, tree::SaplingNoteTreeRootHash,
};
Ok(Spend {
cv: ValueCommitment::zcash_deserialize(&mut reader)?,
anchor: SaplingNoteTreeRootHash(reader.read_32_bytes()?),
nullifier: Nullifier::from(reader.read_32_bytes()?),
rk: reader.read_32_bytes()?.into(),
zkproof: Groth16Proof::zcash_deserialize(&mut reader)?,
spend_auth_sig: reader.read_64_bytes()?.into(),
})
}
}
impl ZcashSerialize for Output {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
self.cv.zcash_serialize(&mut writer)?;
writer.write_all(&self.cm_u.to_bytes())?;
self.ephemeral_key.zcash_serialize(&mut writer)?;
self.enc_ciphertext.zcash_serialize(&mut writer)?;
self.out_ciphertext.zcash_serialize(&mut writer)?;
self.zkproof.zcash_serialize(&mut writer)?;
Ok(())
}
}
impl ZcashDeserialize for Output {
fn zcash_deserialize<R: io::Read>(mut reader: R) -> Result<Self, SerializationError> {
use crate::sapling::{
commitment::ValueCommitment, keys::EphemeralPublicKey, note::EncryptedCiphertext,
note::OutCiphertext,
};
Ok(Output {
cv: ValueCommitment::zcash_deserialize(&mut reader)?,
cm_u: jubjub::Fq::zcash_deserialize(&mut reader)?,
ephemeral_key: EphemeralPublicKey::zcash_deserialize(&mut reader)?,
enc_ciphertext: EncryptedCiphertext::zcash_deserialize(&mut reader)?,
out_ciphertext: OutCiphertext::zcash_deserialize(&mut reader)?,
zkproof: Groth16Proof::zcash_deserialize(&mut reader)?,
})
}
}
impl ZcashSerialize for Transaction {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
// Post-Sapling, transaction size is limited to MAX_BLOCK_BYTES.

View File

@ -1,55 +1,11 @@
use futures::future::Either;
use crate::{
primitives::{
redjubjub::{self, Binding, SpendAuth},
Groth16Proof,
},
sapling::{commitment, keys, note, tree},
primitives::redjubjub::{Binding, Signature},
sapling::{Output, Spend},
serialization::serde_helpers,
};
/// A _Spend Description_, as described in [protocol specification §7.3][ps].
///
/// [ps]: https://zips.z.cash/protocol/protocol.pdf#spendencoding
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Spend {
/// A value commitment to the value of the input note.
pub cv: commitment::ValueCommitment,
/// A root of the Sapling note commitment tree at some block height in the past.
pub anchor: tree::SaplingNoteTreeRootHash,
/// The nullifier of the input note.
pub nullifier: note::Nullifier,
/// The randomized public key for `spend_auth_sig`.
pub rk: redjubjub::VerificationKeyBytes<SpendAuth>,
/// The ZK spend proof.
pub zkproof: Groth16Proof,
/// A signature authorizing this spend.
pub spend_auth_sig: redjubjub::Signature<SpendAuth>,
}
/// A _Output Description_, as described in [protocol specification §7.4][ps].
///
/// [ps]: https://zips.z.cash/protocol/protocol.pdf#outputencoding
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Output {
/// A value commitment to the value of the input note.
pub cv: commitment::ValueCommitment,
/// The u-coordinate of the note commitment for the output note.
#[serde(with = "serde_helpers::Fq")]
pub cm_u: jubjub::Fq,
/// An encoding of an ephemeral Jubjub public key.
pub ephemeral_key: keys::EphemeralPublicKey,
/// A ciphertext component for the encrypted output note.
pub enc_ciphertext: note::EncryptedCiphertext,
/// A ciphertext component for the encrypted output note.
pub out_ciphertext: note::OutCiphertext,
/// The ZK output proof.
pub zkproof: Groth16Proof,
}
impl Eq for Output {}
/// Sapling-on-Groth16 spend and output descriptions.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ShieldedData {
@ -75,7 +31,7 @@ pub struct ShieldedData {
/// over all output descriptions.
pub rest_outputs: Vec<Output>,
/// A signature on the transaction hash.
pub binding_sig: redjubjub::Signature<Binding>,
pub binding_sig: Signature<Binding>,
}
impl ShieldedData {

View File

@ -10,7 +10,7 @@ use crate::{
};
use super::super::{
JoinSplit, JoinSplitData, LockTime, Memo, Output, ShieldedData, Spend, Transaction,
JoinSplit, JoinSplitData, LockTime, Memo, ShieldedData, Transaction,
};
impl Transaction {
@ -206,45 +206,17 @@ impl<P: ZkSnarkProof + Arbitrary + 'static> Arbitrary for JoinSplitData<P> {
type Strategy = BoxedStrategy<Self>;
}
impl Arbitrary for Output {
type Parameters = ();
fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
(
any::<sapling::commitment::ValueCommitment>(),
any::<sapling::commitment::NoteCommitment>(),
any::<sapling::keys::EphemeralPublicKey>(),
any::<sapling::note::EncryptedCiphertext>(),
any::<sapling::note::OutCiphertext>(),
any::<Groth16Proof>(),
)
.prop_map(
|(cv, cm, ephemeral_key, enc_ciphertext, out_ciphertext, zkproof)| Self {
cv,
cm_u: cm.extract_u(),
ephemeral_key,
enc_ciphertext,
out_ciphertext,
zkproof,
},
)
.boxed()
}
type Strategy = BoxedStrategy<Self>;
}
impl Arbitrary for ShieldedData {
type Parameters = ();
fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
(
prop_oneof![
any::<Spend>().prop_map(Either::Left),
any::<Output>().prop_map(Either::Right)
any::<sapling::Spend>().prop_map(Either::Left),
any::<sapling::Output>().prop_map(Either::Right)
],
vec(any::<Spend>(), 0..10),
vec(any::<Output>(), 0..10),
vec(any::<sapling::Spend>(), 0..10),
vec(any::<sapling::Output>(), 0..10),
vec(any::<u8>(), 64),
)
.prop_map(|(first, rest_spends, rest_outputs, sig_bytes)| Self {
@ -263,37 +235,6 @@ impl Arbitrary for ShieldedData {
type Strategy = BoxedStrategy<Self>;
}
impl Arbitrary for Spend {
type Parameters = ();
fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
(
any::<sapling::tree::SaplingNoteTreeRootHash>(),
any::<sapling::commitment::ValueCommitment>(),
any::<sapling::note::Nullifier>(),
array::uniform32(any::<u8>()),
any::<Groth16Proof>(),
vec(any::<u8>(), 64),
)
.prop_map(
|(anchor, cv, nullifier, rpk_bytes, proof, sig_bytes)| Self {
anchor,
cv,
nullifier,
rk: redjubjub::VerificationKeyBytes::from(rpk_bytes),
zkproof: proof,
spend_auth_sig: redjubjub::Signature::from({
let mut b = [0u8; 64];
b.copy_from_slice(sig_bytes.as_slice());
b
}),
},
)
.boxed()
}
type Strategy = BoxedStrategy<Self>;
}
impl Arbitrary for Transaction {
type Parameters = ();