diff --git a/zcash_client_backend/src/data_api/wallet.rs b/zcash_client_backend/src/data_api/wallet.rs index d96a47e284..160529dc6a 100644 --- a/zcash_client_backend/src/data_api/wallet.rs +++ b/zcash_client_backend/src/data_api/wallet.rs @@ -459,7 +459,7 @@ where // Create the transaction. The type of the proposal ensures that there // are no possible transparent inputs, so we ignore those - let mut builder = Builder::new(params.clone(), proposal.target_height()); + let mut builder = Builder::new(params.clone(), proposal.target_height(), None); for selected in proposal.sapling_inputs() { let (note, key, merkle_path) = select_key_for_note(selected, usk.sapling(), &dfvk) diff --git a/zcash_client_backend/src/fees/fixed.rs b/zcash_client_backend/src/fees/fixed.rs index 28b309bb5a..59732f8382 100644 --- a/zcash_client_backend/src/fees/fixed.rs +++ b/zcash_client_backend/src/fees/fixed.rs @@ -78,6 +78,8 @@ impl ChangeStrategy for SingleOutputChangeStrategy { transparent_outputs, sapling_inputs.len(), sapling_outputs.len() + 1, + //Orchard is not yet supported in zcash_client_backend + 0, ) .unwrap(); // fixed::FeeRule::fee_required is infallible. diff --git a/zcash_client_backend/src/fees/zip317.rs b/zcash_client_backend/src/fees/zip317.rs index 142bf7ce65..6d2ddc62ab 100644 --- a/zcash_client_backend/src/fees/zip317.rs +++ b/zcash_client_backend/src/fees/zip317.rs @@ -167,6 +167,8 @@ impl ChangeStrategy for SingleOutputChangeStrategy { // add one for Sapling change, then account for Sapling output padding performed by // the transaction builder std::cmp::max(sapling_outputs.len() + 1, 2), + //Orchard is not yet supported in zcash_client_backend + 0, ) .map_err(ChangeError::StrategyError)?; diff --git a/zcash_client_backend/src/keys.rs b/zcash_client_backend/src/keys.rs index 54a585dcb4..d7480c74d6 100644 --- a/zcash_client_backend/src/keys.rs +++ b/zcash_client_backend/src/keys.rs @@ -703,6 +703,8 @@ mod tests { #[test] #[cfg(feature = "transparent-inputs")] fn ufvk_derivation() { + use super::UnifiedSpendingKey; + for tv in test_vectors::UNIFIED { let usk = UnifiedSpendingKey::from_seed( &MAIN_NETWORK, diff --git a/zcash_extensions/src/transparent/demo.rs b/zcash_extensions/src/transparent/demo.rs index b949817327..b5cd5e0be0 100644 --- a/zcash_extensions/src/transparent/demo.rs +++ b/zcash_extensions/src/transparent/demo.rs @@ -638,7 +638,7 @@ mod tests { fn demo_builder<'a>(height: BlockHeight) -> DemoBuilder> { DemoBuilder { - txn_builder: Builder::new(FutureNetwork, height), + txn_builder: Builder::new(FutureNetwork, height, None), extension_id: 0, } } diff --git a/zcash_primitives/CHANGELOG.md b/zcash_primitives/CHANGELOG.md index 4f3d765178..47c2f3a4b6 100644 --- a/zcash_primitives/CHANGELOG.md +++ b/zcash_primitives/CHANGELOG.md @@ -7,6 +7,33 @@ and this library adheres to Rust's notion of ## [Unreleased] +### Added +- `zcash_primitives::transaction::builder`: + - `Builder::add_orchard_spend` + - `Builder::add_orchard_output` +- `zcash_primitives::transaction::components::orchard::builder` module + +### Changed +- `zcash_primitives::transaction`: + - `builder::Builder::{new, new_with_rng}` now take an optional `orchard_anchor` + argument which must be provided in order to enable Orchard spends and recipients. + - All `builder::Builder` methods now require the bound `R: CryptoRng` on + `Builder<'a, P, R>`. A non-`CryptoRng` randomness source is still accepted + by `builder::Builder::test_only_new_with_rng`, which **MUST NOT** be used in + production. + - `builder::Error` has several additional variants for Orchard-related errors. + - `fees::FeeRule::fee_required` now takes an additional argument, + `orchard_action_count` + - `Unauthorized`'s associated type `OrchardAuth` is now + `orchard::builder::InProgress` + instead of `zcash_primitives::transaction::components::orchard::Unauthorized` +- `zcash_primitives::consensus::NetworkUpgrade` now implements `PartialEq`, `Eq` + +### Removed +- `impl {PartialEq, Eq} for transaction::builder::Error` + (use `assert_matches!` where error comparisons are required) +- `zcash_primitives::transaction::components::orchard::Unauthorized` + ## [0.12.0] - 2023-06-06 ### Added - `zcash_primitives::transaction`: @@ -27,7 +54,7 @@ and this library adheres to Rust's notion of `incrementalmerkletree::Hashable` and `merkle_tree::HashSer`. - The `Hashable` bound on the `Node` parameter to the `IncrementalWitness` type has been removed. -- `sapling::SAPLING_COMMITMENT_TREE_DEPTH_U8` and `sapling::SAPLING_COMMITMENT_TREE_DEPTH` +- `sapling::SAPLING_COMMITMENT_TREE_DEPTH_U8` and `sapling::SAPLING_COMMITMENT_TREE_DEPTH` have been removed; use `sapling::NOTE_COMMITMENT_TREE_DEPTH` instead. - `merkle_tree::{CommitmentTree, IncrementalWitness, MerklePath}` have been removed in favor of versions of these types that are now provided by the @@ -89,7 +116,7 @@ and this library adheres to Rust's notion of - The bounds on the `H` parameter to the following methods have changed: - `merkle_tree::incremental::read_frontier_v0` - `merkle_tree::incremental::read_auth_fragment_v1` -- The depth of the `merkle_tree::{CommitmentTree, IncrementalWitness, and MerklePath}` +- The depth of the `merkle_tree::{CommitmentTree, IncrementalWitness, and MerklePath}` data types are now statically constrained using const generic type parameters. - `transaction::fees::fixed::FeeRule::standard()` now uses the ZIP 317 minimum fee (10000 zatoshis rather than 1000 zatoshis) as the fixed fee. To be compliant with diff --git a/zcash_primitives/src/consensus.rs b/zcash_primitives/src/consensus.rs index 02ceffa165..dc972f7008 100644 --- a/zcash_primitives/src/consensus.rs +++ b/zcash_primitives/src/consensus.rs @@ -361,7 +361,7 @@ impl Parameters for Network { /// consensus rules enforced by the network are altered. /// /// See [ZIP 200](https://zips.z.cash/zip-0200) for more details. -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum NetworkUpgrade { /// The [Overwinter] network upgrade. /// diff --git a/zcash_primitives/src/transaction/builder.rs b/zcash_primitives/src/transaction/builder.rs index 958a64454e..ae016adc05 100644 --- a/zcash_primitives/src/transaction/builder.rs +++ b/zcash_primitives/src/transaction/builder.rs @@ -8,7 +8,7 @@ use std::sync::mpsc::Sender; use rand::{rngs::OsRng, CryptoRng, RngCore}; use crate::{ - consensus::{self, BlockHeight, BranchId}, + consensus::{self, BlockHeight, BranchId, NetworkUpgrade}, keys::OutgoingViewingKey, legacy::TransparentAddress, memo::MemoBytes, @@ -50,7 +50,7 @@ use crate::{ const DEFAULT_TX_EXPIRY_DELTA: u32 = 40; /// Errors that can occur during transaction construction. -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug)] pub enum Error { /// Insufficient funds were provided to the transaction builder; the given /// additional amount is required in order to construct the transaction. @@ -66,6 +66,15 @@ pub enum Error { TransparentBuild(transparent::builder::Error), /// An error occurred in constructing the Sapling parts of a transaction. SaplingBuild(sapling_builder::Error), + /// An error occurred in constructing the Orchard parts of a transaction. + OrchardBuild(orchard::builder::BuildError), + /// An error occurred in adding an Orchard Spend to a transaction. + OrchardSpend(orchard::builder::SpendError), + /// An error occurred in adding an Orchard Output to a transaction. + OrchardRecipient(orchard::builder::OutputError), + /// The builder was constructed either without an Orchard anchor or before NU5 + /// activation, but an Orchard spend or recipient was added. + OrchardAnchorNotAvailable, /// An error occurred in constructing the TZE parts of a transaction. #[cfg(feature = "zfuture")] TzeBuild(tze::builder::Error), @@ -88,6 +97,13 @@ impl fmt::Display for Error { Error::Fee(e) => write!(f, "An error occurred in fee calculation: {}", e), Error::TransparentBuild(err) => err.fmt(f), Error::SaplingBuild(err) => err.fmt(f), + Error::OrchardBuild(err) => write!(f, "{:?}", err), + Error::OrchardSpend(err) => write!(f, "Could not add Orchard spend: {}", err), + Error::OrchardRecipient(err) => write!(f, "Could not add Orchard recipient: {}", err), + Error::OrchardAnchorNotAvailable => write!( + f, + "Cannot create Orchard transactions without an Orchard anchor, or before NU5 activation" + ), #[cfg(feature = "zfuture")] Error::TzeBuild(err) => err.fmt(f), } @@ -139,6 +155,12 @@ pub struct Builder<'a, P, R> { expiry_height: BlockHeight, transparent_builder: TransparentBuilder, sapling_builder: SaplingBuilder

, + orchard_builder: Option, + // TODO: In the future, instead of taking the spending keys as arguments when calling + // `add_sapling_spend` or `add_orchard_spend`, we will build an unauthorized, unproven + // transaction, and then the caller will be responsible for using the spending keys or their + // derivatives for proving and signing to complete transaction creation. + orchard_saks: Vec, #[cfg(feature = "zfuture")] tze_builder: TzeBuilder<'a, TransactionData>, #[cfg(not(feature = "zfuture"))] @@ -190,8 +212,12 @@ impl<'a, P: consensus::Parameters> Builder<'a, P, OsRng> { /// /// The expiry height will be set to the given height plus the default transaction /// expiry delta (20 blocks). - pub fn new(params: P, target_height: BlockHeight) -> Self { - Builder::new_with_rng(params, target_height, OsRng) + pub fn new( + params: P, + target_height: BlockHeight, + orchard_anchor: Option, + ) -> Self { + Builder::new_with_rng(params, target_height, orchard_anchor, OsRng) } } @@ -202,18 +228,34 @@ impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng> Builder<'a, P, R> { /// # Default values /// /// The expiry height will be set to the given height plus the default transaction - /// expiry delta. - pub fn new_with_rng(params: P, target_height: BlockHeight, rng: R) -> Builder<'a, P, R> { - Self::new_internal(params, rng, target_height) + /// expiry delta (20 blocks). + pub fn new_with_rng( + params: P, + target_height: BlockHeight, + orchard_anchor: Option, + rng: R, + ) -> Builder<'a, P, R> { + let orchard_builder = if params.is_nu_active(NetworkUpgrade::Nu5, target_height) { + orchard_anchor.map(|anchor| { + orchard::builder::Builder::new( + orchard::bundle::Flags::from_parts(true, true), + anchor, + ) + }) + } else { + None + }; + + Self::new_internal(params, rng, target_height, orchard_builder) } -} -impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> { /// Common utility function for builder construction. - /// - /// WARNING: THIS MUST REMAIN PRIVATE AS IT ALLOWS CONSTRUCTION - /// OF BUILDERS WITH NON-CryptoRng RNGs - fn new_internal(params: P, rng: R, target_height: BlockHeight) -> Builder<'a, P, R> { + fn new_internal( + params: P, + rng: R, + target_height: BlockHeight, + orchard_builder: Option, + ) -> Self { Builder { params: params.clone(), rng, @@ -221,6 +263,8 @@ impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> { expiry_height: target_height + DEFAULT_TX_EXPIRY_DELTA, transparent_builder: TransparentBuilder::empty(), sapling_builder: SaplingBuilder::new(params, target_height), + orchard_builder, + orchard_saks: Vec::new(), #[cfg(feature = "zfuture")] tze_builder: TzeBuilder::empty(), #[cfg(not(feature = "zfuture"))] @@ -229,6 +273,48 @@ impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> { } } + /// Adds an Orchard note to be spent in this bundle. + /// + /// Returns an error if the given Merkle path does not have the required anchor for + /// the given note. + pub fn add_orchard_spend( + &mut self, + sk: orchard::keys::SpendingKey, + note: orchard::Note, + merkle_path: orchard::tree::MerklePath, + ) -> Result<(), Error> { + self.orchard_builder + .as_mut() + .ok_or(Error::OrchardAnchorNotAvailable)? + .add_spend(orchard::keys::FullViewingKey::from(&sk), note, merkle_path) + .map_err(Error::OrchardSpend)?; + + self.orchard_saks + .push(orchard::keys::SpendAuthorizingKey::from(&sk)); + + Ok(()) + } + + /// Adds an Orchard recipient to the transaction. + pub fn add_orchard_output( + &mut self, + ovk: Option, + recipient: orchard::Address, + value: u64, + memo: MemoBytes, + ) -> Result<(), Error> { + self.orchard_builder + .as_mut() + .ok_or(Error::OrchardAnchorNotAvailable)? + .add_recipient( + ovk, + recipient, + orchard::value::NoteValue::from_raw(value), + Some(*memo.as_array()), + ) + .map_err(Error::OrchardRecipient) + } + /// Adds a Sapling note to be spent in this transaction. /// /// Returns an error if the given Merkle path does not have the same anchor as the @@ -295,11 +381,18 @@ impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> { self.progress_notifier = Some(progress_notifier); } - /// Returns the sum of the transparent, Sapling, and TZE value balances. + /// Returns the sum of the transparent, Sapling, Orchard, and TZE value balances. fn value_balance(&self) -> Result { let value_balances = [ self.transparent_builder.value_balance()?, self.sapling_builder.value_balance(), + if let Some(builder) = &self.orchard_builder { + builder + .value_balance() + .map_err(|_| BalanceError::Overflow)? + } else { + Amount::zero() + }, #[cfg(feature = "zfuture")] self.tze_builder.value_balance()?, ]; @@ -327,6 +420,17 @@ impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> { self.transparent_builder.outputs(), self.sapling_builder.inputs().len(), self.sapling_builder.bundle_output_count(), + match std::cmp::max( + self.orchard_builder + .as_ref() + .map_or(0, |builder| builder.outputs().len()), + self.orchard_builder + .as_ref() + .map_or(0, |builder| builder.spends().len()), + ) { + 1 => 2, + n => n, + }, ) .map_err(Error::Fee)?; self.build_internal(prover, fee) @@ -400,6 +504,13 @@ impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> { ) .map_err(Error::SaplingBuild)?; + let orchard_bundle: Option> = + if let Some(builder) = self.orchard_builder { + Some(builder.build(&mut rng).map_err(Error::OrchardBuild)?) + } else { + None + }; + #[cfg(feature = "zfuture")] let (tze_bundle, tze_signers) = self.tze_builder.build(); @@ -411,7 +522,7 @@ impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> { transparent_bundle, sprout_bundle: None, sapling_bundle, - orchard_bundle: None, + orchard_bundle, #[cfg(feature = "zfuture")] tze_bundle, }; @@ -456,6 +567,21 @@ impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> { None => (None, SaplingMetadata::empty()), }; + let orchard_bundle = unauthed_tx + .orchard_bundle + .map(|b| { + b.create_proof(&orchard::circuit::ProvingKey::build(), &mut rng) + .and_then(|b| { + b.apply_signatures( + &mut rng, + *shielded_sig_commitment.as_ref(), + &self.orchard_saks, + ) + }) + }) + .transpose() + .map_err(Error::OrchardBuild)?; + let authorized_tx = TransactionData { version: unauthed_tx.version, consensus_branch_id: unauthed_tx.consensus_branch_id, @@ -464,7 +590,7 @@ impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> { transparent_bundle, sprout_bundle: unauthed_tx.sprout_bundle, sapling_bundle, - orchard_bundle: None, + orchard_bundle, #[cfg(feature = "zfuture")] tze_bundle, }; @@ -511,13 +637,15 @@ impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng> ExtensionTxBuilder<'a #[cfg(any(test, feature = "test-dependencies"))] mod testing { use rand::RngCore; + use rand_core::CryptoRng; use std::convert::Infallible; use super::{Builder, Error, SaplingMetadata}; use crate::{ consensus::{self, BlockHeight}, sapling::prover::mock::MockTxProver, - transaction::{fees::fixed, Transaction}, + transaction::fees::fixed, + transaction::Transaction, }; impl<'a, P: consensus::Parameters, R: RngCore> Builder<'a, P, R> { @@ -530,10 +658,34 @@ mod testing { /// expiry delta. /// /// WARNING: DO NOT USE IN PRODUCTION - pub fn test_only_new_with_rng(params: P, height: BlockHeight, rng: R) -> Builder<'a, P, R> { - Self::new_internal(params, rng, height) + pub fn test_only_new_with_rng( + params: P, + height: BlockHeight, + rng: R, + ) -> Builder<'a, P, impl RngCore + CryptoRng> { + struct FakeCryptoRng(R); + impl CryptoRng for FakeCryptoRng {} + impl RngCore for FakeCryptoRng { + fn next_u32(&mut self) -> u32 { + self.0.next_u32() + } + + fn next_u64(&mut self) -> u64 { + self.0.next_u64() + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + self.0.fill_bytes(dest) + } + + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> { + self.0.try_fill_bytes(dest) + } + } + Builder::new_internal(params, FakeCryptoRng(rng), height, None) } - + } + impl<'a, P: consensus::Parameters, R: RngCore + CryptoRng> Builder<'a, P, R> { pub fn mock_build(self) -> Result<(Transaction, SaplingMetadata), Error> { #[allow(deprecated)] self.build(&MockTxProver, &fixed::FeeRule::standard()) @@ -543,6 +695,7 @@ mod testing { #[cfg(test)] mod tests { + use assert_matches::assert_matches; use ff::Field; use incrementalmerkletree::{frontier::CommitmentTree, witness::IncrementalWitness}; use rand_core::OsRng; @@ -587,7 +740,7 @@ mod tests { .activation_height(NetworkUpgrade::Sapling) .unwrap(); - let mut builder = Builder::new(TEST_NETWORK, sapling_activation_height); + let mut builder = Builder::new(TEST_NETWORK, sapling_activation_height, None); assert_eq!( builder.add_sapling_output( Some(ovk), @@ -624,6 +777,8 @@ mod tests { #[cfg(not(feature = "zfuture"))] tze_builder: std::marker::PhantomData, progress_notifier: None, + orchard_builder: None, + orchard_saks: Vec::new(), }; let tsk = AccountPrivKey::from_seed(&TEST_NETWORK, &[0u8; 32], AccountId::from(0)).unwrap(); @@ -675,7 +830,7 @@ mod tests { let tx_height = TEST_NETWORK .activation_height(NetworkUpgrade::Sapling) .unwrap(); - let mut builder = Builder::new(TEST_NETWORK, tx_height); + let mut builder = Builder::new(TEST_NETWORK, tx_height, None); // Create a tx with a sapling spend. binding_sig should be present builder @@ -691,7 +846,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!( + assert_matches!( builder.mock_build(), Err(Error::SaplingBuild(sapling_builder::Error::BindingSig)) ); @@ -702,7 +857,7 @@ mod tests { let tx_height = TEST_NETWORK .activation_height(NetworkUpgrade::Sapling) .unwrap(); - let mut builder = Builder::new(TEST_NETWORK, tx_height); + let mut builder = Builder::new(TEST_NETWORK, tx_height, None); assert_eq!( builder.add_transparent_output( &TransparentAddress::PublicKey([0; 20]), @@ -727,8 +882,8 @@ mod tests { // Fails with no inputs or outputs // 0.0001 t-ZEC fee { - let builder = Builder::new(TEST_NETWORK, tx_height); - assert_eq!( + let builder = Builder::new(TEST_NETWORK, tx_height, None); + assert_matches!( builder.mock_build(), Err(Error::InsufficientFunds(MINIMUM_FEE)) ); @@ -741,7 +896,7 @@ mod tests { // Fail if there is only a Sapling output // 0.0005 z-ZEC out, 0.0001 t-ZEC fee { - let mut builder = Builder::new(TEST_NETWORK, tx_height); + let mut builder = Builder::new(TEST_NETWORK, tx_height, None); builder .add_sapling_output( ovk, @@ -750,29 +905,27 @@ mod tests { MemoBytes::empty(), ) .unwrap(); - assert_eq!( + assert_matches!( builder.mock_build(), - Err(Error::InsufficientFunds( - (Amount::from_i64(50000).unwrap() + MINIMUM_FEE).unwrap() - )) + Err(Error::InsufficientFunds(expected)) if + expected == (Amount::from_i64(50000).unwrap() + MINIMUM_FEE).unwrap() ); } // Fail if there is only a transparent output // 0.0005 t-ZEC out, 0.0001 t-ZEC fee { - let mut builder = Builder::new(TEST_NETWORK, tx_height); + let mut builder = Builder::new(TEST_NETWORK, tx_height, None); builder .add_transparent_output( &TransparentAddress::PublicKey([0; 20]), Amount::from_u64(50000).unwrap(), ) .unwrap(); - assert_eq!( + assert_matches!( builder.mock_build(), - Err(Error::InsufficientFunds( + Err(Error::InsufficientFunds(expected)) if expected == (Amount::from_i64(50000).unwrap() + MINIMUM_FEE).unwrap() - )) ); } @@ -785,7 +938,7 @@ mod tests { // Fail if there is insufficient input // 0.0003 z-ZEC out, 0.0002 t-ZEC out, 0.0001 t-ZEC fee, 0.00059999 z-ZEC in { - let mut builder = Builder::new(TEST_NETWORK, tx_height); + let mut builder = Builder::new(TEST_NETWORK, tx_height, None); builder .add_sapling_spend( extsk.clone(), @@ -808,9 +961,9 @@ mod tests { Amount::from_u64(20000).unwrap(), ) .unwrap(); - assert_eq!( + assert_matches!( builder.mock_build(), - Err(Error::InsufficientFunds(Amount::from_i64(1).unwrap())) + Err(Error::InsufficientFunds(expected)) if expected == Amount::from_i64(1).unwrap() ); } @@ -826,7 +979,7 @@ mod tests { // (Still fails because we are using a MockTxProver which doesn't correctly // compute bindingSig.) { - let mut builder = Builder::new(TEST_NETWORK, tx_height); + let mut builder = Builder::new(TEST_NETWORK, tx_height, None); builder .add_sapling_spend( extsk.clone(), @@ -852,7 +1005,7 @@ mod tests { Amount::from_u64(20000).unwrap(), ) .unwrap(); - assert_eq!( + assert_matches!( builder.mock_build(), Err(Error::SaplingBuild(sapling_builder::Error::BindingSig)) ) diff --git a/zcash_primitives/src/transaction/components/orchard.rs b/zcash_primitives/src/transaction/components/orchard.rs index 440e8ab8b4..9f7df66b6a 100644 --- a/zcash_primitives/src/transaction/components/orchard.rs +++ b/zcash_primitives/src/transaction/components/orchard.rs @@ -20,14 +20,6 @@ pub const FLAG_SPENDS_ENABLED: u8 = 0b0000_0001; pub const FLAG_OUTPUTS_ENABLED: u8 = 0b0000_0010; pub const FLAGS_EXPECTED_UNSET: u8 = !(FLAG_SPENDS_ENABLED | FLAG_OUTPUTS_ENABLED); -/// Marker for a bundle with no proofs or signatures. -#[derive(Debug)] -pub struct Unauthorized; - -impl Authorization for Unauthorized { - type SpendAuth = (); -} - pub trait MapAuth { fn map_spend_auth(&self, s: A::SpendAuth) -> B::SpendAuth; fn map_authorization(&self, a: A) -> B; diff --git a/zcash_primitives/src/transaction/fees.rs b/zcash_primitives/src/transaction/fees.rs index e29913acc1..515c74f272 100644 --- a/zcash_primitives/src/transaction/fees.rs +++ b/zcash_primitives/src/transaction/fees.rs @@ -21,6 +21,7 @@ pub trait FeeRule { /// Implementations of this method should compute the fee amount given exactly the inputs and /// outputs specified, and should NOT compute speculative fees given any additional change /// outputs that may need to be created in order for inputs and outputs to balance. + #[allow(clippy::too_many_arguments)] fn fee_required( &self, params: &P, @@ -29,6 +30,7 @@ pub trait FeeRule { transparent_outputs: &[impl transparent::OutputView], sapling_input_count: usize, sapling_output_count: usize, + orchard_action_count: usize, ) -> Result; } diff --git a/zcash_primitives/src/transaction/fees/fixed.rs b/zcash_primitives/src/transaction/fees/fixed.rs index c3028dcace..0b2278a544 100644 --- a/zcash_primitives/src/transaction/fees/fixed.rs +++ b/zcash_primitives/src/transaction/fees/fixed.rs @@ -56,6 +56,7 @@ impl super::FeeRule for FeeRule { _transparent_outputs: &[impl transparent::OutputView], _sapling_input_count: usize, _sapling_output_count: usize, + _orchard_action_count: usize, ) -> Result { Ok(self.fixed_fee) } diff --git a/zcash_primitives/src/transaction/fees/zip317.rs b/zcash_primitives/src/transaction/fees/zip317.rs index 60fcd767f9..21ecf4cb95 100644 --- a/zcash_primitives/src/transaction/fees/zip317.rs +++ b/zcash_primitives/src/transaction/fees/zip317.rs @@ -129,6 +129,7 @@ impl super::FeeRule for FeeRule { transparent_outputs: &[impl transparent::OutputView], sapling_input_count: usize, sapling_output_count: usize, + orchard_action_count: usize, ) -> Result { let non_p2pkh_inputs: Vec<_> = transparent_inputs .iter() @@ -151,7 +152,8 @@ impl super::FeeRule for FeeRule { let logical_actions = max( ceildiv(t_in_total_size, self.p2pkh_standard_input_size), ceildiv(t_out_total_size, self.p2pkh_standard_output_size), - ) + max(sapling_input_count, sapling_output_count); + ) + max(sapling_input_count, sapling_output_count) + + orchard_action_count; (self.marginal_fee * max(self.grace_actions, logical_actions)) .ok_or_else(|| BalanceError::Overflow.into()) diff --git a/zcash_primitives/src/transaction/mod.rs b/zcash_primitives/src/transaction/mod.rs index a500b54bb7..5d3ebf365e 100644 --- a/zcash_primitives/src/transaction/mod.rs +++ b/zcash_primitives/src/transaction/mod.rs @@ -270,7 +270,8 @@ pub struct Unauthorized; impl Authorization for Unauthorized { type TransparentAuth = transparent::builder::Unauthorized; type SaplingAuth = sapling::builder::Unauthorized; - type OrchardAuth = orchard_serialization::Unauthorized; + type OrchardAuth = + orchard::builder::InProgress; #[cfg(feature = "zfuture")] type TzeAuth = tze::builder::Unauthorized;