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:
parent
d945cd28e8
commit
1fc859d0c5
|
@ -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;
|
|
@ -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)?,
|
||||
})
|
||||
}
|
||||
}
|
|
@ -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(),
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
mod arbitrary;
|
|
@ -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>;
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 = ();
|
||||
|
|
Loading…
Reference in New Issue