Merge pull request #1007 from zcash/sapling-trymapauth-etc

Changes to Sapling bundle mapping
This commit is contained in:
Kris Nuttycombe 2023-10-07 13:25:24 -06:00 committed by GitHub
commit a47b512f59
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 164 additions and 16 deletions

View File

@ -12,9 +12,20 @@ and this library adheres to Rust's notion of
- `circuit` module (moved from `zcash_proofs::circuit::sapling`). - `circuit` module (moved from `zcash_proofs::circuit::sapling`).
- `constants` module. - `constants` module.
- `prover::{SpendProver, OutputProver}` - `prover::{SpendProver, OutputProver}`
- `impl Debug for keys::{ExpandedSpendingKey, ProofGenerationKey}`
- `zcash_primitives::transaction::components::sapling`:
- `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.
- Test helpers, behind the `test-dependencies` feature flag: - Test helpers, behind the `test-dependencies` feature flag:
- `zcash_primitives::prover::mock::{MockSpendProver, MockOutputProver}` - `zcash_primitives::prover::mock::{MockSpendProver, MockOutputProver}`
### Changed
- `zcash_primitives::transaction::components::sapling`:
- `MapAuth` trait methods now take `&mut self` instead of `&self`.
### Removed ### Removed
- `zcash_primitives::constants`: - `zcash_primitives::constants`:
- All `const` values (moved to `zcash_primitives::sapling::constants`). - All `const` values (moved to `zcash_primitives::sapling::constants`).

View File

@ -1,5 +1,7 @@
//! The Sapling circuits. //! The Sapling circuits.
use core::fmt;
use group::{ff::PrimeField, Curve}; use group::{ff::PrimeField, Curve};
use bellman::{Circuit, ConstraintSystem, SynthesisError}; use bellman::{Circuit, ConstraintSystem, SynthesisError};
@ -46,6 +48,7 @@ impl ValueCommitmentOpening {
} }
/// This is an instance of the `Spend` circuit. /// This is an instance of the `Spend` circuit.
#[derive(Clone)]
pub struct Spend { pub struct Spend {
/// The opening of a Pedersen commitment to the value being spent. /// The opening of a Pedersen commitment to the value being spent.
pub value_commitment_opening: Option<ValueCommitmentOpening>, pub value_commitment_opening: Option<ValueCommitmentOpening>,
@ -71,7 +74,16 @@ pub struct Spend {
pub anchor: Option<bls12_381::Scalar>, pub anchor: Option<bls12_381::Scalar>,
} }
impl fmt::Debug for Spend {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Spend")
.field("anchor", &self.anchor)
.finish_non_exhaustive()
}
}
/// This is an output circuit instance. /// This is an output circuit instance.
#[derive(Clone)]
pub struct Output { pub struct Output {
/// The opening of a Pedersen commitment to the value being spent. /// The opening of a Pedersen commitment to the value being spent.
pub value_commitment_opening: Option<ValueCommitmentOpening>, pub value_commitment_opening: Option<ValueCommitmentOpening>,
@ -86,6 +98,12 @@ pub struct Output {
pub esk: Option<jubjub::Fr>, pub esk: Option<jubjub::Fr>,
} }
impl fmt::Debug for Output {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Output").finish_non_exhaustive()
}
}
/// Exposes a Pedersen commitment to the value as an /// Exposes a Pedersen commitment to the value as an
/// input to the circuit /// input to the circuit
fn expose_value_commitment<CS>( fn expose_value_commitment<CS>(

View File

@ -4,6 +4,7 @@
//! //!
//! [section 4.2.2]: https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents //! [section 4.2.2]: https://zips.z.cash/protocol/protocol.pdf#saplingkeycomponents
use std::fmt;
use std::io::{self, Read, Write}; use std::io::{self, Read, Write};
use super::{ use super::{
@ -46,6 +47,13 @@ pub struct ExpandedSpendingKey {
pub ovk: OutgoingViewingKey, pub ovk: OutgoingViewingKey,
} }
impl fmt::Debug for ExpandedSpendingKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ExpandedSpendingKey")
.finish_non_exhaustive()
}
}
impl ExpandedSpendingKey { impl ExpandedSpendingKey {
pub fn from_spending_key(sk: &[u8]) -> Self { pub fn from_spending_key(sk: &[u8]) -> Self {
let ask = jubjub::Fr::from_bytes_wide(prf_expand(sk, &[0x00]).as_array()); let ask = jubjub::Fr::from_bytes_wide(prf_expand(sk, &[0x00]).as_array());
@ -119,6 +127,14 @@ pub struct ProofGenerationKey {
pub nsk: jubjub::Fr, pub nsk: jubjub::Fr,
} }
impl fmt::Debug for ProofGenerationKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ProofGenerationKey")
.field("ak", &self.ak)
.finish_non_exhaustive()
}
}
impl ProofGenerationKey { impl ProofGenerationKey {
pub fn to_viewing_key(&self) -> ViewingKey { pub fn to_viewing_key(&self) -> ViewingKey {
ViewingKey { ViewingKey {
@ -473,16 +489,9 @@ impl SharedSecret {
pub mod testing { pub mod testing {
use proptest::collection::vec; use proptest::collection::vec;
use proptest::prelude::*; use proptest::prelude::*;
use std::fmt::{self, Debug, Formatter};
use super::{ExpandedSpendingKey, FullViewingKey, SaplingIvk}; use super::{ExpandedSpendingKey, FullViewingKey, SaplingIvk};
impl Debug for ExpandedSpendingKey {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "Spending keys cannot be Debug-formatted.")
}
}
prop_compose! { prop_compose! {
pub fn arb_expanded_spending_key()(v in vec(any::<u8>(), 32..252)) -> ExpandedSpendingKey { pub fn arb_expanded_spending_key()(v in vec(any::<u8>(), 32..252)) -> ExpandedSpendingKey {
ExpandedSpendingKey::from_spending_key(&v) ExpandedSpendingKey::from_spending_key(&v)

View File

@ -56,11 +56,14 @@ impl Authorization for Authorized {
type AuthSig = redjubjub::Signature; 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> { pub trait MapAuth<A: Authorization, B: Authorization> {
fn map_spend_proof(&self, p: A::SpendProof) -> B::SpendProof; fn map_spend_proof(&mut self, p: A::SpendProof) -> B::SpendProof;
fn map_output_proof(&self, p: A::OutputProof) -> B::OutputProof; fn map_output_proof(&mut self, p: A::OutputProof) -> B::OutputProof;
fn map_auth_sig(&self, s: A::AuthSig) -> B::AuthSig; fn map_auth_sig(&mut self, s: A::AuthSig) -> B::AuthSig;
fn map_authorization(&self, a: A) -> B; fn map_authorization(&mut self, a: A) -> B;
} }
/// The identity map. /// The identity map.
@ -71,31 +74,98 @@ pub trait MapAuth<A: Authorization, B: Authorization> {
/// [`TransactionData::map_authorization`]: crate::transaction::TransactionData::map_authorization /// [`TransactionData::map_authorization`]: crate::transaction::TransactionData::map_authorization
impl MapAuth<Authorized, Authorized> for () { impl MapAuth<Authorized, Authorized> for () {
fn map_spend_proof( fn map_spend_proof(
&self, &mut self,
p: <Authorized as Authorization>::SpendProof, p: <Authorized as Authorization>::SpendProof,
) -> <Authorized as Authorization>::SpendProof { ) -> <Authorized as Authorization>::SpendProof {
p p
} }
fn map_output_proof( fn map_output_proof(
&self, &mut self,
p: <Authorized as Authorization>::OutputProof, p: <Authorized as Authorization>::OutputProof,
) -> <Authorized as Authorization>::OutputProof { ) -> <Authorized as Authorization>::OutputProof {
p p
} }
fn map_auth_sig( fn map_auth_sig(
&self, &mut self,
s: <Authorized as Authorization>::AuthSig, s: <Authorized as Authorization>::AuthSig,
) -> <Authorized as Authorization>::AuthSig { ) -> <Authorized as Authorization>::AuthSig {
s s
} }
fn map_authorization(&self, a: Authorized) -> Authorized { fn map_authorization(&mut self, a: Authorized) -> Authorized {
a 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)] #[derive(Debug, Clone)]
pub struct Bundle<A: Authorization> { pub struct Bundle<A: Authorization> {
shielded_spends: Vec<SpendDescription<A>>, shielded_spends: Vec<SpendDescription<A>>,
@ -160,7 +230,8 @@ impl<A: Authorization> Bundle<A> {
&self.authorization &self.authorization
} }
pub fn map_authorization<B: Authorization, F: MapAuth<A, B>>(self, f: F) -> Bundle<B> { /// 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 { Bundle {
shielded_spends: self shielded_spends: self
.shielded_spends .shielded_spends
@ -190,6 +261,45 @@ impl<A: Authorization> Bundle<A> {
authorization: f.map_authorization(self.authorization), 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> { impl DynamicUsage for Bundle<Authorized> {