Merge pull request #1064 from zcash/1044-builder-progress

zcash_primitives: Add `ProverProgress` trait to `sapling::builder`
This commit is contained in:
str4d 2023-12-11 17:06:59 +00:00 committed by GitHub
commit cf850c4baf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 127 additions and 63 deletions

View File

@ -637,7 +637,7 @@ mod tests {
}
}
fn demo_builder<'a>(height: BlockHeight) -> DemoBuilder<Builder<'a, FutureNetwork, OsRng>> {
fn demo_builder<'a>(height: BlockHeight) -> DemoBuilder<Builder<'a, FutureNetwork, OsRng, ()>> {
DemoBuilder {
txn_builder: Builder::new(FutureNetwork, height, None),
extension_id: 0,

View File

@ -19,8 +19,9 @@ and this library adheres to Rust's notion of
- `builder::{InProgressProofs, Unproven, Proven}`
- `builder::{InProgressSignatures, Unsigned, PartiallyAuthorized}`
- `builder::{MaybeSigned, SigningParts}`
- `builder::{SpendDescriptionInfo::value}`
- `builder::{SaplingOutputInfo}`
- `builder::SpendDescriptionInfo::value`
- `builder::SaplingOutputInfo`
- `builder::ProverProgress`
- `bundle` module, containing the following types moved from
`zcash_primitives::transaction::components::sapling`:
- `Bundle`
@ -180,10 +181,15 @@ and this library adheres to Rust's notion of
`redjubjub::VerificationKey<Binding>` instead of
`zcash_primitives::sapling::redjubjub::PublicKey`.
- `zcash_primitives::transaction`:
- `builder::Builder` now has a generic parameter for the type of progress
notifier, which needs to implement `sapling::builder::ProverProgress` in
order to build transactions.
- `builder::Builder::{build, build_zfuture}` now take
`&impl SpendProver, &impl OutputProver` instead of `&impl TxProver`.
- `builder::Builder::add_sapling_spend` no longer takes a `diversifier`
argument as the diversifier may be obtained from the note.
- `builder::Builder::with_progress_notifier` now consumes `self` and returns a
`Builder` typed on the provided channel.
- `components::transparent::TxOut.value` now has type `NonNegativeAmount`
instead of `Amount`.
- `components::transparent::fees` has been moved to

View File

@ -1,31 +1,28 @@
//! Types and functions for building Sapling transaction components.
use core::fmt;
use std::{marker::PhantomData, sync::mpsc::Sender};
use std::marker::PhantomData;
use group::ff::Field;
use rand::{seq::SliceRandom, RngCore};
use rand_core::CryptoRng;
use redjubjub::{Binding, SpendAuth};
use crate::{
sapling::{
self,
bundle::{
Authorization, Authorized, Bundle, GrothProofBytes, MapAuth, OutputDescription,
SpendDescription,
},
keys::{OutgoingViewingKey, SpendAuthorizingKey, SpendValidatingKey},
note_encryption::{sapling_note_encryption, Zip212Enforcement},
prover::{OutputProver, SpendProver},
util::generate_random_rseed_internal,
value::{
CommitmentSum, NoteValue, TrapdoorSum, ValueCommitTrapdoor, ValueCommitment, ValueSum,
},
zip32::ExtendedSpendingKey,
Diversifier, MerklePath, Node, Note, PaymentAddress, ProofGenerationKey, SaplingIvk,
use crate::sapling::{
self,
bundle::{
Authorization, Authorized, Bundle, GrothProofBytes, MapAuth, OutputDescription,
SpendDescription,
},
transaction::builder::Progress,
keys::{OutgoingViewingKey, SpendAuthorizingKey, SpendValidatingKey},
note_encryption::{sapling_note_encryption, Zip212Enforcement},
prover::{OutputProver, SpendProver},
util::generate_random_rseed_internal,
value::{
CommitmentSum, NoteValue, TrapdoorSum, ValueCommitTrapdoor, ValueCommitment, ValueSum,
},
zip32::ExtendedSpendingKey,
Diversifier, MerklePath, Node, Note, PaymentAddress, ProofGenerationKey, SaplingIvk,
};
/// If there are any shielded inputs, always have at least two shielded outputs, padding
@ -571,21 +568,47 @@ impl InProgressProofs for Proven {
type OutputProof = GrothProofBytes;
}
struct CreateProofs<'a, SP: SpendProver, OP: OutputProver, R: RngCore> {
/// Reports on the progress made towards creating proofs for a bundle.
pub trait ProverProgress {
/// Updates the progress instance with the number of steps completed and the total
/// number of steps.
fn update(&mut self, cur: u32, end: u32);
}
impl ProverProgress for () {
fn update(&mut self, _: u32, _: u32) {}
}
impl<U: From<(u32, u32)>> ProverProgress for std::sync::mpsc::Sender<U> {
fn update(&mut self, cur: u32, end: u32) {
// If the send fails, we should ignore the error, not crash.
self.send(U::from((cur, end))).unwrap_or(());
}
}
impl<U: ProverProgress> ProverProgress for &mut U {
fn update(&mut self, cur: u32, end: u32) {
(*self).update(cur, end);
}
}
struct CreateProofs<'a, SP: SpendProver, OP: OutputProver, R: RngCore, U: ProverProgress> {
spend_prover: &'a SP,
output_prover: &'a OP,
rng: R,
progress_notifier: Option<&'a Sender<Progress>>,
progress_notifier: U,
total_progress: u32,
progress: u32,
}
impl<'a, SP: SpendProver, OP: OutputProver, R: RngCore> CreateProofs<'a, SP, OP, R> {
impl<'a, SP: SpendProver, OP: OutputProver, R: RngCore, U: ProverProgress>
CreateProofs<'a, SP, OP, R, U>
{
fn new(
spend_prover: &'a SP,
output_prover: &'a OP,
rng: R,
progress_notifier: Option<&'a Sender<Progress>>,
progress_notifier: U,
total_progress: u32,
) -> Self {
// Keep track of the total number of steps computed
@ -602,17 +625,19 @@ impl<'a, SP: SpendProver, OP: OutputProver, R: RngCore> CreateProofs<'a, SP, OP,
fn update_progress(&mut self) {
// Update progress and send a notification on the channel
self.progress += 1;
if let Some(sender) = self.progress_notifier {
// If the send fails, we should ignore the error, not crash.
sender
.send(Progress::new(self.progress, Some(self.total_progress)))
.unwrap_or(());
}
self.progress_notifier
.update(self.progress, self.total_progress);
}
}
impl<'a, S: InProgressSignatures, SP: SpendProver, OP: OutputProver, R: RngCore>
MapAuth<InProgress<Unproven, S>, InProgress<Proven, S>> for CreateProofs<'a, SP, OP, R>
impl<
'a,
S: InProgressSignatures,
SP: SpendProver,
OP: OutputProver,
R: RngCore,
U: ProverProgress,
> MapAuth<InProgress<Unproven, S>, InProgress<Proven, S>> for CreateProofs<'a, SP, OP, R, U>
{
fn map_spend_proof(&mut self, spend: sapling::circuit::Spend) -> GrothProofBytes {
let proof = self.spend_prover.create_proof(spend, &mut self.rng);
@ -645,7 +670,7 @@ impl<S: InProgressSignatures, V> Bundle<InProgress<Unproven, S>, V> {
spend_prover: &SP,
output_prover: &OP,
rng: impl RngCore,
progress_notifier: Option<&Sender<Progress>>,
progress_notifier: impl ProverProgress,
) -> Bundle<InProgress<Proven, S>, V> {
let total_progress =
self.shielded_spends().len() as u32 + self.shielded_outputs().len() as u32;
@ -901,7 +926,7 @@ pub mod testing {
.unwrap();
let bundle =
bundle.create_proofs(&MockSpendProver, &MockOutputProver, &mut rng, None);
bundle.create_proofs(&MockSpendProver, &MockOutputProver, &mut rng, ());
bundle
.apply_signatures(&mut rng, fake_sighash_bytes, &[extsk.expsk.ask])

View File

@ -130,11 +130,16 @@ pub struct Progress {
end: Option<u32>,
}
impl Progress {
pub fn new(cur: u32, end: Option<u32>) -> Self {
Self { cur, end }
impl From<(u32, u32)> for Progress {
fn from((cur, end): (u32, u32)) -> Self {
Self {
cur,
end: Some(end),
}
}
}
impl Progress {
/// Returns the number of steps completed so far while building the transaction.
///
/// Note that each step may not be of the same complexity/duration.
@ -152,7 +157,7 @@ impl Progress {
}
/// Generates a [`Transaction`] from its inputs and outputs.
pub struct Builder<'a, P, R> {
pub struct Builder<'a, P, R, U: sapling::builder::ProverProgress> {
params: P,
rng: R,
target_height: BlockHeight,
@ -170,10 +175,10 @@ pub struct Builder<'a, P, R> {
tze_builder: TzeBuilder<'a, TransactionData<Unauthorized>>,
#[cfg(not(feature = "zfuture"))]
tze_builder: std::marker::PhantomData<&'a ()>,
progress_notifier: Option<Sender<Progress>>,
progress_notifier: U,
}
impl<'a, P, R> Builder<'a, P, R> {
impl<'a, P, R, U: sapling::builder::ProverProgress> Builder<'a, P, R, U> {
/// Returns the network parameters that the builder has been configured for.
pub fn params(&self) -> &P {
&self.params
@ -210,7 +215,7 @@ impl<'a, P, R> Builder<'a, P, R> {
}
}
impl<'a, P: consensus::Parameters> Builder<'a, P, OsRng> {
impl<'a, P: consensus::Parameters> Builder<'a, P, OsRng, ()> {
/// Creates a new `Builder` targeted for inclusion in the block with the given height,
/// using default values for general transaction fields and the default OS random.
///
@ -227,7 +232,7 @@ impl<'a, P: consensus::Parameters> Builder<'a, P, OsRng> {
}
}
impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng> Builder<'a, P, R> {
impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng> Builder<'a, P, R, ()> {
/// Creates a new `Builder` targeted for inclusion in the block with the given height
/// and randomness source, using default values for general transaction fields.
///
@ -240,7 +245,7 @@ impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng> Builder<'a, P, R> {
target_height: BlockHeight,
orchard_anchor: Option<orchard::tree::Anchor>,
rng: R,
) -> Builder<'a, P, R> {
) -> Self {
let orchard_builder = if params.is_nu_active(NetworkUpgrade::Nu5, target_height) {
orchard_anchor.map(|anchor| {
orchard::builder::Builder::new(
@ -278,10 +283,39 @@ impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng> Builder<'a, P, R> {
tze_builder: TzeBuilder::empty(),
#[cfg(not(feature = "zfuture"))]
tze_builder: std::marker::PhantomData,
progress_notifier: None,
progress_notifier: (),
}
}
/// Sets the notifier channel, where progress of building the transaction is sent.
///
/// An update is sent after every Sapling Spend or Output is computed, and the `u32`
/// sent represents the total steps completed so far. It will eventually send number
/// of spends + outputs. If there's an error building the transaction, the channel is
/// closed.
pub fn with_progress_notifier(
self,
progress_notifier: Sender<Progress>,
) -> Builder<'a, P, R, Sender<Progress>> {
Builder {
params: self.params,
rng: self.rng,
target_height: self.target_height,
expiry_height: self.expiry_height,
transparent_builder: self.transparent_builder,
sapling_builder: self.sapling_builder,
orchard_builder: self.orchard_builder,
sapling_asks: self.sapling_asks,
orchard_saks: self.orchard_saks,
tze_builder: self.tze_builder,
progress_notifier,
}
}
}
impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng, U: sapling::builder::ProverProgress>
Builder<'a, P, R, U>
{
/// Adds an Orchard note to be spent in this bundle.
///
/// Returns an error if the given Merkle path does not have the required anchor for
@ -380,16 +414,6 @@ impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng> Builder<'a, P, R> {
self.transparent_builder.add_output(to, value)
}
/// Sets the notifier channel, where progress of building the transaction is sent.
///
/// An update is sent after every Spend or Output is computed, and the `u32` sent
/// represents the total steps completed so far. It will eventually send number of
/// spends + outputs. If there's an error building the transaction, the channel is
/// closed.
pub fn with_progress_notifier(&mut self, progress_notifier: Sender<Progress>) {
self.progress_notifier = Some(progress_notifier);
}
/// Returns the sum of the transparent, Sapling, Orchard, and TZE value balances.
fn value_balance(&self) -> Result<Amount, BalanceError> {
let value_balances = [
@ -543,7 +567,7 @@ impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng> Builder<'a, P, R> {
spend_prover,
output_prover,
&mut rng,
self.progress_notifier.as_ref(),
self.progress_notifier,
),
tx_metadata,
)
@ -650,8 +674,8 @@ impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng> Builder<'a, P, R> {
}
#[cfg(feature = "zfuture")]
impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng> ExtensionTxBuilder<'a>
for Builder<'a, P, R>
impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng, U: sapling::builder::ProverProgress>
ExtensionTxBuilder<'a> for Builder<'a, P, R, U>
{
type BuildCtx = TransactionData<Unauthorized>;
type BuildError = tze::builder::Error;
@ -691,12 +715,15 @@ mod testing {
use super::{Builder, Error, SaplingMetadata};
use crate::{
consensus::{self, BlockHeight},
sapling::prover::mock::{MockOutputProver, MockSpendProver},
sapling::{
self,
prover::mock::{MockOutputProver, MockSpendProver},
},
transaction::fees::fixed,
transaction::Transaction,
};
impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> {
impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R, ()> {
/// Creates a new `Builder` targeted for inclusion in the block with the given height
/// and randomness source, using default values for general transaction fields.
///
@ -710,7 +737,7 @@ mod testing {
params: P,
height: BlockHeight,
rng: R,
) -> Builder<'a, P, impl RngCore + CryptoRng> {
) -> Builder<'a, P, impl RngCore + CryptoRng, ()> {
struct FakeCryptoRng<R: RngCore>(R);
impl<R: RngCore> CryptoRng for FakeCryptoRng<R> {}
impl<R: RngCore> RngCore for FakeCryptoRng<R> {
@ -733,7 +760,13 @@ mod testing {
Builder::new_internal(params, FakeCryptoRng(rng), height, None)
}
}
impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng> Builder<'a, P, R> {
impl<
'a,
P: consensus::Parameters,
R: RngCore + CryptoRng,
U: sapling::builder::ProverProgress,
> Builder<'a, P, R, U>
{
pub fn mock_build(self) -> Result<(Transaction, SaplingMetadata), Error<Infallible>> {
#[allow(deprecated)]
self.build(
@ -801,7 +834,7 @@ mod tests {
tze_builder: TzeBuilder::empty(),
#[cfg(not(feature = "zfuture"))]
tze_builder: std::marker::PhantomData,
progress_notifier: None,
progress_notifier: (),
orchard_builder: None,
sapling_asks: vec![],
orchard_saks: Vec::new(),