zcash_primitives: Move Sapling bundle types into `sapling` module

This commit is contained in:
Jack Grigg 2023-11-09 03:05:55 +00:00
parent f5595122f9
commit 54eb03e34e
19 changed files with 934 additions and 846 deletions

View File

@ -7,8 +7,8 @@ use zcash_primitives::{
block::{BlockHash, BlockHeader},
consensus::BlockHeight,
merkle_tree::read_commitment_tree,
sapling::{note::ExtractedNoteCommitment, Node, Nullifier, NOTE_COMMITMENT_TREE_DEPTH},
transaction::{components::sapling, TxId},
sapling::{self, note::ExtractedNoteCommitment, Node, Nullifier, NOTE_COMMITMENT_TREE_DEPTH},
transaction::TxId,
};
use zcash_note_encryption::{EphemeralKeyBytes, COMPACT_NOTE_SIZE};
@ -116,8 +116,12 @@ impl compact_formats::CompactSaplingOutput {
}
}
impl<Proof> From<&sapling::OutputDescription<Proof>> for compact_formats::CompactSaplingOutput {
fn from(out: &sapling::OutputDescription<Proof>) -> compact_formats::CompactSaplingOutput {
impl<Proof> From<&sapling::bundle::OutputDescription<Proof>>
for compact_formats::CompactSaplingOutput
{
fn from(
out: &sapling::bundle::OutputDescription<Proof>,
) -> compact_formats::CompactSaplingOutput {
compact_formats::CompactSaplingOutput {
cmu: out.cmu().to_bytes().to_vec(),
ephemeral_key: out.ephemeral_key().as_ref().to_vec(),
@ -126,11 +130,13 @@ impl<Proof> From<&sapling::OutputDescription<Proof>> for compact_formats::Compac
}
}
impl TryFrom<compact_formats::CompactSaplingOutput> for sapling::CompactOutputDescription {
impl TryFrom<compact_formats::CompactSaplingOutput>
for sapling::note_encryption::CompactOutputDescription
{
type Error = ();
fn try_from(value: compact_formats::CompactSaplingOutput) -> Result<Self, Self::Error> {
Ok(sapling::CompactOutputDescription {
Ok(sapling::note_encryption::CompactOutputDescription {
cmu: value.cmu()?,
ephemeral_key: value.ephemeral_key()?,
enc_ciphertext: value.ciphertext.try_into().map_err(|_| ())?,
@ -144,10 +150,10 @@ impl compact_formats::CompactSaplingSpend {
}
}
impl<A: sapling::Authorization> From<&sapling::SpendDescription<A>>
impl<A: sapling::bundle::Authorization> From<&sapling::bundle::SpendDescription<A>>
for compact_formats::CompactSaplingSpend
{
fn from(spend: &sapling::SpendDescription<A>) -> compact_formats::CompactSaplingSpend {
fn from(spend: &sapling::bundle::SpendDescription<A>) -> compact_formats::CompactSaplingSpend {
compact_formats::CompactSaplingSpend {
nf: spend.nullifier().to_vec(),
}

View File

@ -12,10 +12,9 @@ use zcash_primitives::{
consensus,
sapling::{
self,
note_encryption::{PreparedIncomingViewingKey, SaplingDomain},
note_encryption::{CompactOutputDescription, PreparedIncomingViewingKey, SaplingDomain},
SaplingIvk,
},
transaction::components::sapling::CompactOutputDescription,
zip32::{sapling::DiversifiableFullViewingKey, AccountId, Scope},
};

View File

@ -18,11 +18,34 @@ and this library adheres to Rust's notion of
- `builder::{InProgressProofs, Unproven, Proven}`
- `builder::{InProgressSignatures, Unsigned, PartiallyAuthorized}`
- `builder::{MaybeSigned, SigningParts}`
- `bundle` module, containing the following types moved from
`zcash_primitives::transaction::components::sapling`:
- `Bundle`
- `SpendDescription, SpendDescriptionV5`
- `OutputDescription, OutputDescriptionV5`
- `Authorization, Authorized, MapAuth`
- `GrothProofBytes`
- `bundle::Bundle::<InProgress<Unproven, _>>::create_proofs`
- `bundle::Bundle::<InProgress<_, Unsigned>>::prepare`
- `bundle::Bundle::<InProgress<_, PartiallyAuthorized>>::{sign, append_signatures}`
- `bundle::Bundle::<InProgress<Proven, PartiallyAuthorized>>::finalize`
- `bundle::Bundle::<InProgress<Proven, Unsigned>>::apply_signatures`
- `bundle::Bundle::try_map_authorization`
- `bundle::TryMapAuth`
- `impl bundle::{MapAuth, TryMapAuth} for (FnMut, FnMut, FnMut, FnMut)`
helpers to enable calling `Bundle::{map_authorization, try_map_authorization}`
with a set of closures.
- `bundle::testing` module, containing the following functions moved from
`zcash_primitives::transaction::components::sapling::testing`:
- `arb_output_description`
- `arb_bundle`
- `circuit` module (moved from `zcash_proofs::circuit::sapling`).
- `circuit::{SpendParameters, OutputParameters}`
- `circuit::{SpendVerifyingKey, PreparedSpendVerifyingKey}`
- `circuit::{OutputVerifyingKey, PreparedOutputVerifyingKey}`
- `constants` module.
- `note_encryption::CompactOutputDescription` (moved from
`zcash_primitives::transaction::components::sapling`).
- `prover::{SpendProver, OutputProver}`
- `value`:
- `ValueCommitTrapdoor::from_bytes`
@ -31,17 +54,6 @@ and this library adheres to Rust's notion of
- `impl Debug for keys::{ExpandedSpendingKey, ProofGenerationKey}`
- `zcash_primitives::transaction`:
- `builder::get_fee`
- `components::sapling`:
- `Bundle::<InProgress<Unproven, _>>::create_proofs`
- `Bundle::<InProgress<_, Unsigned>>::prepare`
- `Bundle::<InProgress<_, PartiallyAuthorized>>::{sign, append_signatures}`
- `Bundle::<InProgress<Proven, PartiallyAuthorized>>::finalize`
- `Bundle::<InProgress<Proven, Unsigned>>::apply_signatures`
- `Bundle::try_map_authorization`
- `TryMapAuth`
- `impl {MapAuth, TryMapAuth} for (FnMut, FnMut, FnMut, FnMut)` helpers to
enable calling `Bundle::{map_authorization, try_map_authorization}` with a
set of closures.
- `fees::StandardFeeRule`
- Constants in `fees::zip317`:
- `MARGINAL_FEE`
@ -82,13 +94,12 @@ and this library adheres to Rust's notion of
- `Error::DuplicateSignature`
- `Error::InvalidExternalSignature`
- `Error::MissingSignatures`
- `bundle::MapAuth` trait methods now take `&mut self` instead of `&self`.
- `circuit::ValueCommitmentOpening::value` is now represented as a `NoteValue`
instead of as a bare `u64`.
- `zcash_primitives::transaction`:
- `builder::Builder::{build, build_zfuture}` now take
`&impl SpendProver, &impl OutputProver` instead of `&impl TxProver`.
- `components::sapling`:
- `MapAuth` trait methods now take `&mut self` instead of `&self`.
- `components::transparent::TxOut.value` now has type `NonNegativeAmount`
instead of `Amount`.
- `Unauthorized::SaplingAuth` now has type `InProgress<Proven, Unsigned>`.
@ -114,9 +125,20 @@ and this library adheres to Rust's notion of
- `zcash_primitives::constants`:
- All `const` values (moved to `zcash_primitives::sapling::constants`).
- `zcash_primitives::transaction::components::sapling`:
- The following types were removed from this module (moved into
`zcash_primitives::sapling::bundle`):
- `Bundle`
- `SpendDescription, SpendDescriptionV5`
- `OutputDescription, OutputDescriptionV5`
- `Authorization, Authorized, MapAuth`
- `GrothProofBytes`
- `CompactOutputDescription` (moved to
`zcash_primitives::sapling::note_encryption`).
- `Unproven`
- `builder` (moved to `zcash_primitives::sapling::builder`).
- `builder::Unauthorized` (use `builder::InProgress` instead).
- `testing::{arb_bundle, arb_output_description}` (moved into
`zcash_primitives::sapling::bundle::testing`).
- `SpendDescription::<Unauthorized>::apply_signature`
- `Bundle::<Unauthorized>::apply_signatures` (use
`Bundle::<InProgress<Proven, Unsigned>>::apply_signatures` instead).

View File

@ -11,13 +11,12 @@ use zcash_primitives::{
builder::SaplingBuilder,
note_encryption::{
try_sapling_compact_note_decryption, try_sapling_note_decryption,
PreparedIncomingViewingKey, SaplingDomain,
CompactOutputDescription, PreparedIncomingViewingKey, SaplingDomain,
},
prover::mock::{MockOutputProver, MockSpendProver},
value::NoteValue,
Diversifier, SaplingIvk,
},
transaction::components::sapling::CompactOutputDescription,
};
#[cfg(unix)]

View File

@ -2,6 +2,7 @@
mod address;
pub mod builder;
pub mod bundle;
pub mod circuit;
pub mod constants;
pub mod group_hash;
@ -25,6 +26,7 @@ use constants::SPENDING_KEY_GENERATOR;
use self::redjubjub::{PrivateKey, PublicKey, Signature};
pub use address::PaymentAddress;
pub use bundle::Bundle;
pub use keys::{Diversifier, NullifierDerivingKey, ProofGenerationKey, SaplingIvk, ViewingKey};
pub use note::{nullifier::Nullifier, Note, Rseed};
pub use tree::{

View File

@ -13,6 +13,10 @@ use crate::{
memo::MemoBytes,
sapling::{
self,
bundle::{
Authorization, Authorized, Bundle, GrothProofBytes, MapAuth, OutputDescription,
SpendDescription,
},
constants::{SPENDING_KEY_GENERATOR, VALUE_COMMITMENT_RANDOMNESS_GENERATOR},
note_encryption::sapling_note_encryption,
prover::{OutputProver, SpendProver},
@ -29,10 +33,7 @@ use crate::{
builder::Progress,
components::{
amount::{Amount, NonNegativeAmount},
sapling::{
fees, Authorization, Authorized, Bundle, GrothProofBytes, MapAuth,
OutputDescription, SpendDescription,
},
sapling::fees,
},
},
zip32::ExtendedSpendingKey,
@ -885,16 +886,14 @@ pub mod testing {
TEST_NETWORK,
},
sapling::{
bundle::{Authorized, Bundle},
prover::mock::{MockOutputProver, MockSpendProver},
redjubjub::PrivateKey,
testing::{arb_node, arb_note},
value::testing::arb_positive_note_value,
Diversifier,
},
transaction::components::{
amount::MAX_MONEY,
sapling::{Authorized, Bundle},
},
transaction::components::amount::MAX_MONEY,
zip32::sapling::testing::arb_extended_spending_key,
};
use incrementalmerkletree::{

View File

@ -0,0 +1,736 @@
use core::fmt::Debug;
use memuse::DynamicUsage;
use zcash_note_encryption::{
EphemeralKeyBytes, ShieldedOutput, COMPACT_NOTE_SIZE, ENC_CIPHERTEXT_SIZE,
};
use crate::{
consensus,
sapling::{
note::ExtractedNoteCommitment,
note_encryption::{CompactOutputDescription, SaplingDomain},
redjubjub::{self, PublicKey, Signature},
value::ValueCommitment,
Nullifier,
},
transaction::components::{Amount, GROTH_PROOF_SIZE},
};
pub type GrothProofBytes = [u8; GROTH_PROOF_SIZE];
/// Defines the authorization type of a Sapling bundle.
pub trait Authorization: Debug {
type SpendProof: Clone + Debug;
type OutputProof: Clone + Debug;
type AuthSig: Clone + Debug;
}
/// Authorizing data for a bundle of Sapling spends and outputs, ready to be committed to
/// the ledger.
#[derive(Debug, Copy, Clone)]
pub struct Authorized {
// TODO: Make this private.
pub binding_sig: redjubjub::Signature,
}
impl Authorization for Authorized {
type SpendProof = GrothProofBytes;
type OutputProof = GrothProofBytes;
type AuthSig = redjubjub::Signature;
}
/// A map from one bundle authorization to another.
///
/// For use with [`Bundle::map_authorization`].
pub trait MapAuth<A: Authorization, B: Authorization> {
fn map_spend_proof(&mut self, p: A::SpendProof) -> B::SpendProof;
fn map_output_proof(&mut self, p: A::OutputProof) -> B::OutputProof;
fn map_auth_sig(&mut self, s: A::AuthSig) -> B::AuthSig;
fn map_authorization(&mut self, a: A) -> B;
}
/// The identity map.
///
/// This can be used with [`TransactionData::map_authorization`] when you want to map the
/// authorization of a subset of the transaction's bundles.
///
/// [`TransactionData::map_authorization`]: crate::transaction::TransactionData::map_authorization
impl MapAuth<Authorized, Authorized> for () {
fn map_spend_proof(
&mut self,
p: <Authorized as Authorization>::SpendProof,
) -> <Authorized as Authorization>::SpendProof {
p
}
fn map_output_proof(
&mut self,
p: <Authorized as Authorization>::OutputProof,
) -> <Authorized as Authorization>::OutputProof {
p
}
fn map_auth_sig(
&mut self,
s: <Authorized as Authorization>::AuthSig,
) -> <Authorized as Authorization>::AuthSig {
s
}
fn map_authorization(&mut self, a: Authorized) -> Authorized {
a
}
}
/// A helper for implementing `MapAuth` with a set of closures.
impl<A, B, F, G, H, I> MapAuth<A, B> for (F, G, H, I)
where
A: Authorization,
B: Authorization,
F: FnMut(A::SpendProof) -> B::SpendProof,
G: FnMut(A::OutputProof) -> B::OutputProof,
H: FnMut(A::AuthSig) -> B::AuthSig,
I: FnMut(A) -> B,
{
fn map_spend_proof(&mut self, p: A::SpendProof) -> B::SpendProof {
self.0(p)
}
fn map_output_proof(&mut self, p: A::OutputProof) -> B::OutputProof {
self.1(p)
}
fn map_auth_sig(&mut self, s: A::AuthSig) -> B::AuthSig {
self.2(s)
}
fn map_authorization(&mut self, a: A) -> B {
self.3(a)
}
}
/// A fallible map from one bundle authorization to another.
///
/// For use with [`Bundle::try_map_authorization`].
pub trait TryMapAuth<A: Authorization, B: Authorization> {
type Error;
fn try_map_spend_proof(&mut self, p: A::SpendProof) -> Result<B::SpendProof, Self::Error>;
fn try_map_output_proof(&mut self, p: A::OutputProof) -> Result<B::OutputProof, Self::Error>;
fn try_map_auth_sig(&mut self, s: A::AuthSig) -> Result<B::AuthSig, Self::Error>;
fn try_map_authorization(&mut self, a: A) -> Result<B, Self::Error>;
}
/// A helper for implementing `TryMapAuth` with a set of closures.
impl<A, B, E, F, G, H, I> TryMapAuth<A, B> for (F, G, H, I)
where
A: Authorization,
B: Authorization,
F: FnMut(A::SpendProof) -> Result<B::SpendProof, E>,
G: FnMut(A::OutputProof) -> Result<B::OutputProof, E>,
H: FnMut(A::AuthSig) -> Result<B::AuthSig, E>,
I: FnMut(A) -> Result<B, E>,
{
type Error = E;
fn try_map_spend_proof(&mut self, p: A::SpendProof) -> Result<B::SpendProof, Self::Error> {
self.0(p)
}
fn try_map_output_proof(&mut self, p: A::OutputProof) -> Result<B::OutputProof, Self::Error> {
self.1(p)
}
fn try_map_auth_sig(&mut self, s: A::AuthSig) -> Result<B::AuthSig, Self::Error> {
self.2(s)
}
fn try_map_authorization(&mut self, a: A) -> Result<B, Self::Error> {
self.3(a)
}
}
#[derive(Debug, Clone)]
pub struct Bundle<A: Authorization> {
shielded_spends: Vec<SpendDescription<A>>,
shielded_outputs: Vec<OutputDescription<A::OutputProof>>,
value_balance: Amount,
authorization: A,
}
impl<A: Authorization> Bundle<A> {
/// Constructs a `Bundle` from its constituent parts.
#[cfg(feature = "temporary-zcashd")]
pub fn temporary_zcashd_from_parts(
shielded_spends: Vec<SpendDescription<A>>,
shielded_outputs: Vec<OutputDescription<A::OutputProof>>,
value_balance: Amount,
authorization: A,
) -> Self {
Self::from_parts(
shielded_spends,
shielded_outputs,
value_balance,
authorization,
)
}
/// Constructs a `Bundle` from its constituent parts.
pub(crate) fn from_parts(
shielded_spends: Vec<SpendDescription<A>>,
shielded_outputs: Vec<OutputDescription<A::OutputProof>>,
value_balance: Amount,
authorization: A,
) -> Self {
Bundle {
shielded_spends,
shielded_outputs,
value_balance,
authorization,
}
}
/// Returns the list of spends in this bundle.
pub fn shielded_spends(&self) -> &[SpendDescription<A>] {
&self.shielded_spends
}
/// Returns the list of outputs in this bundle.
pub fn shielded_outputs(&self) -> &[OutputDescription<A::OutputProof>] {
&self.shielded_outputs
}
/// Returns the net value moved into or out of the Sapling shielded pool.
///
/// This is the sum of Sapling spends minus the sum of Sapling outputs.
pub fn value_balance(&self) -> &Amount {
&self.value_balance
}
/// Returns the authorization for this bundle.
///
/// In the case of a `Bundle<Authorized>`, this is the binding signature.
pub fn authorization(&self) -> &A {
&self.authorization
}
/// Transitions this bundle from one authorization state to another.
pub fn map_authorization<B: Authorization, F: MapAuth<A, B>>(self, mut f: F) -> Bundle<B> {
Bundle {
shielded_spends: self
.shielded_spends
.into_iter()
.map(|d| SpendDescription {
cv: d.cv,
anchor: d.anchor,
nullifier: d.nullifier,
rk: d.rk,
zkproof: f.map_spend_proof(d.zkproof),
spend_auth_sig: f.map_auth_sig(d.spend_auth_sig),
})
.collect(),
shielded_outputs: self
.shielded_outputs
.into_iter()
.map(|o| OutputDescription {
cv: o.cv,
cmu: o.cmu,
ephemeral_key: o.ephemeral_key,
enc_ciphertext: o.enc_ciphertext,
out_ciphertext: o.out_ciphertext,
zkproof: f.map_output_proof(o.zkproof),
})
.collect(),
value_balance: self.value_balance,
authorization: f.map_authorization(self.authorization),
}
}
/// Transitions this bundle from one authorization state to another.
pub fn try_map_authorization<B: Authorization, F: TryMapAuth<A, B>>(
self,
mut f: F,
) -> Result<Bundle<B>, F::Error> {
Ok(Bundle {
shielded_spends: self
.shielded_spends
.into_iter()
.map(|d| {
Ok(SpendDescription {
cv: d.cv,
anchor: d.anchor,
nullifier: d.nullifier,
rk: d.rk,
zkproof: f.try_map_spend_proof(d.zkproof)?,
spend_auth_sig: f.try_map_auth_sig(d.spend_auth_sig)?,
})
})
.collect::<Result<_, _>>()?,
shielded_outputs: self
.shielded_outputs
.into_iter()
.map(|o| {
Ok(OutputDescription {
cv: o.cv,
cmu: o.cmu,
ephemeral_key: o.ephemeral_key,
enc_ciphertext: o.enc_ciphertext,
out_ciphertext: o.out_ciphertext,
zkproof: f.try_map_output_proof(o.zkproof)?,
})
})
.collect::<Result<_, _>>()?,
value_balance: self.value_balance,
authorization: f.try_map_authorization(self.authorization)?,
})
}
}
impl DynamicUsage for Bundle<Authorized> {
fn dynamic_usage(&self) -> usize {
self.shielded_spends.dynamic_usage() + self.shielded_outputs.dynamic_usage()
}
fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) {
let bounds = (
self.shielded_spends.dynamic_usage_bounds(),
self.shielded_outputs.dynamic_usage_bounds(),
);
(
bounds.0 .0 + bounds.1 .0,
bounds.0 .1.zip(bounds.1 .1).map(|(a, b)| a + b),
)
}
}
#[derive(Clone)]
pub struct SpendDescription<A: Authorization> {
cv: ValueCommitment,
anchor: bls12_381::Scalar,
nullifier: Nullifier,
rk: PublicKey,
zkproof: A::SpendProof,
spend_auth_sig: A::AuthSig,
}
impl<A: Authorization> std::fmt::Debug for SpendDescription<A> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(
f,
"SpendDescription(cv = {:?}, anchor = {:?}, nullifier = {:?}, rk = {:?}, spend_auth_sig = {:?})",
self.cv, self.anchor, self.nullifier, self.rk, self.spend_auth_sig
)
}
}
impl<A: Authorization> SpendDescription<A> {
#[cfg(feature = "temporary-zcashd")]
pub fn temporary_zcashd_from_parts(
cv: ValueCommitment,
anchor: bls12_381::Scalar,
nullifier: Nullifier,
rk: PublicKey,
zkproof: A::SpendProof,
spend_auth_sig: A::AuthSig,
) -> Self {
Self::from_parts(cv, anchor, nullifier, rk, zkproof, spend_auth_sig)
}
pub(crate) fn from_parts(
cv: ValueCommitment,
anchor: bls12_381::Scalar,
nullifier: Nullifier,
rk: PublicKey,
zkproof: A::SpendProof,
spend_auth_sig: A::AuthSig,
) -> Self {
Self {
cv,
anchor,
nullifier,
rk,
zkproof,
spend_auth_sig,
}
}
/// Returns the commitment to the value consumed by this spend.
pub fn cv(&self) -> &ValueCommitment {
&self.cv
}
/// Returns the root of the Sapling commitment tree that this spend commits to.
pub fn anchor(&self) -> &bls12_381::Scalar {
&self.anchor
}
/// Returns the nullifier of the note being spent.
pub fn nullifier(&self) -> &Nullifier {
&self.nullifier
}
/// Returns the randomized verification key for the note being spent.
pub fn rk(&self) -> &PublicKey {
&self.rk
}
/// Returns the proof for this spend.
pub fn zkproof(&self) -> &A::SpendProof {
&self.zkproof
}
/// Returns the authorization signature for this spend.
pub fn spend_auth_sig(&self) -> &A::AuthSig {
&self.spend_auth_sig
}
}
impl DynamicUsage for SpendDescription<Authorized> {
fn dynamic_usage(&self) -> usize {
self.zkproof.dynamic_usage()
}
fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) {
self.zkproof.dynamic_usage_bounds()
}
}
#[derive(Clone)]
pub struct SpendDescriptionV5 {
cv: ValueCommitment,
nullifier: Nullifier,
rk: PublicKey,
}
impl SpendDescriptionV5 {
pub(crate) fn from_parts(cv: ValueCommitment, nullifier: Nullifier, rk: PublicKey) -> Self {
Self { cv, nullifier, rk }
}
pub fn into_spend_description(
self,
anchor: bls12_381::Scalar,
zkproof: GrothProofBytes,
spend_auth_sig: Signature,
) -> SpendDescription<Authorized> {
SpendDescription {
cv: self.cv,
anchor,
nullifier: self.nullifier,
rk: self.rk,
zkproof,
spend_auth_sig,
}
}
}
#[derive(Clone)]
pub struct OutputDescription<Proof> {
cv: ValueCommitment,
cmu: ExtractedNoteCommitment,
ephemeral_key: EphemeralKeyBytes,
enc_ciphertext: [u8; 580],
out_ciphertext: [u8; 80],
zkproof: Proof,
}
impl<Proof> OutputDescription<Proof> {
/// Returns the commitment to the value consumed by this output.
pub fn cv(&self) -> &ValueCommitment {
&self.cv
}
/// Returns the commitment to the new note being created.
pub fn cmu(&self) -> &ExtractedNoteCommitment {
&self.cmu
}
pub fn ephemeral_key(&self) -> &EphemeralKeyBytes {
&self.ephemeral_key
}
/// Returns the encrypted note ciphertext.
pub fn enc_ciphertext(&self) -> &[u8; 580] {
&self.enc_ciphertext
}
/// Returns the output recovery ciphertext.
pub fn out_ciphertext(&self) -> &[u8; 80] {
&self.out_ciphertext
}
/// Returns the proof for this output.
pub fn zkproof(&self) -> &Proof {
&self.zkproof
}
#[cfg(feature = "temporary-zcashd")]
pub fn temporary_zcashd_from_parts(
cv: ValueCommitment,
cmu: ExtractedNoteCommitment,
ephemeral_key: EphemeralKeyBytes,
enc_ciphertext: [u8; 580],
out_ciphertext: [u8; 80],
zkproof: Proof,
) -> Self {
Self::from_parts(
cv,
cmu,
ephemeral_key,
enc_ciphertext,
out_ciphertext,
zkproof,
)
}
pub(crate) fn from_parts(
cv: ValueCommitment,
cmu: ExtractedNoteCommitment,
ephemeral_key: EphemeralKeyBytes,
enc_ciphertext: [u8; 580],
out_ciphertext: [u8; 80],
zkproof: Proof,
) -> Self {
OutputDescription {
cv,
cmu,
ephemeral_key,
enc_ciphertext,
out_ciphertext,
zkproof,
}
}
}
#[cfg(test)]
impl<Proof> OutputDescription<Proof> {
pub(crate) fn cv_mut(&mut self) -> &mut ValueCommitment {
&mut self.cv
}
pub(crate) fn cmu_mut(&mut self) -> &mut ExtractedNoteCommitment {
&mut self.cmu
}
pub(crate) fn ephemeral_key_mut(&mut self) -> &mut EphemeralKeyBytes {
&mut self.ephemeral_key
}
pub(crate) fn enc_ciphertext_mut(&mut self) -> &mut [u8; 580] {
&mut self.enc_ciphertext
}
pub(crate) fn out_ciphertext_mut(&mut self) -> &mut [u8; 80] {
&mut self.out_ciphertext
}
}
impl<Proof: DynamicUsage> DynamicUsage for OutputDescription<Proof> {
fn dynamic_usage(&self) -> usize {
self.zkproof.dynamic_usage()
}
fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) {
self.zkproof.dynamic_usage_bounds()
}
}
impl<P: consensus::Parameters, A> ShieldedOutput<SaplingDomain<P>, ENC_CIPHERTEXT_SIZE>
for OutputDescription<A>
{
fn ephemeral_key(&self) -> EphemeralKeyBytes {
self.ephemeral_key.clone()
}
fn cmstar_bytes(&self) -> [u8; 32] {
self.cmu.to_bytes()
}
fn enc_ciphertext(&self) -> &[u8; ENC_CIPHERTEXT_SIZE] {
&self.enc_ciphertext
}
}
impl<A> std::fmt::Debug for OutputDescription<A> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(
f,
"OutputDescription(cv = {:?}, cmu = {:?}, ephemeral_key = {:?})",
self.cv, self.cmu, self.ephemeral_key
)
}
}
#[derive(Clone)]
pub struct OutputDescriptionV5 {
cv: ValueCommitment,
cmu: ExtractedNoteCommitment,
ephemeral_key: EphemeralKeyBytes,
enc_ciphertext: [u8; 580],
out_ciphertext: [u8; 80],
}
memuse::impl_no_dynamic_usage!(OutputDescriptionV5);
impl OutputDescriptionV5 {
pub(crate) fn from_parts(
cv: ValueCommitment,
cmu: ExtractedNoteCommitment,
ephemeral_key: EphemeralKeyBytes,
enc_ciphertext: [u8; 580],
out_ciphertext: [u8; 80],
) -> Self {
Self {
cv,
cmu,
ephemeral_key,
enc_ciphertext,
out_ciphertext,
}
}
pub fn into_output_description(
self,
zkproof: GrothProofBytes,
) -> OutputDescription<GrothProofBytes> {
OutputDescription {
cv: self.cv,
cmu: self.cmu,
ephemeral_key: self.ephemeral_key,
enc_ciphertext: self.enc_ciphertext,
out_ciphertext: self.out_ciphertext,
zkproof,
}
}
}
impl<A> From<OutputDescription<A>> for CompactOutputDescription {
fn from(out: OutputDescription<A>) -> CompactOutputDescription {
CompactOutputDescription {
ephemeral_key: out.ephemeral_key,
cmu: out.cmu,
enc_ciphertext: out.enc_ciphertext[..COMPACT_NOTE_SIZE].try_into().unwrap(),
}
}
}
#[cfg(any(test, feature = "test-dependencies"))]
pub mod testing {
use ff::Field;
use group::{Group, GroupEncoding};
use proptest::collection::vec;
use proptest::prelude::*;
use rand::{rngs::StdRng, SeedableRng};
use crate::{
sapling::{
constants::{SPENDING_KEY_GENERATOR, VALUE_COMMITMENT_RANDOMNESS_GENERATOR},
note::ExtractedNoteCommitment,
redjubjub::{PrivateKey, PublicKey},
value::{
testing::{arb_note_value_bounded, arb_trapdoor},
ValueCommitment, MAX_NOTE_VALUE,
},
Nullifier,
},
transaction::components::{amount::testing::arb_amount, GROTH_PROOF_SIZE},
};
use super::{Authorized, Bundle, GrothProofBytes, OutputDescription, SpendDescription};
prop_compose! {
fn arb_extended_point()(rng_seed in prop::array::uniform32(any::<u8>())) -> jubjub::ExtendedPoint {
let mut rng = StdRng::from_seed(rng_seed);
let scalar = jubjub::Scalar::random(&mut rng);
jubjub::ExtendedPoint::generator() * scalar
}
}
prop_compose! {
/// produce a spend description with invalid data (useful only for serialization
/// roundtrip testing).
fn arb_spend_description(n_spends: usize)(
value in arb_note_value_bounded(MAX_NOTE_VALUE.checked_div(n_spends as u64).unwrap_or(0)),
rcv in arb_trapdoor(),
anchor in vec(any::<u8>(), 64)
.prop_map(|v| <[u8;64]>::try_from(v.as_slice()).unwrap())
.prop_map(|v| bls12_381::Scalar::from_bytes_wide(&v)),
nullifier in prop::array::uniform32(any::<u8>())
.prop_map(|v| Nullifier::from_slice(&v).unwrap()),
zkproof in vec(any::<u8>(), GROTH_PROOF_SIZE)
.prop_map(|v| <[u8;GROTH_PROOF_SIZE]>::try_from(v.as_slice()).unwrap()),
rng_seed in prop::array::uniform32(prop::num::u8::ANY),
fake_sighash_bytes in prop::array::uniform32(prop::num::u8::ANY),
) -> SpendDescription<Authorized> {
let mut rng = StdRng::from_seed(rng_seed);
let sk1 = PrivateKey(jubjub::Fr::random(&mut rng));
let rk = PublicKey::from_private(&sk1, SPENDING_KEY_GENERATOR);
let cv = ValueCommitment::derive(value, rcv);
SpendDescription {
cv,
anchor,
nullifier,
rk,
zkproof,
spend_auth_sig: sk1.sign(&fake_sighash_bytes, &mut rng, SPENDING_KEY_GENERATOR),
}
}
}
prop_compose! {
/// produce an output description with invalid data (useful only for serialization
/// roundtrip testing).
pub fn arb_output_description(n_outputs: usize)(
value in arb_note_value_bounded(MAX_NOTE_VALUE.checked_div(n_outputs as u64).unwrap_or(0)),
rcv in arb_trapdoor(),
cmu in vec(any::<u8>(), 64)
.prop_map(|v| <[u8;64]>::try_from(v.as_slice()).unwrap())
.prop_map(|v| bls12_381::Scalar::from_bytes_wide(&v)),
enc_ciphertext in vec(any::<u8>(), 580)
.prop_map(|v| <[u8;580]>::try_from(v.as_slice()).unwrap()),
epk in arb_extended_point(),
out_ciphertext in vec(any::<u8>(), 80)
.prop_map(|v| <[u8;80]>::try_from(v.as_slice()).unwrap()),
zkproof in vec(any::<u8>(), GROTH_PROOF_SIZE)
.prop_map(|v| <[u8;GROTH_PROOF_SIZE]>::try_from(v.as_slice()).unwrap()),
) -> OutputDescription<GrothProofBytes> {
let cv = ValueCommitment::derive(value, rcv);
let cmu = ExtractedNoteCommitment::from_bytes(&cmu.to_bytes()).unwrap();
OutputDescription {
cv,
cmu,
ephemeral_key: epk.to_bytes().into(),
enc_ciphertext,
out_ciphertext,
zkproof,
}
}
}
prop_compose! {
pub fn arb_bundle()(
n_spends in 0usize..30,
n_outputs in 0usize..30,
)(
shielded_spends in vec(arb_spend_description(n_spends), n_spends),
shielded_outputs in vec(arb_output_description(n_outputs), n_outputs),
value_balance in arb_amount(),
rng_seed in prop::array::uniform32(prop::num::u8::ANY),
fake_bvk_bytes in prop::array::uniform32(prop::num::u8::ANY),
) -> Option<Bundle<Authorized>> {
if shielded_spends.is_empty() && shielded_outputs.is_empty() {
None
} else {
let mut rng = StdRng::from_seed(rng_seed);
let bsk = PrivateKey(jubjub::Fr::random(&mut rng));
Some(
Bundle {
shielded_spends,
shielded_outputs,
value_balance,
authorization: Authorized { binding_sig: bsk.sign(&fake_bvk_bytes, &mut rng, VALUE_COMMITMENT_RANDOMNESS_GENERATOR) },
}
)
}
}
}
}

View File

@ -19,6 +19,7 @@ use crate::{
consensus::{self, BlockHeight, NetworkUpgrade::Canopy, ZIP212_GRACE_PERIOD},
memo::MemoBytes,
sapling::{
bundle::{GrothProofBytes, OutputDescription},
keys::{
DiversifiedTransmissionKey, EphemeralPublicKey, EphemeralSecretKey, OutgoingViewingKey,
SharedSecret,
@ -26,10 +27,7 @@ use crate::{
value::ValueCommitment,
Diversifier, Note, PaymentAddress, Rseed,
},
transaction::components::{
amount::NonNegativeAmount,
sapling::{self, OutputDescription},
},
transaction::components::amount::NonNegativeAmount,
};
use super::note::ExtractedNoteCommitment;
@ -333,6 +331,31 @@ impl<P: consensus::Parameters> BatchDomain for SaplingDomain<P> {
}
}
#[derive(Clone)]
pub struct CompactOutputDescription {
pub ephemeral_key: EphemeralKeyBytes,
pub cmu: ExtractedNoteCommitment,
pub enc_ciphertext: [u8; COMPACT_NOTE_SIZE],
}
memuse::impl_no_dynamic_usage!(CompactOutputDescription);
impl<P: consensus::Parameters> ShieldedOutput<SaplingDomain<P>, COMPACT_NOTE_SIZE>
for CompactOutputDescription
{
fn ephemeral_key(&self) -> EphemeralKeyBytes {
self.ephemeral_key.clone()
}
fn cmstar_bytes(&self) -> [u8; 32] {
self.cmu.to_bytes()
}
fn enc_ciphertext(&self) -> &[u8; COMPACT_NOTE_SIZE] {
&self.enc_ciphertext
}
}
/// Creates a new encryption context for the given note.
///
/// Setting `ovk` to `None` represents the `ovk = ⊥` case, where the note cannot be
@ -457,7 +480,7 @@ pub fn try_sapling_output_recovery_with_ock<P: consensus::Parameters>(
params: &P,
height: BlockHeight,
ock: &OutgoingCipherKey,
output: &OutputDescription<sapling::GrothProofBytes>,
output: &OutputDescription<GrothProofBytes>,
) -> Option<(Note, PaymentAddress, MemoBytes)> {
let domain = SaplingDomain {
params: params.clone(),
@ -479,7 +502,7 @@ pub fn try_sapling_output_recovery<P: consensus::Parameters>(
params: &P,
height: BlockHeight,
ovk: &OutgoingViewingKey,
output: &OutputDescription<sapling::GrothProofBytes>,
output: &OutputDescription<GrothProofBytes>,
) -> Option<(Note, PaymentAddress, MemoBytes)> {
let domain = SaplingDomain {
params: params.clone(),
@ -509,7 +532,7 @@ mod tests {
use super::{
prf_ock, sapling_note_encryption, try_sapling_compact_note_decryption,
try_sapling_note_decryption, try_sapling_output_recovery,
try_sapling_output_recovery_with_ock, SaplingDomain,
try_sapling_output_recovery_with_ock, CompactOutputDescription, SaplingDomain,
};
use crate::{
@ -521,6 +544,7 @@ mod tests {
keys::OutgoingViewingKey,
memo::MemoBytes,
sapling::{
bundle::{GrothProofBytes, OutputDescription},
keys::{DiversifiedTransmissionKey, EphemeralSecretKey},
note::ExtractedNoteCommitment,
note_encryption::PreparedIncomingViewingKey,
@ -528,10 +552,7 @@ mod tests {
value::{NoteValue, ValueCommitTrapdoor, ValueCommitment},
Diversifier, PaymentAddress, Rseed, SaplingIvk,
},
transaction::components::{
sapling::{self, CompactOutputDescription, OutputDescription},
GROTH_PROOF_SIZE,
},
transaction::components::GROTH_PROOF_SIZE,
};
fn random_enc_ciphertext<R: RngCore + CryptoRng>(
@ -541,7 +562,7 @@ mod tests {
OutgoingViewingKey,
OutgoingCipherKey,
PreparedIncomingViewingKey,
OutputDescription<sapling::GrothProofBytes>,
OutputDescription<GrothProofBytes>,
) {
let ivk = SaplingIvk(jubjub::Fr::random(&mut rng));
let prepared_ivk = PreparedIncomingViewingKey::new(&ivk);
@ -577,7 +598,7 @@ mod tests {
) -> (
OutgoingViewingKey,
OutgoingCipherKey,
OutputDescription<sapling::GrothProofBytes>,
OutputDescription<GrothProofBytes>,
) {
let diversifier = Diversifier([0; 11]);
let pa = ivk.to_payment_address(diversifier).unwrap();

View File

@ -7,10 +7,11 @@ use rand_core::RngCore;
use crate::{
sapling::{
self,
bundle::GrothProofBytes,
value::{NoteValue, ValueCommitTrapdoor},
MerklePath,
},
transaction::components::{sapling::GrothProofBytes, GROTH_PROOF_SIZE},
transaction::components::GROTH_PROOF_SIZE,
};
use super::{
@ -188,11 +189,12 @@ pub mod mock {
use crate::{
sapling::{
self,
bundle::GrothProofBytes,
circuit::ValueCommitmentOpening,
value::{NoteValue, ValueCommitTrapdoor},
Diversifier, PaymentAddress, ProofGenerationKey, Rseed,
},
transaction::components::{sapling::GrothProofBytes, GROTH_PROOF_SIZE},
transaction::components::GROTH_PROOF_SIZE,
};
pub struct MockSpendProver;

View File

@ -31,8 +31,8 @@
//! you should only need to interact with [`NoteValue`] (which can be safely constructed
//! from a `u64`) and `valueBalanceSapling` (which can be represented as an `i64`).
//!
//! [`Bundle`]: crate::transaction::components::sapling::Bundle
//! [`Bundle::value_balance`]: crate::transaction::components::sapling::Bundle::value_balance
//! [`Bundle`]: crate::sapling::Bundle
//! [`Bundle::value_balance`]: crate::sapling::Bundle::value_balance
//! [`SaplingBuilder::value_balance`]: crate::sapling::builder::SaplingBuilder::value_balance
//! [`SaplingBuilder::add_output`]: crate::sapling::builder::SaplingBuilder::add_output
//! [Rust documentation]: https://doc.rust-lang.org/stable/std/primitive.i64.html

View File

@ -4,9 +4,9 @@ use group::GroupEncoding;
use rand_core::{CryptoRng, RngCore};
use super::SaplingVerificationContextInner;
use crate::{
sapling::circuit::{OutputVerifyingKey, SpendVerifyingKey},
transaction::components::sapling::{Authorized, Bundle},
use crate::sapling::{
bundle::{Authorized, Bundle},
circuit::{OutputVerifyingKey, SpendVerifyingKey},
};
/// Batch validation context for Sapling.

View File

@ -6,12 +6,13 @@ pub mod sapling;
pub mod sprout;
pub mod transparent;
pub mod tze;
pub use self::{
amount::Amount,
sapling::{OutputDescription, SpendDescription},
sprout::JsDescription,
transparent::{OutPoint, TxIn, TxOut},
};
pub use crate::sapling::bundle::{OutputDescription, SpendDescription};
#[cfg(feature = "zfuture")]
pub use self::tze::{TzeIn, TzeOut};

View File

@ -1,408 +1,24 @@
use core::fmt::Debug;
use ff::PrimeField;
use memuse::DynamicUsage;
use std::io::{self, Read, Write};
use zcash_note_encryption::{
EphemeralKeyBytes, ShieldedOutput, COMPACT_NOTE_SIZE, ENC_CIPHERTEXT_SIZE,
};
use zcash_note_encryption::EphemeralKeyBytes;
use crate::{
consensus,
sapling::{
note::ExtractedNoteCommitment,
note_encryption::SaplingDomain,
redjubjub::{self, PublicKey, Signature},
value::ValueCommitment,
Nullifier,
use crate::sapling::{
bundle::{
Authorized, GrothProofBytes, OutputDescription, OutputDescriptionV5, SpendDescription,
SpendDescriptionV5,
},
note::ExtractedNoteCommitment,
redjubjub::{PublicKey, Signature},
value::ValueCommitment,
Nullifier,
};
use super::{amount::Amount, GROTH_PROOF_SIZE};
pub type GrothProofBytes = [u8; GROTH_PROOF_SIZE];
use super::GROTH_PROOF_SIZE;
pub mod fees;
/// Defines the authorization type of a Sapling bundle.
pub trait Authorization: Debug {
type SpendProof: Clone + Debug;
type OutputProof: Clone + Debug;
type AuthSig: Clone + Debug;
}
/// Authorizing data for a bundle of Sapling spends and outputs, ready to be committed to
/// the ledger.
#[derive(Debug, Copy, Clone)]
pub struct Authorized {
// TODO: Make this private.
pub binding_sig: redjubjub::Signature,
}
impl Authorization for Authorized {
type SpendProof = GrothProofBytes;
type OutputProof = GrothProofBytes;
type AuthSig = redjubjub::Signature;
}
/// A map from one bundle authorization to another.
///
/// For use with [`Bundle::map_authorization`].
pub trait MapAuth<A: Authorization, B: Authorization> {
fn map_spend_proof(&mut self, p: A::SpendProof) -> B::SpendProof;
fn map_output_proof(&mut self, p: A::OutputProof) -> B::OutputProof;
fn map_auth_sig(&mut self, s: A::AuthSig) -> B::AuthSig;
fn map_authorization(&mut self, a: A) -> B;
}
/// The identity map.
///
/// This can be used with [`TransactionData::map_authorization`] when you want to map the
/// authorization of a subset of the transaction's bundles.
///
/// [`TransactionData::map_authorization`]: crate::transaction::TransactionData::map_authorization
impl MapAuth<Authorized, Authorized> for () {
fn map_spend_proof(
&mut self,
p: <Authorized as Authorization>::SpendProof,
) -> <Authorized as Authorization>::SpendProof {
p
}
fn map_output_proof(
&mut self,
p: <Authorized as Authorization>::OutputProof,
) -> <Authorized as Authorization>::OutputProof {
p
}
fn map_auth_sig(
&mut self,
s: <Authorized as Authorization>::AuthSig,
) -> <Authorized as Authorization>::AuthSig {
s
}
fn map_authorization(&mut self, a: Authorized) -> Authorized {
a
}
}
/// A helper for implementing `MapAuth` with a set of closures.
impl<A, B, F, G, H, I> MapAuth<A, B> for (F, G, H, I)
where
A: Authorization,
B: Authorization,
F: FnMut(A::SpendProof) -> B::SpendProof,
G: FnMut(A::OutputProof) -> B::OutputProof,
H: FnMut(A::AuthSig) -> B::AuthSig,
I: FnMut(A) -> B,
{
fn map_spend_proof(&mut self, p: A::SpendProof) -> B::SpendProof {
self.0(p)
}
fn map_output_proof(&mut self, p: A::OutputProof) -> B::OutputProof {
self.1(p)
}
fn map_auth_sig(&mut self, s: A::AuthSig) -> B::AuthSig {
self.2(s)
}
fn map_authorization(&mut self, a: A) -> B {
self.3(a)
}
}
/// A fallible map from one bundle authorization to another.
///
/// For use with [`Bundle::try_map_authorization`].
pub trait TryMapAuth<A: Authorization, B: Authorization> {
type Error;
fn try_map_spend_proof(&mut self, p: A::SpendProof) -> Result<B::SpendProof, Self::Error>;
fn try_map_output_proof(&mut self, p: A::OutputProof) -> Result<B::OutputProof, Self::Error>;
fn try_map_auth_sig(&mut self, s: A::AuthSig) -> Result<B::AuthSig, Self::Error>;
fn try_map_authorization(&mut self, a: A) -> Result<B, Self::Error>;
}
/// A helper for implementing `TryMapAuth` with a set of closures.
impl<A, B, E, F, G, H, I> TryMapAuth<A, B> for (F, G, H, I)
where
A: Authorization,
B: Authorization,
F: FnMut(A::SpendProof) -> Result<B::SpendProof, E>,
G: FnMut(A::OutputProof) -> Result<B::OutputProof, E>,
H: FnMut(A::AuthSig) -> Result<B::AuthSig, E>,
I: FnMut(A) -> Result<B, E>,
{
type Error = E;
fn try_map_spend_proof(&mut self, p: A::SpendProof) -> Result<B::SpendProof, Self::Error> {
self.0(p)
}
fn try_map_output_proof(&mut self, p: A::OutputProof) -> Result<B::OutputProof, Self::Error> {
self.1(p)
}
fn try_map_auth_sig(&mut self, s: A::AuthSig) -> Result<B::AuthSig, Self::Error> {
self.2(s)
}
fn try_map_authorization(&mut self, a: A) -> Result<B, Self::Error> {
self.3(a)
}
}
#[derive(Debug, Clone)]
pub struct Bundle<A: Authorization> {
shielded_spends: Vec<SpendDescription<A>>,
shielded_outputs: Vec<OutputDescription<A::OutputProof>>,
value_balance: Amount,
authorization: A,
}
impl<A: Authorization> Bundle<A> {
/// Constructs a `Bundle` from its constituent parts.
#[cfg(feature = "temporary-zcashd")]
pub fn temporary_zcashd_from_parts(
shielded_spends: Vec<SpendDescription<A>>,
shielded_outputs: Vec<OutputDescription<A::OutputProof>>,
value_balance: Amount,
authorization: A,
) -> Self {
Self::from_parts(
shielded_spends,
shielded_outputs,
value_balance,
authorization,
)
}
/// Constructs a `Bundle` from its constituent parts.
pub(crate) fn from_parts(
shielded_spends: Vec<SpendDescription<A>>,
shielded_outputs: Vec<OutputDescription<A::OutputProof>>,
value_balance: Amount,
authorization: A,
) -> Self {
Bundle {
shielded_spends,
shielded_outputs,
value_balance,
authorization,
}
}
/// Returns the list of spends in this bundle.
pub fn shielded_spends(&self) -> &[SpendDescription<A>] {
&self.shielded_spends
}
/// Returns the list of outputs in this bundle.
pub fn shielded_outputs(&self) -> &[OutputDescription<A::OutputProof>] {
&self.shielded_outputs
}
/// Returns the net value moved into or out of the Sapling shielded pool.
///
/// This is the sum of Sapling spends minus the sum of Sapling outputs.
pub fn value_balance(&self) -> &Amount {
&self.value_balance
}
/// Returns the authorization for this bundle.
///
/// In the case of a `Bundle<Authorized>`, this is the binding signature.
pub fn authorization(&self) -> &A {
&self.authorization
}
/// Transitions this bundle from one authorization state to another.
pub fn map_authorization<B: Authorization, F: MapAuth<A, B>>(self, mut f: F) -> Bundle<B> {
Bundle {
shielded_spends: self
.shielded_spends
.into_iter()
.map(|d| SpendDescription {
cv: d.cv,
anchor: d.anchor,
nullifier: d.nullifier,
rk: d.rk,
zkproof: f.map_spend_proof(d.zkproof),
spend_auth_sig: f.map_auth_sig(d.spend_auth_sig),
})
.collect(),
shielded_outputs: self
.shielded_outputs
.into_iter()
.map(|o| OutputDescription {
cv: o.cv,
cmu: o.cmu,
ephemeral_key: o.ephemeral_key,
enc_ciphertext: o.enc_ciphertext,
out_ciphertext: o.out_ciphertext,
zkproof: f.map_output_proof(o.zkproof),
})
.collect(),
value_balance: self.value_balance,
authorization: f.map_authorization(self.authorization),
}
}
/// Transitions this bundle from one authorization state to another.
pub fn try_map_authorization<B: Authorization, F: TryMapAuth<A, B>>(
self,
mut f: F,
) -> Result<Bundle<B>, F::Error> {
Ok(Bundle {
shielded_spends: self
.shielded_spends
.into_iter()
.map(|d| {
Ok(SpendDescription {
cv: d.cv,
anchor: d.anchor,
nullifier: d.nullifier,
rk: d.rk,
zkproof: f.try_map_spend_proof(d.zkproof)?,
spend_auth_sig: f.try_map_auth_sig(d.spend_auth_sig)?,
})
})
.collect::<Result<_, _>>()?,
shielded_outputs: self
.shielded_outputs
.into_iter()
.map(|o| {
Ok(OutputDescription {
cv: o.cv,
cmu: o.cmu,
ephemeral_key: o.ephemeral_key,
enc_ciphertext: o.enc_ciphertext,
out_ciphertext: o.out_ciphertext,
zkproof: f.try_map_output_proof(o.zkproof)?,
})
})
.collect::<Result<_, _>>()?,
value_balance: self.value_balance,
authorization: f.try_map_authorization(self.authorization)?,
})
}
}
impl DynamicUsage for Bundle<Authorized> {
fn dynamic_usage(&self) -> usize {
self.shielded_spends.dynamic_usage() + self.shielded_outputs.dynamic_usage()
}
fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) {
let bounds = (
self.shielded_spends.dynamic_usage_bounds(),
self.shielded_outputs.dynamic_usage_bounds(),
);
(
bounds.0 .0 + bounds.1 .0,
bounds.0 .1.zip(bounds.1 .1).map(|(a, b)| a + b),
)
}
}
#[derive(Clone)]
pub struct SpendDescription<A: Authorization> {
cv: ValueCommitment,
anchor: bls12_381::Scalar,
nullifier: Nullifier,
rk: PublicKey,
zkproof: A::SpendProof,
spend_auth_sig: A::AuthSig,
}
impl<A: Authorization> std::fmt::Debug for SpendDescription<A> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(
f,
"SpendDescription(cv = {:?}, anchor = {:?}, nullifier = {:?}, rk = {:?}, spend_auth_sig = {:?})",
self.cv, self.anchor, self.nullifier, self.rk, self.spend_auth_sig
)
}
}
impl<A: Authorization> SpendDescription<A> {
#[cfg(feature = "temporary-zcashd")]
pub fn temporary_zcashd_from_parts(
cv: ValueCommitment,
anchor: bls12_381::Scalar,
nullifier: Nullifier,
rk: PublicKey,
zkproof: A::SpendProof,
spend_auth_sig: A::AuthSig,
) -> Self {
Self::from_parts(cv, anchor, nullifier, rk, zkproof, spend_auth_sig)
}
pub(crate) fn from_parts(
cv: ValueCommitment,
anchor: bls12_381::Scalar,
nullifier: Nullifier,
rk: PublicKey,
zkproof: A::SpendProof,
spend_auth_sig: A::AuthSig,
) -> Self {
Self {
cv,
anchor,
nullifier,
rk,
zkproof,
spend_auth_sig,
}
}
/// Returns the commitment to the value consumed by this spend.
pub fn cv(&self) -> &ValueCommitment {
&self.cv
}
/// Returns the root of the Sapling commitment tree that this spend commits to.
pub fn anchor(&self) -> &bls12_381::Scalar {
&self.anchor
}
/// Returns the nullifier of the note being spent.
pub fn nullifier(&self) -> &Nullifier {
&self.nullifier
}
/// Returns the randomized verification key for the note being spent.
pub fn rk(&self) -> &PublicKey {
&self.rk
}
/// Returns the proof for this spend.
pub fn zkproof(&self) -> &A::SpendProof {
&self.zkproof
}
/// Returns the authorization signature for this spend.
pub fn spend_auth_sig(&self) -> &A::AuthSig {
&self.spend_auth_sig
}
}
impl DynamicUsage for SpendDescription<Authorized> {
fn dynamic_usage(&self) -> usize {
self.zkproof.dynamic_usage()
}
fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) {
self.zkproof.dynamic_usage_bounds()
}
}
/// Consensus rules (§4.4) & (§4.5):
/// - Canonical encoding is enforced here.
/// - "Not small order" is enforced here.
@ -487,195 +103,39 @@ impl SpendDescription<Authorized> {
let zkproof = read_zkproof(&mut reader)?;
let spend_auth_sig = Self::read_spend_auth_sig(&mut reader)?;
Ok(SpendDescription {
Ok(SpendDescription::from_parts(
cv,
anchor,
nullifier,
rk,
zkproof,
spend_auth_sig,
})
))
}
pub fn write_v4<W: Write>(&self, mut writer: W) -> io::Result<()> {
writer.write_all(&self.cv.to_bytes())?;
writer.write_all(self.anchor.to_repr().as_ref())?;
writer.write_all(&self.nullifier.0)?;
self.rk.write(&mut writer)?;
writer.write_all(&self.zkproof)?;
self.spend_auth_sig.write(&mut writer)
writer.write_all(&self.cv().to_bytes())?;
writer.write_all(self.anchor().to_repr().as_ref())?;
writer.write_all(&self.nullifier().0)?;
self.rk().write(&mut writer)?;
writer.write_all(self.zkproof())?;
self.spend_auth_sig().write(&mut writer)
}
pub fn write_v5_without_witness_data<W: Write>(&self, mut writer: W) -> io::Result<()> {
writer.write_all(&self.cv.to_bytes())?;
writer.write_all(&self.nullifier.0)?;
self.rk.write(&mut writer)
writer.write_all(&self.cv().to_bytes())?;
writer.write_all(&self.nullifier().0)?;
self.rk().write(&mut writer)
}
}
#[derive(Clone)]
pub struct SpendDescriptionV5 {
cv: ValueCommitment,
nullifier: Nullifier,
rk: PublicKey,
}
impl SpendDescriptionV5 {
pub fn read<R: Read>(mut reader: &mut R) -> io::Result<Self> {
let cv = read_value_commitment(&mut reader)?;
let nullifier = SpendDescription::read_nullifier(&mut reader)?;
let rk = SpendDescription::read_rk(&mut reader)?;
Ok(SpendDescriptionV5 { cv, nullifier, rk })
}
pub fn into_spend_description(
self,
anchor: bls12_381::Scalar,
zkproof: GrothProofBytes,
spend_auth_sig: Signature,
) -> SpendDescription<Authorized> {
SpendDescription {
cv: self.cv,
anchor,
nullifier: self.nullifier,
rk: self.rk,
zkproof,
spend_auth_sig,
}
}
}
#[derive(Clone)]
pub struct OutputDescription<Proof> {
cv: ValueCommitment,
cmu: ExtractedNoteCommitment,
ephemeral_key: EphemeralKeyBytes,
enc_ciphertext: [u8; 580],
out_ciphertext: [u8; 80],
zkproof: Proof,
}
impl<Proof> OutputDescription<Proof> {
/// Returns the commitment to the value consumed by this output.
pub fn cv(&self) -> &ValueCommitment {
&self.cv
}
/// Returns the commitment to the new note being created.
pub fn cmu(&self) -> &ExtractedNoteCommitment {
&self.cmu
}
pub fn ephemeral_key(&self) -> &EphemeralKeyBytes {
&self.ephemeral_key
}
/// Returns the encrypted note ciphertext.
pub fn enc_ciphertext(&self) -> &[u8; 580] {
&self.enc_ciphertext
}
/// Returns the output recovery ciphertext.
pub fn out_ciphertext(&self) -> &[u8; 80] {
&self.out_ciphertext
}
/// Returns the proof for this output.
pub fn zkproof(&self) -> &Proof {
&self.zkproof
}
#[cfg(feature = "temporary-zcashd")]
pub fn temporary_zcashd_from_parts(
cv: ValueCommitment,
cmu: ExtractedNoteCommitment,
ephemeral_key: EphemeralKeyBytes,
enc_ciphertext: [u8; 580],
out_ciphertext: [u8; 80],
zkproof: Proof,
) -> Self {
Self::from_parts(
cv,
cmu,
ephemeral_key,
enc_ciphertext,
out_ciphertext,
zkproof,
)
}
pub(crate) fn from_parts(
cv: ValueCommitment,
cmu: ExtractedNoteCommitment,
ephemeral_key: EphemeralKeyBytes,
enc_ciphertext: [u8; 580],
out_ciphertext: [u8; 80],
zkproof: Proof,
) -> Self {
OutputDescription {
cv,
cmu,
ephemeral_key,
enc_ciphertext,
out_ciphertext,
zkproof,
}
}
}
#[cfg(test)]
impl<Proof> OutputDescription<Proof> {
pub(crate) fn cv_mut(&mut self) -> &mut ValueCommitment {
&mut self.cv
}
pub(crate) fn cmu_mut(&mut self) -> &mut ExtractedNoteCommitment {
&mut self.cmu
}
pub(crate) fn ephemeral_key_mut(&mut self) -> &mut EphemeralKeyBytes {
&mut self.ephemeral_key
}
pub(crate) fn enc_ciphertext_mut(&mut self) -> &mut [u8; 580] {
&mut self.enc_ciphertext
}
pub(crate) fn out_ciphertext_mut(&mut self) -> &mut [u8; 80] {
&mut self.out_ciphertext
}
}
impl<Proof: DynamicUsage> DynamicUsage for OutputDescription<Proof> {
fn dynamic_usage(&self) -> usize {
self.zkproof.dynamic_usage()
}
fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) {
self.zkproof.dynamic_usage_bounds()
}
}
impl<P: consensus::Parameters, A> ShieldedOutput<SaplingDomain<P>, ENC_CIPHERTEXT_SIZE>
for OutputDescription<A>
{
fn ephemeral_key(&self) -> EphemeralKeyBytes {
self.ephemeral_key.clone()
}
fn cmstar_bytes(&self) -> [u8; 32] {
self.cmu.to_bytes()
}
fn enc_ciphertext(&self) -> &[u8; ENC_CIPHERTEXT_SIZE] {
&self.enc_ciphertext
}
}
impl<A> std::fmt::Debug for OutputDescription<A> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(
f,
"OutputDescription(cv = {:?}, cmu = {:?}, ephemeral_key = {:?})",
self.cv, self.cmu, self.ephemeral_key
)
Ok(SpendDescriptionV5::from_parts(cv, nullifier, rk))
}
}
@ -703,45 +163,34 @@ impl OutputDescription<GrothProofBytes> {
let zkproof = read_zkproof(&mut reader)?;
Ok(OutputDescription {
Ok(OutputDescription::from_parts(
cv,
cmu,
ephemeral_key,
enc_ciphertext,
out_ciphertext,
zkproof,
})
))
}
pub fn write_v4<W: Write>(&self, mut writer: W) -> io::Result<()> {
writer.write_all(&self.cv.to_bytes())?;
writer.write_all(self.cmu.to_bytes().as_ref())?;
writer.write_all(self.ephemeral_key.as_ref())?;
writer.write_all(&self.enc_ciphertext)?;
writer.write_all(&self.out_ciphertext)?;
writer.write_all(&self.zkproof)
writer.write_all(&self.cv().to_bytes())?;
writer.write_all(self.cmu().to_bytes().as_ref())?;
writer.write_all(self.ephemeral_key().as_ref())?;
writer.write_all(self.enc_ciphertext())?;
writer.write_all(self.out_ciphertext())?;
writer.write_all(self.zkproof())
}
pub fn write_v5_without_proof<W: Write>(&self, mut writer: W) -> io::Result<()> {
writer.write_all(&self.cv.to_bytes())?;
writer.write_all(self.cmu.to_bytes().as_ref())?;
writer.write_all(self.ephemeral_key.as_ref())?;
writer.write_all(&self.enc_ciphertext)?;
writer.write_all(&self.out_ciphertext)
writer.write_all(&self.cv().to_bytes())?;
writer.write_all(self.cmu().to_bytes().as_ref())?;
writer.write_all(self.ephemeral_key().as_ref())?;
writer.write_all(self.enc_ciphertext())?;
writer.write_all(self.out_ciphertext())
}
}
#[derive(Clone)]
pub struct OutputDescriptionV5 {
cv: ValueCommitment,
cmu: ExtractedNoteCommitment,
ephemeral_key: EphemeralKeyBytes,
enc_ciphertext: [u8; 580],
out_ciphertext: [u8; 80],
}
memuse::impl_no_dynamic_usage!(OutputDescriptionV5);
impl OutputDescriptionV5 {
pub fn read<R: Read>(mut reader: &mut R) -> io::Result<Self> {
let cv = read_value_commitment(&mut reader)?;
@ -758,190 +207,25 @@ impl OutputDescriptionV5 {
reader.read_exact(&mut enc_ciphertext)?;
reader.read_exact(&mut out_ciphertext)?;
Ok(OutputDescriptionV5 {
Ok(OutputDescriptionV5::from_parts(
cv,
cmu,
ephemeral_key,
enc_ciphertext,
out_ciphertext,
})
}
pub fn into_output_description(
self,
zkproof: GrothProofBytes,
) -> OutputDescription<GrothProofBytes> {
OutputDescription {
cv: self.cv,
cmu: self.cmu,
ephemeral_key: self.ephemeral_key,
enc_ciphertext: self.enc_ciphertext,
out_ciphertext: self.out_ciphertext,
zkproof,
}
}
}
#[derive(Clone)]
pub struct CompactOutputDescription {
pub ephemeral_key: EphemeralKeyBytes,
pub cmu: ExtractedNoteCommitment,
pub enc_ciphertext: [u8; COMPACT_NOTE_SIZE],
}
memuse::impl_no_dynamic_usage!(CompactOutputDescription);
impl<A> From<OutputDescription<A>> for CompactOutputDescription {
fn from(out: OutputDescription<A>) -> CompactOutputDescription {
CompactOutputDescription {
ephemeral_key: out.ephemeral_key,
cmu: out.cmu,
enc_ciphertext: out.enc_ciphertext[..COMPACT_NOTE_SIZE].try_into().unwrap(),
}
}
}
impl<P: consensus::Parameters> ShieldedOutput<SaplingDomain<P>, COMPACT_NOTE_SIZE>
for CompactOutputDescription
{
fn ephemeral_key(&self) -> EphemeralKeyBytes {
self.ephemeral_key.clone()
}
fn cmstar_bytes(&self) -> [u8; 32] {
self.cmu.to_bytes()
}
fn enc_ciphertext(&self) -> &[u8; COMPACT_NOTE_SIZE] {
&self.enc_ciphertext
))
}
}
#[cfg(any(test, feature = "test-dependencies"))]
pub mod testing {
use ff::Field;
use group::{Group, GroupEncoding};
use proptest::collection::vec;
use proptest::prelude::*;
use rand::{rngs::StdRng, SeedableRng};
use crate::{
sapling::{
constants::{SPENDING_KEY_GENERATOR, VALUE_COMMITMENT_RANDOMNESS_GENERATOR},
note::ExtractedNoteCommitment,
redjubjub::{PrivateKey, PublicKey},
value::{
testing::{arb_note_value_bounded, arb_trapdoor},
ValueCommitment, MAX_NOTE_VALUE,
},
Nullifier,
},
transaction::{
components::{amount::testing::arb_amount, GROTH_PROOF_SIZE},
TxVersion,
},
sapling::bundle::{testing::arb_bundle, Authorized, Bundle},
transaction::TxVersion,
};
use super::{Authorized, Bundle, GrothProofBytes, OutputDescription, SpendDescription};
prop_compose! {
fn arb_extended_point()(rng_seed in prop::array::uniform32(any::<u8>())) -> jubjub::ExtendedPoint {
let mut rng = StdRng::from_seed(rng_seed);
let scalar = jubjub::Scalar::random(&mut rng);
jubjub::ExtendedPoint::generator() * scalar
}
}
prop_compose! {
/// produce a spend description with invalid data (useful only for serialization
/// roundtrip testing).
fn arb_spend_description(n_spends: usize)(
value in arb_note_value_bounded(MAX_NOTE_VALUE.checked_div(n_spends as u64).unwrap_or(0)),
rcv in arb_trapdoor(),
anchor in vec(any::<u8>(), 64)
.prop_map(|v| <[u8;64]>::try_from(v.as_slice()).unwrap())
.prop_map(|v| bls12_381::Scalar::from_bytes_wide(&v)),
nullifier in prop::array::uniform32(any::<u8>())
.prop_map(|v| Nullifier::from_slice(&v).unwrap()),
zkproof in vec(any::<u8>(), GROTH_PROOF_SIZE)
.prop_map(|v| <[u8;GROTH_PROOF_SIZE]>::try_from(v.as_slice()).unwrap()),
rng_seed in prop::array::uniform32(prop::num::u8::ANY),
fake_sighash_bytes in prop::array::uniform32(prop::num::u8::ANY),
) -> SpendDescription<Authorized> {
let mut rng = StdRng::from_seed(rng_seed);
let sk1 = PrivateKey(jubjub::Fr::random(&mut rng));
let rk = PublicKey::from_private(&sk1, SPENDING_KEY_GENERATOR);
let cv = ValueCommitment::derive(value, rcv);
SpendDescription {
cv,
anchor,
nullifier,
rk,
zkproof,
spend_auth_sig: sk1.sign(&fake_sighash_bytes, &mut rng, SPENDING_KEY_GENERATOR),
}
}
}
prop_compose! {
/// produce an output description with invalid data (useful only for serialization
/// roundtrip testing).
pub fn arb_output_description(n_outputs: usize)(
value in arb_note_value_bounded(MAX_NOTE_VALUE.checked_div(n_outputs as u64).unwrap_or(0)),
rcv in arb_trapdoor(),
cmu in vec(any::<u8>(), 64)
.prop_map(|v| <[u8;64]>::try_from(v.as_slice()).unwrap())
.prop_map(|v| bls12_381::Scalar::from_bytes_wide(&v)),
enc_ciphertext in vec(any::<u8>(), 580)
.prop_map(|v| <[u8;580]>::try_from(v.as_slice()).unwrap()),
epk in arb_extended_point(),
out_ciphertext in vec(any::<u8>(), 80)
.prop_map(|v| <[u8;80]>::try_from(v.as_slice()).unwrap()),
zkproof in vec(any::<u8>(), GROTH_PROOF_SIZE)
.prop_map(|v| <[u8;GROTH_PROOF_SIZE]>::try_from(v.as_slice()).unwrap()),
) -> OutputDescription<GrothProofBytes> {
let cv = ValueCommitment::derive(value, rcv);
let cmu = ExtractedNoteCommitment::from_bytes(&cmu.to_bytes()).unwrap();
OutputDescription {
cv,
cmu,
ephemeral_key: epk.to_bytes().into(),
enc_ciphertext,
out_ciphertext,
zkproof,
}
}
}
prop_compose! {
pub fn arb_bundle()(
n_spends in 0usize..30,
n_outputs in 0usize..30,
)(
shielded_spends in vec(arb_spend_description(n_spends), n_spends),
shielded_outputs in vec(arb_output_description(n_outputs), n_outputs),
value_balance in arb_amount(),
rng_seed in prop::array::uniform32(prop::num::u8::ANY),
fake_bvk_bytes in prop::array::uniform32(prop::num::u8::ANY),
) -> Option<Bundle<Authorized>> {
if shielded_spends.is_empty() && shielded_outputs.is_empty() {
None
} else {
let mut rng = StdRng::from_seed(rng_seed);
let bsk = PrivateKey(jubjub::Fr::random(&mut rng));
Some(
Bundle {
shielded_spends,
shielded_outputs,
value_balance,
authorization: Authorized { binding_sig: bsk.sign(&fake_bvk_bytes, &mut rng, VALUE_COMMITMENT_RANDOMNESS_GENERATOR) },
}
)
}
}
}
pub fn arb_bundle_for_version(
v: TxVersion,
) -> impl Strategy<Value = Option<Bundle<Authorized>>> {

View File

@ -24,16 +24,17 @@ use zcash_encoding::{Array, CompactSize, Vector};
use crate::{
consensus::{BlockHeight, BranchId},
sapling::{builder as sapling_builder, redjubjub},
sapling::{
self, builder as sapling_builder,
bundle::{OutputDescription, OutputDescriptionV5, SpendDescription, SpendDescriptionV5},
redjubjub,
},
};
use self::{
components::{
amount::{Amount, BalanceError},
orchard as orchard_serialization,
sapling::{
self, OutputDescription, OutputDescriptionV5, SpendDescription, SpendDescriptionV5,
},
orchard as orchard_serialization, sapling as sapling_serialization,
sprout::{self, JsDescription},
transparent::{self, TxIn, TxOut},
OutPoint,
@ -252,7 +253,7 @@ impl TxVersion {
/// Authorization state for a bundle of transaction data.
pub trait Authorization {
type TransparentAuth: transparent::Authorization;
type SaplingAuth: sapling::Authorization;
type SaplingAuth: sapling::bundle::Authorization;
type OrchardAuth: orchard::bundle::Authorization;
#[cfg(feature = "zfuture")]
@ -264,7 +265,7 @@ pub struct Authorized;
impl Authorization for Authorized {
type TransparentAuth = transparent::Authorized;
type SaplingAuth = sapling::Authorized;
type SaplingAuth = sapling::bundle::Authorized;
type OrchardAuth = orchard::bundle::Authorized;
#[cfg(feature = "zfuture")]
@ -490,7 +491,7 @@ impl<A: Authorization> TransactionData<A> {
pub fn map_authorization<B: Authorization>(
self,
f_transparent: impl transparent::MapAuth<A::TransparentAuth, B::TransparentAuth>,
f_sapling: impl sapling::MapAuth<A::SaplingAuth, B::SaplingAuth>,
f_sapling: impl sapling::bundle::MapAuth<A::SaplingAuth, B::SaplingAuth>,
mut f_orchard: impl orchard_serialization::MapAuth<A::OrchardAuth, B::OrchardAuth>,
#[cfg(feature = "zfuture")] f_tze: impl tze::MapAuth<A::TzeAuth, B::TzeAuth>,
) -> TransactionData<B> {
@ -604,10 +605,10 @@ impl Transaction {
let (value_balance, shielded_spends, shielded_outputs) = if version.has_sapling() {
let vb = Self::read_amount(&mut reader)?;
#[allow(clippy::redundant_closure)]
let ss: Vec<SpendDescription<sapling::Authorized>> =
let ss: Vec<SpendDescription<sapling::bundle::Authorized>> =
Vector::read(&mut reader, |r| SpendDescription::read(r))?;
#[allow(clippy::redundant_closure)]
let so: Vec<OutputDescription<sapling::GrothProofBytes>> =
let so: Vec<OutputDescription<sapling::bundle::GrothProofBytes>> =
Vector::read(&mut reader, |r| OutputDescription::read(r))?;
(vb, ss, so)
} else {
@ -661,7 +662,7 @@ impl Transaction {
shielded_spends,
shielded_outputs,
value_balance,
sapling::Authorized { binding_sig },
sapling::bundle::Authorized { binding_sig },
)
}),
orchard_bundle: None,
@ -741,14 +742,14 @@ impl Transaction {
#[cfg(feature = "temporary-zcashd")]
pub fn temporary_zcashd_read_v5_sapling<R: Read>(
reader: R,
) -> io::Result<Option<sapling::Bundle<sapling::Authorized>>> {
) -> io::Result<Option<sapling::Bundle<sapling::bundle::Authorized>>> {
Self::read_v5_sapling(reader)
}
#[allow(clippy::redundant_closure)]
fn read_v5_sapling<R: Read>(
mut reader: R,
) -> io::Result<Option<sapling::Bundle<sapling::Authorized>>> {
) -> io::Result<Option<sapling::Bundle<sapling::bundle::Authorized>>> {
let sd_v5s = Vector::read(&mut reader, SpendDescriptionV5::read)?;
let od_v5s = Vector::read(&mut reader, OutputDescriptionV5::read)?;
let n_spends = sd_v5s.len();
@ -760,16 +761,20 @@ impl Transaction {
};
let anchor = if n_spends > 0 {
Some(sapling::read_base(&mut reader, "anchor")?)
Some(sapling_serialization::read_base(&mut reader, "anchor")?)
} else {
None
};
let v_spend_proofs = Array::read(&mut reader, n_spends, |r| sapling::read_zkproof(r))?;
let v_spend_proofs = Array::read(&mut reader, n_spends, |r| {
sapling_serialization::read_zkproof(r)
})?;
let v_spend_auth_sigs = Array::read(&mut reader, n_spends, |r| {
SpendDescription::read_spend_auth_sig(r)
})?;
let v_output_proofs = Array::read(&mut reader, n_outputs, |r| sapling::read_zkproof(r))?;
let v_output_proofs = Array::read(&mut reader, n_outputs, |r| {
sapling_serialization::read_zkproof(r)
})?;
let binding_sig = if n_spends > 0 || n_outputs > 0 {
Some(redjubjub::Signature::read(&mut reader)?)
@ -801,7 +806,7 @@ impl Transaction {
shielded_spends,
shielded_outputs,
value_balance,
sapling::Authorized { binding_sig },
sapling::bundle::Authorized { binding_sig },
)
}))
}
@ -934,7 +939,7 @@ impl Transaction {
#[cfg(feature = "temporary-zcashd")]
pub fn temporary_zcashd_write_v5_sapling<W: Write>(
sapling_bundle: Option<&sapling::Bundle<sapling::Authorized>>,
sapling_bundle: Option<&sapling::Bundle<sapling::bundle::Authorized>>,
writer: W,
) -> io::Result<()> {
Self::write_v5_sapling_inner(sapling_bundle, writer)
@ -945,7 +950,7 @@ impl Transaction {
}
fn write_v5_sapling_inner<W: Write>(
sapling_bundle: Option<&sapling::Bundle<sapling::Authorized>>,
sapling_bundle: Option<&sapling::Bundle<sapling::bundle::Authorized>>,
mut writer: W,
) -> io::Result<()> {
if let Some(bundle) = sapling_bundle {

View File

@ -1,16 +1,15 @@
use crate::legacy::Script;
use blake2b_simd::Hash as Blake2bHash;
use super::{
components::{
amount::NonNegativeAmount,
sapling::{self, GrothProofBytes},
transparent,
},
components::{amount::NonNegativeAmount, transparent},
sighash_v4::v4_signature_hash,
sighash_v5::v5_signature_hash,
Authorization, TransactionData, TxDigests, TxVersion,
};
use crate::{
legacy::Script,
sapling::{self, bundle::GrothProofBytes},
};
#[cfg(feature = "zfuture")]
use {super::components::Amount, crate::extensions::transparent::Precondition};
@ -79,7 +78,7 @@ pub trait TransparentAuthorizingContext: transparent::Authorization {
pub fn signature_hash<
'a,
TA: TransparentAuthorizingContext,
SA: sapling::Authorization<SpendProof = GrothProofBytes, OutputProof = GrothProofBytes>,
SA: sapling::bundle::Authorization<SpendProof = GrothProofBytes, OutputProof = GrothProofBytes>,
A: Authorization<SaplingAuth = SA, TransparentAuth = TA>,
>(
tx: &TransactionData<A>,

View File

@ -1,11 +1,16 @@
use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams};
use ff::PrimeField;
use crate::consensus::BranchId;
use crate::{
consensus::BranchId,
sapling::{
self,
bundle::{GrothProofBytes, OutputDescription, SpendDescription},
},
};
use super::{
components::{
sapling::{self, GrothProofBytes, OutputDescription, SpendDescription},
sprout::JsDescription,
transparent::{self, TxIn, TxOut},
},
@ -97,7 +102,7 @@ fn joinsplits_hash(
}
fn shielded_spends_hash<
A: sapling::Authorization<SpendProof = GrothProofBytes, OutputProof = GrothProofBytes>,
A: sapling::bundle::Authorization<SpendProof = GrothProofBytes, OutputProof = GrothProofBytes>,
>(
shielded_spends: &[SpendDescription<A>],
) -> Blake2bHash {
@ -127,7 +132,7 @@ fn shielded_outputs_hash(shielded_outputs: &[OutputDescription<GrothProofBytes>]
}
pub fn v4_signature_hash<
SA: sapling::Authorization<SpendProof = GrothProofBytes, OutputProof = GrothProofBytes>,
SA: sapling::bundle::Authorization<SpendProof = GrothProofBytes, OutputProof = GrothProofBytes>,
A: Authorization<SaplingAuth = SA>,
>(
tx: &TransactionData<A>,

View File

@ -193,7 +193,7 @@ struct TestUnauthorized;
impl Authorization for TestUnauthorized {
type TransparentAuth = TestTransparentAuth;
type SaplingAuth = sapling::Authorized;
type SaplingAuth = sapling::bundle::Authorized;
type OrchardAuth = orchard::bundle::Authorized;
#[cfg(feature = "zfuture")]

View File

@ -7,12 +7,17 @@ use byteorder::{LittleEndian, WriteBytesExt};
use ff::PrimeField;
use orchard::bundle::{self as orchard};
use crate::consensus::{BlockHeight, BranchId};
use crate::{
consensus::{BlockHeight, BranchId},
sapling::{
self,
bundle::{OutputDescription, SpendDescription},
},
};
use super::{
components::{
amount::Amount,
sapling::{self, OutputDescription, SpendDescription},
transparent::{self, TxIn, TxOut},
},
Authorization, Authorized, TransactionDigest, TransparentDigests, TxDigests, TxId, TxVersion,
@ -136,7 +141,7 @@ pub(crate) fn hash_tze_outputs(tze_outputs: &[TzeOut]) -> Blake2bHash {
/// * \[(cv, anchor, rk, zkproof)*\] - personalized with ZCASH_SAPLING_SPENDS_NONCOMPACT_HASH_PERSONALIZATION
///
/// Then, hash these together personalized by ZCASH_SAPLING_SPENDS_HASH_PERSONALIZATION
pub(crate) fn hash_sapling_spends<A: sapling::Authorization>(
pub(crate) fn hash_sapling_spends<A: sapling::bundle::Authorization>(
shielded_spends: &[SpendDescription<A>],
) -> Blake2bHash {
let mut h = hasher(ZCASH_SAPLING_SPENDS_HASH_PERSONALIZATION);
@ -250,7 +255,9 @@ pub(crate) fn hash_transparent_txid_data(
}
/// Implements [ZIP 244 section T.3](https://zips.z.cash/zip-0244#t-3-sapling-digest)
fn hash_sapling_txid_data<A: sapling::Authorization>(bundle: &sapling::Bundle<A>) -> Blake2bHash {
fn hash_sapling_txid_data<A: sapling::bundle::Authorization>(
bundle: &sapling::Bundle<A>,
) -> Blake2bHash {
let mut h = hasher(ZCASH_SAPLING_HASH_PERSONALIZATION);
if !(bundle.shielded_spends().is_empty() && bundle.shielded_outputs().is_empty()) {
h.write_all(hash_sapling_spends(bundle.shielded_spends()).as_bytes())
@ -460,7 +467,7 @@ impl TransactionDigest<Authorized> for BlockTxCommitmentDigester {
fn digest_sapling(
&self,
sapling_bundle: Option<&sapling::Bundle<sapling::Authorized>>,
sapling_bundle: Option<&sapling::Bundle<sapling::bundle::Authorized>>,
) -> Blake2bHash {
let mut h = hasher(ZCASH_SAPLING_SIGS_HASH_PERSONALIZATION);
if let Some(bundle) = sapling_bundle {

View File

@ -6,11 +6,12 @@ use std::path::Path;
use zcash_primitives::{
sapling::{
self,
bundle::GrothProofBytes,
prover::{OutputProver, SpendProver},
value::{NoteValue, ValueCommitTrapdoor},
Diversifier, MerklePath, PaymentAddress, ProofGenerationKey, Rseed,
},
transaction::components::{sapling::GrothProofBytes, GROTH_PROOF_SIZE},
transaction::components::GROTH_PROOF_SIZE,
};
use crate::{load_parameters, parse_parameters, OutputParameters, SpendParameters};