Derive transaction version & branch id from target height.

This commit is contained in:
Kris Nuttycombe 2021-06-02 13:45:29 -06:00
parent 7466ef42d8
commit 72ac97a35f
6 changed files with 92 additions and 93 deletions

View File

@ -2,13 +2,13 @@
use std::fmt::Debug;
use zcash_primitives::{
consensus::{self, BranchId, NetworkUpgrade},
consensus::{self, NetworkUpgrade},
memo::MemoBytes,
sapling::prover::TxProver,
transaction::{
builder::Builder,
components::{amount::DEFAULT_FEE, Amount},
Transaction, TxVersion,
Transaction,
},
zip32::{ExtendedFullViewingKey, ExtendedSpendingKey},
};
@ -228,10 +228,7 @@ where
}
.map_err(Error::Builder)?;
let consensus_branch_id = BranchId::for_height(params, height);
let (tx, tx_metadata) = builder
.build(TxVersion::Sapling, consensus_branch_id, &prover)
.map_err(Error::Builder)?;
let (tx, tx_metadata) = builder.build(&prover).map_err(Error::Builder)?;
let output_index = match to {
// Sapling outputs are shuffled, so we need to look up where the output ended up.

View File

@ -467,7 +467,7 @@ mod tests {
use zcash_proofs::prover::LocalTxProver;
use zcash_primitives::{
consensus::{BranchId, H0, TEST_NETWORK},
consensus::{H0, TEST_NETWORK},
extensions::transparent::{self as tze, Extension, FromPayload, ToPayload},
legacy::TransparentAddress,
merkle_tree::{CommitmentTree, IncrementalWitness},
@ -479,7 +479,7 @@ mod tests {
amount::{Amount, DEFAULT_FEE},
TzeIn, TzeOut, TzeOutPoint,
},
Transaction, TransactionData, TxVersion,
Transaction, TransactionData,
},
zip32::ExtendedSpendingKey,
};
@ -723,7 +723,7 @@ mod tests {
.map_err(|e| format!("open failure: {:?}", e))
.unwrap();
let (tx_a, _) = builder_a
.build(TxVersion::ZFuture, BranchId::ZFuture, &prover)
.build(&prover)
.map_err(|e| format!("build failure: {:?}", e))
.unwrap();
@ -745,7 +745,7 @@ mod tests {
.map_err(|e| format!("transfer failure: {:?}", e))
.unwrap();
let (tx_b, _) = builder_b
.build(TxVersion::ZFuture, BranchId::ZFuture, &prover)
.build(&prover)
.map_err(|e| format!("build failure: {:?}", e))
.unwrap();
@ -774,7 +774,7 @@ mod tests {
.unwrap();
let (tx_c, _) = builder_c
.build(TxVersion::ZFuture, BranchId::ZFuture, &prover)
.build(&prover)
.map_err(|e| format!("build failure: {:?}", e))
.unwrap();

View File

@ -1,5 +1,6 @@
//! Structs for building transactions.
use core::array;
use std::error;
use std::fmt;
use std::sync::mpsc::Sender;
@ -7,7 +8,7 @@ use std::sync::mpsc::Sender;
use rand::{rngs::OsRng, CryptoRng, RngCore};
use crate::{
consensus::{self, BlockHeight},
consensus::{self, BlockHeight, BranchId},
legacy::TransparentAddress,
memo::MemoBytes,
merkle_tree::MerklePath,
@ -104,7 +105,7 @@ impl Progress {
}
enum ChangeAddress {
SaplingChangeAddress(OutgoingViewingKey, PaymentAddress)
SaplingChangeAddress(OutgoingViewingKey, PaymentAddress),
}
/// Generates a [`Transaction`] from its inputs and outputs.
@ -202,14 +203,7 @@ impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> {
memo: Option<MemoBytes>,
) -> Result<(), Error> {
self.sapling_builder
.add_output(
&mut self.rng,
&self.params,
ovk,
to,
value,
memo,
)
.add_output(&mut self.rng, &self.params, ovk, to, value, memo)
.map_err(Error::SaplingBuildError)
}
@ -256,6 +250,23 @@ impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> {
self.progress_notifier = Some(progress_notifier);
}
pub fn value_balance(&self) -> Result<Amount, Error> {
let value_balances = [
self.transparent_builder
.value_balance()
.ok_or(Error::InvalidAmount)?,
self.sapling_builder.value_balance(),
#[cfg(feature = "zfuture")]
self.tze_builder
.value_balance()
.ok_or(Error::InvalidAmount)?,
];
array::IntoIter::new(value_balances)
.sum::<Option<_>>()
.ok_or(Error::InvalidAmount)
}
/// Builds a transaction from the configured spends and outputs.
///
/// Upon success, returns a tuple containing the final transaction, and the
@ -267,28 +278,19 @@ impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> {
/// the network.
pub fn build(
mut self,
version: TxVersion,
consensus_branch_id: consensus::BranchId,
prover: &impl TxProver,
) -> Result<(Transaction, SaplingMetadata), Error> {
let consensus_branch_id = BranchId::for_height(&self.params, self.target_height);
// determine transaction version
let version = TxVersion::suggested_for_branch(consensus_branch_id);
//
// Consistency checks
//
// Valid change
let change = self
.transparent_builder
.value_balance()
.and_then(|ta| ta + self.sapling_builder.value_balance())
.and_then(|b| b - self.fee)
.ok_or(Error::InvalidAmount)?;
#[cfg(feature = "zfuture")]
let change = self
.tze_builder
.value_balance()
.and_then(|b| change + b)
.ok_or(Error::InvalidAmount)?;
let change = (self.value_balance()? - self.fee).ok_or(Error::InvalidAmount)?;
if change.is_negative() {
return Err(Error::ChangeIsNegative(change));
@ -304,7 +306,10 @@ impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> {
self.add_sapling_output(Some(ovk), addr, change, None)?;
}
None => {
let (ovk, addr) = self.sapling_builder.get_candidate_change_address().map_err(Error::SaplingBuildError)?;
let (ovk, addr) = self
.sapling_builder
.get_candidate_change_address()
.map_err(Error::SaplingBuildError)?;
self.add_sapling_output(Some(ovk), addr, change, None)?;
}
}
@ -474,12 +479,8 @@ impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> {
Self::new_internal(params, height, rng)
}
pub fn mock_build(
self,
version: TxVersion,
consensus_branch_id: consensus::BranchId,
) -> Result<(Transaction, SaplingMetadata), Error> {
self.build(version, consensus_branch_id, &MockTxProver)
pub fn mock_build(self) -> Result<(Transaction, SaplingMetadata), Error> {
self.build(&MockTxProver)
}
}
@ -489,17 +490,14 @@ mod tests {
use rand_core::OsRng;
use crate::{
consensus::{self, Parameters, H0, TEST_NETWORK},
consensus::{Parameters, H0, TEST_NETWORK},
legacy::TransparentAddress,
merkle_tree::{CommitmentTree, IncrementalWitness},
sapling::{prover::mock::MockTxProver, Node, Rseed},
transaction::{
components::{
amount::{Amount, DEFAULT_FEE},
sapling::builder::{self as sapling},
transparent::builder::{self as transparent},
},
TxVersion,
transaction::components::{
amount::{Amount, DEFAULT_FEE},
sapling::builder::{self as sapling},
transparent::builder::{self as transparent},
},
zip32::{ExtendedFullViewingKey, ExtendedSpendingKey},
};
@ -557,13 +555,7 @@ mod tests {
.add_transparent_output(&TransparentAddress::PublicKey([0; 20]), Amount::zero())
.unwrap();
let (tx, _) = builder
.build(
TxVersion::Sapling,
consensus::BranchId::Sapling,
&MockTxProver,
)
.unwrap();
let (tx, _) = builder.build(&MockTxProver).unwrap();
// No binding signature, because only t input and outputs
assert!(tx.binding_sig.is_none());
}
@ -598,11 +590,7 @@ mod tests {
// Expect a binding signature error, because our inputs aren't valid, but this shows
// that a binding signature was attempted
assert_eq!(
builder.build(
TxVersion::Sapling,
consensus::BranchId::Sapling,
&MockTxProver
),
builder.build(&MockTxProver),
Err(Error::SaplingBuildError(sapling::Error::BindingSig))
);
}
@ -633,11 +621,7 @@ mod tests {
{
let builder = Builder::new(TEST_NETWORK, H0);
assert_eq!(
builder.build(
TxVersion::Sapling,
consensus::BranchId::Sapling,
&MockTxProver
),
builder.build(&MockTxProver),
Err(Error::ChangeIsNegative(
(Amount::zero() - DEFAULT_FEE).unwrap()
))
@ -656,11 +640,7 @@ mod tests {
.add_sapling_output(ovk, to.clone(), Amount::from_u64(50000).unwrap(), None)
.unwrap();
assert_eq!(
builder.build(
TxVersion::Sapling,
consensus::BranchId::Sapling,
&MockTxProver
),
builder.build(&MockTxProver),
Err(Error::ChangeIsNegative(
(Amount::from_i64(-50000).unwrap() - DEFAULT_FEE).unwrap()
))
@ -678,11 +658,7 @@ mod tests {
)
.unwrap();
assert_eq!(
builder.build(
TxVersion::Sapling,
consensus::BranchId::Sapling,
&MockTxProver
),
builder.build(&MockTxProver),
Err(Error::ChangeIsNegative(
(Amount::from_i64(-50000).unwrap() - DEFAULT_FEE).unwrap()
))
@ -719,11 +695,7 @@ mod tests {
)
.unwrap();
assert_eq!(
builder.build(
TxVersion::Sapling,
consensus::BranchId::Sapling,
&MockTxProver
),
builder.build(&MockTxProver),
Err(Error::ChangeIsNegative(Amount::from_i64(-1).unwrap()))
);
}
@ -764,11 +736,7 @@ mod tests {
)
.unwrap();
assert_eq!(
builder.build(
TxVersion::Sapling,
consensus::BranchId::Sapling,
&MockTxProver
),
builder.build(&MockTxProver),
Err(Error::SaplingBuildError(sapling::Error::BindingSig))
)
}

View File

@ -1,6 +1,6 @@
use std::convert::TryFrom;
use std::iter::Sum;
use std::ops::{Add, AddAssign, Sub, SubAssign};
use std::ops::{Add, AddAssign, Neg, Sub, SubAssign};
pub const COIN: i64 = 1_0000_0000;
pub const MAX_MONEY: i64 = 21_000_000 * COIN;
@ -172,6 +172,14 @@ impl Sum<Amount> for Option<Amount> {
}
}
impl Neg for Amount {
type Output = Self;
fn neg(self) -> Self {
Amount(-self.0)
}
}
#[cfg(any(test, feature = "test-dependencies"))]
pub mod testing {
use proptest::prelude::prop_compose;

View File

@ -278,8 +278,15 @@ impl<P: consensus::Parameters> SaplingBuilder<P> {
value: Amount,
memo: Option<MemoBytes>,
) -> Result<(), Error> {
let output =
SaplingOutput::new_internal(params, self.target_height, &mut rng, ovk, to, value, memo)?;
let output = SaplingOutput::new_internal(
params,
self.target_height,
&mut rng,
ovk,
to,
value,
memo,
)?;
self.value_balance -= value;
@ -290,7 +297,9 @@ impl<P: consensus::Parameters> SaplingBuilder<P> {
/// Send change to the specified change address. If no change address
/// was set, send change to the first Sapling address given as input.
pub fn get_candidate_change_address(&self) -> Result<(OutgoingViewingKey, PaymentAddress), Error> {
pub fn get_candidate_change_address(
&self,
) -> Result<(OutgoingViewingKey, PaymentAddress), Error> {
if !self.spends.is_empty() {
PaymentAddress::from_parts(self.spends[0].diversifier, self.spends[0].note.pk_d)
.map(|addr| (self.spends[0].extsk.expsk.ovk, addr))

View File

@ -5,7 +5,11 @@ use std::fmt;
use std::io::{self, Read, Write};
use std::ops::Deref;
use crate::{consensus::BlockHeight, sapling::redjubjub::Signature, serialize::Vector};
use crate::{
consensus::{BlockHeight, BranchId},
sapling::redjubjub::Signature,
serialize::Vector,
};
use self::util::sha256d::{HashReader, HashWriter};
@ -169,6 +173,19 @@ impl TxVersion {
TxVersion::ZFuture => true,
}
}
pub fn suggested_for_branch(consensus_branch_id: BranchId) -> Self {
match consensus_branch_id {
BranchId::Sprout => TxVersion::Sprout(2),
BranchId::Overwinter => TxVersion::Overwinter,
BranchId::Sapling | BranchId::Blossom | BranchId::Heartwood | BranchId::Canopy => {
TxVersion::Sapling
}
BranchId::Nu5 => TxVersion::ZFuture,
#[cfg(feature = "zfuture")]
BranchId::ZFuture => TxVersion::ZFuture,
}
}
}
/// A Zcash transaction.