Merge pull request #114 from zcash/sapling_builder_cleanup
A few minor cleanups for consistency with the `orchard` builder API
This commit is contained in:
commit
71711b9e4b
32
CHANGELOG.md
32
CHANGELOG.md
|
@ -10,6 +10,7 @@ The entries below are relative to the `zcash_primitives::sapling` module as of
|
|||
`zcash_primitives 0.13.0`.
|
||||
|
||||
### Added
|
||||
- `sapling_crypto::Anchor`
|
||||
- `sapling_crypto::BatchValidator` (moved from `zcash_proofs::sapling`).
|
||||
- `sapling_crypto::SaplingVerificationContext` (moved from
|
||||
`zcash_proofs::sapling`).
|
||||
|
@ -25,6 +26,7 @@ The entries below are relative to the `zcash_primitives::sapling` module as of
|
|||
- `OutputInfo`
|
||||
- `ProverProgress`
|
||||
- `BundleType`
|
||||
- `SigningMetadata`
|
||||
- `bundle` bundle builder function.
|
||||
- `sapling_crypto::bundle` module:
|
||||
- The following types moved from
|
||||
|
@ -32,7 +34,7 @@ The entries below are relative to the `zcash_primitives::sapling` module as of
|
|||
- `Bundle`
|
||||
- `SpendDescription, SpendDescriptionV5`
|
||||
- `OutputDescription, OutputDescriptionV5`
|
||||
- `Authorization, Authorized, MapAuth`
|
||||
- `Authorization, Authorized`
|
||||
- `GrothProofBytes`
|
||||
- `Bundle::<InProgress<Unproven, _>>::create_proofs`
|
||||
- `Bundle::<InProgress<_, Unsigned>>::prepare`
|
||||
|
@ -40,10 +42,6 @@ The entries below are relative to the `zcash_primitives::sapling` module as of
|
|||
- `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.
|
||||
- `testing` module, containing the following functions moved from
|
||||
`zcash_primitives::transaction::components::sapling::testing`:
|
||||
- `arb_output_description`
|
||||
|
@ -91,21 +89,26 @@ The entries below are relative to the `zcash_primitives::sapling` module as of
|
|||
- `sapling_crypto::address::PaymentAddress::create_note` now takes its `value`
|
||||
argument as a `NoteValue` instead of as a bare `u64`.
|
||||
- `sapling_crypto::builder`:
|
||||
- `SaplingBuilder` no longer has a `P: zcash_primitives::consensus::Parameters`
|
||||
- `SaplingBuilder` has been renamed to `Builder`
|
||||
- `MaybeSigned::SigningMetadata` has been renamed to `MaybeSigned::SigningParts`
|
||||
- `Builder` no longer has a `P: zcash_primitives::consensus::Parameters`
|
||||
type parameter.
|
||||
- `SaplingBuilder::new` now takes a `Zip212Enforcement` argument instead of a
|
||||
- `Builder::new` now takes a `Zip212Enforcement` argument instead of a
|
||||
`P: zcash_primitives::consensus::Parameters` argument and a target height.
|
||||
- `SaplingBuilder::add_spend` now takes `extsk` by reference. Also, it no
|
||||
It also now takes as an argument the Sapling anchor to be used for all
|
||||
spends in the bundle.
|
||||
- `Builder::add_spend` now takes `extsk` by reference. Also, it no
|
||||
longer takes a `diversifier` argument as the diversifier may be obtained
|
||||
from the note.
|
||||
- `SaplingBuilder::add_output` now takes an `Option<[u8; 512]>` memo instead
|
||||
from the note. All calls to `add_spend` are now required to use an anchor
|
||||
that corresponds to the anchor provided at builder construction.
|
||||
- `Builder::add_output` now takes an `Option<[u8; 512]>` memo instead
|
||||
of a `MemoBytes`.
|
||||
- `SaplingBuilder::build` no longer takes a prover, proving context, progress
|
||||
- `Builder::build` no longer takes a prover, proving context, progress
|
||||
notifier, or target height. 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.
|
||||
- `SaplingBuilder::build` now takes a `BundleType` argument that instructs
|
||||
- `Builder::build` now takes a `BundleType` argument that instructs
|
||||
it how to pad the bundle with dummy outputs.
|
||||
- `Error` has new error variants:
|
||||
- `Error::DuplicateSignature`
|
||||
|
@ -116,6 +119,9 @@ The entries below are relative to the `zcash_primitives::sapling` module as of
|
|||
- `Bundle` now has a second generic parameter `V`.
|
||||
- `Bundle::value_balance` now returns `&V` instead of
|
||||
`&zcash_primitives::transaction::components::Amount`.
|
||||
- `Bundle::map_authorization` now takes a context argument and explicit
|
||||
functions for each mappable field, rather than a `MapAuth` value, in
|
||||
order to simplify handling of context values.
|
||||
- `Authorized::binding_sig` now has type `redjubjub::Signature<Binding>`.
|
||||
- `Authorized::AuthSig` now has type `redjubjub::Signature<SpendAuth>`.
|
||||
- `SpendDescription::temporary_zcashd_from_parts` now takes `rk` as
|
||||
|
@ -126,7 +132,6 @@ The entries below are relative to the `zcash_primitives::sapling` module as of
|
|||
`redjubjub::Signature<SpendAuth>` instead of
|
||||
`sapling_crypto::redjubjub::Signature`.
|
||||
- `testing::arb_bundle` now takes a `value_balance: V` argument.
|
||||
- `MapAuth` trait methods now take `&mut self` instead of `&self`.
|
||||
- `sapling_crypto::circuit::ValueCommitmentOpening::value` is now represented as
|
||||
a `NoteValue` instead of as a bare `u64`.
|
||||
- `sapling_crypto::keys`:
|
||||
|
@ -170,6 +175,7 @@ The entries below are relative to the `zcash_primitives::sapling` module as of
|
|||
- `OutputDescription::read`
|
||||
- `OutputDescription::{write_v4, write_v5_without_proof}`
|
||||
- `OutputDescriptionV5::read`
|
||||
- `MapAuth` trait
|
||||
- `sapling_crypto::builder`:
|
||||
- `SpendDescriptionInfo`
|
||||
- `sapling_crypto::note_encryption::SaplingDomain::for_height` (use
|
||||
|
|
301
src/builder.rs
301
src/builder.rs
|
@ -4,14 +4,14 @@ use core::fmt;
|
|||
use std::{iter, marker::PhantomData};
|
||||
|
||||
use group::ff::Field;
|
||||
use incrementalmerkletree::Position;
|
||||
use rand::{seq::SliceRandom, RngCore};
|
||||
use rand_core::CryptoRng;
|
||||
use redjubjub::{Binding, SpendAuth};
|
||||
|
||||
use crate::{
|
||||
bundle::{
|
||||
Authorization, Authorized, Bundle, GrothProofBytes, MapAuth, OutputDescription,
|
||||
SpendDescription,
|
||||
Authorization, Authorized, Bundle, GrothProofBytes, OutputDescription, SpendDescription,
|
||||
},
|
||||
circuit,
|
||||
keys::{OutgoingViewingKey, SpendAuthorizingKey, SpendValidatingKey},
|
||||
|
@ -22,7 +22,8 @@ use crate::{
|
|||
CommitmentSum, NoteValue, TrapdoorSum, ValueCommitTrapdoor, ValueCommitment, ValueSum,
|
||||
},
|
||||
zip32::ExtendedSpendingKey,
|
||||
Diversifier, MerklePath, Node, Note, PaymentAddress, ProofGenerationKey, SaplingIvk,
|
||||
Anchor, Diversifier, MerklePath, Node, Note, PaymentAddress, ProofGenerationKey, SaplingIvk,
|
||||
NOTE_COMMITMENT_TREE_DEPTH,
|
||||
};
|
||||
|
||||
/// If there are any shielded inputs, always have at least two shielded outputs, padding
|
||||
|
@ -34,12 +35,50 @@ const MIN_SHIELDED_OUTPUTS: usize = 2;
|
|||
pub enum BundleType {
|
||||
/// A transactional bundle will be padded if necessary to contain at least 2 outputs,
|
||||
/// irrespective of whether any genuine outputs are required.
|
||||
Transactional { anchor: Node },
|
||||
Transactional {
|
||||
/// A flag that, when set to `true`, indicates that the resulting bundle should be
|
||||
/// produced with the minimum required number of spends (1) and outputs (2 with
|
||||
/// padding) to be usable on its own in a transaction, irrespective of whether any
|
||||
/// spends or outputs have been requested. If no explicit spends or outputs have
|
||||
/// been added, all of the spends and outputs in the resulting bundle will be
|
||||
/// dummies.
|
||||
bundle_required: bool,
|
||||
},
|
||||
/// A coinbase bundle is required to have no spends. No output padding is performed.
|
||||
Coinbase,
|
||||
}
|
||||
|
||||
impl BundleType {
|
||||
/// The default bundle type allows both spends and outputs, and does not require a
|
||||
/// bundle to be produced if no spends or outputs have been added to the bundle.
|
||||
pub const DEFAULT: BundleType = BundleType::Transactional {
|
||||
bundle_required: false,
|
||||
};
|
||||
|
||||
/// Returns the number of logical spends that a builder will produce in constructing a bundle
|
||||
/// of this type, given the specified numbers of spends and outputs.
|
||||
///
|
||||
/// Returns an error if the specified number of spends and outputs is incompatible with
|
||||
/// this bundle type.
|
||||
pub fn num_spends(&self, requested_spends: usize) -> Result<usize, &'static str> {
|
||||
match self {
|
||||
BundleType::Transactional { bundle_required } => {
|
||||
Ok(if *bundle_required || requested_spends > 0 {
|
||||
core::cmp::max(requested_spends, 1)
|
||||
} else {
|
||||
0
|
||||
})
|
||||
}
|
||||
BundleType::Coinbase => {
|
||||
if requested_spends == 0 {
|
||||
Ok(0)
|
||||
} else {
|
||||
Err("Spends not allowed in coinbase bundles")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the number of logical outputs that a builder will produce in constructing a bundle
|
||||
/// of this type, given the specified numbers of spends and outputs.
|
||||
///
|
||||
|
@ -47,16 +86,20 @@ impl BundleType {
|
|||
/// this bundle type.
|
||||
pub fn num_outputs(
|
||||
&self,
|
||||
num_spends: usize,
|
||||
num_outputs: usize,
|
||||
requested_spends: usize,
|
||||
requested_outputs: usize,
|
||||
) -> Result<usize, &'static str> {
|
||||
match self {
|
||||
BundleType::Transactional { .. } => {
|
||||
Ok(core::cmp::max(num_outputs, MIN_SHIELDED_OUTPUTS))
|
||||
BundleType::Transactional { bundle_required } => {
|
||||
Ok(if *bundle_required || requested_outputs > 0 {
|
||||
core::cmp::max(requested_outputs, MIN_SHIELDED_OUTPUTS)
|
||||
} else {
|
||||
0
|
||||
})
|
||||
}
|
||||
BundleType::Coinbase => {
|
||||
if num_spends == 0 {
|
||||
Ok(num_outputs)
|
||||
if requested_spends == 0 {
|
||||
Ok(requested_outputs)
|
||||
} else {
|
||||
Err("Spends not allowed in coinbase bundles")
|
||||
}
|
||||
|
@ -109,6 +152,7 @@ pub struct SpendInfo {
|
|||
proof_generation_key: ProofGenerationKey,
|
||||
note: Note,
|
||||
merkle_path: MerklePath,
|
||||
dummy_ask: Option<SpendAuthorizingKey>,
|
||||
}
|
||||
|
||||
impl SpendInfo {
|
||||
|
@ -122,6 +166,7 @@ impl SpendInfo {
|
|||
proof_generation_key,
|
||||
note,
|
||||
merkle_path,
|
||||
dummy_ask: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -130,12 +175,33 @@ impl SpendInfo {
|
|||
self.note.value()
|
||||
}
|
||||
|
||||
fn has_matching_anchor(&self, anchor: Node) -> bool {
|
||||
/// Defined in [Zcash Protocol Spec § 4.8.2: Dummy Notes (Sapling)][saplingdummynotes].
|
||||
///
|
||||
/// [saplingdummynotes]: https://zips.z.cash/protocol/protocol.pdf#saplingdummynotes
|
||||
fn dummy<R: RngCore>(mut rng: R) -> Self {
|
||||
let (sk, _, note) = Note::dummy(&mut rng);
|
||||
let merkle_path = MerklePath::from_parts(
|
||||
iter::repeat_with(|| Node::from_scalar(jubjub::Base::random(&mut rng)))
|
||||
.take(NOTE_COMMITMENT_TREE_DEPTH.into())
|
||||
.collect(),
|
||||
Position::from(0),
|
||||
)
|
||||
.expect("The path length corresponds to the length of the generated vector.");
|
||||
|
||||
SpendInfo {
|
||||
proof_generation_key: sk.proof_generation_key(),
|
||||
note,
|
||||
merkle_path,
|
||||
dummy_ask: Some(sk.ask),
|
||||
}
|
||||
}
|
||||
|
||||
fn has_matching_anchor(&self, anchor: &Anchor) -> bool {
|
||||
if self.note.value() == NoteValue::ZERO {
|
||||
true
|
||||
} else {
|
||||
let node = Node::from_cmu(&self.note.cmu());
|
||||
self.merkle_path.root(node) == anchor
|
||||
&Anchor::from(self.merkle_path.root(node)) == anchor
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -145,6 +211,7 @@ impl SpendInfo {
|
|||
note: self.note,
|
||||
merkle_path: self.merkle_path,
|
||||
rcv: ValueCommitTrapdoor::random(rng),
|
||||
dummy_ask: self.dummy_ask,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -155,6 +222,7 @@ struct PreparedSpendInfo {
|
|||
note: Note,
|
||||
merkle_path: MerklePath,
|
||||
rcv: ValueCommitTrapdoor,
|
||||
dummy_ask: Option<SpendAuthorizingKey>,
|
||||
}
|
||||
|
||||
impl PreparedSpendInfo {
|
||||
|
@ -197,7 +265,10 @@ impl PreparedSpendInfo {
|
|||
nullifier,
|
||||
rk,
|
||||
zkproof,
|
||||
SigningParts { ak, alpha },
|
||||
SigningMetadata {
|
||||
dummy_ask: self.dummy_ask,
|
||||
parts: SigningParts { ak, alpha },
|
||||
},
|
||||
))
|
||||
}
|
||||
}
|
||||
|
@ -323,7 +394,7 @@ impl PreparedOutputInfo {
|
|||
}
|
||||
}
|
||||
|
||||
/// Metadata about a transaction created by a [`SaplingBuilder`].
|
||||
/// Metadata about a transaction created by a [`Builder`].
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct SaplingMetadata {
|
||||
spend_indices: Vec<usize>,
|
||||
|
@ -339,22 +410,22 @@ impl SaplingMetadata {
|
|||
}
|
||||
|
||||
/// Returns the index within the transaction of the [`SpendDescription`] corresponding
|
||||
/// to the `n`-th call to [`SaplingBuilder::add_spend`].
|
||||
/// to the `n`-th call to [`Builder::add_spend`].
|
||||
///
|
||||
/// Note positions are randomized when building transactions for indistinguishability.
|
||||
/// This means that the transaction consumer cannot assume that e.g. the first spend
|
||||
/// they added (via the first call to [`SaplingBuilder::add_spend`]) is the first
|
||||
/// they added (via the first call to [`Builder::add_spend`]) is the first
|
||||
/// [`SpendDescription`] in the transaction.
|
||||
pub fn spend_index(&self, n: usize) -> Option<usize> {
|
||||
self.spend_indices.get(n).copied()
|
||||
}
|
||||
|
||||
/// Returns the index within the transaction of the [`OutputDescription`] corresponding
|
||||
/// to the `n`-th call to [`SaplingBuilder::add_output`].
|
||||
/// to the `n`-th call to [`Builder::add_output`].
|
||||
///
|
||||
/// Note positions are randomized when building transactions for indistinguishability.
|
||||
/// This means that the transaction consumer cannot assume that e.g. the first output
|
||||
/// they added (via the first call to [`SaplingBuilder::add_output`]) is the first
|
||||
/// they added (via the first call to [`Builder::add_output`]) is the first
|
||||
/// [`OutputDescription`] in the transaction.
|
||||
pub fn output_index(&self, n: usize) -> Option<usize> {
|
||||
self.output_indices.get(n).copied()
|
||||
|
@ -362,22 +433,28 @@ impl SaplingMetadata {
|
|||
}
|
||||
|
||||
/// A mutable builder type for constructing Sapling bundles.
|
||||
pub struct SaplingBuilder {
|
||||
pub struct Builder {
|
||||
value_balance: ValueSum,
|
||||
spends: Vec<SpendInfo>,
|
||||
outputs: Vec<OutputInfo>,
|
||||
zip212_enforcement: Zip212Enforcement,
|
||||
bundle_type: BundleType,
|
||||
anchor: Anchor,
|
||||
}
|
||||
|
||||
impl SaplingBuilder {
|
||||
pub fn new(zip212_enforcement: Zip212Enforcement, bundle_type: BundleType) -> Self {
|
||||
SaplingBuilder {
|
||||
impl Builder {
|
||||
pub fn new(
|
||||
zip212_enforcement: Zip212Enforcement,
|
||||
bundle_type: BundleType,
|
||||
anchor: Anchor,
|
||||
) -> Self {
|
||||
Builder {
|
||||
value_balance: ValueSum::zero(),
|
||||
spends: vec![],
|
||||
outputs: vec![],
|
||||
zip212_enforcement,
|
||||
bundle_type,
|
||||
anchor,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -422,8 +499,8 @@ impl SaplingBuilder {
|
|||
|
||||
// Consistency check: all anchors must equal the first one
|
||||
match self.bundle_type {
|
||||
BundleType::Transactional { anchor } => {
|
||||
if !spend.has_matching_anchor(anchor) {
|
||||
BundleType::Transactional { .. } => {
|
||||
if !spend.has_matching_anchor(&self.anchor) {
|
||||
return Err(Error::AnchorMismatch);
|
||||
}
|
||||
}
|
||||
|
@ -441,8 +518,7 @@ impl SaplingBuilder {
|
|||
}
|
||||
|
||||
/// Adds a Sapling address to send funds to.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn add_output<R: RngCore>(
|
||||
pub fn add_output(
|
||||
&mut self,
|
||||
ovk: Option<OutgoingViewingKey>,
|
||||
to: PaymentAddress,
|
||||
|
@ -466,10 +542,11 @@ impl SaplingBuilder {
|
|||
) -> Result<Option<(UnauthorizedBundle<V>, SaplingMetadata)>, Error> {
|
||||
bundle::<SP, OP, _, _>(
|
||||
rng,
|
||||
self.spends,
|
||||
self.outputs,
|
||||
self.bundle_type,
|
||||
self.zip212_enforcement,
|
||||
self.anchor,
|
||||
self.spends,
|
||||
self.outputs,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -478,15 +555,16 @@ impl SaplingBuilder {
|
|||
/// and outputs.
|
||||
pub fn bundle<SP: SpendProver, OP: OutputProver, R: RngCore, V: TryFrom<i64>>(
|
||||
mut rng: R,
|
||||
spends: Vec<SpendInfo>,
|
||||
outputs: Vec<OutputInfo>,
|
||||
bundle_type: BundleType,
|
||||
zip212_enforcement: Zip212Enforcement,
|
||||
anchor: Anchor,
|
||||
spends: Vec<SpendInfo>,
|
||||
outputs: Vec<OutputInfo>,
|
||||
) -> Result<Option<(UnauthorizedBundle<V>, SaplingMetadata)>, Error> {
|
||||
match bundle_type {
|
||||
BundleType::Transactional { anchor } => {
|
||||
BundleType::Transactional { .. } => {
|
||||
for spend in &spends {
|
||||
if !spend.has_matching_anchor(anchor) {
|
||||
if !spend.has_matching_anchor(&anchor) {
|
||||
return Err(Error::AnchorMismatch);
|
||||
}
|
||||
}
|
||||
|
@ -498,6 +576,11 @@ pub fn bundle<SP: SpendProver, OP: OutputProver, R: RngCore, V: TryFrom<i64>>(
|
|||
}
|
||||
}
|
||||
|
||||
let requested_spend_count = spends.len();
|
||||
let bundle_spend_count = bundle_type
|
||||
.num_spends(requested_spend_count)
|
||||
.map_err(|_| Error::BundleTypeNotSatisfiable)?;
|
||||
|
||||
let requested_output_count = outputs.len();
|
||||
let bundle_output_count = bundle_type
|
||||
.num_outputs(spends.len(), requested_output_count)
|
||||
|
@ -510,8 +593,14 @@ pub fn bundle<SP: SpendProver, OP: OutputProver, R: RngCore, V: TryFrom<i64>>(
|
|||
tx_metadata.spend_indices.resize(spends.len(), 0);
|
||||
tx_metadata.output_indices.resize(requested_output_count, 0);
|
||||
|
||||
// Record initial spend positions
|
||||
let mut indexed_spends: Vec<_> = spends.into_iter().enumerate().collect();
|
||||
// Create any required dummy spends and record initial spend positions
|
||||
let mut indexed_spends: Vec<_> = spends
|
||||
.into_iter()
|
||||
.chain(iter::repeat_with(|| SpendInfo::dummy(&mut rng)))
|
||||
.enumerate()
|
||||
.take(bundle_spend_count)
|
||||
.collect();
|
||||
|
||||
// Create any required dummy outputs and record initial output positions
|
||||
let mut indexed_outputs: Vec<_> = outputs
|
||||
.into_iter()
|
||||
|
@ -530,7 +619,9 @@ pub fn bundle<SP: SpendProver, OP: OutputProver, R: RngCore, V: TryFrom<i64>>(
|
|||
.enumerate()
|
||||
.map(|(i, (pos, spend))| {
|
||||
// Record the post-randomized spend location
|
||||
tx_metadata.spend_indices[pos] = i;
|
||||
if pos < requested_spend_count {
|
||||
tx_metadata.spend_indices[pos] = i;
|
||||
}
|
||||
|
||||
spend.prepare(&mut rng)
|
||||
})
|
||||
|
@ -607,7 +698,7 @@ pub fn bundle<SP: SpendProver, OP: OutputProver, R: RngCore, V: TryFrom<i64>>(
|
|||
|
||||
/// Type alias for an in-progress bundle that has no proofs or signatures.
|
||||
///
|
||||
/// This is returned by [`SaplingBuilder::build`].
|
||||
/// This is returned by [`Builder::build`].
|
||||
pub type UnauthorizedBundle<V> = Bundle<InProgress<Unproven, Unsigned>, V>;
|
||||
|
||||
/// Marker trait representing bundle proofs in the process of being created.
|
||||
|
@ -719,17 +810,7 @@ impl<'a, SP: SpendProver, OP: OutputProver, R: RngCore, U: ProverProgress>
|
|||
self.progress_notifier
|
||||
.update(self.progress, self.total_progress);
|
||||
}
|
||||
}
|
||||
|
||||
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: circuit::Spend) -> GrothProofBytes {
|
||||
let proof = self.spend_prover.create_proof(spend, &mut self.rng);
|
||||
self.update_progress();
|
||||
|
@ -742,11 +823,10 @@ impl<
|
|||
OP::encode_proof(proof)
|
||||
}
|
||||
|
||||
fn map_auth_sig(&mut self, s: S::AuthSig) -> S::AuthSig {
|
||||
s
|
||||
}
|
||||
|
||||
fn map_authorization(&mut self, a: InProgress<Unproven, S>) -> InProgress<Proven, S> {
|
||||
fn map_authorization<S: InProgressSignatures>(
|
||||
&mut self,
|
||||
a: InProgress<Unproven, S>,
|
||||
) -> InProgress<Proven, S> {
|
||||
InProgress {
|
||||
sigs: a.sigs,
|
||||
_proof_state: PhantomData::default(),
|
||||
|
@ -765,13 +845,21 @@ impl<S: InProgressSignatures, V> Bundle<InProgress<Unproven, S>, V> {
|
|||
) -> Bundle<InProgress<Proven, S>, V> {
|
||||
let total_progress =
|
||||
self.shielded_spends().len() as u32 + self.shielded_outputs().len() as u32;
|
||||
self.map_authorization(CreateProofs::new(
|
||||
let mut cp = CreateProofs::new(
|
||||
spend_prover,
|
||||
output_prover,
|
||||
rng,
|
||||
progress_notifier,
|
||||
total_progress,
|
||||
))
|
||||
);
|
||||
|
||||
self.map_authorization(
|
||||
&mut cp,
|
||||
|cp, spend| cp.map_spend_proof(spend),
|
||||
|cp, output| cp.map_output_proof(output),
|
||||
|_cp, sig| sig,
|
||||
|cp, auth| cp.map_authorization(auth),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -788,7 +876,7 @@ impl fmt::Debug for Unsigned {
|
|||
}
|
||||
|
||||
impl InProgressSignatures for Unsigned {
|
||||
type AuthSig = SigningParts;
|
||||
type AuthSig = SigningMetadata;
|
||||
}
|
||||
|
||||
/// The parts needed to sign a [`SpendDescription`].
|
||||
|
@ -808,6 +896,18 @@ pub struct PartiallyAuthorized {
|
|||
sighash: [u8; 32],
|
||||
}
|
||||
|
||||
/// Container for metadata needed to sign a Sapling input.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SigningMetadata {
|
||||
/// If this action is spending a dummy note, this field holds that note's spend
|
||||
/// authorizing key.
|
||||
///
|
||||
/// These keys are used automatically in [`Bundle<Unauthorized>::prepare`] or
|
||||
/// [`Bundle<Unauthorized>::apply_signatures`] to sign dummy spends.
|
||||
dummy_ask: Option<SpendAuthorizingKey>,
|
||||
parts: SigningParts,
|
||||
}
|
||||
|
||||
impl InProgressSignatures for PartiallyAuthorized {
|
||||
type AuthSig = MaybeSigned;
|
||||
}
|
||||
|
@ -818,7 +918,7 @@ impl InProgressSignatures for PartiallyAuthorized {
|
|||
#[derive(Clone, Debug)]
|
||||
pub enum MaybeSigned {
|
||||
/// The information needed to sign this [`SpendDescription`].
|
||||
SigningMetadata(SigningParts),
|
||||
SigningParts(SigningParts),
|
||||
/// The signature for this [`SpendDescription`].
|
||||
Signature(redjubjub::Signature<SpendAuth>),
|
||||
}
|
||||
|
@ -841,18 +941,24 @@ impl<P: InProgressProofs, V> Bundle<InProgress<P, Unsigned>, V> {
|
|||
mut rng: R,
|
||||
sighash: [u8; 32],
|
||||
) -> Bundle<InProgress<P, PartiallyAuthorized>, V> {
|
||||
self.map_authorization((
|
||||
|proof| proof,
|
||||
|proof| proof,
|
||||
MaybeSigned::SigningMetadata,
|
||||
|auth: InProgress<P, Unsigned>| InProgress {
|
||||
self.map_authorization(
|
||||
&mut rng,
|
||||
|_, proof| proof,
|
||||
|_, proof| proof,
|
||||
|rng, SigningMetadata { dummy_ask, parts }| match dummy_ask {
|
||||
None => MaybeSigned::SigningParts(parts),
|
||||
Some(ask) => {
|
||||
MaybeSigned::Signature(ask.randomize(&parts.alpha).sign(rng, &sighash))
|
||||
}
|
||||
},
|
||||
|rng, auth: InProgress<P, Unsigned>| InProgress {
|
||||
sigs: PartiallyAuthorized {
|
||||
binding_signature: auth.sigs.bsk.sign(&mut rng, &sighash),
|
||||
binding_signature: auth.sigs.bsk.sign(rng, &sighash),
|
||||
sighash,
|
||||
},
|
||||
_proof_state: PhantomData::default(),
|
||||
},
|
||||
))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -883,17 +989,18 @@ impl<P: InProgressProofs, V> Bundle<InProgress<P, PartiallyAuthorized>, V> {
|
|||
pub fn sign<R: RngCore + CryptoRng>(self, mut rng: R, ask: &SpendAuthorizingKey) -> Self {
|
||||
let expected_ak = ask.into();
|
||||
let sighash = self.authorization().sigs.sighash;
|
||||
self.map_authorization((
|
||||
|proof| proof,
|
||||
|proof| proof,
|
||||
|maybe| match maybe {
|
||||
MaybeSigned::SigningMetadata(parts) if parts.ak == expected_ak => {
|
||||
MaybeSigned::Signature(ask.randomize(&parts.alpha).sign(&mut rng, &sighash))
|
||||
self.map_authorization(
|
||||
&mut rng,
|
||||
|_, proof| proof,
|
||||
|_, proof| proof,
|
||||
|rng, maybe| match maybe {
|
||||
MaybeSigned::SigningParts(parts) if parts.ak == expected_ak => {
|
||||
MaybeSigned::Signature(ask.randomize(&parts.alpha).sign(rng, &sighash))
|
||||
}
|
||||
s => s,
|
||||
},
|
||||
|partial| partial,
|
||||
))
|
||||
|_, partial| partial,
|
||||
)
|
||||
}
|
||||
|
||||
/// Appends externally computed [`redjubjub::Signature`]s.
|
||||
|
@ -911,24 +1018,25 @@ impl<P: InProgressProofs, V> Bundle<InProgress<P, PartiallyAuthorized>, V> {
|
|||
fn append_signature(self, signature: &redjubjub::Signature<SpendAuth>) -> Result<Self, Error> {
|
||||
let sighash = self.authorization().sigs.sighash;
|
||||
let mut signature_valid_for = 0usize;
|
||||
let bundle = self.map_authorization((
|
||||
|proof| proof,
|
||||
|proof| proof,
|
||||
|maybe| match maybe {
|
||||
MaybeSigned::SigningMetadata(parts) => {
|
||||
let bundle = self.map_authorization(
|
||||
&mut signature_valid_for,
|
||||
|_, proof| proof,
|
||||
|_, proof| proof,
|
||||
|ctx, maybe| match maybe {
|
||||
MaybeSigned::SigningParts(parts) => {
|
||||
let rk = parts.ak.randomize(&parts.alpha);
|
||||
if rk.verify(&sighash, signature).is_ok() {
|
||||
signature_valid_for += 1;
|
||||
**ctx += 1;
|
||||
MaybeSigned::Signature(*signature)
|
||||
} else {
|
||||
// Signature isn't for this input.
|
||||
MaybeSigned::SigningMetadata(parts)
|
||||
MaybeSigned::SigningParts(parts)
|
||||
}
|
||||
}
|
||||
s => s,
|
||||
},
|
||||
|partial| partial,
|
||||
));
|
||||
|_, partial| partial,
|
||||
);
|
||||
match signature_valid_for {
|
||||
0 => Err(Error::InvalidExternalSignature),
|
||||
1 => Ok(bundle),
|
||||
|
@ -942,16 +1050,17 @@ impl<V> Bundle<InProgress<Proven, PartiallyAuthorized>, V> {
|
|||
///
|
||||
/// Returns an error if any signatures are missing.
|
||||
pub fn finalize(self) -> Result<Bundle<Authorized, V>, Error> {
|
||||
self.try_map_authorization((
|
||||
Ok,
|
||||
Ok,
|
||||
|maybe: MaybeSigned| maybe.finalize(),
|
||||
|partial: InProgress<Proven, PartiallyAuthorized>| {
|
||||
self.try_map_authorization(
|
||||
(),
|
||||
|_, v| Ok(v),
|
||||
|_, v| Ok(v),
|
||||
|_, maybe: MaybeSigned| maybe.finalize(),
|
||||
|_, partial: InProgress<Proven, PartiallyAuthorized>| {
|
||||
Ok(Authorized {
|
||||
binding_sig: partial.sigs.binding_signature,
|
||||
})
|
||||
},
|
||||
))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -970,13 +1079,13 @@ pub mod testing {
|
|||
testing::{arb_node, arb_note},
|
||||
value::testing::arb_positive_note_value,
|
||||
zip32::testing::arb_extended_spending_key,
|
||||
Node, NOTE_COMMITMENT_TREE_DEPTH,
|
||||
Anchor, Node,
|
||||
};
|
||||
use incrementalmerkletree::{
|
||||
frontier::testing::arb_commitment_tree, witness::IncrementalWitness, Hashable, Level,
|
||||
frontier::testing::arb_commitment_tree, witness::IncrementalWitness,
|
||||
};
|
||||
|
||||
use super::{BundleType, SaplingBuilder};
|
||||
use super::{Builder, BundleType};
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn arb_bundle<V: fmt::Debug + From<i64>>(
|
||||
|
@ -1005,17 +1114,11 @@ pub mod testing {
|
|||
let anchor = spendable_notes
|
||||
.first()
|
||||
.zip(commitment_trees.first())
|
||||
.map_or_else(
|
||||
|| Node::empty_root(Level::from(NOTE_COMMITMENT_TREE_DEPTH)),
|
||||
|(note, tree)| {
|
||||
let node = Node::from_cmu(¬e.cmu());
|
||||
Node::from_scalar(*tree.root(node).inner())
|
||||
},
|
||||
);
|
||||
let mut builder = SaplingBuilder::new(
|
||||
zip212_enforcement,
|
||||
BundleType::Transactional { anchor },
|
||||
);
|
||||
.map_or_else(Anchor::empty_tree, |(note, tree)| {
|
||||
let node = Node::from_cmu(¬e.cmu());
|
||||
Anchor::from(*tree.root(node).inner())
|
||||
});
|
||||
let mut builder = Builder::new(zip212_enforcement, BundleType::DEFAULT, anchor);
|
||||
let mut rng = StdRng::from_seed(rng_seed);
|
||||
|
||||
for (note, path) in spendable_notes
|
||||
|
|
145
src/bundle.rs
145
src/bundle.rs
|
@ -1,6 +1,7 @@
|
|||
use core::fmt::Debug;
|
||||
|
||||
use memuse::DynamicUsage;
|
||||
|
||||
use redjubjub::{Binding, SpendAuth};
|
||||
|
||||
use zcash_note_encryption::{
|
||||
|
@ -38,115 +39,6 @@ impl Authorization for Authorized {
|
|||
type AuthSig = redjubjub::Signature<SpendAuth>;
|
||||
}
|
||||
|
||||
/// 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 [`Bundle::map_authorization`] when you want to map the
|
||||
/// authorization of a subset of a transaction's bundles (excluding the Sapling bundle) in
|
||||
/// a higher-level transaction type.
|
||||
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, V> {
|
||||
shielded_spends: Vec<SpendDescription<A>>,
|
||||
|
@ -200,7 +92,14 @@ impl<A: Authorization, V> Bundle<A, V> {
|
|||
}
|
||||
|
||||
/// 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, V> {
|
||||
pub fn map_authorization<R, B: Authorization>(
|
||||
self,
|
||||
mut context: R,
|
||||
spend_proof: impl Fn(&mut R, A::SpendProof) -> B::SpendProof,
|
||||
output_proof: impl Fn(&mut R, A::OutputProof) -> B::OutputProof,
|
||||
auth_sig: impl Fn(&mut R, A::AuthSig) -> B::AuthSig,
|
||||
auth: impl FnOnce(&mut R, A) -> B,
|
||||
) -> Bundle<B, V> {
|
||||
Bundle {
|
||||
shielded_spends: self
|
||||
.shielded_spends
|
||||
|
@ -210,8 +109,8 @@ impl<A: Authorization, V> Bundle<A, V> {
|
|||
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),
|
||||
zkproof: spend_proof(&mut context, d.zkproof),
|
||||
spend_auth_sig: auth_sig(&mut context, d.spend_auth_sig),
|
||||
})
|
||||
.collect(),
|
||||
shielded_outputs: self
|
||||
|
@ -223,19 +122,23 @@ impl<A: Authorization, V> Bundle<A, V> {
|
|||
ephemeral_key: o.ephemeral_key,
|
||||
enc_ciphertext: o.enc_ciphertext,
|
||||
out_ciphertext: o.out_ciphertext,
|
||||
zkproof: f.map_output_proof(o.zkproof),
|
||||
zkproof: output_proof(&mut context, o.zkproof),
|
||||
})
|
||||
.collect(),
|
||||
value_balance: self.value_balance,
|
||||
authorization: f.map_authorization(self.authorization),
|
||||
authorization: auth(&mut context, self.authorization),
|
||||
}
|
||||
}
|
||||
|
||||
/// Transitions this bundle from one authorization state to another.
|
||||
pub fn try_map_authorization<B: Authorization, F: TryMapAuth<A, B>>(
|
||||
pub fn try_map_authorization<R, B: Authorization, Error>(
|
||||
self,
|
||||
mut f: F,
|
||||
) -> Result<Bundle<B, V>, F::Error> {
|
||||
mut context: R,
|
||||
spend_proof: impl Fn(&mut R, A::SpendProof) -> Result<B::SpendProof, Error>,
|
||||
output_proof: impl Fn(&mut R, A::OutputProof) -> Result<B::OutputProof, Error>,
|
||||
auth_sig: impl Fn(&mut R, A::AuthSig) -> Result<B::AuthSig, Error>,
|
||||
auth: impl Fn(&mut R, A) -> Result<B, Error>,
|
||||
) -> Result<Bundle<B, V>, Error> {
|
||||
Ok(Bundle {
|
||||
shielded_spends: self
|
||||
.shielded_spends
|
||||
|
@ -246,8 +149,8 @@ impl<A: Authorization, V> Bundle<A, V> {
|
|||
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)?,
|
||||
zkproof: spend_proof(&mut context, d.zkproof)?,
|
||||
spend_auth_sig: auth_sig(&mut context, d.spend_auth_sig)?,
|
||||
})
|
||||
})
|
||||
.collect::<Result<_, _>>()?,
|
||||
|
@ -261,12 +164,12 @@ impl<A: Authorization, V> Bundle<A, V> {
|
|||
ephemeral_key: o.ephemeral_key,
|
||||
enc_ciphertext: o.enc_ciphertext,
|
||||
out_ciphertext: o.out_ciphertext,
|
||||
zkproof: f.try_map_output_proof(o.zkproof)?,
|
||||
zkproof: output_proof(&mut context, o.zkproof)?,
|
||||
})
|
||||
})
|
||||
.collect::<Result<_, _>>()?,
|
||||
value_balance: self.value_balance,
|
||||
authorization: f.try_map_authorization(self.authorization)?,
|
||||
authorization: auth(&mut context, self.authorization)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,7 +34,8 @@ pub use bundle::Bundle;
|
|||
pub use keys::{Diversifier, NullifierDerivingKey, ProofGenerationKey, SaplingIvk, ViewingKey};
|
||||
pub use note::{nullifier::Nullifier, Note, Rseed};
|
||||
pub use tree::{
|
||||
merkle_hash, CommitmentTree, IncrementalWitness, MerklePath, Node, NOTE_COMMITMENT_TREE_DEPTH,
|
||||
merkle_hash, Anchor, CommitmentTree, IncrementalWitness, MerklePath, Node,
|
||||
NOTE_COMMITMENT_TREE_DEPTH,
|
||||
};
|
||||
pub use verifier::{BatchValidator, SaplingVerificationContext};
|
||||
|
||||
|
|
27
src/note.rs
27
src/note.rs
|
@ -2,6 +2,11 @@ use group::{ff::Field, GroupEncoding};
|
|||
use rand_core::{CryptoRng, RngCore};
|
||||
use zcash_spec::PrfExpand;
|
||||
|
||||
use crate::{
|
||||
keys::{ExpandedSpendingKey, FullViewingKey},
|
||||
zip32::ExtendedSpendingKey,
|
||||
};
|
||||
|
||||
use super::{
|
||||
keys::EphemeralSecretKey, value::NoteValue, Nullifier, NullifierDerivingKey, PaymentAddress,
|
||||
};
|
||||
|
@ -148,6 +153,28 @@ impl Note {
|
|||
))),
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates a dummy spent note.
|
||||
///
|
||||
/// Defined in [Zcash Protocol Spec § 4.8.2: Dummy Notes (Sapling)][saplingdummynotes].
|
||||
///
|
||||
/// [saplingdummynotes]: https://zips.z.cash/protocol/nu5.pdf#saplingdummynotes
|
||||
pub(crate) fn dummy<R: RngCore>(mut rng: R) -> (ExpandedSpendingKey, FullViewingKey, Self) {
|
||||
let mut sk_bytes = [0; 32];
|
||||
rng.fill_bytes(&mut sk_bytes);
|
||||
|
||||
let extsk = ExtendedSpendingKey::master(&sk_bytes[..]);
|
||||
let fvk = extsk.to_diversifiable_full_viewing_key().fvk().clone();
|
||||
let recipient = extsk.default_address();
|
||||
|
||||
let mut rseed_bytes = [0; 32];
|
||||
rng.fill_bytes(&mut rseed_bytes);
|
||||
let rseed = Rseed::AfterZip212(rseed_bytes);
|
||||
|
||||
let note = Note::from_parts(recipient.1, NoteValue::ZERO, rseed);
|
||||
|
||||
(extsk.expsk, fvk, note)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "test-dependencies"))]
|
||||
|
|
37
src/tree.rs
37
src/tree.rs
|
@ -67,6 +67,43 @@ fn merkle_hash_field(depth: usize, lhs: &[u8; 32], rhs: &[u8; 32]) -> jubjub::Ba
|
|||
.get_u()
|
||||
}
|
||||
|
||||
/// The root of a Sapling commitment tree.
|
||||
#[derive(Eq, PartialEq, Clone, Copy, Debug)]
|
||||
pub struct Anchor(jubjub::Base);
|
||||
|
||||
impl From<jubjub::Base> for Anchor {
|
||||
fn from(anchor_field: jubjub::Base) -> Anchor {
|
||||
Anchor(anchor_field)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Node> for Anchor {
|
||||
fn from(anchor: Node) -> Anchor {
|
||||
Anchor(anchor.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Anchor {
|
||||
/// The anchor of the empty Orchard note commitment tree.
|
||||
///
|
||||
/// This anchor does not correspond to any valid anchor for a spend, so it
|
||||
/// may only be used for coinbase bundles or in circumstances where Orchard
|
||||
/// functionality is not active.
|
||||
pub fn empty_tree() -> Anchor {
|
||||
Anchor(Node::empty_root(NOTE_COMMITMENT_TREE_DEPTH.into()).0)
|
||||
}
|
||||
|
||||
/// Parses an Orchard anchor from a byte encoding.
|
||||
pub fn from_bytes(bytes: [u8; 32]) -> CtOption<Anchor> {
|
||||
jubjub::Base::from_repr(bytes).map(Self)
|
||||
}
|
||||
|
||||
/// Returns the byte encoding of this anchor.
|
||||
pub fn to_bytes(self) -> [u8; 32] {
|
||||
self.0.to_repr()
|
||||
}
|
||||
}
|
||||
|
||||
/// A node within the Sapling commitment tree.
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub struct Node(jubjub::Base);
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
//! single [`Bundle`].
|
||||
//! - `valueBalanceSapling`, which is a signed 63-bit integer. This is represented
|
||||
//! by a user-defined type parameter on [`Bundle`], returned by
|
||||
//! [`Bundle::value_balance`] and [`SaplingBuilder::value_balance`].
|
||||
//! [`Bundle::value_balance`] and [`Builder::value_balance`].
|
||||
//!
|
||||
//! If your specific instantiation of the Sapling protocol requires a smaller bound on
|
||||
//! valid note values (for example, Zcash's `MAX_MONEY` fits into a 51-bit integer), you
|
||||
|
@ -17,7 +17,7 @@
|
|||
//! - Define your `valueBalanceSapling` type to enforce your valid value range. This can
|
||||
//! be checked in its `TryFrom<i64>` implementation.
|
||||
//! - Define your own "amount" type for note values, and convert it to `NoteValue` prior
|
||||
//! to calling [`SaplingBuilder::add_output`].
|
||||
//! to calling [`Builder::add_output`].
|
||||
//!
|
||||
//! Inside the circuit, note values are constrained to be unsigned 64-bit integers.
|
||||
//!
|
||||
|
@ -33,8 +33,8 @@
|
|||
//!
|
||||
//! [`Bundle`]: crate::Bundle
|
||||
//! [`Bundle::value_balance`]: crate::Bundle::value_balance
|
||||
//! [`SaplingBuilder::value_balance`]: crate::builder::SaplingBuilder::value_balance
|
||||
//! [`SaplingBuilder::add_output`]: crate::builder::SaplingBuilder::add_output
|
||||
//! [`Builder::value_balance`]: crate::builder::Builder::value_balance
|
||||
//! [`Builder::add_output`]: crate::builder::Builder::add_output
|
||||
//! [Rust documentation]: https://doc.rust-lang.org/stable/std/primitive.i64.html
|
||||
|
||||
use bitvec::{array::BitArray, order::Lsb0};
|
||||
|
|
Loading…
Reference in New Issue