Derive transaction version & branch id from target height.
This commit is contained in:
parent
7466ef42d8
commit
72ac97a35f
|
@ -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.
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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))
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue