Merge pull request #1039 from zcash/736-refactor-sapling-components
Refactor Sapling components and builder into `zcash_primitives::sapling`
This commit is contained in:
commit
2d6a02eb2d
|
@ -6,9 +6,10 @@ use std::fmt::{self, Debug, Display};
|
|||
use shardtree::error::ShardTreeError;
|
||||
use zcash_primitives::transaction::components::amount::NonNegativeAmount;
|
||||
use zcash_primitives::{
|
||||
sapling,
|
||||
transaction::{
|
||||
builder,
|
||||
components::{amount::BalanceError, sapling, transparent},
|
||||
components::{amount::BalanceError, transparent},
|
||||
},
|
||||
zip32::AccountId,
|
||||
};
|
||||
|
|
|
@ -7,8 +7,8 @@ use zcash_primitives::{
|
|||
block::{BlockHash, BlockHeader},
|
||||
consensus::BlockHeight,
|
||||
merkle_tree::read_commitment_tree,
|
||||
sapling::{note::ExtractedNoteCommitment, Node, Nullifier, NOTE_COMMITMENT_TREE_DEPTH},
|
||||
transaction::{components::sapling, TxId},
|
||||
sapling::{self, note::ExtractedNoteCommitment, Node, Nullifier, NOTE_COMMITMENT_TREE_DEPTH},
|
||||
transaction::TxId,
|
||||
};
|
||||
|
||||
use zcash_note_encryption::{EphemeralKeyBytes, COMPACT_NOTE_SIZE};
|
||||
|
@ -116,8 +116,12 @@ impl compact_formats::CompactSaplingOutput {
|
|||
}
|
||||
}
|
||||
|
||||
impl<Proof> From<&sapling::OutputDescription<Proof>> for compact_formats::CompactSaplingOutput {
|
||||
fn from(out: &sapling::OutputDescription<Proof>) -> compact_formats::CompactSaplingOutput {
|
||||
impl<Proof> From<&sapling::bundle::OutputDescription<Proof>>
|
||||
for compact_formats::CompactSaplingOutput
|
||||
{
|
||||
fn from(
|
||||
out: &sapling::bundle::OutputDescription<Proof>,
|
||||
) -> compact_formats::CompactSaplingOutput {
|
||||
compact_formats::CompactSaplingOutput {
|
||||
cmu: out.cmu().to_bytes().to_vec(),
|
||||
ephemeral_key: out.ephemeral_key().as_ref().to_vec(),
|
||||
|
@ -126,11 +130,13 @@ impl<Proof> From<&sapling::OutputDescription<Proof>> for compact_formats::Compac
|
|||
}
|
||||
}
|
||||
|
||||
impl TryFrom<compact_formats::CompactSaplingOutput> for sapling::CompactOutputDescription {
|
||||
impl TryFrom<compact_formats::CompactSaplingOutput>
|
||||
for sapling::note_encryption::CompactOutputDescription
|
||||
{
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: compact_formats::CompactSaplingOutput) -> Result<Self, Self::Error> {
|
||||
Ok(sapling::CompactOutputDescription {
|
||||
Ok(sapling::note_encryption::CompactOutputDescription {
|
||||
cmu: value.cmu()?,
|
||||
ephemeral_key: value.ephemeral_key()?,
|
||||
enc_ciphertext: value.ciphertext.try_into().map_err(|_| ())?,
|
||||
|
@ -144,10 +150,10 @@ impl compact_formats::CompactSaplingSpend {
|
|||
}
|
||||
}
|
||||
|
||||
impl<A: sapling::Authorization> From<&sapling::SpendDescription<A>>
|
||||
impl<A: sapling::bundle::Authorization> From<&sapling::bundle::SpendDescription<A>>
|
||||
for compact_formats::CompactSaplingSpend
|
||||
{
|
||||
fn from(spend: &sapling::SpendDescription<A>) -> compact_formats::CompactSaplingSpend {
|
||||
fn from(spend: &sapling::bundle::SpendDescription<A>) -> compact_formats::CompactSaplingSpend {
|
||||
compact_formats::CompactSaplingSpend {
|
||||
nf: spend.nullifier().to_vec(),
|
||||
}
|
||||
|
|
|
@ -12,10 +12,9 @@ use zcash_primitives::{
|
|||
consensus,
|
||||
sapling::{
|
||||
self,
|
||||
note_encryption::{PreparedIncomingViewingKey, SaplingDomain},
|
||||
note_encryption::{CompactOutputDescription, PreparedIncomingViewingKey, SaplingDomain},
|
||||
SaplingIvk,
|
||||
},
|
||||
transaction::components::sapling::CompactOutputDescription,
|
||||
zip32::{sapling::DiversifiableFullViewingKey, AccountId, Scope},
|
||||
};
|
||||
|
||||
|
|
|
@ -11,11 +11,41 @@ and this library adheres to Rust's notion of
|
|||
- `zcash_primitives::sapling`:
|
||||
- `BatchValidator` (moved from `zcash_proofs::sapling`).
|
||||
- `SaplingVerificationContext` (moved from `zcash_proofs::sapling`).
|
||||
- `builder` (moved from
|
||||
`zcash_primitives::transaction::components::sapling::builder`).
|
||||
- `builder::UnauthorizedBundle`
|
||||
- `builder::InProgress`
|
||||
- `builder::{InProgressProofs, Unproven, Proven}`
|
||||
- `builder::{InProgressSignatures, Unsigned, PartiallyAuthorized}`
|
||||
- `builder::{MaybeSigned, SigningParts}`
|
||||
- `bundle` module, containing the following types moved from
|
||||
`zcash_primitives::transaction::components::sapling`:
|
||||
- `Bundle`
|
||||
- `SpendDescription, SpendDescriptionV5`
|
||||
- `OutputDescription, OutputDescriptionV5`
|
||||
- `Authorization, Authorized, MapAuth`
|
||||
- `GrothProofBytes`
|
||||
- `bundle::Bundle::<InProgress<Unproven, _>>::create_proofs`
|
||||
- `bundle::Bundle::<InProgress<_, Unsigned>>::prepare`
|
||||
- `bundle::Bundle::<InProgress<_, PartiallyAuthorized>>::{sign, append_signatures}`
|
||||
- `bundle::Bundle::<InProgress<Proven, PartiallyAuthorized>>::finalize`
|
||||
- `bundle::Bundle::<InProgress<Proven, Unsigned>>::apply_signatures`
|
||||
- `bundle::Bundle::try_map_authorization`
|
||||
- `bundle::TryMapAuth`
|
||||
- `impl bundle::{MapAuth, TryMapAuth} for (FnMut, FnMut, FnMut, FnMut)`
|
||||
helpers to enable calling `Bundle::{map_authorization, try_map_authorization}`
|
||||
with a set of closures.
|
||||
- `bundle::testing` module, containing the following functions moved from
|
||||
`zcash_primitives::transaction::components::sapling::testing`:
|
||||
- `arb_output_description`
|
||||
- `arb_bundle`
|
||||
- `circuit` module (moved from `zcash_proofs::circuit::sapling`).
|
||||
- `circuit::{SpendParameters, OutputParameters}`
|
||||
- `circuit::{SpendVerifyingKey, PreparedSpendVerifyingKey}`
|
||||
- `circuit::{OutputVerifyingKey, PreparedOutputVerifyingKey}`
|
||||
- `constants` module.
|
||||
- `note_encryption::CompactOutputDescription` (moved from
|
||||
`zcash_primitives::transaction::components::sapling`).
|
||||
- `prover::{SpendProver, OutputProver}`
|
||||
- `value`:
|
||||
- `ValueCommitTrapdoor::from_bytes`
|
||||
|
@ -25,21 +55,13 @@ and this library adheres to Rust's notion of
|
|||
- `zcash_primitives::transaction`:
|
||||
- `builder::get_fee`
|
||||
- `components::sapling`:
|
||||
- `builder::UnauthorizedBundle`
|
||||
- `builder::InProgress`
|
||||
- `builder::{InProgressProofs, Unproven, Proven}`
|
||||
- `builder::{InProgressSignatures, Unsigned, PartiallyAuthorized}`
|
||||
- `builder::{MaybeSigned, SigningParts}`
|
||||
- `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.
|
||||
- Sapling bundle component parsers, behind the `temporary-zcashd` feature
|
||||
flag:
|
||||
- `temporary_zcashd_read_spend_v4`
|
||||
- `temporary_zcashd_read_output_v4`
|
||||
- `temporary_zcashd_write_output_v4`
|
||||
- `temporary_zcashd_read_v4_components`
|
||||
- `temporary_zcashd_write_v4_components`
|
||||
- `fees::StandardFeeRule`
|
||||
- Constants in `fees::zip317`:
|
||||
- `MARGINAL_FEE`
|
||||
|
@ -70,23 +92,22 @@ and this library adheres to Rust's notion of
|
|||
newtypes.
|
||||
- `address::PaymentAddress::create_note` now takes its `value` argument as a
|
||||
`NoteValue` instead of as a bare `u64`.
|
||||
- `builder::SaplingBuilder::add_spend` now takes `extsk` by reference.
|
||||
- `builder::SaplingBuilder::build` no longer takes a prover, proving context,
|
||||
or progress notifier. Instead, it has `SpendProver, OutputProver` generic
|
||||
parameters and returns `(UnauthorizedBundle, SaplingMetadata)`. The caller
|
||||
can then use `Bundle::<InProgress<Unproven, _>>::create_proofs` to create
|
||||
spend and output proofs for the bundle.
|
||||
- `builder::Error` has new error variants:
|
||||
- `Error::DuplicateSignature`
|
||||
- `Error::InvalidExternalSignature`
|
||||
- `Error::MissingSignatures`
|
||||
- `bundle::MapAuth` trait methods now take `&mut self` instead of `&self`.
|
||||
- `circuit::ValueCommitmentOpening::value` is now represented as a `NoteValue`
|
||||
instead of as a bare `u64`.
|
||||
- `zcash_primitives::transaction`:
|
||||
- `builder::Builder::{build, build_zfuture}` now take
|
||||
`&impl SpendProver, &impl OutputProver` instead of `&impl TxProver`.
|
||||
- `components::sapling`:
|
||||
- `MapAuth` trait methods now take `&mut self` instead of `&self`.
|
||||
- `builder::SaplingBuilder::add_spend` now takes `extsk` by reference.
|
||||
- `builder::SaplingBuilder::build` no longer takes a prover, proving context,
|
||||
or progress notifier. Instead, it has `SpendProver, OutputProver` generic
|
||||
parameters and returns `(UnauthorizedBundle, SaplingMetadata)`. The caller
|
||||
can then use `Bundle::<InProgress<Unproven, _>>::create_proofs` to create
|
||||
spend and output proofs for the bundle.
|
||||
- `builder::Error` has new error variants:
|
||||
- `Error::DuplicateSignature`
|
||||
- `Error::InvalidExternalSignature`
|
||||
- `Error::MissingSignatures`
|
||||
- `components::transparent::TxOut.value` now has type `NonNegativeAmount`
|
||||
instead of `Amount`.
|
||||
- `Unauthorized::SaplingAuth` now has type `InProgress<Proven, Unsigned>`.
|
||||
|
@ -111,9 +132,28 @@ and this library adheres to Rust's notion of
|
|||
### Removed
|
||||
- `zcash_primitives::constants`:
|
||||
- All `const` values (moved to `zcash_primitives::sapling::constants`).
|
||||
- `zcash_primitives::sapling::bundle`:
|
||||
- `SpendDescription::{read, read_nullifier, read_rk, read_spend_auth_sig}`
|
||||
- `SpendDescription::{write_v4, write_v5_without_witness_data}`
|
||||
- `SpendDescriptionV5::read`
|
||||
- `OutputDescription::read`
|
||||
- `OutputDescription::{write_v4, write_v5_without_proof}`
|
||||
- `OutputDescriptionV5::read`
|
||||
- `zcash_primitives::transaction::components::sapling`:
|
||||
- The following types were removed from this module (moved into
|
||||
`zcash_primitives::sapling::bundle`):
|
||||
- `Bundle`
|
||||
- `SpendDescription, SpendDescriptionV5`
|
||||
- `OutputDescription, OutputDescriptionV5`
|
||||
- `Authorization, Authorized, MapAuth`
|
||||
- `GrothProofBytes`
|
||||
- `CompactOutputDescription` (moved to
|
||||
`zcash_primitives::sapling::note_encryption`).
|
||||
- `Unproven`
|
||||
- `builder` (moved to `zcash_primitives::sapling::builder`).
|
||||
- `builder::Unauthorized` (use `builder::InProgress` instead).
|
||||
- `testing::{arb_bundle, arb_output_description}` (moved into
|
||||
`zcash_primitives::sapling::bundle::testing`).
|
||||
- `SpendDescription::<Unauthorized>::apply_signature`
|
||||
- `Bundle::<Unauthorized>::apply_signatures` (use
|
||||
`Bundle::<InProgress<Proven, Unsigned>>::apply_signatures` instead).
|
||||
|
|
|
@ -8,15 +8,15 @@ use zcash_primitives::{
|
|||
consensus::{NetworkUpgrade::Canopy, Parameters, TEST_NETWORK},
|
||||
memo::MemoBytes,
|
||||
sapling::{
|
||||
builder::SaplingBuilder,
|
||||
note_encryption::{
|
||||
try_sapling_compact_note_decryption, try_sapling_note_decryption,
|
||||
PreparedIncomingViewingKey, SaplingDomain,
|
||||
CompactOutputDescription, PreparedIncomingViewingKey, SaplingDomain,
|
||||
},
|
||||
prover::mock::{MockOutputProver, MockSpendProver},
|
||||
value::NoteValue,
|
||||
Diversifier, SaplingIvk,
|
||||
},
|
||||
transaction::components::sapling::{builder::SaplingBuilder, CompactOutputDescription},
|
||||
};
|
||||
|
||||
#[cfg(unix)]
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
//! Structs and constants specific to the Sapling shielded pool.
|
||||
|
||||
mod address;
|
||||
pub mod builder;
|
||||
pub mod bundle;
|
||||
pub mod circuit;
|
||||
pub mod constants;
|
||||
pub mod group_hash;
|
||||
|
@ -24,6 +26,7 @@ use constants::SPENDING_KEY_GENERATOR;
|
|||
use self::redjubjub::{PrivateKey, PublicKey, Signature};
|
||||
|
||||
pub use address::PaymentAddress;
|
||||
pub use bundle::Bundle;
|
||||
pub use keys::{Diversifier, NullifierDerivingKey, ProofGenerationKey, SaplingIvk, ViewingKey};
|
||||
pub use note::{nullifier::Nullifier, Note, Rseed};
|
||||
pub use tree::{
|
||||
|
|
|
@ -13,6 +13,10 @@ use crate::{
|
|||
memo::MemoBytes,
|
||||
sapling::{
|
||||
self,
|
||||
bundle::{
|
||||
Authorization, Authorized, Bundle, GrothProofBytes, MapAuth, OutputDescription,
|
||||
SpendDescription,
|
||||
},
|
||||
constants::{SPENDING_KEY_GENERATOR, VALUE_COMMITMENT_RANDOMNESS_GENERATOR},
|
||||
note_encryption::sapling_note_encryption,
|
||||
prover::{OutputProver, SpendProver},
|
||||
|
@ -29,10 +33,7 @@ use crate::{
|
|||
builder::Progress,
|
||||
components::{
|
||||
amount::{Amount, NonNegativeAmount},
|
||||
sapling::{
|
||||
fees, Authorization, Authorized, Bundle, GrothProofBytes, MapAuth,
|
||||
OutputDescription, SpendDescription,
|
||||
},
|
||||
sapling::fees,
|
||||
},
|
||||
},
|
||||
zip32::ExtendedSpendingKey,
|
||||
|
@ -147,17 +148,17 @@ impl SpendDescriptionInfo {
|
|||
)
|
||||
.ok_or(Error::SpendProof)?;
|
||||
|
||||
Ok(SpendDescription {
|
||||
Ok(SpendDescription::from_parts(
|
||||
cv,
|
||||
anchor,
|
||||
nullifier,
|
||||
rk,
|
||||
zkproof,
|
||||
spend_auth_sig: SigningParts {
|
||||
SigningParts {
|
||||
ak,
|
||||
alpha: self.alpha,
|
||||
},
|
||||
})
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -248,14 +249,14 @@ impl SaplingOutputInfo {
|
|||
|
||||
let epk = encryptor.epk();
|
||||
|
||||
OutputDescription {
|
||||
OutputDescription::from_parts(
|
||||
cv,
|
||||
cmu,
|
||||
ephemeral_key: epk.to_bytes(),
|
||||
epk.to_bytes(),
|
||||
enc_ciphertext,
|
||||
out_ciphertext,
|
||||
zkproof,
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -341,7 +342,7 @@ impl<P> SaplingBuilder<P> {
|
|||
///
|
||||
/// This may be larger than the number of outputs that have been added to the builder,
|
||||
/// depending on whether padding is going to be applied.
|
||||
pub(in crate::transaction) fn bundle_output_count(&self) -> usize {
|
||||
pub(crate) fn bundle_output_count(&self) -> usize {
|
||||
// This matches the padding behaviour in `Self::build`.
|
||||
match self.spends.len() {
|
||||
0 => self.outputs.len(),
|
||||
|
@ -529,15 +530,15 @@ impl<P: consensus::Parameters> SaplingBuilder<P> {
|
|||
None
|
||||
} else {
|
||||
Some((
|
||||
Bundle {
|
||||
Bundle::from_parts(
|
||||
shielded_spends,
|
||||
shielded_outputs,
|
||||
value_balance,
|
||||
authorization: InProgress {
|
||||
InProgress {
|
||||
sigs: Unsigned { bsk },
|
||||
_proof_state: PhantomData::default(),
|
||||
},
|
||||
},
|
||||
),
|
||||
tx_metadata,
|
||||
))
|
||||
};
|
||||
|
@ -676,7 +677,8 @@ impl<S: InProgressSignatures> Bundle<InProgress<Unproven, S>> {
|
|||
rng: impl RngCore,
|
||||
progress_notifier: Option<&Sender<Progress>>,
|
||||
) -> Bundle<InProgress<Proven, S>> {
|
||||
let total_progress = self.shielded_spends.len() as u32 + self.shielded_outputs.len() as u32;
|
||||
let total_progress =
|
||||
self.shielded_spends().len() as u32 + self.shielded_outputs().len() as u32;
|
||||
self.map_authorization(CreateProofs::new(
|
||||
spend_prover,
|
||||
output_prover,
|
||||
|
@ -803,7 +805,7 @@ impl<P: InProgressProofs> Bundle<InProgress<P, PartiallyAuthorized>> {
|
|||
/// This will apply signatures for all notes controlled by this spending key.
|
||||
pub fn sign<R: RngCore + CryptoRng>(self, mut rng: R, ask: &PrivateKey) -> Self {
|
||||
let expected_ak = PublicKey::from_private(ask, SPENDING_KEY_GENERATOR);
|
||||
let sighash = self.authorization.sigs.sighash;
|
||||
let sighash = self.authorization().sigs.sighash;
|
||||
self.map_authorization((
|
||||
|proof| proof,
|
||||
|proof| proof,
|
||||
|
@ -827,7 +829,7 @@ impl<P: InProgressProofs> Bundle<InProgress<P, PartiallyAuthorized>> {
|
|||
}
|
||||
|
||||
fn append_signature(self, signature: &Signature) -> Result<Self, Error> {
|
||||
let sighash = self.authorization.sigs.sighash;
|
||||
let sighash = self.authorization().sigs.sighash;
|
||||
let mut signature_valid_for = 0usize;
|
||||
let bundle = self.map_authorization((
|
||||
|proof| proof,
|
||||
|
@ -884,16 +886,14 @@ pub mod testing {
|
|||
TEST_NETWORK,
|
||||
},
|
||||
sapling::{
|
||||
bundle::{Authorized, Bundle},
|
||||
prover::mock::{MockOutputProver, MockSpendProver},
|
||||
redjubjub::PrivateKey,
|
||||
testing::{arb_node, arb_note},
|
||||
value::testing::arb_positive_note_value,
|
||||
Diversifier,
|
||||
},
|
||||
transaction::components::{
|
||||
amount::MAX_MONEY,
|
||||
sapling::{Authorized, Bundle},
|
||||
},
|
||||
transaction::components::amount::MAX_MONEY,
|
||||
zip32::sapling::testing::arb_extended_spending_key,
|
||||
};
|
||||
use incrementalmerkletree::{
|
|
@ -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},
|
||||
memo::MemoBytes,
|
||||
sapling::{
|
||||
bundle::{GrothProofBytes, OutputDescription},
|
||||
keys::{
|
||||
DiversifiedTransmissionKey, EphemeralPublicKey, EphemeralSecretKey, OutgoingViewingKey,
|
||||
SharedSecret,
|
||||
|
@ -26,10 +27,7 @@ use crate::{
|
|||
value::ValueCommitment,
|
||||
Diversifier, Note, PaymentAddress, Rseed,
|
||||
},
|
||||
transaction::components::{
|
||||
amount::NonNegativeAmount,
|
||||
sapling::{self, OutputDescription},
|
||||
},
|
||||
transaction::components::amount::NonNegativeAmount,
|
||||
};
|
||||
|
||||
use super::note::ExtractedNoteCommitment;
|
||||
|
@ -333,6 +331,31 @@ impl<P: consensus::Parameters> BatchDomain for SaplingDomain<P> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CompactOutputDescription {
|
||||
pub ephemeral_key: EphemeralKeyBytes,
|
||||
pub cmu: ExtractedNoteCommitment,
|
||||
pub enc_ciphertext: [u8; COMPACT_NOTE_SIZE],
|
||||
}
|
||||
|
||||
memuse::impl_no_dynamic_usage!(CompactOutputDescription);
|
||||
|
||||
impl<P: consensus::Parameters> ShieldedOutput<SaplingDomain<P>, COMPACT_NOTE_SIZE>
|
||||
for CompactOutputDescription
|
||||
{
|
||||
fn ephemeral_key(&self) -> EphemeralKeyBytes {
|
||||
self.ephemeral_key.clone()
|
||||
}
|
||||
|
||||
fn cmstar_bytes(&self) -> [u8; 32] {
|
||||
self.cmu.to_bytes()
|
||||
}
|
||||
|
||||
fn enc_ciphertext(&self) -> &[u8; COMPACT_NOTE_SIZE] {
|
||||
&self.enc_ciphertext
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new encryption context for the given note.
|
||||
///
|
||||
/// Setting `ovk` to `None` represents the `ovk = ⊥` case, where the note cannot be
|
||||
|
@ -457,7 +480,7 @@ pub fn try_sapling_output_recovery_with_ock<P: consensus::Parameters>(
|
|||
params: &P,
|
||||
height: BlockHeight,
|
||||
ock: &OutgoingCipherKey,
|
||||
output: &OutputDescription<sapling::GrothProofBytes>,
|
||||
output: &OutputDescription<GrothProofBytes>,
|
||||
) -> Option<(Note, PaymentAddress, MemoBytes)> {
|
||||
let domain = SaplingDomain {
|
||||
params: params.clone(),
|
||||
|
@ -479,7 +502,7 @@ pub fn try_sapling_output_recovery<P: consensus::Parameters>(
|
|||
params: &P,
|
||||
height: BlockHeight,
|
||||
ovk: &OutgoingViewingKey,
|
||||
output: &OutputDescription<sapling::GrothProofBytes>,
|
||||
output: &OutputDescription<GrothProofBytes>,
|
||||
) -> Option<(Note, PaymentAddress, MemoBytes)> {
|
||||
let domain = SaplingDomain {
|
||||
params: params.clone(),
|
||||
|
@ -509,7 +532,7 @@ mod tests {
|
|||
use super::{
|
||||
prf_ock, sapling_note_encryption, try_sapling_compact_note_decryption,
|
||||
try_sapling_note_decryption, try_sapling_output_recovery,
|
||||
try_sapling_output_recovery_with_ock, SaplingDomain,
|
||||
try_sapling_output_recovery_with_ock, CompactOutputDescription, SaplingDomain,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
|
@ -521,6 +544,7 @@ mod tests {
|
|||
keys::OutgoingViewingKey,
|
||||
memo::MemoBytes,
|
||||
sapling::{
|
||||
bundle::{GrothProofBytes, OutputDescription},
|
||||
keys::{DiversifiedTransmissionKey, EphemeralSecretKey},
|
||||
note::ExtractedNoteCommitment,
|
||||
note_encryption::PreparedIncomingViewingKey,
|
||||
|
@ -528,10 +552,7 @@ mod tests {
|
|||
value::{NoteValue, ValueCommitTrapdoor, ValueCommitment},
|
||||
Diversifier, PaymentAddress, Rseed, SaplingIvk,
|
||||
},
|
||||
transaction::components::{
|
||||
sapling::{self, CompactOutputDescription, OutputDescription},
|
||||
GROTH_PROOF_SIZE,
|
||||
},
|
||||
transaction::components::GROTH_PROOF_SIZE,
|
||||
};
|
||||
|
||||
fn random_enc_ciphertext<R: RngCore + CryptoRng>(
|
||||
|
@ -541,7 +562,7 @@ mod tests {
|
|||
OutgoingViewingKey,
|
||||
OutgoingCipherKey,
|
||||
PreparedIncomingViewingKey,
|
||||
OutputDescription<sapling::GrothProofBytes>,
|
||||
OutputDescription<GrothProofBytes>,
|
||||
) {
|
||||
let ivk = SaplingIvk(jubjub::Fr::random(&mut rng));
|
||||
let prepared_ivk = PreparedIncomingViewingKey::new(&ivk);
|
||||
|
@ -577,7 +598,7 @@ mod tests {
|
|||
) -> (
|
||||
OutgoingViewingKey,
|
||||
OutgoingCipherKey,
|
||||
OutputDescription<sapling::GrothProofBytes>,
|
||||
OutputDescription<GrothProofBytes>,
|
||||
) {
|
||||
let diversifier = Diversifier([0; 11]);
|
||||
let pa = ivk.to_payment_address(diversifier).unwrap();
|
||||
|
|
|
@ -7,10 +7,11 @@ use rand_core::RngCore;
|
|||
use crate::{
|
||||
sapling::{
|
||||
self,
|
||||
bundle::GrothProofBytes,
|
||||
value::{NoteValue, ValueCommitTrapdoor},
|
||||
MerklePath,
|
||||
},
|
||||
transaction::components::{sapling::GrothProofBytes, GROTH_PROOF_SIZE},
|
||||
transaction::components::GROTH_PROOF_SIZE,
|
||||
};
|
||||
|
||||
use super::{
|
||||
|
@ -188,11 +189,12 @@ pub mod mock {
|
|||
use crate::{
|
||||
sapling::{
|
||||
self,
|
||||
bundle::GrothProofBytes,
|
||||
circuit::ValueCommitmentOpening,
|
||||
value::{NoteValue, ValueCommitTrapdoor},
|
||||
Diversifier, PaymentAddress, ProofGenerationKey, Rseed,
|
||||
},
|
||||
transaction::components::{sapling::GrothProofBytes, GROTH_PROOF_SIZE},
|
||||
transaction::components::GROTH_PROOF_SIZE,
|
||||
};
|
||||
|
||||
pub struct MockSpendProver;
|
||||
|
|
|
@ -31,10 +31,10 @@
|
|||
//! you should only need to interact with [`NoteValue`] (which can be safely constructed
|
||||
//! from a `u64`) and `valueBalanceSapling` (which can be represented as an `i64`).
|
||||
//!
|
||||
//! [`Bundle`]: crate::transaction::components::sapling::Bundle
|
||||
//! [`Bundle::value_balance`]: crate::transaction::components::sapling::Bundle::value_balance
|
||||
//! [`SaplingBuilder::value_balance`]: crate::transaction::components::sapling::builder::SaplingBuilder::value_balance
|
||||
//! [`SaplingBuilder::add_output`]: crate::transaction::components::sapling::builder::SaplingBuilder::add_output
|
||||
//! [`Bundle`]: crate::sapling::Bundle
|
||||
//! [`Bundle::value_balance`]: crate::sapling::Bundle::value_balance
|
||||
//! [`SaplingBuilder::value_balance`]: crate::sapling::builder::SaplingBuilder::value_balance
|
||||
//! [`SaplingBuilder::add_output`]: crate::sapling::builder::SaplingBuilder::add_output
|
||||
//! [Rust documentation]: https://doc.rust-lang.org/stable/std/primitive.i64.html
|
||||
|
||||
use bitvec::{array::BitArray, order::Lsb0};
|
||||
|
|
|
@ -4,9 +4,9 @@ use group::GroupEncoding;
|
|||
use rand_core::{CryptoRng, RngCore};
|
||||
|
||||
use super::SaplingVerificationContextInner;
|
||||
use crate::{
|
||||
sapling::circuit::{OutputVerifyingKey, SpendVerifyingKey},
|
||||
transaction::components::sapling::{Authorized, Bundle},
|
||||
use crate::sapling::{
|
||||
bundle::{Authorized, Bundle},
|
||||
circuit::{OutputVerifyingKey, SpendVerifyingKey},
|
||||
};
|
||||
|
||||
/// Batch validation context for Sapling.
|
||||
|
|
|
@ -14,16 +14,14 @@ use crate::{
|
|||
memo::MemoBytes,
|
||||
sapling::{
|
||||
self,
|
||||
builder::{self as sapling_builder, SaplingBuilder, SaplingMetadata},
|
||||
prover::{OutputProver, SpendProver},
|
||||
redjubjub, Diversifier, Note, PaymentAddress,
|
||||
},
|
||||
transaction::{
|
||||
components::{
|
||||
amount::{Amount, BalanceError},
|
||||
sapling::{
|
||||
builder::{self as sapling_builder, SaplingBuilder, SaplingMetadata},
|
||||
fees as sapling_fees,
|
||||
},
|
||||
sapling::fees as sapling_fees,
|
||||
transparent::{self, builder::TransparentBuilder},
|
||||
},
|
||||
fees::FeeRule,
|
||||
|
|
|
@ -6,12 +6,13 @@ pub mod sapling;
|
|||
pub mod sprout;
|
||||
pub mod transparent;
|
||||
pub mod tze;
|
||||
|
||||
pub use self::{
|
||||
amount::Amount,
|
||||
sapling::{OutputDescription, SpendDescription},
|
||||
sprout::JsDescription,
|
||||
transparent::{OutPoint, TxIn, TxOut},
|
||||
};
|
||||
pub use crate::sapling::bundle::{OutputDescription, SpendDescription};
|
||||
|
||||
#[cfg(feature = "zfuture")]
|
||||
pub use self::tze::{TzeIn, TzeOut};
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -13,27 +13,23 @@ mod tests;
|
|||
|
||||
use blake2b_simd::Hash as Blake2bHash;
|
||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||
use ff::PrimeField;
|
||||
use memuse::DynamicUsage;
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt;
|
||||
use std::fmt::Debug;
|
||||
use std::io::{self, Read, Write};
|
||||
use std::ops::Deref;
|
||||
use zcash_encoding::{Array, CompactSize, Vector};
|
||||
use zcash_encoding::{CompactSize, Vector};
|
||||
|
||||
use crate::{
|
||||
consensus::{BlockHeight, BranchId},
|
||||
sapling::redjubjub,
|
||||
sapling::{self, builder as sapling_builder, redjubjub},
|
||||
};
|
||||
|
||||
use self::{
|
||||
components::{
|
||||
amount::{Amount, BalanceError},
|
||||
orchard as orchard_serialization,
|
||||
sapling::{
|
||||
self, OutputDescription, OutputDescriptionV5, SpendDescription, SpendDescriptionV5,
|
||||
},
|
||||
orchard as orchard_serialization, sapling as sapling_serialization,
|
||||
sprout::{self, JsDescription},
|
||||
transparent::{self, TxIn, TxOut},
|
||||
OutPoint,
|
||||
|
@ -252,7 +248,7 @@ impl TxVersion {
|
|||
/// Authorization state for a bundle of transaction data.
|
||||
pub trait Authorization {
|
||||
type TransparentAuth: transparent::Authorization;
|
||||
type SaplingAuth: sapling::Authorization;
|
||||
type SaplingAuth: sapling::bundle::Authorization;
|
||||
type OrchardAuth: orchard::bundle::Authorization;
|
||||
|
||||
#[cfg(feature = "zfuture")]
|
||||
|
@ -264,7 +260,7 @@ pub struct Authorized;
|
|||
|
||||
impl Authorization for Authorized {
|
||||
type TransparentAuth = transparent::Authorized;
|
||||
type SaplingAuth = sapling::Authorized;
|
||||
type SaplingAuth = sapling::bundle::Authorized;
|
||||
type OrchardAuth = orchard::bundle::Authorized;
|
||||
|
||||
#[cfg(feature = "zfuture")]
|
||||
|
@ -276,7 +272,7 @@ pub struct Unauthorized;
|
|||
impl Authorization for Unauthorized {
|
||||
type TransparentAuth = transparent::builder::Unauthorized;
|
||||
type SaplingAuth =
|
||||
sapling::builder::InProgress<sapling::builder::Proven, sapling::builder::Unsigned>;
|
||||
sapling_builder::InProgress<sapling_builder::Proven, sapling_builder::Unsigned>;
|
||||
type OrchardAuth =
|
||||
orchard::builder::InProgress<orchard::builder::Unproven, orchard::builder::Unauthorized>;
|
||||
|
||||
|
@ -490,7 +486,7 @@ impl<A: Authorization> TransactionData<A> {
|
|||
pub fn map_authorization<B: Authorization>(
|
||||
self,
|
||||
f_transparent: impl transparent::MapAuth<A::TransparentAuth, B::TransparentAuth>,
|
||||
f_sapling: impl sapling::MapAuth<A::SaplingAuth, B::SaplingAuth>,
|
||||
f_sapling: impl sapling::bundle::MapAuth<A::SaplingAuth, B::SaplingAuth>,
|
||||
mut f_orchard: impl orchard_serialization::MapAuth<A::OrchardAuth, B::OrchardAuth>,
|
||||
#[cfg(feature = "zfuture")] f_tze: impl tze::MapAuth<A::TzeAuth, B::TzeAuth>,
|
||||
) -> TransactionData<B> {
|
||||
|
@ -601,18 +597,8 @@ impl Transaction {
|
|||
0u32.into()
|
||||
};
|
||||
|
||||
let (value_balance, shielded_spends, shielded_outputs) = if version.has_sapling() {
|
||||
let vb = Self::read_amount(&mut reader)?;
|
||||
#[allow(clippy::redundant_closure)]
|
||||
let ss: Vec<SpendDescription<sapling::Authorized>> =
|
||||
Vector::read(&mut reader, |r| SpendDescription::read(r))?;
|
||||
#[allow(clippy::redundant_closure)]
|
||||
let so: Vec<OutputDescription<sapling::GrothProofBytes>> =
|
||||
Vector::read(&mut reader, |r| OutputDescription::read(r))?;
|
||||
(vb, ss, so)
|
||||
} else {
|
||||
(Amount::zero(), vec![], vec![])
|
||||
};
|
||||
let (value_balance, shielded_spends, shielded_outputs) =
|
||||
sapling_serialization::read_v4_components(&mut reader, version.has_sapling())?;
|
||||
|
||||
let sprout_bundle = if version.has_sprout() {
|
||||
let joinsplits = Vector::read(&mut reader, |r| {
|
||||
|
@ -661,7 +647,7 @@ impl Transaction {
|
|||
shielded_spends,
|
||||
shielded_outputs,
|
||||
value_balance,
|
||||
sapling::Authorized { binding_sig },
|
||||
sapling::bundle::Authorized { binding_sig },
|
||||
)
|
||||
}),
|
||||
orchard_bundle: None,
|
||||
|
@ -698,7 +684,7 @@ impl Transaction {
|
|||
let (consensus_branch_id, lock_time, expiry_height) =
|
||||
Self::read_v5_header_fragment(&mut reader)?;
|
||||
let transparent_bundle = Self::read_transparent(&mut reader)?;
|
||||
let sapling_bundle = Self::read_v5_sapling(&mut reader)?;
|
||||
let sapling_bundle = sapling_serialization::read_v5_bundle(&mut reader)?;
|
||||
let orchard_bundle = orchard_serialization::read_v5_bundle(&mut reader)?;
|
||||
|
||||
#[cfg(feature = "zfuture")]
|
||||
|
@ -741,69 +727,8 @@ impl Transaction {
|
|||
#[cfg(feature = "temporary-zcashd")]
|
||||
pub fn temporary_zcashd_read_v5_sapling<R: Read>(
|
||||
reader: R,
|
||||
) -> io::Result<Option<sapling::Bundle<sapling::Authorized>>> {
|
||||
Self::read_v5_sapling(reader)
|
||||
}
|
||||
|
||||
#[allow(clippy::redundant_closure)]
|
||||
fn read_v5_sapling<R: Read>(
|
||||
mut reader: R,
|
||||
) -> io::Result<Option<sapling::Bundle<sapling::Authorized>>> {
|
||||
let sd_v5s = Vector::read(&mut reader, SpendDescriptionV5::read)?;
|
||||
let od_v5s = Vector::read(&mut reader, OutputDescriptionV5::read)?;
|
||||
let n_spends = sd_v5s.len();
|
||||
let n_outputs = od_v5s.len();
|
||||
let value_balance = if n_spends > 0 || n_outputs > 0 {
|
||||
Self::read_amount(&mut reader)?
|
||||
} else {
|
||||
Amount::zero()
|
||||
};
|
||||
|
||||
let anchor = if n_spends > 0 {
|
||||
Some(sapling::read_base(&mut reader, "anchor")?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let v_spend_proofs = Array::read(&mut reader, n_spends, |r| sapling::read_zkproof(r))?;
|
||||
let v_spend_auth_sigs = Array::read(&mut reader, n_spends, |r| {
|
||||
SpendDescription::read_spend_auth_sig(r)
|
||||
})?;
|
||||
let v_output_proofs = Array::read(&mut reader, n_outputs, |r| sapling::read_zkproof(r))?;
|
||||
|
||||
let binding_sig = if n_spends > 0 || n_outputs > 0 {
|
||||
Some(redjubjub::Signature::read(&mut reader)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let shielded_spends = sd_v5s
|
||||
.into_iter()
|
||||
.zip(
|
||||
v_spend_proofs
|
||||
.into_iter()
|
||||
.zip(v_spend_auth_sigs.into_iter()),
|
||||
)
|
||||
.map(|(sd_5, (zkproof, spend_auth_sig))| {
|
||||
// the following `unwrap` is safe because we know n_spends > 0.
|
||||
sd_5.into_spend_description(anchor.unwrap(), zkproof, spend_auth_sig)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let shielded_outputs = od_v5s
|
||||
.into_iter()
|
||||
.zip(v_output_proofs.into_iter())
|
||||
.map(|(od_5, zkproof)| od_5.into_output_description(zkproof))
|
||||
.collect();
|
||||
|
||||
Ok(binding_sig.map(|binding_sig| {
|
||||
sapling::Bundle::from_parts(
|
||||
shielded_spends,
|
||||
shielded_outputs,
|
||||
value_balance,
|
||||
sapling::Authorized { binding_sig },
|
||||
)
|
||||
}))
|
||||
) -> io::Result<Option<sapling::Bundle<sapling::bundle::Authorized>>> {
|
||||
sapling_serialization::read_v5_bundle(reader)
|
||||
}
|
||||
|
||||
#[cfg(feature = "zfuture")]
|
||||
|
@ -841,34 +766,11 @@ impl Transaction {
|
|||
writer.write_u32::<LittleEndian>(u32::from(self.expiry_height))?;
|
||||
}
|
||||
|
||||
if self.version.has_sapling() {
|
||||
writer.write_all(
|
||||
&self
|
||||
.sapling_bundle
|
||||
.as_ref()
|
||||
.map_or(Amount::zero(), |b| *b.value_balance())
|
||||
.to_i64_le_bytes(),
|
||||
)?;
|
||||
Vector::write(
|
||||
&mut writer,
|
||||
self.sapling_bundle
|
||||
.as_ref()
|
||||
.map_or(&[], |b| b.shielded_spends()),
|
||||
|w, e| e.write_v4(w),
|
||||
)?;
|
||||
Vector::write(
|
||||
&mut writer,
|
||||
self.sapling_bundle
|
||||
.as_ref()
|
||||
.map_or(&[], |b| b.shielded_outputs()),
|
||||
|w, e| e.write_v4(w),
|
||||
)?;
|
||||
} else if self.sapling_bundle.is_some() {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"Sapling components may not be present if Sapling is not active.",
|
||||
));
|
||||
}
|
||||
sapling_serialization::write_v4_components(
|
||||
&mut writer,
|
||||
self.sapling_bundle.as_ref(),
|
||||
self.version.has_sapling(),
|
||||
)?;
|
||||
|
||||
if self.version.has_sprout() {
|
||||
if let Some(bundle) = self.sprout_bundle.as_ref() {
|
||||
|
@ -934,62 +836,14 @@ impl Transaction {
|
|||
|
||||
#[cfg(feature = "temporary-zcashd")]
|
||||
pub fn temporary_zcashd_write_v5_sapling<W: Write>(
|
||||
sapling_bundle: Option<&sapling::Bundle<sapling::Authorized>>,
|
||||
sapling_bundle: Option<&sapling::Bundle<sapling::bundle::Authorized>>,
|
||||
writer: W,
|
||||
) -> io::Result<()> {
|
||||
Self::write_v5_sapling_inner(sapling_bundle, writer)
|
||||
sapling_serialization::write_v5_bundle(writer, sapling_bundle)
|
||||
}
|
||||
|
||||
pub fn write_v5_sapling<W: Write>(&self, writer: W) -> io::Result<()> {
|
||||
Self::write_v5_sapling_inner(self.sapling_bundle.as_ref(), writer)
|
||||
}
|
||||
|
||||
fn write_v5_sapling_inner<W: Write>(
|
||||
sapling_bundle: Option<&sapling::Bundle<sapling::Authorized>>,
|
||||
mut writer: W,
|
||||
) -> io::Result<()> {
|
||||
if let Some(bundle) = sapling_bundle {
|
||||
Vector::write(&mut writer, bundle.shielded_spends(), |w, e| {
|
||||
e.write_v5_without_witness_data(w)
|
||||
})?;
|
||||
|
||||
Vector::write(&mut writer, bundle.shielded_outputs(), |w, e| {
|
||||
e.write_v5_without_proof(w)
|
||||
})?;
|
||||
|
||||
if !(bundle.shielded_spends().is_empty() && bundle.shielded_outputs().is_empty()) {
|
||||
writer.write_all(&bundle.value_balance().to_i64_le_bytes())?;
|
||||
}
|
||||
if !bundle.shielded_spends().is_empty() {
|
||||
writer.write_all(bundle.shielded_spends()[0].anchor().to_repr().as_ref())?;
|
||||
}
|
||||
|
||||
Array::write(
|
||||
&mut writer,
|
||||
bundle.shielded_spends().iter().map(|s| &s.zkproof()[..]),
|
||||
|w, e| w.write_all(e),
|
||||
)?;
|
||||
Array::write(
|
||||
&mut writer,
|
||||
bundle.shielded_spends().iter().map(|s| s.spend_auth_sig()),
|
||||
|w, e| e.write(w),
|
||||
)?;
|
||||
|
||||
Array::write(
|
||||
&mut writer,
|
||||
bundle.shielded_outputs().iter().map(|s| &s.zkproof()[..]),
|
||||
|w, e| w.write_all(e),
|
||||
)?;
|
||||
|
||||
if !(bundle.shielded_spends().is_empty() && bundle.shielded_outputs().is_empty()) {
|
||||
bundle.authorization().binding_sig.write(&mut writer)?;
|
||||
}
|
||||
} else {
|
||||
CompactSize::write(&mut writer, 0)?;
|
||||
CompactSize::write(&mut writer, 0)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
sapling_serialization::write_v5_bundle(writer, self.sapling_bundle.as_ref())
|
||||
}
|
||||
|
||||
#[cfg(feature = "zfuture")]
|
||||
|
|
|
@ -1,16 +1,15 @@
|
|||
use crate::legacy::Script;
|
||||
use blake2b_simd::Hash as Blake2bHash;
|
||||
|
||||
use super::{
|
||||
components::{
|
||||
amount::NonNegativeAmount,
|
||||
sapling::{self, GrothProofBytes},
|
||||
transparent,
|
||||
},
|
||||
components::{amount::NonNegativeAmount, transparent},
|
||||
sighash_v4::v4_signature_hash,
|
||||
sighash_v5::v5_signature_hash,
|
||||
Authorization, TransactionData, TxDigests, TxVersion,
|
||||
};
|
||||
use crate::{
|
||||
legacy::Script,
|
||||
sapling::{self, bundle::GrothProofBytes},
|
||||
};
|
||||
|
||||
#[cfg(feature = "zfuture")]
|
||||
use {super::components::Amount, crate::extensions::transparent::Precondition};
|
||||
|
@ -79,7 +78,7 @@ pub trait TransparentAuthorizingContext: transparent::Authorization {
|
|||
pub fn signature_hash<
|
||||
'a,
|
||||
TA: TransparentAuthorizingContext,
|
||||
SA: sapling::Authorization<SpendProof = GrothProofBytes, OutputProof = GrothProofBytes>,
|
||||
SA: sapling::bundle::Authorization<SpendProof = GrothProofBytes, OutputProof = GrothProofBytes>,
|
||||
A: Authorization<SaplingAuth = SA, TransparentAuth = TA>,
|
||||
>(
|
||||
tx: &TransactionData<A>,
|
||||
|
|
|
@ -1,11 +1,17 @@
|
|||
use blake2b_simd::{Hash as Blake2bHash, Params as Blake2bParams};
|
||||
use ff::PrimeField;
|
||||
|
||||
use crate::consensus::BranchId;
|
||||
use crate::{
|
||||
consensus::BranchId,
|
||||
sapling::{
|
||||
self,
|
||||
bundle::{GrothProofBytes, OutputDescription, SpendDescription},
|
||||
},
|
||||
};
|
||||
|
||||
use super::{
|
||||
components::{
|
||||
sapling::{self, GrothProofBytes, OutputDescription, SpendDescription},
|
||||
sapling as sapling_serialization,
|
||||
sprout::JsDescription,
|
||||
transparent::{self, TxIn, TxOut},
|
||||
},
|
||||
|
@ -97,7 +103,7 @@ fn joinsplits_hash(
|
|||
}
|
||||
|
||||
fn shielded_spends_hash<
|
||||
A: sapling::Authorization<SpendProof = GrothProofBytes, OutputProof = GrothProofBytes>,
|
||||
A: sapling::bundle::Authorization<SpendProof = GrothProofBytes, OutputProof = GrothProofBytes>,
|
||||
>(
|
||||
shielded_spends: &[SpendDescription<A>],
|
||||
) -> Blake2bHash {
|
||||
|
@ -118,7 +124,7 @@ fn shielded_spends_hash<
|
|||
fn shielded_outputs_hash(shielded_outputs: &[OutputDescription<GrothProofBytes>]) -> Blake2bHash {
|
||||
let mut data = Vec::with_capacity(shielded_outputs.len() * 948);
|
||||
for s_out in shielded_outputs {
|
||||
s_out.write_v4(&mut data).unwrap();
|
||||
sapling_serialization::write_output_v4(&mut data, s_out).unwrap();
|
||||
}
|
||||
Blake2bParams::new()
|
||||
.hash_length(32)
|
||||
|
@ -127,7 +133,7 @@ fn shielded_outputs_hash(shielded_outputs: &[OutputDescription<GrothProofBytes>]
|
|||
}
|
||||
|
||||
pub fn v4_signature_hash<
|
||||
SA: sapling::Authorization<SpendProof = GrothProofBytes, OutputProof = GrothProofBytes>,
|
||||
SA: sapling::bundle::Authorization<SpendProof = GrothProofBytes, OutputProof = GrothProofBytes>,
|
||||
A: Authorization<SaplingAuth = SA>,
|
||||
>(
|
||||
tx: &TransactionData<A>,
|
||||
|
|
|
@ -193,7 +193,7 @@ struct TestUnauthorized;
|
|||
|
||||
impl Authorization for TestUnauthorized {
|
||||
type TransparentAuth = TestTransparentAuth;
|
||||
type SaplingAuth = sapling::Authorized;
|
||||
type SaplingAuth = sapling::bundle::Authorized;
|
||||
type OrchardAuth = orchard::bundle::Authorized;
|
||||
|
||||
#[cfg(feature = "zfuture")]
|
||||
|
|
|
@ -7,12 +7,17 @@ use byteorder::{LittleEndian, WriteBytesExt};
|
|||
use ff::PrimeField;
|
||||
use orchard::bundle::{self as orchard};
|
||||
|
||||
use crate::consensus::{BlockHeight, BranchId};
|
||||
use crate::{
|
||||
consensus::{BlockHeight, BranchId},
|
||||
sapling::{
|
||||
self,
|
||||
bundle::{OutputDescription, SpendDescription},
|
||||
},
|
||||
};
|
||||
|
||||
use super::{
|
||||
components::{
|
||||
amount::Amount,
|
||||
sapling::{self, OutputDescription, SpendDescription},
|
||||
transparent::{self, TxIn, TxOut},
|
||||
},
|
||||
Authorization, Authorized, TransactionDigest, TransparentDigests, TxDigests, TxId, TxVersion,
|
||||
|
@ -136,7 +141,7 @@ pub(crate) fn hash_tze_outputs(tze_outputs: &[TzeOut]) -> Blake2bHash {
|
|||
/// * \[(cv, anchor, rk, zkproof)*\] - personalized with ZCASH_SAPLING_SPENDS_NONCOMPACT_HASH_PERSONALIZATION
|
||||
///
|
||||
/// Then, hash these together personalized by ZCASH_SAPLING_SPENDS_HASH_PERSONALIZATION
|
||||
pub(crate) fn hash_sapling_spends<A: sapling::Authorization>(
|
||||
pub(crate) fn hash_sapling_spends<A: sapling::bundle::Authorization>(
|
||||
shielded_spends: &[SpendDescription<A>],
|
||||
) -> Blake2bHash {
|
||||
let mut h = hasher(ZCASH_SAPLING_SPENDS_HASH_PERSONALIZATION);
|
||||
|
@ -250,7 +255,9 @@ pub(crate) fn hash_transparent_txid_data(
|
|||
}
|
||||
|
||||
/// Implements [ZIP 244 section T.3](https://zips.z.cash/zip-0244#t-3-sapling-digest)
|
||||
fn hash_sapling_txid_data<A: sapling::Authorization>(bundle: &sapling::Bundle<A>) -> Blake2bHash {
|
||||
fn hash_sapling_txid_data<A: sapling::bundle::Authorization>(
|
||||
bundle: &sapling::Bundle<A>,
|
||||
) -> Blake2bHash {
|
||||
let mut h = hasher(ZCASH_SAPLING_HASH_PERSONALIZATION);
|
||||
if !(bundle.shielded_spends().is_empty() && bundle.shielded_outputs().is_empty()) {
|
||||
h.write_all(hash_sapling_spends(bundle.shielded_spends()).as_bytes())
|
||||
|
@ -460,7 +467,7 @@ impl TransactionDigest<Authorized> for BlockTxCommitmentDigester {
|
|||
|
||||
fn digest_sapling(
|
||||
&self,
|
||||
sapling_bundle: Option<&sapling::Bundle<sapling::Authorized>>,
|
||||
sapling_bundle: Option<&sapling::Bundle<sapling::bundle::Authorized>>,
|
||||
) -> Blake2bHash {
|
||||
let mut h = hasher(ZCASH_SAPLING_SIGS_HASH_PERSONALIZATION);
|
||||
if let Some(bundle) = sapling_bundle {
|
||||
|
|
|
@ -6,11 +6,12 @@ use std::path::Path;
|
|||
use zcash_primitives::{
|
||||
sapling::{
|
||||
self,
|
||||
bundle::GrothProofBytes,
|
||||
prover::{OutputProver, SpendProver},
|
||||
value::{NoteValue, ValueCommitTrapdoor},
|
||||
Diversifier, MerklePath, PaymentAddress, ProofGenerationKey, Rseed,
|
||||
},
|
||||
transaction::components::{sapling::GrothProofBytes, GROTH_PROOF_SIZE},
|
||||
transaction::components::GROTH_PROOF_SIZE,
|
||||
};
|
||||
|
||||
use crate::{load_parameters, parse_parameters, OutputParameters, SpendParameters};
|
||||
|
|
Loading…
Reference in New Issue