2018-11-20 05:37:21 -08:00
|
|
|
//! Structs for building transactions.
|
|
|
|
|
2019-03-08 19:20:32 -08:00
|
|
|
use std::error;
|
|
|
|
use std::fmt;
|
2021-04-26 10:14:01 -07:00
|
|
|
use std::sync::mpsc::Sender;
|
2018-11-20 05:37:21 -08:00
|
|
|
|
2021-04-01 16:35:32 -07:00
|
|
|
use rand::{rngs::OsRng, CryptoRng, RngCore};
|
2020-05-06 16:52:03 -07:00
|
|
|
|
2018-11-20 05:37:21 -08:00
|
|
|
use crate::{
|
2020-08-05 13:08:58 -07:00
|
|
|
consensus::{self, BlockHeight},
|
2019-05-24 05:32:55 -07:00
|
|
|
legacy::TransparentAddress,
|
2020-10-29 09:48:26 -07:00
|
|
|
memo::MemoBytes,
|
2020-02-07 16:25:24 -08:00
|
|
|
merkle_tree::MerklePath,
|
2021-03-04 16:45:45 -08:00
|
|
|
sapling::{
|
2021-04-01 16:35:32 -07:00
|
|
|
keys::OutgoingViewingKey, prover::TxProver, Diversifier, Node, Note, PaymentAddress,
|
2021-03-04 16:45:45 -08:00
|
|
|
},
|
2018-11-20 05:37:21 -08:00
|
|
|
transaction::{
|
2020-05-06 16:52:03 -07:00
|
|
|
components::{
|
2021-03-04 13:40:45 -08:00
|
|
|
amount::{Amount, DEFAULT_FEE},
|
2021-04-01 16:35:32 -07:00
|
|
|
sapling::builder::{self as sapling, SaplingBuilder, SaplingMetadata},
|
2021-05-11 13:17:54 -07:00
|
|
|
transparent::builder::{self as transparent, TransparentBuilder},
|
2020-05-06 16:52:03 -07:00
|
|
|
},
|
2021-04-01 13:08:17 -07:00
|
|
|
signature_hash_data, SignableInput, Transaction, TransactionData, TxVersion, SIGHASH_ALL,
|
2018-11-20 05:37:21 -08:00
|
|
|
},
|
2020-05-06 16:52:03 -07:00
|
|
|
zip32::ExtendedSpendingKey,
|
2018-11-20 05:37:21 -08:00
|
|
|
};
|
|
|
|
|
2021-04-01 16:35:32 -07:00
|
|
|
#[cfg(not(feature = "zfuture"))]
|
|
|
|
use std::marker::PhantomData;
|
|
|
|
|
2019-07-31 08:20:13 -07:00
|
|
|
#[cfg(feature = "transparent-inputs")]
|
2021-05-11 13:17:54 -07:00
|
|
|
use crate::transaction::components::{OutPoint, TxOut};
|
2019-07-31 08:20:13 -07:00
|
|
|
|
2020-09-23 09:53:48 -07:00
|
|
|
#[cfg(feature = "zfuture")]
|
|
|
|
use crate::{
|
2021-04-01 17:10:01 -07:00
|
|
|
extensions::transparent::{ExtensionTxBuilder, ToPayload},
|
|
|
|
transaction::components::{
|
|
|
|
tze::builder::{self as tzebuilder, TzeBuilder},
|
|
|
|
TzeOut, TzeOutPoint,
|
|
|
|
},
|
2020-09-23 09:53:48 -07:00
|
|
|
};
|
|
|
|
|
2020-10-21 11:37:21 -07:00
|
|
|
#[cfg(any(test, feature = "test-dependencies"))]
|
2021-03-04 09:48:04 -08:00
|
|
|
use crate::sapling::prover::mock::MockTxProver;
|
2020-10-21 11:37:21 -07:00
|
|
|
|
2018-11-20 05:37:21 -08:00
|
|
|
const DEFAULT_TX_EXPIRY_DELTA: u32 = 20;
|
|
|
|
|
|
|
|
#[derive(Debug, PartialEq)]
|
2019-08-13 07:10:57 -07:00
|
|
|
pub enum Error {
|
2019-07-25 12:53:42 -07:00
|
|
|
ChangeIsNegative(Amount),
|
2018-11-20 05:37:21 -08:00
|
|
|
InvalidAmount,
|
|
|
|
NoChangeAddress,
|
2021-05-11 13:17:54 -07:00
|
|
|
TransparentBuildError(transparent::Error),
|
2021-04-01 16:35:32 -07:00
|
|
|
SaplingBuildError(sapling::Error),
|
2021-04-01 17:10:01 -07:00
|
|
|
#[cfg(feature = "zfuture")]
|
|
|
|
TzeBuildError(tzebuilder::Error),
|
2018-11-20 05:37:21 -08:00
|
|
|
}
|
|
|
|
|
2019-03-08 19:20:32 -08:00
|
|
|
impl fmt::Display for Error {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
match self {
|
|
|
|
Error::ChangeIsNegative(amount) => {
|
|
|
|
write!(f, "Change is negative ({:?} zatoshis)", amount)
|
|
|
|
}
|
|
|
|
Error::InvalidAmount => write!(f, "Invalid amount"),
|
|
|
|
Error::NoChangeAddress => write!(f, "No change address specified or discoverable"),
|
2021-05-11 13:17:54 -07:00
|
|
|
Error::TransparentBuildError(err) => err.fmt(f),
|
2021-04-01 16:35:32 -07:00
|
|
|
Error::SaplingBuildError(err) => err.fmt(f),
|
2021-04-01 17:10:01 -07:00
|
|
|
#[cfg(feature = "zfuture")]
|
|
|
|
Error::TzeBuildError(err) => err.fmt(f),
|
2019-03-08 19:20:32 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl error::Error for Error {}
|
|
|
|
|
2021-05-18 05:45:48 -07:00
|
|
|
/// Reports on the progress made by the builder towards building a transaction.
|
2021-04-26 10:14:01 -07:00
|
|
|
pub struct Progress {
|
2021-05-18 05:45:48 -07:00
|
|
|
/// The number of steps completed.
|
2021-04-26 10:14:01 -07:00
|
|
|
cur: u32,
|
2021-05-18 05:45:48 -07:00
|
|
|
/// The expected total number of steps (as of this progress update), if known.
|
2021-04-26 10:14:01 -07:00
|
|
|
end: Option<u32>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Progress {
|
2021-06-01 07:53:53 -07:00
|
|
|
pub fn new(cur: u32, end: Option<u32>) -> Self {
|
2021-04-26 10:14:01 -07:00
|
|
|
Self { cur, end }
|
|
|
|
}
|
|
|
|
|
2021-05-18 05:45:48 -07:00
|
|
|
/// Returns the number of steps completed so far while building the transaction.
|
|
|
|
///
|
|
|
|
/// Note that each step may not be of the same complexity/duration.
|
2021-04-26 10:14:01 -07:00
|
|
|
pub fn cur(&self) -> u32 {
|
|
|
|
self.cur
|
|
|
|
}
|
|
|
|
|
2021-05-18 05:45:48 -07:00
|
|
|
/// Returns the total expected number of steps before this transaction will be ready,
|
|
|
|
/// or `None` if the end is unknown as of this progress update.
|
|
|
|
///
|
|
|
|
/// Note that each step may not be of the same complexity/duration.
|
2021-04-26 10:14:01 -07:00
|
|
|
pub fn end(&self) -> Option<u32> {
|
|
|
|
self.end
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-02 11:35:18 -07:00
|
|
|
enum ChangeAddress {
|
|
|
|
SaplingChangeAddress(OutgoingViewingKey, PaymentAddress)
|
|
|
|
}
|
|
|
|
|
2018-11-20 05:37:21 -08:00
|
|
|
/// Generates a [`Transaction`] from its inputs and outputs.
|
2020-10-21 11:37:21 -07:00
|
|
|
pub struct Builder<'a, P: consensus::Parameters, R: RngCore> {
|
2020-08-05 13:27:40 -07:00
|
|
|
params: P,
|
2019-06-12 15:11:51 -07:00
|
|
|
rng: R,
|
2021-04-01 13:08:17 -07:00
|
|
|
target_height: BlockHeight,
|
|
|
|
expiry_height: BlockHeight,
|
2018-11-20 05:37:21 -08:00
|
|
|
fee: Amount,
|
2021-04-01 13:08:17 -07:00
|
|
|
transparent_builder: TransparentBuilder,
|
|
|
|
sapling_builder: SaplingBuilder<P>,
|
2021-06-02 11:35:18 -07:00
|
|
|
change_address: Option<ChangeAddress>,
|
2020-09-23 09:53:48 -07:00
|
|
|
#[cfg(feature = "zfuture")]
|
2021-04-01 13:08:17 -07:00
|
|
|
tze_builder: TzeBuilder<'a, TransactionData>,
|
|
|
|
#[cfg(not(feature = "zfuture"))]
|
|
|
|
tze_builder: PhantomData<&'a ()>,
|
2021-05-26 13:58:08 -07:00
|
|
|
progress_notifier: Option<Sender<Progress>>,
|
2018-11-20 05:37:21 -08:00
|
|
|
}
|
|
|
|
|
2020-05-06 16:52:03 -07:00
|
|
|
impl<'a, P: consensus::Parameters> Builder<'a, P, OsRng> {
|
2020-08-05 20:36:02 -07:00
|
|
|
/// Creates a new `Builder` targeted for inclusion in the block with the given height,
|
|
|
|
/// using default values for general transaction fields and the default OS random.
|
|
|
|
///
|
|
|
|
/// # Default values
|
|
|
|
///
|
|
|
|
/// The expiry height will be set to the given height plus the default transaction
|
|
|
|
/// expiry delta (20 blocks).
|
|
|
|
///
|
|
|
|
/// The fee will be set to the default fee (0.0001 ZEC).
|
2021-04-01 13:08:17 -07:00
|
|
|
pub fn new(params: P, target_height: BlockHeight) -> Self {
|
|
|
|
Builder::new_with_rng(params, target_height, OsRng)
|
2020-05-27 11:09:59 -07:00
|
|
|
}
|
2020-08-05 20:36:02 -07:00
|
|
|
}
|
|
|
|
|
2020-05-06 16:52:03 -07:00
|
|
|
impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng> Builder<'a, P, R> {
|
2020-09-08 15:53:27 -07:00
|
|
|
/// Creates a new `Builder` targeted for inclusion in the block with the given height
|
|
|
|
/// and randomness source, using default values for general transaction fields.
|
|
|
|
///
|
|
|
|
/// # Default values
|
|
|
|
///
|
|
|
|
/// The expiry height will be set to the given height plus the default transaction
|
|
|
|
/// expiry delta (20 blocks).
|
|
|
|
///
|
|
|
|
/// The fee will be set to the default fee (0.0001 ZEC).
|
2021-04-01 13:08:17 -07:00
|
|
|
pub fn new_with_rng(params: P, target_height: BlockHeight, rng: R) -> Builder<'a, P, R> {
|
|
|
|
Self::new_internal(params, target_height, rng)
|
2020-09-08 15:53:27 -07:00
|
|
|
}
|
2020-10-21 11:37:21 -07:00
|
|
|
}
|
2020-09-08 15:53:27 -07:00
|
|
|
|
2020-10-21 11:37:21 -07:00
|
|
|
impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> {
|
2020-09-08 15:53:27 -07:00
|
|
|
/// Common utility function for builder construction.
|
2020-10-21 11:37:21 -07:00
|
|
|
///
|
|
|
|
/// WARNING: THIS MUST REMAIN PRIVATE AS IT ALLOWS CONSTRUCTION
|
|
|
|
/// OF BUILDERS WITH NON-CryptoRng RNGs
|
2021-04-01 13:08:17 -07:00
|
|
|
fn new_internal(params: P, target_height: BlockHeight, rng: R) -> Builder<'a, P, R> {
|
2018-11-20 05:37:21 -08:00
|
|
|
Builder {
|
2020-08-05 13:27:40 -07:00
|
|
|
params,
|
2019-06-12 15:11:51 -07:00
|
|
|
rng,
|
2021-04-01 13:08:17 -07:00
|
|
|
target_height,
|
|
|
|
expiry_height: target_height + DEFAULT_TX_EXPIRY_DELTA,
|
2018-11-20 05:37:21 -08:00
|
|
|
fee: DEFAULT_FEE,
|
2021-05-11 13:17:54 -07:00
|
|
|
transparent_builder: TransparentBuilder::empty(),
|
2021-06-02 11:35:18 -07:00
|
|
|
sapling_builder: SaplingBuilder::empty(target_height),
|
|
|
|
change_address: None,
|
2020-09-23 09:53:48 -07:00
|
|
|
#[cfg(feature = "zfuture")]
|
2021-04-01 17:10:01 -07:00
|
|
|
tze_builder: TzeBuilder::empty(),
|
2021-04-01 13:08:17 -07:00
|
|
|
#[cfg(not(feature = "zfuture"))]
|
|
|
|
tze_builder: PhantomData,
|
2021-05-26 13:58:08 -07:00
|
|
|
progress_notifier: None,
|
2018-11-20 05:37:21 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Adds a Sapling note to be spent in this transaction.
|
|
|
|
///
|
2020-02-07 16:25:24 -08:00
|
|
|
/// Returns an error if the given Merkle path does not have the same anchor as the
|
|
|
|
/// paths for previous Sapling notes.
|
2018-11-20 05:37:21 -08:00
|
|
|
pub fn add_sapling_spend(
|
|
|
|
&mut self,
|
|
|
|
extsk: ExtendedSpendingKey,
|
|
|
|
diversifier: Diversifier,
|
2020-07-01 13:26:54 -07:00
|
|
|
note: Note,
|
2020-02-07 16:25:24 -08:00
|
|
|
merkle_path: MerklePath<Node>,
|
2018-11-20 05:37:21 -08:00
|
|
|
) -> Result<(), Error> {
|
2021-04-01 13:08:17 -07:00
|
|
|
self.sapling_builder
|
|
|
|
.add_spend(&mut self.rng, extsk, diversifier, note, merkle_path)
|
2021-04-01 16:35:32 -07:00
|
|
|
.map_err(Error::SaplingBuildError)
|
2018-11-20 05:37:21 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Adds a Sapling address to send funds to.
|
2020-08-05 20:36:02 -07:00
|
|
|
pub fn add_sapling_output(
|
2018-11-20 05:37:21 -08:00
|
|
|
&mut self,
|
2020-08-28 08:12:37 -07:00
|
|
|
ovk: Option<OutgoingViewingKey>,
|
2020-07-01 13:26:54 -07:00
|
|
|
to: PaymentAddress,
|
2018-11-20 05:37:21 -08:00
|
|
|
value: Amount,
|
2020-10-29 09:48:26 -07:00
|
|
|
memo: Option<MemoBytes>,
|
2018-11-20 05:37:21 -08:00
|
|
|
) -> Result<(), Error> {
|
2021-04-01 16:35:32 -07:00
|
|
|
self.sapling_builder
|
|
|
|
.add_output(
|
|
|
|
&mut self.rng,
|
|
|
|
&self.params,
|
|
|
|
ovk,
|
|
|
|
to,
|
|
|
|
value,
|
|
|
|
memo,
|
|
|
|
)
|
|
|
|
.map_err(Error::SaplingBuildError)
|
2018-11-20 05:37:21 -08:00
|
|
|
}
|
|
|
|
|
2019-07-31 08:20:13 -07:00
|
|
|
/// Adds a transparent coin to be spent in this transaction.
|
|
|
|
#[cfg(feature = "transparent-inputs")]
|
2020-08-22 22:56:03 -07:00
|
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "transparent-inputs")))]
|
2019-07-31 08:20:13 -07:00
|
|
|
pub fn add_transparent_input(
|
|
|
|
&mut self,
|
|
|
|
sk: secp256k1::SecretKey,
|
|
|
|
utxo: OutPoint,
|
|
|
|
coin: TxOut,
|
|
|
|
) -> Result<(), Error> {
|
2021-05-11 13:17:54 -07:00
|
|
|
self.transparent_builder
|
|
|
|
.add_input(sk, utxo, coin)
|
|
|
|
.map_err(Error::TransparentBuildError)
|
2019-07-31 08:20:13 -07:00
|
|
|
}
|
|
|
|
|
2019-05-24 05:32:55 -07:00
|
|
|
/// Adds a transparent address to send funds to.
|
|
|
|
pub fn add_transparent_output(
|
|
|
|
&mut self,
|
|
|
|
to: &TransparentAddress,
|
|
|
|
value: Amount,
|
|
|
|
) -> Result<(), Error> {
|
2021-05-11 13:17:54 -07:00
|
|
|
self.transparent_builder
|
|
|
|
.add_output(to, value)
|
|
|
|
.map_err(Error::TransparentBuildError)
|
2019-05-24 05:32:55 -07:00
|
|
|
}
|
|
|
|
|
2018-11-20 05:37:21 -08:00
|
|
|
/// Sets the Sapling address to which any change will be sent.
|
|
|
|
///
|
|
|
|
/// By default, change is sent to the Sapling address corresponding to the first note
|
|
|
|
/// being spent (i.e. the first call to [`Builder::add_sapling_spend`]).
|
2020-07-01 13:26:54 -07:00
|
|
|
pub fn send_change_to(&mut self, ovk: OutgoingViewingKey, to: PaymentAddress) {
|
2021-06-02 11:35:18 -07:00
|
|
|
self.change_address = Some(ChangeAddress::SaplingChangeAddress(ovk, to))
|
2018-11-20 05:37:21 -08:00
|
|
|
}
|
|
|
|
|
2021-05-26 13:58:08 -07:00
|
|
|
/// Sets the notifier channel, where progress of building the transaction is sent.
|
2018-11-20 05:37:21 -08:00
|
|
|
///
|
2021-05-26 13:58:08 -07:00
|
|
|
/// An update is sent after every Spend or Output is computed, and the `u32` sent
|
|
|
|
/// represents the total steps completed so far. It will eventually send number of
|
|
|
|
/// spends + outputs. If there's an error building the transaction, the channel is
|
|
|
|
/// closed.
|
|
|
|
pub fn with_progress_notifier(&mut self, progress_notifier: Sender<Progress>) {
|
|
|
|
self.progress_notifier = Some(progress_notifier);
|
2021-04-26 10:14:01 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Builds a transaction from the configured spends and outputs.
|
|
|
|
///
|
|
|
|
/// Upon success, returns a tuple containing the final transaction, and the
|
2021-04-01 13:08:17 -07:00
|
|
|
/// [`SaplingMetadata`] generated during the build process.
|
2021-04-26 10:14:01 -07:00
|
|
|
///
|
|
|
|
/// `consensus_branch_id` must be valid for the block height that this transaction is
|
|
|
|
/// targeting. An invalid `consensus_branch_id` will *not* result in an error from
|
|
|
|
/// this function, and instead will generate a transaction that will be rejected by
|
|
|
|
/// the network.
|
2021-05-26 13:58:08 -07:00
|
|
|
pub fn build(
|
2018-11-20 05:37:21 -08:00
|
|
|
mut self,
|
2021-04-01 13:08:17 -07:00
|
|
|
version: TxVersion,
|
2019-11-25 07:41:14 -08:00
|
|
|
consensus_branch_id: consensus::BranchId,
|
2020-02-07 16:37:31 -08:00
|
|
|
prover: &impl TxProver,
|
2021-04-01 13:08:17 -07:00
|
|
|
) -> Result<(Transaction, SaplingMetadata), Error> {
|
2018-11-20 05:37:21 -08:00
|
|
|
//
|
|
|
|
// Consistency checks
|
|
|
|
//
|
|
|
|
|
|
|
|
// Valid change
|
2021-04-01 13:08:17 -07:00
|
|
|
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)?;
|
2020-09-23 09:53:48 -07:00
|
|
|
|
|
|
|
#[cfg(feature = "zfuture")]
|
2021-04-01 13:08:17 -07:00
|
|
|
let change = self
|
|
|
|
.tze_builder
|
|
|
|
.value_balance()
|
|
|
|
.and_then(|b| change + b)
|
|
|
|
.ok_or(Error::InvalidAmount)?;
|
2020-05-06 16:52:03 -07:00
|
|
|
|
2018-11-20 05:37:21 -08:00
|
|
|
if change.is_negative() {
|
2019-08-13 07:10:57 -07:00
|
|
|
return Err(Error::ChangeIsNegative(change));
|
2018-11-20 05:37:21 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Change output
|
|
|
|
//
|
|
|
|
|
|
|
|
if change.is_positive() {
|
2021-06-02 11:35:18 -07:00
|
|
|
match self.change_address.take() {
|
|
|
|
Some(ChangeAddress::SaplingChangeAddress(ovk, addr)) => {
|
|
|
|
self.add_sapling_output(Some(ovk), addr, change, None)?;
|
|
|
|
}
|
|
|
|
None => {
|
|
|
|
let (ovk, addr) = self.sapling_builder.get_candidate_change_address().map_err(Error::SaplingBuildError)?;
|
|
|
|
self.add_sapling_output(Some(ovk), addr, change, None)?;
|
|
|
|
}
|
|
|
|
}
|
2018-11-20 05:37:21 -08:00
|
|
|
}
|
|
|
|
|
2021-04-01 13:08:17 -07:00
|
|
|
let (vin, vout) = self.transparent_builder.build();
|
2018-11-20 05:37:21 -08:00
|
|
|
|
|
|
|
let mut ctx = prover.new_sapling_proving_context();
|
2021-04-01 16:35:32 -07:00
|
|
|
let (spend_descs, output_descs, tx_metadata) = self
|
|
|
|
.sapling_builder
|
|
|
|
.build(
|
|
|
|
&self.params,
|
|
|
|
prover,
|
|
|
|
&mut ctx,
|
|
|
|
&mut self.rng,
|
|
|
|
self.target_height,
|
2021-06-01 07:53:53 -07:00
|
|
|
self.progress_notifier.as_ref(),
|
2021-04-01 16:35:32 -07:00
|
|
|
)
|
|
|
|
.map_err(Error::SaplingBuildError)?;
|
2018-11-20 05:37:21 -08:00
|
|
|
|
2021-04-01 13:08:17 -07:00
|
|
|
#[cfg(feature = "zfuture")]
|
|
|
|
let (tze_inputs, tze_outputs) = self.tze_builder.build();
|
2021-04-26 10:14:01 -07:00
|
|
|
|
2021-04-01 13:08:17 -07:00
|
|
|
let mut mtx = TransactionData {
|
|
|
|
version,
|
|
|
|
vin,
|
|
|
|
vout,
|
|
|
|
#[cfg(feature = "zfuture")]
|
|
|
|
tze_inputs,
|
|
|
|
#[cfg(feature = "zfuture")]
|
|
|
|
tze_outputs,
|
|
|
|
lock_time: 0,
|
|
|
|
expiry_height: self.expiry_height,
|
2021-04-01 16:35:32 -07:00
|
|
|
value_balance: self.sapling_builder.value_balance(),
|
2021-04-01 13:08:17 -07:00
|
|
|
shielded_spends: spend_descs,
|
|
|
|
shielded_outputs: output_descs,
|
|
|
|
joinsplits: vec![],
|
|
|
|
joinsplit_pubkey: None,
|
|
|
|
joinsplit_sig: None,
|
|
|
|
binding_sig: None,
|
|
|
|
};
|
2018-11-20 05:37:21 -08:00
|
|
|
|
|
|
|
//
|
2020-10-09 15:19:44 -07:00
|
|
|
// Signatures -- everything but the signatures must already have been added.
|
2018-11-20 05:37:21 -08:00
|
|
|
//
|
|
|
|
|
|
|
|
let mut sighash = [0u8; 32];
|
|
|
|
sighash.copy_from_slice(&signature_hash_data(
|
2021-04-01 13:08:17 -07:00
|
|
|
&mtx,
|
2018-11-20 05:37:21 -08:00
|
|
|
consensus_branch_id,
|
|
|
|
SIGHASH_ALL,
|
2020-06-03 19:39:43 -07:00
|
|
|
SignableInput::Shielded,
|
2018-11-20 05:37:21 -08:00
|
|
|
));
|
|
|
|
|
2021-04-01 13:08:17 -07:00
|
|
|
let (sapling_spend_auth_sigs, sapling_binding_sig) = self
|
|
|
|
.sapling_builder
|
2021-04-01 16:35:32 -07:00
|
|
|
.create_signatures(prover, &mut ctx, &mut self.rng, &sighash, &tx_metadata)
|
|
|
|
.map_err(Error::SaplingBuildError)?;
|
2020-04-03 12:13:39 -07:00
|
|
|
|
2021-04-01 13:08:17 -07:00
|
|
|
for (i, spend_auth_sig) in sapling_spend_auth_sigs.into_iter().enumerate() {
|
|
|
|
mtx.shielded_spends[i].spend_auth_sig = spend_auth_sig;
|
|
|
|
}
|
|
|
|
mtx.binding_sig = sapling_binding_sig;
|
2018-11-20 05:37:21 -08:00
|
|
|
|
2020-09-23 09:53:48 -07:00
|
|
|
#[cfg(feature = "zfuture")]
|
2021-04-01 13:08:17 -07:00
|
|
|
{
|
|
|
|
// Create TZE input witnesses
|
2021-04-01 17:10:01 -07:00
|
|
|
let tze_payloads = self
|
|
|
|
.tze_builder
|
|
|
|
.create_signatures(&mtx)
|
|
|
|
.map_err(Error::TzeBuildError)?;
|
2021-04-01 13:08:17 -07:00
|
|
|
for (i, payload) in tze_payloads.into_iter().enumerate() {
|
|
|
|
mtx.tze_inputs[i].witness.payload = payload;
|
2020-09-09 09:54:18 -07:00
|
|
|
}
|
2020-05-06 16:52:03 -07:00
|
|
|
}
|
|
|
|
|
2021-04-01 13:08:17 -07:00
|
|
|
#[cfg(feature = "transparent-inputs")]
|
|
|
|
{
|
|
|
|
let script_sigs = self
|
|
|
|
.transparent_builder
|
|
|
|
.create_signatures(&mtx, consensus_branch_id);
|
|
|
|
|
|
|
|
for (i, sig) in script_sigs.into_iter().enumerate() {
|
|
|
|
mtx.vin[i].script_sig = sig;
|
|
|
|
}
|
|
|
|
}
|
2019-07-31 08:20:13 -07:00
|
|
|
|
2018-11-20 05:37:21 -08:00
|
|
|
Ok((
|
2021-04-01 13:08:17 -07:00
|
|
|
mtx.freeze().expect("Transaction should be complete"),
|
2018-11-20 05:37:21 -08:00
|
|
|
tx_metadata,
|
|
|
|
))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-23 09:53:48 -07:00
|
|
|
#[cfg(feature = "zfuture")]
|
2020-05-06 16:52:03 -07:00
|
|
|
impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng> ExtensionTxBuilder<'a>
|
|
|
|
for Builder<'a, P, R>
|
|
|
|
{
|
|
|
|
type BuildCtx = TransactionData;
|
2021-04-01 17:10:01 -07:00
|
|
|
type BuildError = tzebuilder::Error;
|
2020-05-06 16:52:03 -07:00
|
|
|
|
|
|
|
fn add_tze_input<WBuilder, W: ToPayload>(
|
|
|
|
&mut self,
|
2020-06-03 19:38:05 -07:00
|
|
|
extension_id: u32,
|
2020-09-09 09:54:18 -07:00
|
|
|
mode: u32,
|
2021-04-01 13:08:17 -07:00
|
|
|
prevout: (TzeOutPoint, TzeOut),
|
2020-05-06 16:52:03 -07:00
|
|
|
witness_builder: WBuilder,
|
|
|
|
) -> Result<(), Self::BuildError>
|
|
|
|
where
|
2021-04-01 17:10:01 -07:00
|
|
|
WBuilder: 'a + (FnOnce(&Self::BuildCtx) -> Result<W, tzebuilder::Error>),
|
2020-05-06 16:52:03 -07:00
|
|
|
{
|
2021-04-01 13:08:17 -07:00
|
|
|
self.tze_builder
|
|
|
|
.add_input(extension_id, mode, prevout, witness_builder);
|
|
|
|
|
2020-05-06 16:52:03 -07:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn add_tze_output<G: ToPayload>(
|
|
|
|
&mut self,
|
2020-06-03 19:38:05 -07:00
|
|
|
extension_id: u32,
|
2020-05-06 16:52:03 -07:00
|
|
|
value: Amount,
|
|
|
|
guarded_by: &G,
|
|
|
|
) -> Result<(), Self::BuildError> {
|
2021-04-01 13:08:17 -07:00
|
|
|
self.tze_builder.add_output(extension_id, value, guarded_by)
|
2020-05-06 16:52:03 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-21 11:37:21 -07:00
|
|
|
#[cfg(any(test, feature = "test-dependencies"))]
|
|
|
|
impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> {
|
|
|
|
/// Creates a new `Builder` targeted for inclusion in the block with the given height
|
|
|
|
/// and randomness source, using default values for general transaction fields.
|
|
|
|
///
|
|
|
|
/// # Default values
|
|
|
|
///
|
|
|
|
/// The expiry height will be set to the given height plus the default transaction
|
|
|
|
/// expiry delta (20 blocks).
|
|
|
|
///
|
|
|
|
/// The fee will be set to the default fee (0.0001 ZEC).
|
|
|
|
///
|
|
|
|
/// WARNING: DO NOT USE IN PRODUCTION
|
|
|
|
pub fn test_only_new_with_rng(params: P, height: BlockHeight, rng: R) -> Builder<'a, P, R> {
|
2021-04-01 13:08:17 -07:00
|
|
|
Self::new_internal(params, height, rng)
|
2020-10-21 11:37:21 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Creates a new `Builder` targeted for inclusion in the block with the given height,
|
|
|
|
/// and randomness source, using default values for general transaction fields
|
|
|
|
/// and the `ZFUTURE_TX_VERSION` and `ZFUTURE_VERSION_GROUP_ID` version identifiers.
|
|
|
|
///
|
|
|
|
/// # Default values
|
|
|
|
///
|
|
|
|
/// The expiry height will be set to the given height plus the default transaction
|
|
|
|
/// expiry delta (20 blocks).
|
|
|
|
///
|
|
|
|
/// The fee will be set to the default fee (0.0001 ZEC).
|
|
|
|
///
|
|
|
|
/// The transaction will be constructed and serialized according to the
|
|
|
|
/// NetworkUpgrade::ZFuture rules. This is intended only for use in
|
|
|
|
/// integration testing of new features.
|
|
|
|
///
|
|
|
|
/// WARNING: DO NOT USE IN PRODUCTION
|
|
|
|
#[cfg(feature = "zfuture")]
|
|
|
|
pub fn test_only_new_with_rng_zfuture(
|
|
|
|
params: P,
|
|
|
|
height: BlockHeight,
|
|
|
|
rng: R,
|
|
|
|
) -> Builder<'a, P, R> {
|
2021-04-01 13:08:17 -07:00
|
|
|
Self::new_internal(params, height, rng)
|
2020-10-21 11:37:21 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn mock_build(
|
|
|
|
self,
|
2021-04-01 13:08:17 -07:00
|
|
|
version: TxVersion,
|
2020-10-21 11:37:21 -07:00
|
|
|
consensus_branch_id: consensus::BranchId,
|
2021-04-01 13:08:17 -07:00
|
|
|
) -> Result<(Transaction, SaplingMetadata), Error> {
|
|
|
|
self.build(version, consensus_branch_id, &MockTxProver)
|
2020-10-21 11:37:21 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-11-20 05:37:21 -08:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use ff::{Field, PrimeField};
|
2019-09-28 00:48:43 -07:00
|
|
|
use rand_core::OsRng;
|
2019-08-06 02:46:40 -07:00
|
|
|
|
2018-11-20 05:37:21 -08:00
|
|
|
use crate::{
|
2020-09-17 10:55:39 -07:00
|
|
|
consensus::{self, Parameters, H0, TEST_NETWORK},
|
2019-05-24 05:32:55 -07:00
|
|
|
legacy::TransparentAddress,
|
2018-11-20 05:37:21 -08:00
|
|
|
merkle_tree::{CommitmentTree, IncrementalWitness},
|
2021-03-04 13:26:39 -08:00
|
|
|
sapling::{prover::mock::MockTxProver, Node, Rseed},
|
2021-04-01 13:08:17 -07:00
|
|
|
transaction::{
|
2021-05-11 13:17:54 -07:00
|
|
|
components::{
|
2021-04-01 16:35:32 -07:00
|
|
|
amount::{Amount, DEFAULT_FEE},
|
|
|
|
sapling::builder::{self as sapling},
|
|
|
|
transparent::builder::{self as transparent},
|
2021-05-11 13:17:54 -07:00
|
|
|
},
|
2021-04-01 13:08:17 -07:00
|
|
|
TxVersion,
|
|
|
|
},
|
2018-11-20 05:37:21 -08:00
|
|
|
zip32::{ExtendedFullViewingKey, ExtendedSpendingKey},
|
|
|
|
};
|
|
|
|
|
2021-04-01 13:08:17 -07:00
|
|
|
use super::{Builder, Error, SaplingBuilder, DEFAULT_TX_EXPIRY_DELTA};
|
2020-09-23 09:53:48 -07:00
|
|
|
|
2021-04-01 16:35:32 -07:00
|
|
|
#[cfg(not(feature = "zfuture"))]
|
|
|
|
use std::marker::PhantomData;
|
|
|
|
|
2020-09-23 09:53:48 -07:00
|
|
|
#[cfg(feature = "zfuture")]
|
2021-04-01 13:08:17 -07:00
|
|
|
use super::TzeBuilder;
|
2020-05-18 19:27:19 -07:00
|
|
|
|
2018-11-20 05:37:21 -08:00
|
|
|
#[test]
|
|
|
|
fn fails_on_negative_output() {
|
|
|
|
let extsk = ExtendedSpendingKey::master(&[]);
|
|
|
|
let extfvk = ExtendedFullViewingKey::from(&extsk);
|
|
|
|
let ovk = extfvk.fvk.ovk;
|
|
|
|
let to = extfvk.default_address().unwrap().1;
|
|
|
|
|
2020-09-17 10:55:39 -07:00
|
|
|
let mut builder = Builder::new(TEST_NETWORK, H0);
|
2019-08-13 07:24:08 -07:00
|
|
|
assert_eq!(
|
2020-08-28 08:12:37 -07:00
|
|
|
builder.add_sapling_output(Some(ovk), to, Amount::from_i64(-1).unwrap(), None),
|
2021-04-01 16:35:32 -07:00
|
|
|
Err(Error::SaplingBuildError(sapling::Error::InvalidAmount))
|
2019-08-13 07:24:08 -07:00
|
|
|
);
|
2018-11-20 05:37:21 -08:00
|
|
|
}
|
|
|
|
|
2020-04-03 12:13:39 -07:00
|
|
|
#[test]
|
|
|
|
fn binding_sig_absent_if_no_shielded_spend_or_output() {
|
2020-09-17 10:55:39 -07:00
|
|
|
use crate::consensus::NetworkUpgrade;
|
2021-04-01 13:08:17 -07:00
|
|
|
use crate::transaction::builder::{self, TransparentBuilder};
|
2020-04-03 12:13:39 -07:00
|
|
|
|
2020-09-17 10:55:39 -07:00
|
|
|
let sapling_activation_height = TEST_NETWORK
|
2020-08-05 13:27:40 -07:00
|
|
|
.activation_height(NetworkUpgrade::Sapling)
|
|
|
|
.unwrap();
|
2020-08-02 22:39:36 -07:00
|
|
|
|
2020-04-03 12:13:39 -07:00
|
|
|
// Create a builder with 0 fee, so we can construct t outputs
|
2020-08-05 13:27:40 -07:00
|
|
|
let mut builder = builder::Builder {
|
2020-09-17 10:55:39 -07:00
|
|
|
params: TEST_NETWORK,
|
2020-04-03 12:13:39 -07:00
|
|
|
rng: OsRng,
|
2021-04-01 13:08:17 -07:00
|
|
|
target_height: sapling_activation_height,
|
|
|
|
expiry_height: sapling_activation_height + DEFAULT_TX_EXPIRY_DELTA,
|
2020-04-03 12:13:39 -07:00
|
|
|
fee: Amount::zero(),
|
2021-05-11 13:17:54 -07:00
|
|
|
transparent_builder: TransparentBuilder::empty(),
|
2021-06-02 11:35:18 -07:00
|
|
|
sapling_builder: SaplingBuilder::empty(sapling_activation_height),
|
|
|
|
change_address: None,
|
2020-09-23 09:53:48 -07:00
|
|
|
#[cfg(feature = "zfuture")]
|
2021-04-01 17:10:01 -07:00
|
|
|
tze_builder: TzeBuilder::empty(),
|
2021-04-01 16:35:32 -07:00
|
|
|
#[cfg(not(feature = "zfuture"))]
|
|
|
|
tze_builder: PhantomData,
|
2021-05-26 13:58:08 -07:00
|
|
|
progress_notifier: None,
|
2020-04-03 12:13:39 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
// Create a tx with only t output. No binding_sig should be present
|
|
|
|
builder
|
|
|
|
.add_transparent_output(&TransparentAddress::PublicKey([0; 20]), Amount::zero())
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let (tx, _) = builder
|
2021-04-01 13:08:17 -07:00
|
|
|
.build(
|
|
|
|
TxVersion::Sapling,
|
|
|
|
consensus::BranchId::Sapling,
|
|
|
|
&MockTxProver,
|
|
|
|
)
|
2020-04-03 12:13:39 -07:00
|
|
|
.unwrap();
|
|
|
|
// No binding signature, because only t input and outputs
|
|
|
|
assert!(tx.binding_sig.is_none());
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn binding_sig_present_if_shielded_spend() {
|
|
|
|
let extsk = ExtendedSpendingKey::master(&[]);
|
|
|
|
let extfvk = ExtendedFullViewingKey::from(&extsk);
|
|
|
|
let to = extfvk.default_address().unwrap().1;
|
|
|
|
|
|
|
|
let mut rng = OsRng;
|
|
|
|
|
|
|
|
let note1 = to
|
2020-07-01 13:26:54 -07:00
|
|
|
.create_note(50000, Rseed::BeforeZip212(jubjub::Fr::random(&mut rng)))
|
2020-04-03 12:13:39 -07:00
|
|
|
.unwrap();
|
2020-08-21 10:33:22 -07:00
|
|
|
let cmu1 = Node::new(note1.cmu().to_repr());
|
2021-02-04 06:34:41 -08:00
|
|
|
let mut tree = CommitmentTree::empty();
|
2020-08-21 10:33:22 -07:00
|
|
|
tree.append(cmu1).unwrap();
|
2020-04-03 12:13:39 -07:00
|
|
|
let witness1 = IncrementalWitness::from_tree(&tree);
|
|
|
|
|
2020-09-17 10:55:39 -07:00
|
|
|
let mut builder = Builder::new(TEST_NETWORK, H0);
|
2020-04-03 12:13:39 -07:00
|
|
|
|
|
|
|
// Create a tx with a sapling spend. binding_sig should be present
|
|
|
|
builder
|
2020-10-30 06:27:49 -07:00
|
|
|
.add_sapling_spend(extsk, *to.diversifier(), note1, witness1.path().unwrap())
|
2020-04-03 12:13:39 -07:00
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
builder
|
|
|
|
.add_transparent_output(&TransparentAddress::PublicKey([0; 20]), Amount::zero())
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
// Expect a binding signature error, because our inputs aren't valid, but this shows
|
|
|
|
// that a binding signature was attempted
|
|
|
|
assert_eq!(
|
2021-04-01 13:08:17 -07:00
|
|
|
builder.build(
|
|
|
|
TxVersion::Sapling,
|
|
|
|
consensus::BranchId::Sapling,
|
|
|
|
&MockTxProver
|
|
|
|
),
|
2021-04-01 16:35:32 -07:00
|
|
|
Err(Error::SaplingBuildError(sapling::Error::BindingSig))
|
2020-04-03 12:13:39 -07:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2019-05-24 05:32:55 -07:00
|
|
|
#[test]
|
|
|
|
fn fails_on_negative_transparent_output() {
|
2020-09-17 10:55:39 -07:00
|
|
|
let mut builder = Builder::new(TEST_NETWORK, H0);
|
2019-08-13 07:24:08 -07:00
|
|
|
assert_eq!(
|
|
|
|
builder.add_transparent_output(
|
|
|
|
&TransparentAddress::PublicKey([0; 20]),
|
|
|
|
Amount::from_i64(-1).unwrap(),
|
|
|
|
),
|
2021-05-11 13:17:54 -07:00
|
|
|
Err(Error::TransparentBuildError(
|
|
|
|
transparent::Error::InvalidAmount
|
|
|
|
))
|
2019-08-13 07:24:08 -07:00
|
|
|
);
|
2019-05-24 05:32:55 -07:00
|
|
|
}
|
|
|
|
|
2018-11-20 05:37:21 -08:00
|
|
|
#[test]
|
|
|
|
fn fails_on_negative_change() {
|
|
|
|
let mut rng = OsRng;
|
|
|
|
|
|
|
|
// Just use the master key as the ExtendedSpendingKey for this test
|
|
|
|
let extsk = ExtendedSpendingKey::master(&[]);
|
|
|
|
|
|
|
|
// Fails with no inputs or outputs
|
|
|
|
// 0.0001 t-ZEC fee
|
|
|
|
{
|
2020-09-17 10:55:39 -07:00
|
|
|
let builder = Builder::new(TEST_NETWORK, H0);
|
2019-08-13 07:24:08 -07:00
|
|
|
assert_eq!(
|
2021-04-01 13:08:17 -07:00
|
|
|
builder.build(
|
|
|
|
TxVersion::Sapling,
|
|
|
|
consensus::BranchId::Sapling,
|
|
|
|
&MockTxProver
|
|
|
|
),
|
2021-05-11 08:57:28 -07:00
|
|
|
Err(Error::ChangeIsNegative(
|
|
|
|
(Amount::zero() - DEFAULT_FEE).unwrap()
|
|
|
|
))
|
2019-08-13 07:24:08 -07:00
|
|
|
);
|
2018-11-20 05:37:21 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
let extfvk = ExtendedFullViewingKey::from(&extsk);
|
2020-08-28 08:12:37 -07:00
|
|
|
let ovk = Some(extfvk.fvk.ovk);
|
2018-11-20 05:37:21 -08:00
|
|
|
let to = extfvk.default_address().unwrap().1;
|
|
|
|
|
|
|
|
// Fail if there is only a Sapling output
|
2020-11-23 09:41:30 -08:00
|
|
|
// 0.0005 z-ZEC out, 0.00001 t-ZEC fee
|
2018-11-20 05:37:21 -08:00
|
|
|
{
|
2020-09-17 10:55:39 -07:00
|
|
|
let mut builder = Builder::new(TEST_NETWORK, H0);
|
2018-11-20 05:37:21 -08:00
|
|
|
builder
|
2020-10-30 06:27:49 -07:00
|
|
|
.add_sapling_output(ovk, to.clone(), Amount::from_u64(50000).unwrap(), None)
|
2018-11-20 05:37:21 -08:00
|
|
|
.unwrap();
|
2019-08-13 07:24:08 -07:00
|
|
|
assert_eq!(
|
2021-04-01 13:08:17 -07:00
|
|
|
builder.build(
|
|
|
|
TxVersion::Sapling,
|
|
|
|
consensus::BranchId::Sapling,
|
|
|
|
&MockTxProver
|
|
|
|
),
|
2020-11-23 09:41:30 -08:00
|
|
|
Err(Error::ChangeIsNegative(
|
2021-05-11 08:57:28 -07:00
|
|
|
(Amount::from_i64(-50000).unwrap() - DEFAULT_FEE).unwrap()
|
2020-11-23 09:41:30 -08:00
|
|
|
))
|
2019-08-13 07:24:08 -07:00
|
|
|
);
|
2018-11-20 05:37:21 -08:00
|
|
|
}
|
|
|
|
|
2019-05-24 05:32:55 -07:00
|
|
|
// Fail if there is only a transparent output
|
2020-11-23 09:41:30 -08:00
|
|
|
// 0.0005 t-ZEC out, 0.00001 t-ZEC fee
|
2019-05-24 05:32:55 -07:00
|
|
|
{
|
2020-09-17 10:55:39 -07:00
|
|
|
let mut builder = Builder::new(TEST_NETWORK, H0);
|
2019-05-24 05:32:55 -07:00
|
|
|
builder
|
2019-07-25 14:37:16 -07:00
|
|
|
.add_transparent_output(
|
|
|
|
&TransparentAddress::PublicKey([0; 20]),
|
|
|
|
Amount::from_u64(50000).unwrap(),
|
|
|
|
)
|
2019-05-24 05:32:55 -07:00
|
|
|
.unwrap();
|
2019-08-13 07:24:08 -07:00
|
|
|
assert_eq!(
|
2021-04-01 13:08:17 -07:00
|
|
|
builder.build(
|
|
|
|
TxVersion::Sapling,
|
|
|
|
consensus::BranchId::Sapling,
|
|
|
|
&MockTxProver
|
|
|
|
),
|
2020-11-23 09:41:30 -08:00
|
|
|
Err(Error::ChangeIsNegative(
|
2021-05-11 08:57:28 -07:00
|
|
|
(Amount::from_i64(-50000).unwrap() - DEFAULT_FEE).unwrap()
|
2020-11-23 09:41:30 -08:00
|
|
|
))
|
2019-08-13 07:24:08 -07:00
|
|
|
);
|
2019-05-24 05:32:55 -07:00
|
|
|
}
|
|
|
|
|
2018-11-20 05:37:21 -08:00
|
|
|
let note1 = to
|
2020-11-23 09:41:30 -08:00
|
|
|
.create_note(50999, Rseed::BeforeZip212(jubjub::Fr::random(&mut rng)))
|
2018-11-20 05:37:21 -08:00
|
|
|
.unwrap();
|
2020-08-21 10:33:22 -07:00
|
|
|
let cmu1 = Node::new(note1.cmu().to_repr());
|
2021-02-04 06:34:41 -08:00
|
|
|
let mut tree = CommitmentTree::empty();
|
2020-08-21 10:33:22 -07:00
|
|
|
tree.append(cmu1).unwrap();
|
2018-11-20 05:37:21 -08:00
|
|
|
let mut witness1 = IncrementalWitness::from_tree(&tree);
|
|
|
|
|
2019-05-24 05:32:55 -07:00
|
|
|
// Fail if there is insufficient input
|
2020-11-23 09:41:30 -08:00
|
|
|
// 0.0003 z-ZEC out, 0.0002 t-ZEC out, 0.00001 t-ZEC fee, 0.00050999 z-ZEC in
|
2018-11-20 05:37:21 -08:00
|
|
|
{
|
2020-09-17 10:55:39 -07:00
|
|
|
let mut builder = Builder::new(TEST_NETWORK, H0);
|
2018-11-20 05:37:21 -08:00
|
|
|
builder
|
|
|
|
.add_sapling_spend(
|
|
|
|
extsk.clone(),
|
2019-08-23 15:08:09 -07:00
|
|
|
*to.diversifier(),
|
2018-11-20 05:37:21 -08:00
|
|
|
note1.clone(),
|
2020-02-07 09:31:38 -08:00
|
|
|
witness1.path().unwrap(),
|
2018-11-20 05:37:21 -08:00
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
builder
|
2020-10-30 06:27:49 -07:00
|
|
|
.add_sapling_output(ovk, to.clone(), Amount::from_u64(30000).unwrap(), None)
|
2019-05-24 05:32:55 -07:00
|
|
|
.unwrap();
|
|
|
|
builder
|
2019-07-25 14:37:16 -07:00
|
|
|
.add_transparent_output(
|
|
|
|
&TransparentAddress::PublicKey([0; 20]),
|
|
|
|
Amount::from_u64(20000).unwrap(),
|
|
|
|
)
|
2018-11-20 05:37:21 -08:00
|
|
|
.unwrap();
|
2019-08-13 07:24:08 -07:00
|
|
|
assert_eq!(
|
2021-04-01 13:08:17 -07:00
|
|
|
builder.build(
|
|
|
|
TxVersion::Sapling,
|
|
|
|
consensus::BranchId::Sapling,
|
|
|
|
&MockTxProver
|
|
|
|
),
|
2019-08-13 07:24:08 -07:00
|
|
|
Err(Error::ChangeIsNegative(Amount::from_i64(-1).unwrap()))
|
|
|
|
);
|
2018-11-20 05:37:21 -08:00
|
|
|
}
|
|
|
|
|
2020-07-29 21:36:12 -07:00
|
|
|
let note2 = to
|
2020-07-01 13:26:54 -07:00
|
|
|
.create_note(1, Rseed::BeforeZip212(jubjub::Fr::random(&mut rng)))
|
2020-07-29 21:36:12 -07:00
|
|
|
.unwrap();
|
2020-08-21 10:33:22 -07:00
|
|
|
let cmu2 = Node::new(note2.cmu().to_repr());
|
|
|
|
tree.append(cmu2).unwrap();
|
|
|
|
witness1.append(cmu2).unwrap();
|
2018-11-20 05:37:21 -08:00
|
|
|
let witness2 = IncrementalWitness::from_tree(&tree);
|
|
|
|
|
|
|
|
// Succeeds if there is sufficient input
|
2019-05-24 05:32:55 -07:00
|
|
|
// 0.0003 z-ZEC out, 0.0002 t-ZEC out, 0.0001 t-ZEC fee, 0.0006 z-ZEC in
|
2018-11-20 05:37:21 -08:00
|
|
|
//
|
|
|
|
// (Still fails because we are using a MockTxProver which doesn't correctly
|
|
|
|
// compute bindingSig.)
|
|
|
|
{
|
2020-09-17 10:55:39 -07:00
|
|
|
let mut builder = Builder::new(TEST_NETWORK, H0);
|
2018-11-20 05:37:21 -08:00
|
|
|
builder
|
2020-02-07 09:31:38 -08:00
|
|
|
.add_sapling_spend(
|
|
|
|
extsk.clone(),
|
|
|
|
*to.diversifier(),
|
|
|
|
note1,
|
|
|
|
witness1.path().unwrap(),
|
|
|
|
)
|
2018-11-20 05:37:21 -08:00
|
|
|
.unwrap();
|
|
|
|
builder
|
2020-02-07 09:31:38 -08:00
|
|
|
.add_sapling_spend(extsk, *to.diversifier(), note2, witness2.path().unwrap())
|
2018-11-20 05:37:21 -08:00
|
|
|
.unwrap();
|
|
|
|
builder
|
2020-08-05 20:36:02 -07:00
|
|
|
.add_sapling_output(ovk, to, Amount::from_u64(30000).unwrap(), None)
|
2019-05-24 05:32:55 -07:00
|
|
|
.unwrap();
|
|
|
|
builder
|
2019-07-25 14:37:16 -07:00
|
|
|
.add_transparent_output(
|
|
|
|
&TransparentAddress::PublicKey([0; 20]),
|
|
|
|
Amount::from_u64(20000).unwrap(),
|
|
|
|
)
|
2018-11-20 05:37:21 -08:00
|
|
|
.unwrap();
|
2019-11-25 07:41:14 -08:00
|
|
|
assert_eq!(
|
2021-04-01 13:08:17 -07:00
|
|
|
builder.build(
|
|
|
|
TxVersion::Sapling,
|
|
|
|
consensus::BranchId::Sapling,
|
|
|
|
&MockTxProver
|
|
|
|
),
|
2021-04-01 16:35:32 -07:00
|
|
|
Err(Error::SaplingBuildError(sapling::Error::BindingSig))
|
2019-11-25 07:41:14 -08:00
|
|
|
)
|
2018-11-20 05:37:21 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|