Merge pull request #1047 from zcash/1044-sapling-bundle-generic-valuebalance
zcash_primitives: Make `value_balance` generic in `sapling::Bundle`
This commit is contained in:
commit
1607d013d1
|
@ -96,6 +96,8 @@ and this library adheres to Rust's notion of
|
|||
- `SaplingVerificationContext::{check_spend, check_output}` now take
|
||||
the `PreparedSpendVerifyingKey` and `PreparedOutputVerifyingKey`
|
||||
newtypes.
|
||||
- `SaplingVerificationContext::final_check` now takes its `value_balance`
|
||||
argument as `V: Into<i64>` instead of `Amount`.
|
||||
- `address::PaymentAddress::create_note` now takes its `value` argument as a
|
||||
`NoteValue` instead of as a bare `u64`.
|
||||
- `builder::SaplingBuilder` no longer has a `P: consensus::Parameters` type
|
||||
|
@ -112,6 +114,9 @@ and this library adheres to Rust's notion of
|
|||
- `Error::DuplicateSignature`
|
||||
- `Error::InvalidExternalSignature`
|
||||
- `Error::MissingSignatures`
|
||||
- `bundle::Bundle` now has a second generic parameter `V`.
|
||||
- `bundle::Bundle::value_balance` now returns `&V` instead of `&Amount`.
|
||||
- `bundle::testing::arb_bundle` now takes a `value_balance: V` argument.
|
||||
- `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`.
|
||||
|
|
|
@ -17,6 +17,7 @@ use zcash_primitives::{
|
|||
value::NoteValue,
|
||||
Diversifier, SaplingIvk,
|
||||
},
|
||||
transaction::components::Amount,
|
||||
};
|
||||
|
||||
#[cfg(unix)]
|
||||
|
@ -46,7 +47,7 @@ fn bench_note_decryption(c: &mut Criterion) {
|
|||
)
|
||||
.unwrap();
|
||||
let (bundle, _) = builder
|
||||
.build::<MockSpendProver, MockOutputProver, _>(&mut rng)
|
||||
.build::<MockSpendProver, MockOutputProver, _, Amount>(&mut rng)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
bundle.shielded_outputs()[0].clone()
|
||||
|
|
|
@ -30,10 +30,7 @@ use crate::{
|
|||
},
|
||||
transaction::{
|
||||
builder::Progress,
|
||||
components::{
|
||||
amount::{Amount, NonNegativeAmount},
|
||||
sapling::fees,
|
||||
},
|
||||
components::{amount::NonNegativeAmount, sapling::fees},
|
||||
},
|
||||
zip32::ExtendedSpendingKey,
|
||||
};
|
||||
|
@ -343,16 +340,16 @@ impl SaplingBuilder {
|
|||
/// Returns the net value represented by the spends and outputs added to this builder,
|
||||
/// or an error if the values added to this builder overflow the range of a Zcash
|
||||
/// monetary amount.
|
||||
fn try_value_balance(&self) -> Result<Amount, Error> {
|
||||
fn try_value_balance<V: TryFrom<i64>>(&self) -> Result<V, Error> {
|
||||
self.value_balance
|
||||
.try_into()
|
||||
.map_err(|_| ())
|
||||
.and_then(Amount::from_i64)
|
||||
.and_then(|vb| V::try_from(vb).map_err(|_| ()))
|
||||
.map_err(|()| Error::InvalidAmount)
|
||||
}
|
||||
|
||||
/// Returns the net value represented by the spends and outputs added to this builder.
|
||||
pub fn value_balance(&self) -> Amount {
|
||||
pub fn value_balance<V: TryFrom<i64>>(&self) -> V {
|
||||
self.try_value_balance()
|
||||
.expect("we check this when mutating self.value_balance")
|
||||
}
|
||||
|
@ -381,7 +378,7 @@ impl SaplingBuilder {
|
|||
}
|
||||
|
||||
self.value_balance = (self.value_balance + note.value()).ok_or(Error::InvalidAmount)?;
|
||||
self.try_value_balance()?;
|
||||
self.try_value_balance::<i64>()?;
|
||||
|
||||
let spend =
|
||||
SpendDescriptionInfo::new_internal(&mut rng, extsk, diversifier, note, merkle_path);
|
||||
|
@ -411,17 +408,17 @@ impl SaplingBuilder {
|
|||
);
|
||||
|
||||
self.value_balance = (self.value_balance - value).ok_or(Error::InvalidAddress)?;
|
||||
self.try_value_balance()?;
|
||||
self.try_value_balance::<i64>()?;
|
||||
|
||||
self.outputs.push(output);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn build<SP: SpendProver, OP: OutputProver, R: RngCore>(
|
||||
pub fn build<SP: SpendProver, OP: OutputProver, R: RngCore, V: TryFrom<i64>>(
|
||||
self,
|
||||
mut rng: R,
|
||||
) -> Result<Option<(UnauthorizedBundle, SaplingMetadata)>, Error> {
|
||||
) -> Result<Option<(UnauthorizedBundle<V>, SaplingMetadata)>, Error> {
|
||||
let value_balance = self.try_value_balance()?;
|
||||
|
||||
// Record initial positions of spends and outputs
|
||||
|
@ -536,7 +533,7 @@ impl SaplingBuilder {
|
|||
/// Type alias for an in-progress bundle that has no proofs or signatures.
|
||||
///
|
||||
/// This is returned by [`SaplingBuilder::build`].
|
||||
pub type UnauthorizedBundle = Bundle<InProgress<Unproven, Unsigned>>;
|
||||
pub type UnauthorizedBundle<V> = Bundle<InProgress<Unproven, Unsigned>, V>;
|
||||
|
||||
/// Marker trait representing bundle proofs in the process of being created.
|
||||
pub trait InProgressProofs: fmt::Debug {
|
||||
|
@ -654,7 +651,7 @@ impl<'a, S: InProgressSignatures, SP: SpendProver, OP: OutputProver, R: RngCore>
|
|||
}
|
||||
}
|
||||
|
||||
impl<S: InProgressSignatures> Bundle<InProgress<Unproven, S>> {
|
||||
impl<S: InProgressSignatures, V> Bundle<InProgress<Unproven, S>, V> {
|
||||
/// Creates the proofs for this bundle.
|
||||
pub fn create_proofs<SP: SpendProver, OP: OutputProver>(
|
||||
self,
|
||||
|
@ -662,7 +659,7 @@ impl<S: InProgressSignatures> Bundle<InProgress<Unproven, S>> {
|
|||
output_prover: &OP,
|
||||
rng: impl RngCore,
|
||||
progress_notifier: Option<&Sender<Progress>>,
|
||||
) -> Bundle<InProgress<Proven, S>> {
|
||||
) -> 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(
|
||||
|
@ -737,7 +734,7 @@ impl MaybeSigned {
|
|||
}
|
||||
}
|
||||
|
||||
impl<P: InProgressProofs> Bundle<InProgress<P, Unsigned>> {
|
||||
impl<P: InProgressProofs, V> Bundle<InProgress<P, Unsigned>, V> {
|
||||
/// Loads the sighash into this bundle, preparing it for signing.
|
||||
///
|
||||
/// This API ensures that all signatures are created over the same sighash.
|
||||
|
@ -745,7 +742,7 @@ impl<P: InProgressProofs> Bundle<InProgress<P, Unsigned>> {
|
|||
self,
|
||||
mut rng: R,
|
||||
sighash: [u8; 32],
|
||||
) -> Bundle<InProgress<P, PartiallyAuthorized>> {
|
||||
) -> Bundle<InProgress<P, PartiallyAuthorized>, V> {
|
||||
self.map_authorization((
|
||||
|proof| proof,
|
||||
|proof| proof,
|
||||
|
@ -765,7 +762,7 @@ impl<P: InProgressProofs> Bundle<InProgress<P, Unsigned>> {
|
|||
}
|
||||
}
|
||||
|
||||
impl Bundle<InProgress<Proven, Unsigned>> {
|
||||
impl<V> Bundle<InProgress<Proven, Unsigned>, V> {
|
||||
/// Applies signatures to this bundle, in order to authorize it.
|
||||
///
|
||||
/// This is a helper method that wraps [`Bundle::prepare`], [`Bundle::sign`], and
|
||||
|
@ -775,7 +772,7 @@ impl Bundle<InProgress<Proven, Unsigned>> {
|
|||
mut rng: R,
|
||||
sighash: [u8; 32],
|
||||
signing_keys: &[PrivateKey],
|
||||
) -> Result<Bundle<Authorized>, Error> {
|
||||
) -> Result<Bundle<Authorized, V>, Error> {
|
||||
signing_keys
|
||||
.iter()
|
||||
.fold(self.prepare(&mut rng, sighash), |partial, ask| {
|
||||
|
@ -785,7 +782,7 @@ impl Bundle<InProgress<Proven, Unsigned>> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<P: InProgressProofs> Bundle<InProgress<P, PartiallyAuthorized>> {
|
||||
impl<P: InProgressProofs, V> Bundle<InProgress<P, PartiallyAuthorized>, V> {
|
||||
/// Signs this bundle with the given [`PrivateKey`].
|
||||
///
|
||||
/// This will apply signatures for all notes controlled by this spending key.
|
||||
|
@ -842,11 +839,11 @@ impl<P: InProgressProofs> Bundle<InProgress<P, PartiallyAuthorized>> {
|
|||
}
|
||||
}
|
||||
|
||||
impl Bundle<InProgress<Proven, PartiallyAuthorized>> {
|
||||
impl<V> Bundle<InProgress<Proven, PartiallyAuthorized>, V> {
|
||||
/// Finalizes this bundle, enabling it to be included in a transaction.
|
||||
///
|
||||
/// Returns an error if any signatures are missing.
|
||||
pub fn finalize(self) -> Result<Bundle<Authorized>, Error> {
|
||||
pub fn finalize(self) -> Result<Bundle<Authorized, V>, Error> {
|
||||
self.try_map_authorization((
|
||||
Ok,
|
||||
Ok,
|
||||
|
@ -862,6 +859,8 @@ impl Bundle<InProgress<Proven, PartiallyAuthorized>> {
|
|||
|
||||
#[cfg(any(test, feature = "test-dependencies"))]
|
||||
pub mod testing {
|
||||
use std::fmt;
|
||||
|
||||
use proptest::collection::vec;
|
||||
use proptest::prelude::*;
|
||||
use rand::{rngs::StdRng, SeedableRng};
|
||||
|
@ -876,7 +875,6 @@ pub mod testing {
|
|||
value::testing::arb_positive_note_value,
|
||||
Diversifier,
|
||||
},
|
||||
transaction::components::amount::MAX_MONEY,
|
||||
zip32::sapling::testing::arb_extended_spending_key,
|
||||
};
|
||||
use incrementalmerkletree::{
|
||||
|
@ -885,53 +883,70 @@ pub mod testing {
|
|||
|
||||
use super::SaplingBuilder;
|
||||
|
||||
prop_compose! {
|
||||
fn arb_bundle(zip212_enforcement: Zip212Enforcement)(n_notes in 1..30usize)(
|
||||
extsk in arb_extended_spending_key(),
|
||||
spendable_notes in vec(
|
||||
arb_positive_note_value(MAX_MONEY as u64 / 10000).prop_flat_map(arb_note),
|
||||
n_notes
|
||||
),
|
||||
commitment_trees in vec(
|
||||
arb_commitment_tree::<_, _, 32>(n_notes, arb_node()).prop_map(
|
||||
|t| IncrementalWitness::from_tree(t).path().unwrap()
|
||||
),
|
||||
n_notes
|
||||
),
|
||||
diversifiers in vec(prop::array::uniform11(any::<u8>()).prop_map(Diversifier), n_notes),
|
||||
rng_seed in prop::array::uniform32(any::<u8>()),
|
||||
fake_sighash_bytes in prop::array::uniform32(any::<u8>()),
|
||||
) -> Bundle<Authorized> {
|
||||
let mut builder = SaplingBuilder::new(zip212_enforcement);
|
||||
let mut rng = StdRng::from_seed(rng_seed);
|
||||
#[allow(dead_code)]
|
||||
fn arb_bundle<V: fmt::Debug + From<i64>>(
|
||||
max_money: u64,
|
||||
zip212_enforcement: Zip212Enforcement,
|
||||
) -> impl Strategy<Value = Bundle<Authorized, V>> {
|
||||
(1..30usize)
|
||||
.prop_flat_map(move |n_notes| {
|
||||
(
|
||||
arb_extended_spending_key(),
|
||||
vec(
|
||||
arb_positive_note_value(max_money / 10000).prop_flat_map(arb_note),
|
||||
n_notes,
|
||||
),
|
||||
vec(
|
||||
arb_commitment_tree::<_, _, 32>(n_notes, arb_node())
|
||||
.prop_map(|t| IncrementalWitness::from_tree(t).path().unwrap()),
|
||||
n_notes,
|
||||
),
|
||||
vec(
|
||||
prop::array::uniform11(any::<u8>()).prop_map(Diversifier),
|
||||
n_notes,
|
||||
),
|
||||
prop::array::uniform32(any::<u8>()),
|
||||
prop::array::uniform32(any::<u8>()),
|
||||
)
|
||||
})
|
||||
.prop_map(
|
||||
move |(
|
||||
extsk,
|
||||
spendable_notes,
|
||||
commitment_trees,
|
||||
diversifiers,
|
||||
rng_seed,
|
||||
fake_sighash_bytes,
|
||||
)| {
|
||||
let mut builder = SaplingBuilder::new(zip212_enforcement);
|
||||
let mut rng = StdRng::from_seed(rng_seed);
|
||||
|
||||
for ((note, path), diversifier) in spendable_notes.into_iter().zip(commitment_trees.into_iter()).zip(diversifiers.into_iter()) {
|
||||
builder.add_spend(
|
||||
&mut rng,
|
||||
&extsk,
|
||||
diversifier,
|
||||
note,
|
||||
path
|
||||
).unwrap();
|
||||
}
|
||||
for ((note, path), diversifier) in spendable_notes
|
||||
.into_iter()
|
||||
.zip(commitment_trees.into_iter())
|
||||
.zip(diversifiers.into_iter())
|
||||
{
|
||||
builder
|
||||
.add_spend(&mut rng, &extsk, diversifier, note, path)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
let (bundle, _) = builder
|
||||
.build::<MockSpendProver, MockOutputProver, _>(&mut rng)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
let (bundle, _) = builder
|
||||
.build::<MockSpendProver, MockOutputProver, _, _>(&mut rng)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
let bundle = bundle.create_proofs(
|
||||
&MockSpendProver,
|
||||
&MockOutputProver,
|
||||
&mut rng,
|
||||
None,
|
||||
);
|
||||
let bundle =
|
||||
bundle.create_proofs(&MockSpendProver, &MockOutputProver, &mut rng, None);
|
||||
|
||||
bundle.apply_signatures(
|
||||
&mut rng,
|
||||
fake_sighash_bytes,
|
||||
&[PrivateKey(extsk.expsk.ask)],
|
||||
).unwrap()
|
||||
}
|
||||
bundle
|
||||
.apply_signatures(
|
||||
&mut rng,
|
||||
fake_sighash_bytes,
|
||||
&[PrivateKey(extsk.expsk.ask)],
|
||||
)
|
||||
.unwrap()
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ use crate::{
|
|||
value::ValueCommitment,
|
||||
Nullifier,
|
||||
},
|
||||
transaction::components::{Amount, GROTH_PROOF_SIZE},
|
||||
transaction::components::GROTH_PROOF_SIZE,
|
||||
};
|
||||
|
||||
pub type GrothProofBytes = [u8; GROTH_PROOF_SIZE];
|
||||
|
@ -151,20 +151,20 @@ where
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Bundle<A: Authorization> {
|
||||
pub struct Bundle<A: Authorization, V> {
|
||||
shielded_spends: Vec<SpendDescription<A>>,
|
||||
shielded_outputs: Vec<OutputDescription<A::OutputProof>>,
|
||||
value_balance: Amount,
|
||||
value_balance: V,
|
||||
authorization: A,
|
||||
}
|
||||
|
||||
impl<A: Authorization> Bundle<A> {
|
||||
impl<A: Authorization, V> Bundle<A, V> {
|
||||
/// 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,
|
||||
value_balance: V,
|
||||
authorization: A,
|
||||
) -> Self {
|
||||
Self::from_parts(
|
||||
|
@ -179,7 +179,7 @@ impl<A: Authorization> Bundle<A> {
|
|||
pub(crate) fn from_parts(
|
||||
shielded_spends: Vec<SpendDescription<A>>,
|
||||
shielded_outputs: Vec<OutputDescription<A::OutputProof>>,
|
||||
value_balance: Amount,
|
||||
value_balance: V,
|
||||
authorization: A,
|
||||
) -> Self {
|
||||
Bundle {
|
||||
|
@ -203,7 +203,7 @@ impl<A: Authorization> Bundle<A> {
|
|||
/// 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 {
|
||||
pub fn value_balance(&self) -> &V {
|
||||
&self.value_balance
|
||||
}
|
||||
|
||||
|
@ -215,7 +215,7 @@ impl<A: Authorization> Bundle<A> {
|
|||
}
|
||||
|
||||
/// 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> {
|
||||
pub fn map_authorization<B: Authorization, F: MapAuth<A, B>>(self, mut f: F) -> Bundle<B, V> {
|
||||
Bundle {
|
||||
shielded_spends: self
|
||||
.shielded_spends
|
||||
|
@ -250,7 +250,7 @@ impl<A: Authorization> Bundle<A> {
|
|||
pub fn try_map_authorization<B: Authorization, F: TryMapAuth<A, B>>(
|
||||
self,
|
||||
mut f: F,
|
||||
) -> Result<Bundle<B>, F::Error> {
|
||||
) -> Result<Bundle<B, V>, F::Error> {
|
||||
Ok(Bundle {
|
||||
shielded_spends: self
|
||||
.shielded_spends
|
||||
|
@ -286,20 +286,28 @@ impl<A: Authorization> Bundle<A> {
|
|||
}
|
||||
}
|
||||
|
||||
impl DynamicUsage for Bundle<Authorized> {
|
||||
impl<V: DynamicUsage> DynamicUsage for Bundle<Authorized, V> {
|
||||
fn dynamic_usage(&self) -> usize {
|
||||
self.shielded_spends.dynamic_usage() + self.shielded_outputs.dynamic_usage()
|
||||
self.shielded_spends.dynamic_usage()
|
||||
+ self.shielded_outputs.dynamic_usage()
|
||||
+ self.value_balance.dynamic_usage()
|
||||
}
|
||||
|
||||
fn dynamic_usage_bounds(&self) -> (usize, Option<usize>) {
|
||||
let bounds = (
|
||||
self.shielded_spends.dynamic_usage_bounds(),
|
||||
self.shielded_outputs.dynamic_usage_bounds(),
|
||||
self.value_balance.dynamic_usage_bounds(),
|
||||
);
|
||||
|
||||
(
|
||||
bounds.0 .0 + bounds.1 .0,
|
||||
bounds.0 .1.zip(bounds.1 .1).map(|(a, b)| a + b),
|
||||
bounds.0 .0 + bounds.1 .0 + bounds.2 .0,
|
||||
bounds
|
||||
.0
|
||||
.1
|
||||
.zip(bounds.1 .1)
|
||||
.zip(bounds.2 .1)
|
||||
.map(|((a, b), c)| a + b + c),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -611,6 +619,8 @@ impl<A> From<OutputDescription<A>> for CompactOutputDescription {
|
|||
|
||||
#[cfg(any(test, feature = "test-dependencies"))]
|
||||
pub mod testing {
|
||||
use std::fmt;
|
||||
|
||||
use ff::Field;
|
||||
use group::{Group, GroupEncoding};
|
||||
use proptest::collection::vec;
|
||||
|
@ -628,7 +638,7 @@ pub mod testing {
|
|||
},
|
||||
Nullifier,
|
||||
},
|
||||
transaction::components::{amount::testing::arb_amount, GROTH_PROOF_SIZE},
|
||||
transaction::components::GROTH_PROOF_SIZE,
|
||||
};
|
||||
|
||||
use super::{
|
||||
|
@ -705,32 +715,40 @@ pub mod testing {
|
|||
}
|
||||
}
|
||||
|
||||
prop_compose! {
|
||||
pub fn arb_bundle()(
|
||||
n_spends in 0usize..30,
|
||||
n_outputs in 0usize..30,
|
||||
)(
|
||||
shielded_spends in vec(arb_spend_description(n_spends), n_spends),
|
||||
shielded_outputs in vec(arb_output_description(n_outputs), n_outputs),
|
||||
value_balance in arb_amount(),
|
||||
rng_seed in prop::array::uniform32(prop::num::u8::ANY),
|
||||
fake_bvk_bytes in prop::array::uniform32(prop::num::u8::ANY),
|
||||
) -> Option<Bundle<Authorized>> {
|
||||
if shielded_spends.is_empty() && shielded_outputs.is_empty() {
|
||||
None
|
||||
} else {
|
||||
let mut rng = StdRng::from_seed(rng_seed);
|
||||
let bsk = PrivateKey(jubjub::Fr::random(&mut rng));
|
||||
|
||||
Some(
|
||||
Bundle {
|
||||
shielded_spends,
|
||||
shielded_outputs,
|
||||
value_balance,
|
||||
authorization: Authorized { binding_sig: bsk.sign(&fake_bvk_bytes, &mut rng, VALUE_COMMITMENT_RANDOMNESS_GENERATOR) },
|
||||
}
|
||||
pub fn arb_bundle<V: Copy + fmt::Debug + 'static>(
|
||||
value_balance: V,
|
||||
) -> impl Strategy<Value = Option<Bundle<Authorized, V>>> {
|
||||
(0usize..30, 0usize..30)
|
||||
.prop_flat_map(|(n_spends, n_outputs)| {
|
||||
(
|
||||
vec(arb_spend_description(n_spends), n_spends),
|
||||
vec(arb_output_description(n_outputs), n_outputs),
|
||||
prop::array::uniform32(prop::num::u8::ANY),
|
||||
prop::array::uniform32(prop::num::u8::ANY),
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
.prop_map(
|
||||
move |(shielded_spends, shielded_outputs, rng_seed, fake_bvk_bytes)| {
|
||||
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,
|
||||
),
|
||||
},
|
||||
})
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,10 +23,9 @@ use crate::{
|
|||
DiversifiedTransmissionKey, EphemeralPublicKey, EphemeralSecretKey, OutgoingViewingKey,
|
||||
SharedSecret,
|
||||
},
|
||||
value::ValueCommitment,
|
||||
value::{NoteValue, ValueCommitment},
|
||||
Diversifier, Note, PaymentAddress, Rseed,
|
||||
},
|
||||
transaction::components::amount::NonNegativeAmount,
|
||||
};
|
||||
|
||||
use super::note::ExtractedNoteCommitment;
|
||||
|
@ -93,12 +92,11 @@ where
|
|||
.try_into()
|
||||
.expect("Note plaintext is checked to have length >= COMPACT_NOTE_SIZE."),
|
||||
);
|
||||
let value = NonNegativeAmount::from_u64_le_bytes(
|
||||
let value = NoteValue::from_bytes(
|
||||
plaintext[12..20]
|
||||
.try_into()
|
||||
.expect("Note plaintext is checked to have length >= COMPACT_NOTE_SIZE."),
|
||||
)
|
||||
.ok()?;
|
||||
);
|
||||
let r: [u8; 32] = plaintext[20..COMPACT_NOTE_SIZE]
|
||||
.try_into()
|
||||
.expect("Note plaintext is checked to have length >= COMPACT_NOTE_SIZE.");
|
||||
|
@ -114,7 +112,7 @@ where
|
|||
|
||||
// `diversifier` was checked by `get_pk_d`.
|
||||
let to = PaymentAddress::from_parts_unchecked(diversifier, pk_d)?;
|
||||
let note = to.create_note(value.into(), rseed);
|
||||
let note = to.create_note(value, rseed);
|
||||
Some((note, to))
|
||||
}
|
||||
|
||||
|
|
|
@ -69,6 +69,10 @@ impl NoteValue {
|
|||
NoteValue(value)
|
||||
}
|
||||
|
||||
pub(crate) fn from_bytes(bytes: [u8; 8]) -> Self {
|
||||
NoteValue(u64::from_le_bytes(bytes))
|
||||
}
|
||||
|
||||
pub(crate) fn to_le_bits(self) -> BitArray<[u8; 8], Lsb0> {
|
||||
BitArray::<_, Lsb0>::new(self.0.to_le_bytes())
|
||||
}
|
||||
|
|
|
@ -2,13 +2,10 @@ use bellman::{gadgets::multipack, groth16::Proof};
|
|||
use bls12_381::Bls12;
|
||||
use group::{ff::PrimeField, Curve, GroupEncoding};
|
||||
|
||||
use crate::{
|
||||
sapling::{
|
||||
note::ExtractedNoteCommitment,
|
||||
redjubjub::{PublicKey, Signature},
|
||||
value::{CommitmentSum, ValueCommitment},
|
||||
},
|
||||
transaction::components::Amount,
|
||||
use crate::sapling::{
|
||||
note::ExtractedNoteCommitment,
|
||||
redjubjub::{PublicKey, Signature},
|
||||
value::{CommitmentSum, ValueCommitment},
|
||||
};
|
||||
|
||||
mod single;
|
||||
|
@ -145,9 +142,9 @@ impl SaplingVerificationContextInner {
|
|||
/// Perform consensus checks on the valueBalance and bindingSig parts of a
|
||||
/// Sapling transaction. All SpendDescriptions and OutputDescriptions must
|
||||
/// have been checked before calling this function.
|
||||
fn final_check(
|
||||
fn final_check<V: Into<i64>>(
|
||||
&self,
|
||||
value_balance: Amount,
|
||||
value_balance: V,
|
||||
sighash_value: &[u8; 32],
|
||||
binding_sig: Signature,
|
||||
binding_sig_verifier: impl FnOnce(PublicKey, [u8; 64], Signature) -> bool,
|
||||
|
|
|
@ -45,7 +45,11 @@ impl BatchValidator {
|
|||
/// `BatchValidator` can continue to be used regardless, but some or all of the proofs
|
||||
/// and signatures from this bundle may have already been added to the batch even if
|
||||
/// it fails other consensus rules.
|
||||
pub fn check_bundle(&mut self, bundle: Bundle<Authorized>, sighash: [u8; 32]) -> bool {
|
||||
pub fn check_bundle<V: Copy + Into<i64>>(
|
||||
&mut self,
|
||||
bundle: Bundle<Authorized, V>,
|
||||
sighash: [u8; 32],
|
||||
) -> bool {
|
||||
self.bundles_added = true;
|
||||
|
||||
let mut ctx = SaplingVerificationContextInner::new();
|
||||
|
|
|
@ -2,15 +2,12 @@ use bellman::groth16::{verify_proof, Proof};
|
|||
use bls12_381::Bls12;
|
||||
|
||||
use super::SaplingVerificationContextInner;
|
||||
use crate::{
|
||||
sapling::{
|
||||
circuit::{PreparedOutputVerifyingKey, PreparedSpendVerifyingKey},
|
||||
constants::{SPENDING_KEY_GENERATOR, VALUE_COMMITMENT_RANDOMNESS_GENERATOR},
|
||||
note::ExtractedNoteCommitment,
|
||||
redjubjub::{PublicKey, Signature},
|
||||
value::ValueCommitment,
|
||||
},
|
||||
transaction::components::Amount,
|
||||
use crate::sapling::{
|
||||
circuit::{PreparedOutputVerifyingKey, PreparedSpendVerifyingKey},
|
||||
constants::{SPENDING_KEY_GENERATOR, VALUE_COMMITMENT_RANDOMNESS_GENERATOR},
|
||||
note::ExtractedNoteCommitment,
|
||||
redjubjub::{PublicKey, Signature},
|
||||
value::ValueCommitment,
|
||||
};
|
||||
|
||||
/// A context object for verifying the Sapling components of a single Zcash transaction.
|
||||
|
@ -80,9 +77,9 @@ impl SaplingVerificationContext {
|
|||
/// Perform consensus checks on the valueBalance and bindingSig parts of a
|
||||
/// Sapling transaction. All SpendDescriptions and OutputDescriptions must
|
||||
/// have been checked before calling this function.
|
||||
pub fn final_check(
|
||||
pub fn final_check<V: Into<i64>>(
|
||||
&self,
|
||||
value_balance: Amount,
|
||||
value_balance: V,
|
||||
sighash_value: &[u8; 32],
|
||||
binding_sig: Signature,
|
||||
) -> bool {
|
||||
|
|
|
@ -513,7 +513,7 @@ impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng> Builder<'a, P, R> {
|
|||
let mut rng = self.rng;
|
||||
let (sapling_bundle, tx_metadata) = match self
|
||||
.sapling_builder
|
||||
.build::<SP, OP, _>(&mut rng)
|
||||
.build::<SP, OP, _, _>(&mut rng)
|
||||
.map_err(Error::SaplingBuild)?
|
||||
.map(|(bundle, tx_metadata)| {
|
||||
// We need to create proofs before signatures, because we still support
|
||||
|
|
|
@ -286,7 +286,7 @@ pub(crate) fn read_v4_components<R: Read>(
|
|||
#[cfg(feature = "temporary-zcashd")]
|
||||
pub fn temporary_zcashd_write_v4_components<W: Write>(
|
||||
writer: W,
|
||||
bundle: Option<&Bundle<Authorized>>,
|
||||
bundle: Option<&Bundle<Authorized, Amount>>,
|
||||
tx_has_sapling: bool,
|
||||
) -> io::Result<()> {
|
||||
write_v4_components(writer, bundle, tx_has_sapling)
|
||||
|
@ -295,7 +295,7 @@ pub fn temporary_zcashd_write_v4_components<W: Write>(
|
|||
/// Writes the Sapling components of a v4 transaction.
|
||||
pub(crate) fn write_v4_components<W: Write>(
|
||||
mut writer: W,
|
||||
bundle: Option<&Bundle<Authorized>>,
|
||||
bundle: Option<&Bundle<Authorized, Amount>>,
|
||||
tx_has_sapling: bool,
|
||||
) -> io::Result<()> {
|
||||
if tx_has_sapling {
|
||||
|
@ -326,7 +326,9 @@ pub(crate) fn write_v4_components<W: Write>(
|
|||
|
||||
/// Reads a [`Bundle`] from a v5 transaction format.
|
||||
#[allow(clippy::redundant_closure)]
|
||||
pub(crate) fn read_v5_bundle<R: Read>(mut reader: R) -> io::Result<Option<Bundle<Authorized>>> {
|
||||
pub(crate) fn read_v5_bundle<R: Read>(
|
||||
mut reader: R,
|
||||
) -> io::Result<Option<Bundle<Authorized, Amount>>> {
|
||||
let sd_v5s = Vector::read(&mut reader, read_spend_v5)?;
|
||||
let od_v5s = Vector::read(&mut reader, read_output_v5)?;
|
||||
let n_spends = sd_v5s.len();
|
||||
|
@ -385,7 +387,7 @@ pub(crate) fn read_v5_bundle<R: Read>(mut reader: R) -> io::Result<Option<Bundle
|
|||
/// Writes a [`Bundle`] in the v5 transaction format.
|
||||
pub(crate) fn write_v5_bundle<W: Write>(
|
||||
mut writer: W,
|
||||
sapling_bundle: Option<&Bundle<Authorized>>,
|
||||
sapling_bundle: Option<&Bundle<Authorized, Amount>>,
|
||||
) -> io::Result<()> {
|
||||
if let Some(bundle) = sapling_bundle {
|
||||
Vector::write(&mut writer, bundle.shielded_spends(), |w, e| {
|
||||
|
@ -436,13 +438,26 @@ pub mod testing {
|
|||
use proptest::prelude::*;
|
||||
|
||||
use crate::{
|
||||
sapling::bundle::{testing::arb_bundle, Authorized, Bundle},
|
||||
transaction::TxVersion,
|
||||
sapling::bundle::{testing as t_sap, Authorized, Bundle},
|
||||
transaction::{
|
||||
components::{amount::testing::arb_amount, Amount},
|
||||
TxVersion,
|
||||
},
|
||||
};
|
||||
|
||||
prop_compose! {
|
||||
fn arb_bundle()(
|
||||
value_balance in arb_amount()
|
||||
)(
|
||||
bundle in t_sap::arb_bundle(value_balance)
|
||||
) -> Option<Bundle<Authorized, Amount>> {
|
||||
bundle
|
||||
}
|
||||
}
|
||||
|
||||
pub fn arb_bundle_for_version(
|
||||
v: TxVersion,
|
||||
) -> impl Strategy<Value = Option<Bundle<Authorized>>> {
|
||||
) -> impl Strategy<Value = Option<Bundle<Authorized, Amount>>> {
|
||||
if v.has_sapling() {
|
||||
Strategy::boxed(arb_bundle())
|
||||
} else {
|
||||
|
|
|
@ -309,7 +309,7 @@ pub struct TransactionData<A: Authorization> {
|
|||
expiry_height: BlockHeight,
|
||||
transparent_bundle: Option<transparent::Bundle<A::TransparentAuth>>,
|
||||
sprout_bundle: Option<sprout::Bundle>,
|
||||
sapling_bundle: Option<sapling::Bundle<A::SaplingAuth>>,
|
||||
sapling_bundle: Option<sapling::Bundle<A::SaplingAuth, Amount>>,
|
||||
orchard_bundle: Option<orchard::bundle::Bundle<A::OrchardAuth, Amount>>,
|
||||
#[cfg(feature = "zfuture")]
|
||||
tze_bundle: Option<tze::Bundle<A::TzeAuth>>,
|
||||
|
@ -324,7 +324,7 @@ impl<A: Authorization> TransactionData<A> {
|
|||
expiry_height: BlockHeight,
|
||||
transparent_bundle: Option<transparent::Bundle<A::TransparentAuth>>,
|
||||
sprout_bundle: Option<sprout::Bundle>,
|
||||
sapling_bundle: Option<sapling::Bundle<A::SaplingAuth>>,
|
||||
sapling_bundle: Option<sapling::Bundle<A::SaplingAuth, Amount>>,
|
||||
orchard_bundle: Option<orchard::Bundle<A::OrchardAuth, Amount>>,
|
||||
) -> Self {
|
||||
TransactionData {
|
||||
|
@ -350,7 +350,7 @@ impl<A: Authorization> TransactionData<A> {
|
|||
expiry_height: BlockHeight,
|
||||
transparent_bundle: Option<transparent::Bundle<A::TransparentAuth>>,
|
||||
sprout_bundle: Option<sprout::Bundle>,
|
||||
sapling_bundle: Option<sapling::Bundle<A::SaplingAuth>>,
|
||||
sapling_bundle: Option<sapling::Bundle<A::SaplingAuth, Amount>>,
|
||||
orchard_bundle: Option<orchard::Bundle<A::OrchardAuth, Amount>>,
|
||||
tze_bundle: Option<tze::Bundle<A::TzeAuth>>,
|
||||
) -> Self {
|
||||
|
@ -391,7 +391,7 @@ impl<A: Authorization> TransactionData<A> {
|
|||
self.sprout_bundle.as_ref()
|
||||
}
|
||||
|
||||
pub fn sapling_bundle(&self) -> Option<&sapling::Bundle<A::SaplingAuth>> {
|
||||
pub fn sapling_bundle(&self) -> Option<&sapling::Bundle<A::SaplingAuth, Amount>> {
|
||||
self.sapling_bundle.as_ref()
|
||||
}
|
||||
|
||||
|
@ -460,8 +460,8 @@ impl<A: Authorization> TransactionData<A> {
|
|||
Option<transparent::Bundle<A::TransparentAuth>>,
|
||||
) -> Option<transparent::Bundle<B::TransparentAuth>>,
|
||||
f_sapling: impl FnOnce(
|
||||
Option<sapling::Bundle<A::SaplingAuth>>,
|
||||
) -> Option<sapling::Bundle<B::SaplingAuth>>,
|
||||
Option<sapling::Bundle<A::SaplingAuth, Amount>>,
|
||||
) -> Option<sapling::Bundle<B::SaplingAuth, Amount>>,
|
||||
f_orchard: impl FnOnce(
|
||||
Option<orchard::bundle::Bundle<A::OrchardAuth, Amount>>,
|
||||
) -> Option<orchard::bundle::Bundle<B::OrchardAuth, Amount>>,
|
||||
|
@ -727,7 +727,7 @@ impl Transaction {
|
|||
#[cfg(feature = "temporary-zcashd")]
|
||||
pub fn temporary_zcashd_read_v5_sapling<R: Read>(
|
||||
reader: R,
|
||||
) -> io::Result<Option<sapling::Bundle<sapling::bundle::Authorized>>> {
|
||||
) -> io::Result<Option<sapling::Bundle<sapling::bundle::Authorized, Amount>>> {
|
||||
sapling_serialization::read_v5_bundle(reader)
|
||||
}
|
||||
|
||||
|
@ -836,7 +836,7 @@ impl Transaction {
|
|||
|
||||
#[cfg(feature = "temporary-zcashd")]
|
||||
pub fn temporary_zcashd_write_v5_sapling<W: Write>(
|
||||
sapling_bundle: Option<&sapling::Bundle<sapling::bundle::Authorized>>,
|
||||
sapling_bundle: Option<&sapling::Bundle<sapling::bundle::Authorized, Amount>>,
|
||||
writer: W,
|
||||
) -> io::Result<()> {
|
||||
sapling_serialization::write_v5_bundle(writer, sapling_bundle)
|
||||
|
@ -915,7 +915,7 @@ pub trait TransactionDigest<A: Authorization> {
|
|||
|
||||
fn digest_sapling(
|
||||
&self,
|
||||
sapling_bundle: Option<&sapling::Bundle<A::SaplingAuth>>,
|
||||
sapling_bundle: Option<&sapling::Bundle<A::SaplingAuth, Amount>>,
|
||||
) -> Self::SaplingDigest;
|
||||
|
||||
fn digest_orchard(
|
||||
|
|
|
@ -256,7 +256,7 @@ 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::bundle::Authorization>(
|
||||
bundle: &sapling::Bundle<A>,
|
||||
bundle: &sapling::Bundle<A, Amount>,
|
||||
) -> Blake2bHash {
|
||||
let mut h = hasher(ZCASH_SAPLING_HASH_PERSONALIZATION);
|
||||
if !(bundle.shielded_spends().is_empty() && bundle.shielded_outputs().is_empty()) {
|
||||
|
@ -328,7 +328,7 @@ impl<A: Authorization> TransactionDigest<A> for TxIdDigester {
|
|||
|
||||
fn digest_sapling(
|
||||
&self,
|
||||
sapling_bundle: Option<&sapling::Bundle<A::SaplingAuth>>,
|
||||
sapling_bundle: Option<&sapling::Bundle<A::SaplingAuth, Amount>>,
|
||||
) -> Self::SaplingDigest {
|
||||
sapling_bundle.map(hash_sapling_txid_data)
|
||||
}
|
||||
|
@ -467,7 +467,7 @@ impl TransactionDigest<Authorized> for BlockTxCommitmentDigester {
|
|||
|
||||
fn digest_sapling(
|
||||
&self,
|
||||
sapling_bundle: Option<&sapling::Bundle<sapling::bundle::Authorized>>,
|
||||
sapling_bundle: Option<&sapling::Bundle<sapling::bundle::Authorized, Amount>>,
|
||||
) -> Blake2bHash {
|
||||
let mut h = hasher(ZCASH_SAPLING_SIGS_HASH_PERSONALIZATION);
|
||||
if let Some(bundle) = sapling_bundle {
|
||||
|
|
Loading…
Reference in New Issue