zcash_primitives: Move Sapling bundle types into `sapling` module
This commit is contained in:
parent
f5595122f9
commit
54eb03e34e
|
@ -7,8 +7,8 @@ use zcash_primitives::{
|
||||||
block::{BlockHash, BlockHeader},
|
block::{BlockHash, BlockHeader},
|
||||||
consensus::BlockHeight,
|
consensus::BlockHeight,
|
||||||
merkle_tree::read_commitment_tree,
|
merkle_tree::read_commitment_tree,
|
||||||
sapling::{note::ExtractedNoteCommitment, Node, Nullifier, NOTE_COMMITMENT_TREE_DEPTH},
|
sapling::{self, note::ExtractedNoteCommitment, Node, Nullifier, NOTE_COMMITMENT_TREE_DEPTH},
|
||||||
transaction::{components::sapling, TxId},
|
transaction::TxId,
|
||||||
};
|
};
|
||||||
|
|
||||||
use zcash_note_encryption::{EphemeralKeyBytes, COMPACT_NOTE_SIZE};
|
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 {
|
impl<Proof> From<&sapling::bundle::OutputDescription<Proof>>
|
||||||
fn from(out: &sapling::OutputDescription<Proof>) -> compact_formats::CompactSaplingOutput {
|
for compact_formats::CompactSaplingOutput
|
||||||
|
{
|
||||||
|
fn from(
|
||||||
|
out: &sapling::bundle::OutputDescription<Proof>,
|
||||||
|
) -> compact_formats::CompactSaplingOutput {
|
||||||
compact_formats::CompactSaplingOutput {
|
compact_formats::CompactSaplingOutput {
|
||||||
cmu: out.cmu().to_bytes().to_vec(),
|
cmu: out.cmu().to_bytes().to_vec(),
|
||||||
ephemeral_key: out.ephemeral_key().as_ref().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 = ();
|
type Error = ();
|
||||||
|
|
||||||
fn try_from(value: compact_formats::CompactSaplingOutput) -> Result<Self, Self::Error> {
|
fn try_from(value: compact_formats::CompactSaplingOutput) -> Result<Self, Self::Error> {
|
||||||
Ok(sapling::CompactOutputDescription {
|
Ok(sapling::note_encryption::CompactOutputDescription {
|
||||||
cmu: value.cmu()?,
|
cmu: value.cmu()?,
|
||||||
ephemeral_key: value.ephemeral_key()?,
|
ephemeral_key: value.ephemeral_key()?,
|
||||||
enc_ciphertext: value.ciphertext.try_into().map_err(|_| ())?,
|
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
|
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 {
|
compact_formats::CompactSaplingSpend {
|
||||||
nf: spend.nullifier().to_vec(),
|
nf: spend.nullifier().to_vec(),
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,10 +12,9 @@ use zcash_primitives::{
|
||||||
consensus,
|
consensus,
|
||||||
sapling::{
|
sapling::{
|
||||||
self,
|
self,
|
||||||
note_encryption::{PreparedIncomingViewingKey, SaplingDomain},
|
note_encryption::{CompactOutputDescription, PreparedIncomingViewingKey, SaplingDomain},
|
||||||
SaplingIvk,
|
SaplingIvk,
|
||||||
},
|
},
|
||||||
transaction::components::sapling::CompactOutputDescription,
|
|
||||||
zip32::{sapling::DiversifiableFullViewingKey, AccountId, Scope},
|
zip32::{sapling::DiversifiableFullViewingKey, AccountId, Scope},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -18,11 +18,34 @@ and this library adheres to Rust's notion of
|
||||||
- `builder::{InProgressProofs, Unproven, Proven}`
|
- `builder::{InProgressProofs, Unproven, Proven}`
|
||||||
- `builder::{InProgressSignatures, Unsigned, PartiallyAuthorized}`
|
- `builder::{InProgressSignatures, Unsigned, PartiallyAuthorized}`
|
||||||
- `builder::{MaybeSigned, SigningParts}`
|
- `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` module (moved from `zcash_proofs::circuit::sapling`).
|
||||||
- `circuit::{SpendParameters, OutputParameters}`
|
- `circuit::{SpendParameters, OutputParameters}`
|
||||||
- `circuit::{SpendVerifyingKey, PreparedSpendVerifyingKey}`
|
- `circuit::{SpendVerifyingKey, PreparedSpendVerifyingKey}`
|
||||||
- `circuit::{OutputVerifyingKey, PreparedOutputVerifyingKey}`
|
- `circuit::{OutputVerifyingKey, PreparedOutputVerifyingKey}`
|
||||||
- `constants` module.
|
- `constants` module.
|
||||||
|
- `note_encryption::CompactOutputDescription` (moved from
|
||||||
|
`zcash_primitives::transaction::components::sapling`).
|
||||||
- `prover::{SpendProver, OutputProver}`
|
- `prover::{SpendProver, OutputProver}`
|
||||||
- `value`:
|
- `value`:
|
||||||
- `ValueCommitTrapdoor::from_bytes`
|
- `ValueCommitTrapdoor::from_bytes`
|
||||||
|
@ -31,17 +54,6 @@ and this library adheres to Rust's notion of
|
||||||
- `impl Debug for keys::{ExpandedSpendingKey, ProofGenerationKey}`
|
- `impl Debug for keys::{ExpandedSpendingKey, ProofGenerationKey}`
|
||||||
- `zcash_primitives::transaction`:
|
- `zcash_primitives::transaction`:
|
||||||
- `builder::get_fee`
|
- `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`
|
- `fees::StandardFeeRule`
|
||||||
- Constants in `fees::zip317`:
|
- Constants in `fees::zip317`:
|
||||||
- `MARGINAL_FEE`
|
- `MARGINAL_FEE`
|
||||||
|
@ -82,13 +94,12 @@ and this library adheres to Rust's notion of
|
||||||
- `Error::DuplicateSignature`
|
- `Error::DuplicateSignature`
|
||||||
- `Error::InvalidExternalSignature`
|
- `Error::InvalidExternalSignature`
|
||||||
- `Error::MissingSignatures`
|
- `Error::MissingSignatures`
|
||||||
|
- `bundle::MapAuth` trait methods now take `&mut self` instead of `&self`.
|
||||||
- `circuit::ValueCommitmentOpening::value` is now represented as a `NoteValue`
|
- `circuit::ValueCommitmentOpening::value` is now represented as a `NoteValue`
|
||||||
instead of as a bare `u64`.
|
instead of as a bare `u64`.
|
||||||
- `zcash_primitives::transaction`:
|
- `zcash_primitives::transaction`:
|
||||||
- `builder::Builder::{build, build_zfuture}` now take
|
- `builder::Builder::{build, build_zfuture}` now take
|
||||||
`&impl SpendProver, &impl OutputProver` instead of `&impl TxProver`.
|
`&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`
|
- `components::transparent::TxOut.value` now has type `NonNegativeAmount`
|
||||||
instead of `Amount`.
|
instead of `Amount`.
|
||||||
- `Unauthorized::SaplingAuth` now has type `InProgress<Proven, Unsigned>`.
|
- `Unauthorized::SaplingAuth` now has type `InProgress<Proven, Unsigned>`.
|
||||||
|
@ -114,9 +125,20 @@ and this library adheres to Rust's notion of
|
||||||
- `zcash_primitives::constants`:
|
- `zcash_primitives::constants`:
|
||||||
- All `const` values (moved to `zcash_primitives::sapling::constants`).
|
- All `const` values (moved to `zcash_primitives::sapling::constants`).
|
||||||
- `zcash_primitives::transaction::components::sapling`:
|
- `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`
|
- `Unproven`
|
||||||
- `builder` (moved to `zcash_primitives::sapling::builder`).
|
- `builder` (moved to `zcash_primitives::sapling::builder`).
|
||||||
- `builder::Unauthorized` (use `builder::InProgress` instead).
|
- `builder::Unauthorized` (use `builder::InProgress` instead).
|
||||||
|
- `testing::{arb_bundle, arb_output_description}` (moved into
|
||||||
|
`zcash_primitives::sapling::bundle::testing`).
|
||||||
- `SpendDescription::<Unauthorized>::apply_signature`
|
- `SpendDescription::<Unauthorized>::apply_signature`
|
||||||
- `Bundle::<Unauthorized>::apply_signatures` (use
|
- `Bundle::<Unauthorized>::apply_signatures` (use
|
||||||
`Bundle::<InProgress<Proven, Unsigned>>::apply_signatures` instead).
|
`Bundle::<InProgress<Proven, Unsigned>>::apply_signatures` instead).
|
||||||
|
|
|
@ -11,13 +11,12 @@ use zcash_primitives::{
|
||||||
builder::SaplingBuilder,
|
builder::SaplingBuilder,
|
||||||
note_encryption::{
|
note_encryption::{
|
||||||
try_sapling_compact_note_decryption, try_sapling_note_decryption,
|
try_sapling_compact_note_decryption, try_sapling_note_decryption,
|
||||||
PreparedIncomingViewingKey, SaplingDomain,
|
CompactOutputDescription, PreparedIncomingViewingKey, SaplingDomain,
|
||||||
},
|
},
|
||||||
prover::mock::{MockOutputProver, MockSpendProver},
|
prover::mock::{MockOutputProver, MockSpendProver},
|
||||||
value::NoteValue,
|
value::NoteValue,
|
||||||
Diversifier, SaplingIvk,
|
Diversifier, SaplingIvk,
|
||||||
},
|
},
|
||||||
transaction::components::sapling::CompactOutputDescription,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
mod address;
|
mod address;
|
||||||
pub mod builder;
|
pub mod builder;
|
||||||
|
pub mod bundle;
|
||||||
pub mod circuit;
|
pub mod circuit;
|
||||||
pub mod constants;
|
pub mod constants;
|
||||||
pub mod group_hash;
|
pub mod group_hash;
|
||||||
|
@ -25,6 +26,7 @@ use constants::SPENDING_KEY_GENERATOR;
|
||||||
use self::redjubjub::{PrivateKey, PublicKey, Signature};
|
use self::redjubjub::{PrivateKey, PublicKey, Signature};
|
||||||
|
|
||||||
pub use address::PaymentAddress;
|
pub use address::PaymentAddress;
|
||||||
|
pub use bundle::Bundle;
|
||||||
pub use keys::{Diversifier, NullifierDerivingKey, ProofGenerationKey, SaplingIvk, ViewingKey};
|
pub use keys::{Diversifier, NullifierDerivingKey, ProofGenerationKey, SaplingIvk, ViewingKey};
|
||||||
pub use note::{nullifier::Nullifier, Note, Rseed};
|
pub use note::{nullifier::Nullifier, Note, Rseed};
|
||||||
pub use tree::{
|
pub use tree::{
|
||||||
|
|
|
@ -13,6 +13,10 @@ use crate::{
|
||||||
memo::MemoBytes,
|
memo::MemoBytes,
|
||||||
sapling::{
|
sapling::{
|
||||||
self,
|
self,
|
||||||
|
bundle::{
|
||||||
|
Authorization, Authorized, Bundle, GrothProofBytes, MapAuth, OutputDescription,
|
||||||
|
SpendDescription,
|
||||||
|
},
|
||||||
constants::{SPENDING_KEY_GENERATOR, VALUE_COMMITMENT_RANDOMNESS_GENERATOR},
|
constants::{SPENDING_KEY_GENERATOR, VALUE_COMMITMENT_RANDOMNESS_GENERATOR},
|
||||||
note_encryption::sapling_note_encryption,
|
note_encryption::sapling_note_encryption,
|
||||||
prover::{OutputProver, SpendProver},
|
prover::{OutputProver, SpendProver},
|
||||||
|
@ -29,10 +33,7 @@ use crate::{
|
||||||
builder::Progress,
|
builder::Progress,
|
||||||
components::{
|
components::{
|
||||||
amount::{Amount, NonNegativeAmount},
|
amount::{Amount, NonNegativeAmount},
|
||||||
sapling::{
|
sapling::fees,
|
||||||
fees, Authorization, Authorized, Bundle, GrothProofBytes, MapAuth,
|
|
||||||
OutputDescription, SpendDescription,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
zip32::ExtendedSpendingKey,
|
zip32::ExtendedSpendingKey,
|
||||||
|
@ -885,16 +886,14 @@ pub mod testing {
|
||||||
TEST_NETWORK,
|
TEST_NETWORK,
|
||||||
},
|
},
|
||||||
sapling::{
|
sapling::{
|
||||||
|
bundle::{Authorized, Bundle},
|
||||||
prover::mock::{MockOutputProver, MockSpendProver},
|
prover::mock::{MockOutputProver, MockSpendProver},
|
||||||
redjubjub::PrivateKey,
|
redjubjub::PrivateKey,
|
||||||
testing::{arb_node, arb_note},
|
testing::{arb_node, arb_note},
|
||||||
value::testing::arb_positive_note_value,
|
value::testing::arb_positive_note_value,
|
||||||
Diversifier,
|
Diversifier,
|
||||||
},
|
},
|
||||||
transaction::components::{
|
transaction::components::amount::MAX_MONEY,
|
||||||
amount::MAX_MONEY,
|
|
||||||
sapling::{Authorized, Bundle},
|
|
||||||
},
|
|
||||||
zip32::sapling::testing::arb_extended_spending_key,
|
zip32::sapling::testing::arb_extended_spending_key,
|
||||||
};
|
};
|
||||||
use incrementalmerkletree::{
|
use incrementalmerkletree::{
|
||||||
|
|
|
@ -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) },
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,6 +19,7 @@ use crate::{
|
||||||
consensus::{self, BlockHeight, NetworkUpgrade::Canopy, ZIP212_GRACE_PERIOD},
|
consensus::{self, BlockHeight, NetworkUpgrade::Canopy, ZIP212_GRACE_PERIOD},
|
||||||
memo::MemoBytes,
|
memo::MemoBytes,
|
||||||
sapling::{
|
sapling::{
|
||||||
|
bundle::{GrothProofBytes, OutputDescription},
|
||||||
keys::{
|
keys::{
|
||||||
DiversifiedTransmissionKey, EphemeralPublicKey, EphemeralSecretKey, OutgoingViewingKey,
|
DiversifiedTransmissionKey, EphemeralPublicKey, EphemeralSecretKey, OutgoingViewingKey,
|
||||||
SharedSecret,
|
SharedSecret,
|
||||||
|
@ -26,10 +27,7 @@ use crate::{
|
||||||
value::ValueCommitment,
|
value::ValueCommitment,
|
||||||
Diversifier, Note, PaymentAddress, Rseed,
|
Diversifier, Note, PaymentAddress, Rseed,
|
||||||
},
|
},
|
||||||
transaction::components::{
|
transaction::components::amount::NonNegativeAmount,
|
||||||
amount::NonNegativeAmount,
|
|
||||||
sapling::{self, OutputDescription},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::note::ExtractedNoteCommitment;
|
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.
|
/// Creates a new encryption context for the given note.
|
||||||
///
|
///
|
||||||
/// Setting `ovk` to `None` represents the `ovk = ⊥` case, where the note cannot be
|
/// 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,
|
params: &P,
|
||||||
height: BlockHeight,
|
height: BlockHeight,
|
||||||
ock: &OutgoingCipherKey,
|
ock: &OutgoingCipherKey,
|
||||||
output: &OutputDescription<sapling::GrothProofBytes>,
|
output: &OutputDescription<GrothProofBytes>,
|
||||||
) -> Option<(Note, PaymentAddress, MemoBytes)> {
|
) -> Option<(Note, PaymentAddress, MemoBytes)> {
|
||||||
let domain = SaplingDomain {
|
let domain = SaplingDomain {
|
||||||
params: params.clone(),
|
params: params.clone(),
|
||||||
|
@ -479,7 +502,7 @@ pub fn try_sapling_output_recovery<P: consensus::Parameters>(
|
||||||
params: &P,
|
params: &P,
|
||||||
height: BlockHeight,
|
height: BlockHeight,
|
||||||
ovk: &OutgoingViewingKey,
|
ovk: &OutgoingViewingKey,
|
||||||
output: &OutputDescription<sapling::GrothProofBytes>,
|
output: &OutputDescription<GrothProofBytes>,
|
||||||
) -> Option<(Note, PaymentAddress, MemoBytes)> {
|
) -> Option<(Note, PaymentAddress, MemoBytes)> {
|
||||||
let domain = SaplingDomain {
|
let domain = SaplingDomain {
|
||||||
params: params.clone(),
|
params: params.clone(),
|
||||||
|
@ -509,7 +532,7 @@ mod tests {
|
||||||
use super::{
|
use super::{
|
||||||
prf_ock, sapling_note_encryption, try_sapling_compact_note_decryption,
|
prf_ock, sapling_note_encryption, try_sapling_compact_note_decryption,
|
||||||
try_sapling_note_decryption, try_sapling_output_recovery,
|
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::{
|
use crate::{
|
||||||
|
@ -521,6 +544,7 @@ mod tests {
|
||||||
keys::OutgoingViewingKey,
|
keys::OutgoingViewingKey,
|
||||||
memo::MemoBytes,
|
memo::MemoBytes,
|
||||||
sapling::{
|
sapling::{
|
||||||
|
bundle::{GrothProofBytes, OutputDescription},
|
||||||
keys::{DiversifiedTransmissionKey, EphemeralSecretKey},
|
keys::{DiversifiedTransmissionKey, EphemeralSecretKey},
|
||||||
note::ExtractedNoteCommitment,
|
note::ExtractedNoteCommitment,
|
||||||
note_encryption::PreparedIncomingViewingKey,
|
note_encryption::PreparedIncomingViewingKey,
|
||||||
|
@ -528,10 +552,7 @@ mod tests {
|
||||||
value::{NoteValue, ValueCommitTrapdoor, ValueCommitment},
|
value::{NoteValue, ValueCommitTrapdoor, ValueCommitment},
|
||||||
Diversifier, PaymentAddress, Rseed, SaplingIvk,
|
Diversifier, PaymentAddress, Rseed, SaplingIvk,
|
||||||
},
|
},
|
||||||
transaction::components::{
|
transaction::components::GROTH_PROOF_SIZE,
|
||||||
sapling::{self, CompactOutputDescription, OutputDescription},
|
|
||||||
GROTH_PROOF_SIZE,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
fn random_enc_ciphertext<R: RngCore + CryptoRng>(
|
fn random_enc_ciphertext<R: RngCore + CryptoRng>(
|
||||||
|
@ -541,7 +562,7 @@ mod tests {
|
||||||
OutgoingViewingKey,
|
OutgoingViewingKey,
|
||||||
OutgoingCipherKey,
|
OutgoingCipherKey,
|
||||||
PreparedIncomingViewingKey,
|
PreparedIncomingViewingKey,
|
||||||
OutputDescription<sapling::GrothProofBytes>,
|
OutputDescription<GrothProofBytes>,
|
||||||
) {
|
) {
|
||||||
let ivk = SaplingIvk(jubjub::Fr::random(&mut rng));
|
let ivk = SaplingIvk(jubjub::Fr::random(&mut rng));
|
||||||
let prepared_ivk = PreparedIncomingViewingKey::new(&ivk);
|
let prepared_ivk = PreparedIncomingViewingKey::new(&ivk);
|
||||||
|
@ -577,7 +598,7 @@ mod tests {
|
||||||
) -> (
|
) -> (
|
||||||
OutgoingViewingKey,
|
OutgoingViewingKey,
|
||||||
OutgoingCipherKey,
|
OutgoingCipherKey,
|
||||||
OutputDescription<sapling::GrothProofBytes>,
|
OutputDescription<GrothProofBytes>,
|
||||||
) {
|
) {
|
||||||
let diversifier = Diversifier([0; 11]);
|
let diversifier = Diversifier([0; 11]);
|
||||||
let pa = ivk.to_payment_address(diversifier).unwrap();
|
let pa = ivk.to_payment_address(diversifier).unwrap();
|
||||||
|
|
|
@ -7,10 +7,11 @@ use rand_core::RngCore;
|
||||||
use crate::{
|
use crate::{
|
||||||
sapling::{
|
sapling::{
|
||||||
self,
|
self,
|
||||||
|
bundle::GrothProofBytes,
|
||||||
value::{NoteValue, ValueCommitTrapdoor},
|
value::{NoteValue, ValueCommitTrapdoor},
|
||||||
MerklePath,
|
MerklePath,
|
||||||
},
|
},
|
||||||
transaction::components::{sapling::GrothProofBytes, GROTH_PROOF_SIZE},
|
transaction::components::GROTH_PROOF_SIZE,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
|
@ -188,11 +189,12 @@ pub mod mock {
|
||||||
use crate::{
|
use crate::{
|
||||||
sapling::{
|
sapling::{
|
||||||
self,
|
self,
|
||||||
|
bundle::GrothProofBytes,
|
||||||
circuit::ValueCommitmentOpening,
|
circuit::ValueCommitmentOpening,
|
||||||
value::{NoteValue, ValueCommitTrapdoor},
|
value::{NoteValue, ValueCommitTrapdoor},
|
||||||
Diversifier, PaymentAddress, ProofGenerationKey, Rseed,
|
Diversifier, PaymentAddress, ProofGenerationKey, Rseed,
|
||||||
},
|
},
|
||||||
transaction::components::{sapling::GrothProofBytes, GROTH_PROOF_SIZE},
|
transaction::components::GROTH_PROOF_SIZE,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct MockSpendProver;
|
pub struct MockSpendProver;
|
||||||
|
|
|
@ -31,8 +31,8 @@
|
||||||
//! you should only need to interact with [`NoteValue`] (which can be safely constructed
|
//! 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`).
|
//! from a `u64`) and `valueBalanceSapling` (which can be represented as an `i64`).
|
||||||
//!
|
//!
|
||||||
//! [`Bundle`]: crate::transaction::components::sapling::Bundle
|
//! [`Bundle`]: crate::sapling::Bundle
|
||||||
//! [`Bundle::value_balance`]: crate::transaction::components::sapling::Bundle::value_balance
|
//! [`Bundle::value_balance`]: crate::sapling::Bundle::value_balance
|
||||||
//! [`SaplingBuilder::value_balance`]: crate::sapling::builder::SaplingBuilder::value_balance
|
//! [`SaplingBuilder::value_balance`]: crate::sapling::builder::SaplingBuilder::value_balance
|
||||||
//! [`SaplingBuilder::add_output`]: crate::sapling::builder::SaplingBuilder::add_output
|
//! [`SaplingBuilder::add_output`]: crate::sapling::builder::SaplingBuilder::add_output
|
||||||
//! [Rust documentation]: https://doc.rust-lang.org/stable/std/primitive.i64.html
|
//! [Rust documentation]: https://doc.rust-lang.org/stable/std/primitive.i64.html
|
||||||
|
|
|
@ -4,9 +4,9 @@ use group::GroupEncoding;
|
||||||
use rand_core::{CryptoRng, RngCore};
|
use rand_core::{CryptoRng, RngCore};
|
||||||
|
|
||||||
use super::SaplingVerificationContextInner;
|
use super::SaplingVerificationContextInner;
|
||||||
use crate::{
|
use crate::sapling::{
|
||||||
sapling::circuit::{OutputVerifyingKey, SpendVerifyingKey},
|
bundle::{Authorized, Bundle},
|
||||||
transaction::components::sapling::{Authorized, Bundle},
|
circuit::{OutputVerifyingKey, SpendVerifyingKey},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Batch validation context for Sapling.
|
/// Batch validation context for Sapling.
|
||||||
|
|
|
@ -6,12 +6,13 @@ pub mod sapling;
|
||||||
pub mod sprout;
|
pub mod sprout;
|
||||||
pub mod transparent;
|
pub mod transparent;
|
||||||
pub mod tze;
|
pub mod tze;
|
||||||
|
|
||||||
pub use self::{
|
pub use self::{
|
||||||
amount::Amount,
|
amount::Amount,
|
||||||
sapling::{OutputDescription, SpendDescription},
|
|
||||||
sprout::JsDescription,
|
sprout::JsDescription,
|
||||||
transparent::{OutPoint, TxIn, TxOut},
|
transparent::{OutPoint, TxIn, TxOut},
|
||||||
};
|
};
|
||||||
|
pub use crate::sapling::bundle::{OutputDescription, SpendDescription};
|
||||||
|
|
||||||
#[cfg(feature = "zfuture")]
|
#[cfg(feature = "zfuture")]
|
||||||
pub use self::tze::{TzeIn, TzeOut};
|
pub use self::tze::{TzeIn, TzeOut};
|
||||||
|
|
|
@ -1,408 +1,24 @@
|
||||||
use core::fmt::Debug;
|
|
||||||
|
|
||||||
use ff::PrimeField;
|
use ff::PrimeField;
|
||||||
use memuse::DynamicUsage;
|
|
||||||
|
|
||||||
use std::io::{self, Read, Write};
|
use std::io::{self, Read, Write};
|
||||||
|
|
||||||
use zcash_note_encryption::{
|
use zcash_note_encryption::EphemeralKeyBytes;
|
||||||
EphemeralKeyBytes, ShieldedOutput, COMPACT_NOTE_SIZE, ENC_CIPHERTEXT_SIZE,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::sapling::{
|
||||||
consensus,
|
bundle::{
|
||||||
sapling::{
|
Authorized, GrothProofBytes, OutputDescription, OutputDescriptionV5, SpendDescription,
|
||||||
note::ExtractedNoteCommitment,
|
SpendDescriptionV5,
|
||||||
note_encryption::SaplingDomain,
|
|
||||||
redjubjub::{self, PublicKey, Signature},
|
|
||||||
value::ValueCommitment,
|
|
||||||
Nullifier,
|
|
||||||
},
|
},
|
||||||
|
note::ExtractedNoteCommitment,
|
||||||
|
redjubjub::{PublicKey, Signature},
|
||||||
|
value::ValueCommitment,
|
||||||
|
Nullifier,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{amount::Amount, GROTH_PROOF_SIZE};
|
use super::GROTH_PROOF_SIZE;
|
||||||
|
|
||||||
pub type GrothProofBytes = [u8; GROTH_PROOF_SIZE];
|
|
||||||
|
|
||||||
pub mod fees;
|
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):
|
/// Consensus rules (§4.4) & (§4.5):
|
||||||
/// - Canonical encoding is enforced here.
|
/// - Canonical encoding is enforced here.
|
||||||
/// - "Not small order" is enforced here.
|
/// - "Not small order" is enforced here.
|
||||||
|
@ -487,195 +103,39 @@ impl SpendDescription<Authorized> {
|
||||||
let zkproof = read_zkproof(&mut reader)?;
|
let zkproof = read_zkproof(&mut reader)?;
|
||||||
let spend_auth_sig = Self::read_spend_auth_sig(&mut reader)?;
|
let spend_auth_sig = Self::read_spend_auth_sig(&mut reader)?;
|
||||||
|
|
||||||
Ok(SpendDescription {
|
Ok(SpendDescription::from_parts(
|
||||||
cv,
|
cv,
|
||||||
anchor,
|
anchor,
|
||||||
nullifier,
|
nullifier,
|
||||||
rk,
|
rk,
|
||||||
zkproof,
|
zkproof,
|
||||||
spend_auth_sig,
|
spend_auth_sig,
|
||||||
})
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_v4<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
pub fn write_v4<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
||||||
writer.write_all(&self.cv.to_bytes())?;
|
writer.write_all(&self.cv().to_bytes())?;
|
||||||
writer.write_all(self.anchor.to_repr().as_ref())?;
|
writer.write_all(self.anchor().to_repr().as_ref())?;
|
||||||
writer.write_all(&self.nullifier.0)?;
|
writer.write_all(&self.nullifier().0)?;
|
||||||
self.rk.write(&mut writer)?;
|
self.rk().write(&mut writer)?;
|
||||||
writer.write_all(&self.zkproof)?;
|
writer.write_all(self.zkproof())?;
|
||||||
self.spend_auth_sig.write(&mut writer)
|
self.spend_auth_sig().write(&mut writer)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_v5_without_witness_data<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
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.cv().to_bytes())?;
|
||||||
writer.write_all(&self.nullifier.0)?;
|
writer.write_all(&self.nullifier().0)?;
|
||||||
self.rk.write(&mut writer)
|
self.rk().write(&mut writer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct SpendDescriptionV5 {
|
|
||||||
cv: ValueCommitment,
|
|
||||||
nullifier: Nullifier,
|
|
||||||
rk: PublicKey,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SpendDescriptionV5 {
|
impl SpendDescriptionV5 {
|
||||||
pub fn read<R: Read>(mut reader: &mut R) -> io::Result<Self> {
|
pub fn read<R: Read>(mut reader: &mut R) -> io::Result<Self> {
|
||||||
let cv = read_value_commitment(&mut reader)?;
|
let cv = read_value_commitment(&mut reader)?;
|
||||||
let nullifier = SpendDescription::read_nullifier(&mut reader)?;
|
let nullifier = SpendDescription::read_nullifier(&mut reader)?;
|
||||||
let rk = SpendDescription::read_rk(&mut reader)?;
|
let rk = SpendDescription::read_rk(&mut reader)?;
|
||||||
|
|
||||||
Ok(SpendDescriptionV5 { cv, nullifier, rk })
|
Ok(SpendDescriptionV5::from_parts(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
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -703,45 +163,34 @@ impl OutputDescription<GrothProofBytes> {
|
||||||
|
|
||||||
let zkproof = read_zkproof(&mut reader)?;
|
let zkproof = read_zkproof(&mut reader)?;
|
||||||
|
|
||||||
Ok(OutputDescription {
|
Ok(OutputDescription::from_parts(
|
||||||
cv,
|
cv,
|
||||||
cmu,
|
cmu,
|
||||||
ephemeral_key,
|
ephemeral_key,
|
||||||
enc_ciphertext,
|
enc_ciphertext,
|
||||||
out_ciphertext,
|
out_ciphertext,
|
||||||
zkproof,
|
zkproof,
|
||||||
})
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_v4<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
pub fn write_v4<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
||||||
writer.write_all(&self.cv.to_bytes())?;
|
writer.write_all(&self.cv().to_bytes())?;
|
||||||
writer.write_all(self.cmu.to_bytes().as_ref())?;
|
writer.write_all(self.cmu().to_bytes().as_ref())?;
|
||||||
writer.write_all(self.ephemeral_key.as_ref())?;
|
writer.write_all(self.ephemeral_key().as_ref())?;
|
||||||
writer.write_all(&self.enc_ciphertext)?;
|
writer.write_all(self.enc_ciphertext())?;
|
||||||
writer.write_all(&self.out_ciphertext)?;
|
writer.write_all(self.out_ciphertext())?;
|
||||||
writer.write_all(&self.zkproof)
|
writer.write_all(self.zkproof())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_v5_without_proof<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
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.cv().to_bytes())?;
|
||||||
writer.write_all(self.cmu.to_bytes().as_ref())?;
|
writer.write_all(self.cmu().to_bytes().as_ref())?;
|
||||||
writer.write_all(self.ephemeral_key.as_ref())?;
|
writer.write_all(self.ephemeral_key().as_ref())?;
|
||||||
writer.write_all(&self.enc_ciphertext)?;
|
writer.write_all(self.enc_ciphertext())?;
|
||||||
writer.write_all(&self.out_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 {
|
impl OutputDescriptionV5 {
|
||||||
pub fn read<R: Read>(mut reader: &mut R) -> io::Result<Self> {
|
pub fn read<R: Read>(mut reader: &mut R) -> io::Result<Self> {
|
||||||
let cv = read_value_commitment(&mut reader)?;
|
let cv = read_value_commitment(&mut reader)?;
|
||||||
|
@ -758,190 +207,25 @@ impl OutputDescriptionV5 {
|
||||||
reader.read_exact(&mut enc_ciphertext)?;
|
reader.read_exact(&mut enc_ciphertext)?;
|
||||||
reader.read_exact(&mut out_ciphertext)?;
|
reader.read_exact(&mut out_ciphertext)?;
|
||||||
|
|
||||||
Ok(OutputDescriptionV5 {
|
Ok(OutputDescriptionV5::from_parts(
|
||||||
cv,
|
cv,
|
||||||
cmu,
|
cmu,
|
||||||
ephemeral_key,
|
ephemeral_key,
|
||||||
enc_ciphertext,
|
enc_ciphertext,
|
||||||
out_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"))]
|
#[cfg(any(test, feature = "test-dependencies"))]
|
||||||
pub mod testing {
|
pub mod testing {
|
||||||
use ff::Field;
|
|
||||||
use group::{Group, GroupEncoding};
|
|
||||||
use proptest::collection::vec;
|
|
||||||
use proptest::prelude::*;
|
use proptest::prelude::*;
|
||||||
use rand::{rngs::StdRng, SeedableRng};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
sapling::{
|
sapling::bundle::{testing::arb_bundle, Authorized, Bundle},
|
||||||
constants::{SPENDING_KEY_GENERATOR, VALUE_COMMITMENT_RANDOMNESS_GENERATOR},
|
transaction::TxVersion,
|
||||||
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,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
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(
|
pub fn arb_bundle_for_version(
|
||||||
v: TxVersion,
|
v: TxVersion,
|
||||||
) -> impl Strategy<Value = Option<Bundle<Authorized>>> {
|
) -> impl Strategy<Value = Option<Bundle<Authorized>>> {
|
||||||
|
|
|
@ -24,16 +24,17 @@ use zcash_encoding::{Array, CompactSize, Vector};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
consensus::{BlockHeight, BranchId},
|
consensus::{BlockHeight, BranchId},
|
||||||
sapling::{builder as sapling_builder, redjubjub},
|
sapling::{
|
||||||
|
self, builder as sapling_builder,
|
||||||
|
bundle::{OutputDescription, OutputDescriptionV5, SpendDescription, SpendDescriptionV5},
|
||||||
|
redjubjub,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use self::{
|
use self::{
|
||||||
components::{
|
components::{
|
||||||
amount::{Amount, BalanceError},
|
amount::{Amount, BalanceError},
|
||||||
orchard as orchard_serialization,
|
orchard as orchard_serialization, sapling as sapling_serialization,
|
||||||
sapling::{
|
|
||||||
self, OutputDescription, OutputDescriptionV5, SpendDescription, SpendDescriptionV5,
|
|
||||||
},
|
|
||||||
sprout::{self, JsDescription},
|
sprout::{self, JsDescription},
|
||||||
transparent::{self, TxIn, TxOut},
|
transparent::{self, TxIn, TxOut},
|
||||||
OutPoint,
|
OutPoint,
|
||||||
|
@ -252,7 +253,7 @@ impl TxVersion {
|
||||||
/// Authorization state for a bundle of transaction data.
|
/// Authorization state for a bundle of transaction data.
|
||||||
pub trait Authorization {
|
pub trait Authorization {
|
||||||
type TransparentAuth: transparent::Authorization;
|
type TransparentAuth: transparent::Authorization;
|
||||||
type SaplingAuth: sapling::Authorization;
|
type SaplingAuth: sapling::bundle::Authorization;
|
||||||
type OrchardAuth: orchard::bundle::Authorization;
|
type OrchardAuth: orchard::bundle::Authorization;
|
||||||
|
|
||||||
#[cfg(feature = "zfuture")]
|
#[cfg(feature = "zfuture")]
|
||||||
|
@ -264,7 +265,7 @@ pub struct Authorized;
|
||||||
|
|
||||||
impl Authorization for Authorized {
|
impl Authorization for Authorized {
|
||||||
type TransparentAuth = transparent::Authorized;
|
type TransparentAuth = transparent::Authorized;
|
||||||
type SaplingAuth = sapling::Authorized;
|
type SaplingAuth = sapling::bundle::Authorized;
|
||||||
type OrchardAuth = orchard::bundle::Authorized;
|
type OrchardAuth = orchard::bundle::Authorized;
|
||||||
|
|
||||||
#[cfg(feature = "zfuture")]
|
#[cfg(feature = "zfuture")]
|
||||||
|
@ -490,7 +491,7 @@ impl<A: Authorization> TransactionData<A> {
|
||||||
pub fn map_authorization<B: Authorization>(
|
pub fn map_authorization<B: Authorization>(
|
||||||
self,
|
self,
|
||||||
f_transparent: impl transparent::MapAuth<A::TransparentAuth, B::TransparentAuth>,
|
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>,
|
mut f_orchard: impl orchard_serialization::MapAuth<A::OrchardAuth, B::OrchardAuth>,
|
||||||
#[cfg(feature = "zfuture")] f_tze: impl tze::MapAuth<A::TzeAuth, B::TzeAuth>,
|
#[cfg(feature = "zfuture")] f_tze: impl tze::MapAuth<A::TzeAuth, B::TzeAuth>,
|
||||||
) -> TransactionData<B> {
|
) -> TransactionData<B> {
|
||||||
|
@ -604,10 +605,10 @@ impl Transaction {
|
||||||
let (value_balance, shielded_spends, shielded_outputs) = if version.has_sapling() {
|
let (value_balance, shielded_spends, shielded_outputs) = if version.has_sapling() {
|
||||||
let vb = Self::read_amount(&mut reader)?;
|
let vb = Self::read_amount(&mut reader)?;
|
||||||
#[allow(clippy::redundant_closure)]
|
#[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))?;
|
Vector::read(&mut reader, |r| SpendDescription::read(r))?;
|
||||||
#[allow(clippy::redundant_closure)]
|
#[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))?;
|
Vector::read(&mut reader, |r| OutputDescription::read(r))?;
|
||||||
(vb, ss, so)
|
(vb, ss, so)
|
||||||
} else {
|
} else {
|
||||||
|
@ -661,7 +662,7 @@ impl Transaction {
|
||||||
shielded_spends,
|
shielded_spends,
|
||||||
shielded_outputs,
|
shielded_outputs,
|
||||||
value_balance,
|
value_balance,
|
||||||
sapling::Authorized { binding_sig },
|
sapling::bundle::Authorized { binding_sig },
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
orchard_bundle: None,
|
orchard_bundle: None,
|
||||||
|
@ -741,14 +742,14 @@ impl Transaction {
|
||||||
#[cfg(feature = "temporary-zcashd")]
|
#[cfg(feature = "temporary-zcashd")]
|
||||||
pub fn temporary_zcashd_read_v5_sapling<R: Read>(
|
pub fn temporary_zcashd_read_v5_sapling<R: Read>(
|
||||||
reader: R,
|
reader: R,
|
||||||
) -> io::Result<Option<sapling::Bundle<sapling::Authorized>>> {
|
) -> io::Result<Option<sapling::Bundle<sapling::bundle::Authorized>>> {
|
||||||
Self::read_v5_sapling(reader)
|
Self::read_v5_sapling(reader)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::redundant_closure)]
|
#[allow(clippy::redundant_closure)]
|
||||||
fn read_v5_sapling<R: Read>(
|
fn read_v5_sapling<R: Read>(
|
||||||
mut reader: R,
|
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 sd_v5s = Vector::read(&mut reader, SpendDescriptionV5::read)?;
|
||||||
let od_v5s = Vector::read(&mut reader, OutputDescriptionV5::read)?;
|
let od_v5s = Vector::read(&mut reader, OutputDescriptionV5::read)?;
|
||||||
let n_spends = sd_v5s.len();
|
let n_spends = sd_v5s.len();
|
||||||
|
@ -760,16 +761,20 @@ impl Transaction {
|
||||||
};
|
};
|
||||||
|
|
||||||
let anchor = if n_spends > 0 {
|
let anchor = if n_spends > 0 {
|
||||||
Some(sapling::read_base(&mut reader, "anchor")?)
|
Some(sapling_serialization::read_base(&mut reader, "anchor")?)
|
||||||
} else {
|
} else {
|
||||||
None
|
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| {
|
let v_spend_auth_sigs = Array::read(&mut reader, n_spends, |r| {
|
||||||
SpendDescription::read_spend_auth_sig(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 {
|
let binding_sig = if n_spends > 0 || n_outputs > 0 {
|
||||||
Some(redjubjub::Signature::read(&mut reader)?)
|
Some(redjubjub::Signature::read(&mut reader)?)
|
||||||
|
@ -801,7 +806,7 @@ impl Transaction {
|
||||||
shielded_spends,
|
shielded_spends,
|
||||||
shielded_outputs,
|
shielded_outputs,
|
||||||
value_balance,
|
value_balance,
|
||||||
sapling::Authorized { binding_sig },
|
sapling::bundle::Authorized { binding_sig },
|
||||||
)
|
)
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
@ -934,7 +939,7 @@ impl Transaction {
|
||||||
|
|
||||||
#[cfg(feature = "temporary-zcashd")]
|
#[cfg(feature = "temporary-zcashd")]
|
||||||
pub fn temporary_zcashd_write_v5_sapling<W: Write>(
|
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,
|
writer: W,
|
||||||
) -> io::Result<()> {
|
) -> io::Result<()> {
|
||||||
Self::write_v5_sapling_inner(sapling_bundle, writer)
|
Self::write_v5_sapling_inner(sapling_bundle, writer)
|
||||||
|
@ -945,7 +950,7 @@ impl Transaction {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_v5_sapling_inner<W: Write>(
|
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,
|
mut writer: W,
|
||||||
) -> io::Result<()> {
|
) -> io::Result<()> {
|
||||||
if let Some(bundle) = sapling_bundle {
|
if let Some(bundle) = sapling_bundle {
|
||||||
|
|
|
@ -1,16 +1,15 @@
|
||||||
use crate::legacy::Script;
|
|
||||||
use blake2b_simd::Hash as Blake2bHash;
|
use blake2b_simd::Hash as Blake2bHash;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
components::{
|
components::{amount::NonNegativeAmount, transparent},
|
||||||
amount::NonNegativeAmount,
|
|
||||||
sapling::{self, GrothProofBytes},
|
|
||||||
transparent,
|
|
||||||
},
|
|
||||||
sighash_v4::v4_signature_hash,
|
sighash_v4::v4_signature_hash,
|
||||||
sighash_v5::v5_signature_hash,
|
sighash_v5::v5_signature_hash,
|
||||||
Authorization, TransactionData, TxDigests, TxVersion,
|
Authorization, TransactionData, TxDigests, TxVersion,
|
||||||
};
|
};
|
||||||
|
use crate::{
|
||||||
|
legacy::Script,
|
||||||
|
sapling::{self, bundle::GrothProofBytes},
|
||||||
|
};
|
||||||
|
|
||||||
#[cfg(feature = "zfuture")]
|
#[cfg(feature = "zfuture")]
|
||||||
use {super::components::Amount, crate::extensions::transparent::Precondition};
|
use {super::components::Amount, crate::extensions::transparent::Precondition};
|
||||||
|
@ -79,7 +78,7 @@ pub trait TransparentAuthorizingContext: transparent::Authorization {
|
||||||
pub fn signature_hash<
|
pub fn signature_hash<
|
||||||
'a,
|
'a,
|
||||||
TA: TransparentAuthorizingContext,
|
TA: TransparentAuthorizingContext,
|
||||||
SA: sapling::Authorization<SpendProof = GrothProofBytes, OutputProof = GrothProofBytes>,
|
SA: sapling::bundle::Authorization<SpendProof = GrothProofBytes, OutputProof = GrothProofBytes>,
|
||||||
A: Authorization<SaplingAuth = SA, TransparentAuth = TA>,
|
A: Authorization<SaplingAuth = SA, TransparentAuth = TA>,
|
||||||
>(
|
>(
|
||||||
tx: &TransactionData<A>,
|
tx: &TransactionData<A>,
|
||||||
|
|
|
@ -1,11 +1,16 @@
|
||||||
use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams};
|
use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams};
|
||||||
use ff::PrimeField;
|
use ff::PrimeField;
|
||||||
|
|
||||||
use crate::consensus::BranchId;
|
use crate::{
|
||||||
|
consensus::BranchId,
|
||||||
|
sapling::{
|
||||||
|
self,
|
||||||
|
bundle::{GrothProofBytes, OutputDescription, SpendDescription},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
components::{
|
components::{
|
||||||
sapling::{self, GrothProofBytes, OutputDescription, SpendDescription},
|
|
||||||
sprout::JsDescription,
|
sprout::JsDescription,
|
||||||
transparent::{self, TxIn, TxOut},
|
transparent::{self, TxIn, TxOut},
|
||||||
},
|
},
|
||||||
|
@ -97,7 +102,7 @@ fn joinsplits_hash(
|
||||||
}
|
}
|
||||||
|
|
||||||
fn shielded_spends_hash<
|
fn shielded_spends_hash<
|
||||||
A: sapling::Authorization<SpendProof = GrothProofBytes, OutputProof = GrothProofBytes>,
|
A: sapling::bundle::Authorization<SpendProof = GrothProofBytes, OutputProof = GrothProofBytes>,
|
||||||
>(
|
>(
|
||||||
shielded_spends: &[SpendDescription<A>],
|
shielded_spends: &[SpendDescription<A>],
|
||||||
) -> Blake2bHash {
|
) -> Blake2bHash {
|
||||||
|
@ -127,7 +132,7 @@ fn shielded_outputs_hash(shielded_outputs: &[OutputDescription<GrothProofBytes>]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn v4_signature_hash<
|
pub fn v4_signature_hash<
|
||||||
SA: sapling::Authorization<SpendProof = GrothProofBytes, OutputProof = GrothProofBytes>,
|
SA: sapling::bundle::Authorization<SpendProof = GrothProofBytes, OutputProof = GrothProofBytes>,
|
||||||
A: Authorization<SaplingAuth = SA>,
|
A: Authorization<SaplingAuth = SA>,
|
||||||
>(
|
>(
|
||||||
tx: &TransactionData<A>,
|
tx: &TransactionData<A>,
|
||||||
|
|
|
@ -193,7 +193,7 @@ struct TestUnauthorized;
|
||||||
|
|
||||||
impl Authorization for TestUnauthorized {
|
impl Authorization for TestUnauthorized {
|
||||||
type TransparentAuth = TestTransparentAuth;
|
type TransparentAuth = TestTransparentAuth;
|
||||||
type SaplingAuth = sapling::Authorized;
|
type SaplingAuth = sapling::bundle::Authorized;
|
||||||
type OrchardAuth = orchard::bundle::Authorized;
|
type OrchardAuth = orchard::bundle::Authorized;
|
||||||
|
|
||||||
#[cfg(feature = "zfuture")]
|
#[cfg(feature = "zfuture")]
|
||||||
|
|
|
@ -7,12 +7,17 @@ use byteorder::{LittleEndian, WriteBytesExt};
|
||||||
use ff::PrimeField;
|
use ff::PrimeField;
|
||||||
use orchard::bundle::{self as orchard};
|
use orchard::bundle::{self as orchard};
|
||||||
|
|
||||||
use crate::consensus::{BlockHeight, BranchId};
|
use crate::{
|
||||||
|
consensus::{BlockHeight, BranchId},
|
||||||
|
sapling::{
|
||||||
|
self,
|
||||||
|
bundle::{OutputDescription, SpendDescription},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
components::{
|
components::{
|
||||||
amount::Amount,
|
amount::Amount,
|
||||||
sapling::{self, OutputDescription, SpendDescription},
|
|
||||||
transparent::{self, TxIn, TxOut},
|
transparent::{self, TxIn, TxOut},
|
||||||
},
|
},
|
||||||
Authorization, Authorized, TransactionDigest, TransparentDigests, TxDigests, TxId, TxVersion,
|
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
|
/// * \[(cv, anchor, rk, zkproof)*\] - personalized with ZCASH_SAPLING_SPENDS_NONCOMPACT_HASH_PERSONALIZATION
|
||||||
///
|
///
|
||||||
/// Then, hash these together personalized by ZCASH_SAPLING_SPENDS_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>],
|
shielded_spends: &[SpendDescription<A>],
|
||||||
) -> Blake2bHash {
|
) -> Blake2bHash {
|
||||||
let mut h = hasher(ZCASH_SAPLING_SPENDS_HASH_PERSONALIZATION);
|
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)
|
/// 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);
|
let mut h = hasher(ZCASH_SAPLING_HASH_PERSONALIZATION);
|
||||||
if !(bundle.shielded_spends().is_empty() && bundle.shielded_outputs().is_empty()) {
|
if !(bundle.shielded_spends().is_empty() && bundle.shielded_outputs().is_empty()) {
|
||||||
h.write_all(hash_sapling_spends(bundle.shielded_spends()).as_bytes())
|
h.write_all(hash_sapling_spends(bundle.shielded_spends()).as_bytes())
|
||||||
|
@ -460,7 +467,7 @@ impl TransactionDigest<Authorized> for BlockTxCommitmentDigester {
|
||||||
|
|
||||||
fn digest_sapling(
|
fn digest_sapling(
|
||||||
&self,
|
&self,
|
||||||
sapling_bundle: Option<&sapling::Bundle<sapling::Authorized>>,
|
sapling_bundle: Option<&sapling::Bundle<sapling::bundle::Authorized>>,
|
||||||
) -> Blake2bHash {
|
) -> Blake2bHash {
|
||||||
let mut h = hasher(ZCASH_SAPLING_SIGS_HASH_PERSONALIZATION);
|
let mut h = hasher(ZCASH_SAPLING_SIGS_HASH_PERSONALIZATION);
|
||||||
if let Some(bundle) = sapling_bundle {
|
if let Some(bundle) = sapling_bundle {
|
||||||
|
|
|
@ -6,11 +6,12 @@ use std::path::Path;
|
||||||
use zcash_primitives::{
|
use zcash_primitives::{
|
||||||
sapling::{
|
sapling::{
|
||||||
self,
|
self,
|
||||||
|
bundle::GrothProofBytes,
|
||||||
prover::{OutputProver, SpendProver},
|
prover::{OutputProver, SpendProver},
|
||||||
value::{NoteValue, ValueCommitTrapdoor},
|
value::{NoteValue, ValueCommitTrapdoor},
|
||||||
Diversifier, MerklePath, PaymentAddress, ProofGenerationKey, Rseed,
|
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};
|
use crate::{load_parameters, parse_parameters, OutputParameters, SpendParameters};
|
||||||
|
|
Loading…
Reference in New Issue