Store patial authorizing data for sapling components in bundle authorization.
This commit is contained in:
parent
6635895e55
commit
6348400cf4
|
@ -10,7 +10,7 @@ use zcash_primitives::{
|
|||
Diversifier, PaymentAddress, SaplingIvk, ValueCommitment,
|
||||
},
|
||||
transaction::components::{
|
||||
sapling::{Authorized, OutputDescription},
|
||||
sapling::{GrothProofBytes, OutputDescription},
|
||||
GROTH_PROOF_SIZE,
|
||||
},
|
||||
};
|
||||
|
@ -23,7 +23,7 @@ fn bench_note_decryption(c: &mut Criterion) {
|
|||
let invalid_ivk = SaplingIvk(jubjub::Fr::random(&mut rng));
|
||||
|
||||
// Construct a fake Sapling output as if we had just deserialized a transaction.
|
||||
let output: OutputDescription<Authorized> = {
|
||||
let output: OutputDescription<GrothProofBytes> = {
|
||||
let diversifier = Diversifier([0; 11]);
|
||||
let pk_d = diversifier.g_d().unwrap() * valid_ivk.0;
|
||||
let pa = PaymentAddress::from_parts(diversifier, pk_d).unwrap();
|
||||
|
|
|
@ -386,7 +386,7 @@ pub fn try_sapling_output_recovery_with_ock<P: consensus::Parameters>(
|
|||
params: &P,
|
||||
height: BlockHeight,
|
||||
ock: &OutgoingCipherKey,
|
||||
output: &OutputDescription<sapling::Authorized>,
|
||||
output: &OutputDescription<sapling::GrothProofBytes>,
|
||||
) -> Option<(Note, PaymentAddress, MemoBytes)> {
|
||||
let domain = SaplingDomain {
|
||||
params: params.clone(),
|
||||
|
@ -408,7 +408,7 @@ pub fn try_sapling_output_recovery<P: consensus::Parameters>(
|
|||
params: &P,
|
||||
height: BlockHeight,
|
||||
ovk: &OutgoingViewingKey,
|
||||
output: &OutputDescription<sapling::Authorized>,
|
||||
output: &OutputDescription<sapling::GrothProofBytes>,
|
||||
) -> Option<(Note, PaymentAddress, MemoBytes)> {
|
||||
let domain = SaplingDomain {
|
||||
params: params.clone(),
|
||||
|
@ -465,7 +465,7 @@ mod tests {
|
|||
OutgoingViewingKey,
|
||||
OutgoingCipherKey,
|
||||
SaplingIvk,
|
||||
OutputDescription<sapling::Authorized>,
|
||||
OutputDescription<sapling::GrothProofBytes>,
|
||||
) {
|
||||
let ivk = SaplingIvk(jubjub::Fr::random(&mut rng));
|
||||
|
||||
|
@ -498,7 +498,7 @@ mod tests {
|
|||
) -> (
|
||||
OutgoingViewingKey,
|
||||
OutgoingCipherKey,
|
||||
OutputDescription<sapling::Authorized>,
|
||||
OutputDescription<sapling::GrothProofBytes>,
|
||||
) {
|
||||
let diversifier = Diversifier([0; 11]);
|
||||
let pk_d = diversifier.g_d().unwrap() * ivk.0;
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
use std::array;
|
||||
use std::error;
|
||||
use std::fmt;
|
||||
use std::io;
|
||||
use std::sync::mpsc::Sender;
|
||||
|
||||
#[cfg(not(feature = "zfuture"))]
|
||||
|
@ -17,8 +16,7 @@ use crate::{
|
|||
memo::MemoBytes,
|
||||
merkle_tree::MerklePath,
|
||||
sapling::{
|
||||
keys::OutgoingViewingKey, prover::TxProver, redjubjub, Diversifier, Node, Note,
|
||||
PaymentAddress,
|
||||
keys::OutgoingViewingKey, prover::TxProver, Diversifier, Node, Note, PaymentAddress,
|
||||
},
|
||||
transaction::{
|
||||
components::{
|
||||
|
@ -320,13 +318,14 @@ impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> {
|
|||
|
||||
let transparent_bundle = self.transparent_builder.build();
|
||||
|
||||
let mut rng = self.rng;
|
||||
let mut ctx = prover.new_sapling_proving_context();
|
||||
let (sapling_bundle, tx_metadata) = self
|
||||
let sapling_bundle = self
|
||||
.sapling_builder
|
||||
.build(
|
||||
prover,
|
||||
&mut ctx,
|
||||
&mut self.rng,
|
||||
&mut rng,
|
||||
self.target_height,
|
||||
self.progress_notifier.as_ref(),
|
||||
)
|
||||
|
@ -335,7 +334,7 @@ impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> {
|
|||
#[cfg(feature = "zfuture")]
|
||||
let (tze_bundle, tze_signers) = self.tze_builder.build();
|
||||
|
||||
let unauthed_tx = TransactionData {
|
||||
let unauthed_tx: TransactionData<Unauthorized> = TransactionData {
|
||||
version,
|
||||
consensus_branch_id: BranchId::for_height(&self.params, self.target_height),
|
||||
lock_time: 0,
|
||||
|
@ -362,6 +361,14 @@ impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> {
|
|||
)
|
||||
});
|
||||
|
||||
#[cfg(feature = "zfuture")]
|
||||
let tze_bundle = unauthed_tx
|
||||
.tze_bundle
|
||||
.clone()
|
||||
.map(|b| b.into_authorized(&unauthed_tx, tze_signers))
|
||||
.transpose()
|
||||
.map_err(Error::TzeBuild)?;
|
||||
|
||||
// the commitment being signed is shared across all Sapling inputs; once
|
||||
// V4 transactions are deprecated this should just be the txid, but
|
||||
// for now we need to continue to compute it here.
|
||||
|
@ -372,52 +379,16 @@ impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> {
|
|||
SIGHASH_ALL,
|
||||
);
|
||||
|
||||
let sapling_sigs = self
|
||||
.sapling_builder
|
||||
.create_signatures(
|
||||
prover,
|
||||
&mut ctx,
|
||||
&mut self.rng,
|
||||
shielded_sig_commitment.as_ref(),
|
||||
&tx_metadata,
|
||||
)
|
||||
.map_err(Error::SaplingBuild)?;
|
||||
|
||||
#[cfg(feature = "zfuture")]
|
||||
let tze_bundle = unauthed_tx
|
||||
.tze_bundle
|
||||
.clone()
|
||||
.map(|b| b.into_authorized(&unauthed_tx, tze_signers))
|
||||
let (sapling_bundle, tx_metadata) = match unauthed_tx
|
||||
.sapling_bundle
|
||||
.map(|b| {
|
||||
b.apply_signatures(prover, &mut ctx, &mut rng, shielded_sig_commitment.as_ref())
|
||||
})
|
||||
.transpose()
|
||||
.map_err(Error::TzeBuild)?;
|
||||
|
||||
Ok((
|
||||
Self::apply_signatures(
|
||||
unauthed_tx,
|
||||
sapling_sigs,
|
||||
transparent_bundle,
|
||||
#[cfg(feature = "zfuture")]
|
||||
tze_bundle,
|
||||
)
|
||||
.expect("An IO error occurred applying signatures."),
|
||||
tx_metadata,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn apply_signatures(
|
||||
unauthed_tx: TransactionData<Unauthorized>,
|
||||
sapling_sigs: Option<(Vec<redjubjub::Signature>, redjubjub::Signature)>,
|
||||
transparent_bundle: Option<transparent::Bundle<transparent::Authorized>>,
|
||||
#[cfg(feature = "zfuture")] tze_bundle: Option<tze::Bundle<tze::Authorized>>,
|
||||
) -> io::Result<Transaction> {
|
||||
let signed_sapling_bundle = match (unauthed_tx.sapling_bundle, sapling_sigs) {
|
||||
(Some(bundle), Some((spend_sigs, binding_sig))) => {
|
||||
Some(bundle.apply_signatures(spend_sigs, binding_sig))
|
||||
}
|
||||
(None, None) => None,
|
||||
_ => {
|
||||
panic!("Mismatch between sapling bundle and signatures.");
|
||||
}
|
||||
.map_err(Error::SaplingBuild)?
|
||||
{
|
||||
Some((bundle, meta)) => (Some(bundle), meta),
|
||||
None => (None, SaplingMetadata::empty()),
|
||||
};
|
||||
|
||||
let authorized_tx = TransactionData {
|
||||
|
@ -427,13 +398,15 @@ impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> {
|
|||
expiry_height: unauthed_tx.expiry_height,
|
||||
transparent_bundle,
|
||||
sprout_bundle: unauthed_tx.sprout_bundle,
|
||||
sapling_bundle: signed_sapling_bundle,
|
||||
sapling_bundle,
|
||||
orchard_bundle: None,
|
||||
#[cfg(feature = "zfuture")]
|
||||
tze_bundle,
|
||||
};
|
||||
|
||||
authorized_tx.freeze()
|
||||
// The unwrap() here is safe because the txid hashing
|
||||
// of freeze() should be infalliable.
|
||||
Ok((authorized_tx.freeze().unwrap(), tx_metadata))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -33,14 +33,6 @@ impl Authorization for Unproven {
|
|||
type AuthSig = ();
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||
pub struct Unauthorized;
|
||||
|
||||
impl Authorization for Unauthorized {
|
||||
type Proof = GrothProofBytes;
|
||||
type AuthSig = ();
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct Authorized {
|
||||
pub binding_sig: redjubjub::Signature,
|
||||
|
@ -54,36 +46,11 @@ impl Authorization for Authorized {
|
|||
#[derive(Debug, Clone)]
|
||||
pub struct Bundle<A: Authorization> {
|
||||
pub shielded_spends: Vec<SpendDescription<A>>,
|
||||
pub shielded_outputs: Vec<OutputDescription<A>>,
|
||||
pub shielded_outputs: Vec<OutputDescription<A::Proof>>,
|
||||
pub value_balance: Amount,
|
||||
pub authorization: A,
|
||||
}
|
||||
|
||||
impl Bundle<Unauthorized> {
|
||||
pub fn apply_signatures(
|
||||
self,
|
||||
spend_auth_sigs: Vec<Signature>,
|
||||
binding_sig: Signature,
|
||||
) -> Bundle<Authorized> {
|
||||
assert!(self.shielded_spends.len() == spend_auth_sigs.len());
|
||||
Bundle {
|
||||
shielded_spends: self
|
||||
.shielded_spends
|
||||
.iter()
|
||||
.zip(spend_auth_sigs.iter())
|
||||
.map(|(spend, sig)| spend.apply_signature(*sig))
|
||||
.collect(),
|
||||
shielded_outputs: self
|
||||
.shielded_outputs
|
||||
.into_iter()
|
||||
.map(|o| o.into_authorized())
|
||||
.collect(), //TODO, is there a zero-cost way to do this?
|
||||
value_balance: self.value_balance,
|
||||
authorization: Authorized { binding_sig },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SpendDescription<A: Authorization> {
|
||||
pub cv: jubjub::ExtendedPoint,
|
||||
|
@ -94,19 +61,6 @@ pub struct SpendDescription<A: Authorization> {
|
|||
pub spend_auth_sig: A::AuthSig,
|
||||
}
|
||||
|
||||
impl SpendDescription<Unauthorized> {
|
||||
pub fn apply_signature(&self, spend_auth_sig: Signature) -> SpendDescription<Authorized> {
|
||||
SpendDescription {
|
||||
cv: self.cv,
|
||||
anchor: self.anchor,
|
||||
nullifier: self.nullifier,
|
||||
rk: self.rk.clone(),
|
||||
zkproof: self.zkproof,
|
||||
spend_auth_sig,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Authorization> std::fmt::Debug for SpendDescription<A> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||
write!(
|
||||
|
@ -256,18 +210,16 @@ impl SpendDescriptionV5 {
|
|||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct OutputDescription<A: Authorization> {
|
||||
pub struct OutputDescription<Proof> {
|
||||
pub cv: jubjub::ExtendedPoint,
|
||||
pub cmu: bls12_381::Scalar,
|
||||
pub ephemeral_key: jubjub::ExtendedPoint,
|
||||
pub enc_ciphertext: [u8; 580],
|
||||
pub out_ciphertext: [u8; 80],
|
||||
pub zkproof: A::Proof,
|
||||
pub zkproof: Proof,
|
||||
}
|
||||
|
||||
impl<P: consensus::Parameters, A: Authorization> ShieldedOutput<SaplingDomain<P>>
|
||||
for OutputDescription<A>
|
||||
{
|
||||
impl<P: consensus::Parameters, A> ShieldedOutput<SaplingDomain<P>> for OutputDescription<A> {
|
||||
fn epk(&self) -> &jubjub::ExtendedPoint {
|
||||
&self.ephemeral_key
|
||||
}
|
||||
|
@ -281,7 +233,7 @@ impl<P: consensus::Parameters, A: Authorization> ShieldedOutput<SaplingDomain<P>
|
|||
}
|
||||
}
|
||||
|
||||
impl<A: Authorization> std::fmt::Debug for OutputDescription<A> {
|
||||
impl<A> std::fmt::Debug for OutputDescription<A> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||
write!(
|
||||
f,
|
||||
|
@ -291,7 +243,7 @@ impl<A: Authorization> std::fmt::Debug for OutputDescription<A> {
|
|||
}
|
||||
}
|
||||
|
||||
impl OutputDescription<Authorized> {
|
||||
impl OutputDescription<GrothProofBytes> {
|
||||
pub fn read<R: Read>(mut reader: &mut R) -> io::Result<Self> {
|
||||
// Consensus rules (§4.5):
|
||||
// - Canonical encoding is enforced here.
|
||||
|
@ -323,9 +275,7 @@ impl OutputDescription<Authorized> {
|
|||
zkproof,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Authorization<Proof = GrothProofBytes>> OutputDescription<A> {
|
||||
pub fn write_v4<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
||||
writer.write_all(&self.cv.to_bytes())?;
|
||||
writer.write_all(self.cmu.to_repr().as_ref())?;
|
||||
|
@ -376,7 +326,7 @@ impl OutputDescriptionV5 {
|
|||
pub fn into_output_description(
|
||||
self,
|
||||
zkproof: GrothProofBytes,
|
||||
) -> OutputDescription<Authorized> {
|
||||
) -> OutputDescription<GrothProofBytes> {
|
||||
OutputDescription {
|
||||
cv: self.cv,
|
||||
cmu: self.cmu,
|
||||
|
@ -388,26 +338,13 @@ impl OutputDescriptionV5 {
|
|||
}
|
||||
}
|
||||
|
||||
impl OutputDescription<Unauthorized> {
|
||||
pub fn into_authorized(self) -> OutputDescription<Authorized> {
|
||||
OutputDescription {
|
||||
cv: self.cv,
|
||||
cmu: self.cmu,
|
||||
ephemeral_key: self.ephemeral_key,
|
||||
enc_ciphertext: self.enc_ciphertext,
|
||||
out_ciphertext: self.out_ciphertext,
|
||||
zkproof: self.zkproof,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CompactOutputDescription {
|
||||
pub epk: jubjub::ExtendedPoint,
|
||||
pub cmu: bls12_381::Scalar,
|
||||
pub enc_ciphertext: Vec<u8>,
|
||||
}
|
||||
|
||||
impl<A: Authorization> From<OutputDescription<A>> for CompactOutputDescription {
|
||||
impl<A> From<OutputDescription<A>> for CompactOutputDescription {
|
||||
fn from(out: OutputDescription<A>) -> CompactOutputDescription {
|
||||
CompactOutputDescription {
|
||||
epk: out.ephemeral_key,
|
||||
|
@ -452,7 +389,7 @@ pub mod testing {
|
|||
},
|
||||
};
|
||||
|
||||
use super::{Authorized, Bundle, OutputDescription, SpendDescription};
|
||||
use super::{Authorized, Bundle, GrothProofBytes, OutputDescription, SpendDescription};
|
||||
|
||||
prop_compose! {
|
||||
/// produce a spend description with invalid data (useful only for serialization
|
||||
|
@ -507,7 +444,7 @@ pub mod testing {
|
|||
.prop_map(|v| <[u8;80]>::try_from(v.as_slice()).unwrap()),
|
||||
zkproof in vec(any::<u8>(), GROTH_PROOF_SIZE)
|
||||
.prop_map(|v| <[u8;GROTH_PROOF_SIZE]>::try_from(v.as_slice()).unwrap()),
|
||||
) -> OutputDescription<Authorized> {
|
||||
) -> OutputDescription<GrothProofBytes> {
|
||||
OutputDescription {
|
||||
cv,
|
||||
cmu,
|
||||
|
|
|
@ -23,7 +23,10 @@ use crate::{
|
|||
builder::Progress,
|
||||
components::{
|
||||
amount::Amount,
|
||||
sapling::{Bundle, OutputDescription, SpendDescription, Unauthorized},
|
||||
sapling::{
|
||||
Authorization, Authorized, Bundle, GrothProofBytes, OutputDescription,
|
||||
SpendDescription,
|
||||
},
|
||||
},
|
||||
},
|
||||
zip32::ExtendedSpendingKey,
|
||||
|
@ -56,6 +59,7 @@ impl fmt::Display for Error {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct SpendDescriptionInfo {
|
||||
extsk: ExtendedSpendingKey,
|
||||
diversifier: Diversifier,
|
||||
|
@ -110,7 +114,7 @@ impl SaplingOutput {
|
|||
prover: &Pr,
|
||||
ctx: &mut Pr::SaplingProvingContext,
|
||||
rng: &mut R,
|
||||
) -> OutputDescription<Unauthorized> {
|
||||
) -> OutputDescription<GrothProofBytes> {
|
||||
let encryptor = sapling_note_encryption::<R, P>(
|
||||
self.ovk,
|
||||
self.note.clone(),
|
||||
|
@ -146,7 +150,7 @@ impl SaplingOutput {
|
|||
}
|
||||
|
||||
/// Metadata about a transaction created by a [`SaplingBuilder`].
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct SaplingMetadata {
|
||||
spend_indices: Vec<usize>,
|
||||
output_indices: Vec<usize>,
|
||||
|
@ -192,6 +196,23 @@ pub struct SaplingBuilder<P> {
|
|||
outputs: Vec<SaplingOutput>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Unauthorized {
|
||||
spends: Vec<SpendDescriptionInfo>,
|
||||
tx_metadata: SaplingMetadata,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Unauthorized {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
|
||||
write!(f, "Unauthorized")
|
||||
}
|
||||
}
|
||||
|
||||
impl Authorization for Unauthorized {
|
||||
type Proof = GrothProofBytes;
|
||||
type AuthSig = ();
|
||||
}
|
||||
|
||||
impl<P: consensus::Parameters> SaplingBuilder<P> {
|
||||
pub fn new(params: P, target_height: BlockHeight) -> Self {
|
||||
SaplingBuilder {
|
||||
|
@ -284,13 +305,13 @@ impl<P: consensus::Parameters> SaplingBuilder<P> {
|
|||
}
|
||||
|
||||
pub fn build<Pr: TxProver, R: RngCore>(
|
||||
&self,
|
||||
self,
|
||||
prover: &Pr,
|
||||
ctx: &mut Pr::SaplingProvingContext,
|
||||
mut rng: R,
|
||||
target_height: BlockHeight,
|
||||
progress_notifier: Option<&Sender<Progress>>,
|
||||
) -> Result<(Option<Bundle<Unauthorized>>, SaplingMetadata), Error> {
|
||||
) -> Result<Option<Bundle<Unauthorized>>, Error> {
|
||||
// Record initial positions of spends and outputs
|
||||
let mut indexed_spends: Vec<_> = self.spends.iter().enumerate().collect();
|
||||
let mut indexed_outputs: Vec<_> = self
|
||||
|
@ -378,7 +399,7 @@ impl<P: consensus::Parameters> SaplingBuilder<P> {
|
|||
};
|
||||
|
||||
// Create Sapling OutputDescriptions
|
||||
let shielded_outputs: Vec<OutputDescription<Unauthorized>> = indexed_outputs
|
||||
let shielded_outputs: Vec<OutputDescription<GrothProofBytes>> = indexed_outputs
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
.map(|(i, output)| {
|
||||
|
@ -404,7 +425,6 @@ impl<P: consensus::Parameters> SaplingBuilder<P> {
|
|||
}
|
||||
(diversifier, g_d)
|
||||
};
|
||||
|
||||
let (pk_d, payment_address) = loop {
|
||||
let dummy_ivk = jubjub::Fr::random(&mut rng);
|
||||
let pk_d = g_d * dummy_ivk;
|
||||
|
@ -470,26 +490,43 @@ impl<P: consensus::Parameters> SaplingBuilder<P> {
|
|||
shielded_spends,
|
||||
shielded_outputs,
|
||||
value_balance: self.value_balance,
|
||||
authorization: Unauthorized,
|
||||
authorization: Unauthorized {
|
||||
spends: self.spends,
|
||||
tx_metadata,
|
||||
},
|
||||
})
|
||||
};
|
||||
|
||||
Ok((bundle, tx_metadata))
|
||||
Ok(bundle)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_signatures<Pr: TxProver, R: RngCore>(
|
||||
impl SpendDescription<Unauthorized> {
|
||||
pub fn apply_signature(&self, spend_auth_sig: Signature) -> SpendDescription<Authorized> {
|
||||
SpendDescription {
|
||||
cv: self.cv,
|
||||
anchor: self.anchor,
|
||||
nullifier: self.nullifier,
|
||||
rk: self.rk.clone(),
|
||||
zkproof: self.zkproof,
|
||||
spend_auth_sig,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Bundle<Unauthorized> {
|
||||
pub fn apply_signatures<Pr: TxProver, R: RngCore>(
|
||||
self,
|
||||
prover: &Pr,
|
||||
ctx: &mut Pr::SaplingProvingContext,
|
||||
rng: &mut R,
|
||||
sighash_bytes: &[u8; 32],
|
||||
tx_metadata: &SaplingMetadata,
|
||||
) -> Result<Option<(Vec<Signature>, Signature)>, Error> {
|
||||
) -> Result<(Bundle<Authorized>, SaplingMetadata), Error> {
|
||||
// Create Sapling spendAuth signatures. These must be properly ordered with respect to the
|
||||
// shuffle that is described by tx_metadata.
|
||||
let mut spend_sigs = vec![None; self.spends.len()];
|
||||
for (i, spend) in self.spends.into_iter().enumerate() {
|
||||
spend_sigs[tx_metadata.spend_indices[i]] = Some(spend_sig_internal(
|
||||
let mut spend_sigs = vec![None; self.authorization.spends.len()];
|
||||
for (i, spend) in self.authorization.spends.into_iter().enumerate() {
|
||||
spend_sigs[self.authorization.tx_metadata.spend_indices[i]] = Some(spend_sig_internal(
|
||||
PrivateKey(spend.extsk.expsk.ask),
|
||||
spend.alpha,
|
||||
sighash_bytes,
|
||||
|
@ -497,20 +534,29 @@ impl<P: consensus::Parameters> SaplingBuilder<P> {
|
|||
));
|
||||
}
|
||||
|
||||
if tx_metadata.spend_indices.is_empty() && tx_metadata.output_indices.is_empty() {
|
||||
Ok(None)
|
||||
} else {
|
||||
let spend_sigs = spend_sigs
|
||||
.into_iter()
|
||||
.collect::<Option<Vec<Signature>>>()
|
||||
.unwrap_or_default();
|
||||
let spend_sigs = spend_sigs
|
||||
.into_iter()
|
||||
.collect::<Option<Vec<Signature>>>()
|
||||
.unwrap_or_default();
|
||||
|
||||
let binding_sig = prover
|
||||
.binding_sig(ctx, self.value_balance, sighash_bytes)
|
||||
.map_err(|_| Error::BindingSig)?;
|
||||
let binding_sig = prover
|
||||
.binding_sig(ctx, self.value_balance, sighash_bytes)
|
||||
.map_err(|_| Error::BindingSig)?;
|
||||
|
||||
Ok(Some((spend_sigs, binding_sig)))
|
||||
}
|
||||
Ok((
|
||||
Bundle {
|
||||
shielded_spends: self
|
||||
.shielded_spends
|
||||
.iter()
|
||||
.zip(spend_sigs.iter())
|
||||
.map(|(spend, sig)| spend.apply_signature(*sig))
|
||||
.collect(),
|
||||
shielded_outputs: self.shielded_outputs,
|
||||
value_balance: self.value_balance,
|
||||
authorization: Authorized { binding_sig },
|
||||
},
|
||||
self.authorization.tx_metadata,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -574,23 +620,22 @@ pub mod testing {
|
|||
let prover = MockTxProver;
|
||||
let mut ctx = prover.new_sapling_proving_context();
|
||||
|
||||
let (bundle, meta) = builder.build(
|
||||
let bundle = builder.build(
|
||||
&prover,
|
||||
&mut ctx,
|
||||
&mut rng,
|
||||
target_height.unwrap(),
|
||||
None
|
||||
).unwrap();
|
||||
).unwrap().unwrap();
|
||||
|
||||
let (spend_auth_sigs, binding_sig) = builder.create_signatures(
|
||||
let (bundle, _) = bundle.apply_signatures(
|
||||
&prover,
|
||||
&mut ctx,
|
||||
&mut rng,
|
||||
&fake_sighash_bytes,
|
||||
&meta
|
||||
).unwrap().unwrap();
|
||||
).unwrap();
|
||||
|
||||
bundle.unwrap().apply_signatures(spend_auth_sigs, binding_sig)
|
||||
bundle
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -257,7 +257,7 @@ pub struct Unauthorized;
|
|||
|
||||
impl Authorization for Unauthorized {
|
||||
type TransparentAuth = transparent::builder::Unauthorized;
|
||||
type SaplingAuth = sapling::Unauthorized;
|
||||
type SaplingAuth = sapling::builder::Unauthorized;
|
||||
type OrchardAuth = orchard::builder::Unauthorized;
|
||||
|
||||
#[cfg(feature = "zfuture")]
|
||||
|
@ -547,7 +547,7 @@ impl Transaction {
|
|||
let ss: Vec<SpendDescription<sapling::Authorized>> =
|
||||
Vector::read(&mut reader, |r| SpendDescription::read(r))?;
|
||||
#[allow(clippy::redundant_closure)]
|
||||
let so: Vec<OutputDescription<sapling::Authorized>> =
|
||||
let so: Vec<OutputDescription<sapling::GrothProofBytes>> =
|
||||
Vector::read(&mut reader, |r| OutputDescription::read(r))?;
|
||||
(vb, ss, so)
|
||||
} else {
|
||||
|
|
|
@ -124,9 +124,7 @@ fn shielded_spends_hash<A: sapling::Authorization<Proof = GrothProofBytes>>(
|
|||
.hash(&data)
|
||||
}
|
||||
|
||||
fn shielded_outputs_hash<A: sapling::Authorization<Proof = GrothProofBytes>>(
|
||||
shielded_outputs: &[OutputDescription<A>],
|
||||
) -> Blake2bHash {
|
||||
fn shielded_outputs_hash(shielded_outputs: &[OutputDescription<GrothProofBytes>]) -> Blake2bHash {
|
||||
let mut data = Vec::with_capacity(shielded_outputs.len() * 948);
|
||||
for s_out in shielded_outputs {
|
||||
s_out.write_v4(&mut data).unwrap();
|
||||
|
|
|
@ -168,9 +168,7 @@ pub(crate) fn hash_sapling_spends<A: sapling::Authorization>(
|
|||
/// * [(cv, enc_ciphertext[564..], out_ciphertext, zkproof)*] personalized with ZCASH_SAPLING_OUTPUTS_NONCOMPACT_HASH_PERSONALIZATION
|
||||
///
|
||||
/// Then, hash these together personalized with ZCASH_SAPLING_OUTPUTS_HASH_PERSONALIZATION
|
||||
pub(crate) fn hash_sapling_outputs<A: sapling::Authorization>(
|
||||
shielded_outputs: &[OutputDescription<A>],
|
||||
) -> Blake2bHash {
|
||||
pub(crate) fn hash_sapling_outputs<A>(shielded_outputs: &[OutputDescription<A>]) -> Blake2bHash {
|
||||
let mut ch = hasher(ZCASH_SAPLING_OUTPUTS_COMPACT_HASH_PERSONALIZATION);
|
||||
let mut mh = hasher(ZCASH_SAPLING_OUTPUTS_MEMOS_HASH_PERSONALIZATION);
|
||||
let mut nh = hasher(ZCASH_SAPLING_OUTPUTS_NONCOMPACT_HASH_PERSONALIZATION);
|
||||
|
|
Loading…
Reference in New Issue