2020-08-15 21:55:28 -07:00
|
|
|
//! Transactions and transaction-related structures.
|
2019-09-19 07:45:37 -07:00
|
|
|
|
2020-06-15 15:08:14 -07:00
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
|
2019-12-05 12:56:58 -08:00
|
|
|
mod hash;
|
|
|
|
mod joinsplit;
|
2020-08-14 23:37:25 -07:00
|
|
|
mod lock_time;
|
2020-08-15 18:50:18 -07:00
|
|
|
mod memo;
|
2019-12-05 12:56:58 -08:00
|
|
|
mod serialize;
|
2020-09-05 16:31:11 -07:00
|
|
|
mod sighash;
|
2019-12-04 21:47:50 -08:00
|
|
|
|
2020-09-23 18:52:52 -07:00
|
|
|
#[cfg(any(test, feature = "proptest-impl"))]
|
2021-04-27 17:43:00 -07:00
|
|
|
pub mod arbitrary;
|
2019-12-05 12:56:58 -08:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests;
|
2019-09-19 07:45:37 -07:00
|
|
|
|
2020-08-17 02:26:33 -07:00
|
|
|
pub use hash::Hash;
|
2020-08-17 02:06:38 -07:00
|
|
|
pub use joinsplit::JoinSplitData;
|
2020-08-14 23:37:25 -07:00
|
|
|
pub use lock_time::LockTime;
|
2020-08-15 18:50:18 -07:00
|
|
|
pub use memo::Memo;
|
2021-04-07 05:34:58 -07:00
|
|
|
pub use sapling::FieldNotPresent;
|
2020-09-25 16:26:39 -07:00
|
|
|
pub use sighash::HashType;
|
2019-09-19 07:45:37 -07:00
|
|
|
|
2020-08-17 01:24:33 -07:00
|
|
|
use crate::{
|
|
|
|
block,
|
2020-09-05 16:31:11 -07:00
|
|
|
parameters::NetworkUpgrade,
|
2020-08-17 01:24:33 -07:00
|
|
|
primitives::{Bctv14Proof, Groth16Proof},
|
2020-10-23 15:29:52 -07:00
|
|
|
sapling, sprout, transparent,
|
2020-08-17 01:24:33 -07:00
|
|
|
};
|
2019-09-19 07:45:37 -07:00
|
|
|
|
2019-12-05 12:56:58 -08:00
|
|
|
/// A Zcash transaction.
|
2019-09-25 18:25:46 -07:00
|
|
|
///
|
2019-12-05 12:56:58 -08:00
|
|
|
/// A transaction is an encoded data structure that facilitates the transfer of
|
|
|
|
/// value between two public key addresses on the Zcash ecosystem. Everything is
|
|
|
|
/// designed to ensure that transactions can created, propagated on the network,
|
|
|
|
/// validated, and finally added to the global ledger of transactions (the
|
|
|
|
/// blockchain).
|
2019-09-25 18:25:46 -07:00
|
|
|
///
|
2019-12-05 12:56:58 -08:00
|
|
|
/// Zcash has a number of different transaction formats. They are represented
|
2021-03-12 10:09:39 -08:00
|
|
|
/// internally by different enum variants. Because we checkpoint on Canopy
|
2021-02-25 17:35:54 -08:00
|
|
|
/// activation, we do not validate any pre-Sapling transaction types.
|
2020-06-15 15:08:14 -07:00
|
|
|
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
2021-03-03 13:56:41 -08:00
|
|
|
// XXX consider boxing the Optional fields of V4 and V5 txs
|
2020-02-04 22:53:24 -08:00
|
|
|
#[allow(clippy::large_enum_variant)]
|
2019-12-05 12:56:58 -08:00
|
|
|
pub enum Transaction {
|
|
|
|
/// A fully transparent transaction (`version = 1`).
|
|
|
|
V1 {
|
|
|
|
/// The transparent inputs to the transaction.
|
2020-08-17 01:24:33 -07:00
|
|
|
inputs: Vec<transparent::Input>,
|
2019-12-05 12:56:58 -08:00
|
|
|
/// The transparent outputs from the transaction.
|
2020-08-17 01:24:33 -07:00
|
|
|
outputs: Vec<transparent::Output>,
|
2019-12-05 12:56:58 -08:00
|
|
|
/// The earliest time or block height that this transaction can be added to the
|
|
|
|
/// chain.
|
|
|
|
lock_time: LockTime,
|
|
|
|
},
|
|
|
|
/// A Sprout transaction (`version = 2`).
|
|
|
|
V2 {
|
|
|
|
/// The transparent inputs to the transaction.
|
2020-08-17 01:24:33 -07:00
|
|
|
inputs: Vec<transparent::Input>,
|
2019-12-05 12:56:58 -08:00
|
|
|
/// The transparent outputs from the transaction.
|
2020-08-17 01:24:33 -07:00
|
|
|
outputs: Vec<transparent::Output>,
|
2019-12-05 12:56:58 -08:00
|
|
|
/// The earliest time or block height that this transaction can be added to the
|
|
|
|
/// chain.
|
|
|
|
lock_time: LockTime,
|
|
|
|
/// The JoinSplit data for this transaction, if any.
|
|
|
|
joinsplit_data: Option<JoinSplitData<Bctv14Proof>>,
|
|
|
|
},
|
|
|
|
/// An Overwinter transaction (`version = 3`).
|
|
|
|
V3 {
|
|
|
|
/// The transparent inputs to the transaction.
|
2020-08-17 01:24:33 -07:00
|
|
|
inputs: Vec<transparent::Input>,
|
2019-12-05 12:56:58 -08:00
|
|
|
/// The transparent outputs from the transaction.
|
2020-08-17 01:24:33 -07:00
|
|
|
outputs: Vec<transparent::Output>,
|
2019-12-05 12:56:58 -08:00
|
|
|
/// The earliest time or block height that this transaction can be added to the
|
|
|
|
/// chain.
|
|
|
|
lock_time: LockTime,
|
|
|
|
/// The latest block height that this transaction can be added to the chain.
|
2020-08-16 11:42:02 -07:00
|
|
|
expiry_height: block::Height,
|
2019-12-05 12:56:58 -08:00
|
|
|
/// The JoinSplit data for this transaction, if any.
|
|
|
|
joinsplit_data: Option<JoinSplitData<Bctv14Proof>>,
|
|
|
|
},
|
|
|
|
/// A Sapling transaction (`version = 4`).
|
|
|
|
V4 {
|
|
|
|
/// The transparent inputs to the transaction.
|
2020-08-17 01:24:33 -07:00
|
|
|
inputs: Vec<transparent::Input>,
|
2019-12-05 12:56:58 -08:00
|
|
|
/// The transparent outputs from the transaction.
|
2020-08-17 01:24:33 -07:00
|
|
|
outputs: Vec<transparent::Output>,
|
2019-12-05 12:56:58 -08:00
|
|
|
/// The earliest time or block height that this transaction can be added to the
|
|
|
|
/// chain.
|
|
|
|
lock_time: LockTime,
|
|
|
|
/// The latest block height that this transaction can be added to the chain.
|
2020-08-16 11:42:02 -07:00
|
|
|
expiry_height: block::Height,
|
2019-12-05 12:56:58 -08:00
|
|
|
/// The JoinSplit data for this transaction, if any.
|
|
|
|
joinsplit_data: Option<JoinSplitData<Groth16Proof>>,
|
2021-03-31 14:34:25 -07:00
|
|
|
/// The sapling shielded data for this transaction, if any.
|
|
|
|
sapling_shielded_data: Option<sapling::ShieldedData<sapling::PerSpendAnchor>>,
|
2019-12-05 12:56:58 -08:00
|
|
|
},
|
2021-03-03 13:56:41 -08:00
|
|
|
/// A `version = 5` transaction, which supports `Sapling` and `Orchard`.
|
|
|
|
V5 {
|
|
|
|
/// The earliest time or block height that this transaction can be added to the
|
|
|
|
/// chain.
|
|
|
|
lock_time: LockTime,
|
|
|
|
/// The latest block height that this transaction can be added to the chain.
|
|
|
|
expiry_height: block::Height,
|
|
|
|
/// The transparent inputs to the transaction.
|
|
|
|
inputs: Vec<transparent::Input>,
|
|
|
|
/// The transparent outputs from the transaction.
|
|
|
|
outputs: Vec<transparent::Output>,
|
2021-04-15 15:19:28 -07:00
|
|
|
/// The sapling shielded data for this transaction, if any.
|
|
|
|
sapling_shielded_data: Option<sapling::ShieldedData<sapling::SharedAnchor>>,
|
2021-04-22 23:25:44 -07:00
|
|
|
// TODO: The orchard data for this transaction, if any.
|
2021-03-03 13:56:41 -08:00
|
|
|
},
|
2019-12-04 21:47:50 -08:00
|
|
|
}
|
2020-02-09 20:55:52 -08:00
|
|
|
|
|
|
|
impl Transaction {
|
2021-04-27 17:43:00 -07:00
|
|
|
// hashes
|
|
|
|
|
|
|
|
/// Compute the hash (id) of this transaction.
|
2020-09-02 14:59:16 -07:00
|
|
|
pub fn hash(&self) -> Hash {
|
|
|
|
Hash::from(self)
|
|
|
|
}
|
|
|
|
|
2021-04-27 17:43:00 -07:00
|
|
|
/// Calculate the sighash for the current transaction
|
|
|
|
///
|
|
|
|
/// # Details
|
|
|
|
///
|
|
|
|
/// The `input` argument indicates the transparent Input for which we are
|
|
|
|
/// producing a sighash. It is comprised of the index identifying the
|
|
|
|
/// transparent::Input within the transaction and the transparent::Output
|
|
|
|
/// representing the UTXO being spent by that input.
|
|
|
|
///
|
|
|
|
/// # Panics
|
|
|
|
///
|
|
|
|
/// - if passed in any NetworkUpgrade from before NetworkUpgrade::Overwinter
|
|
|
|
/// - if called on a v1 or v2 transaction
|
|
|
|
/// - if the input index points to a transparent::Input::CoinBase
|
|
|
|
/// - if the input index is out of bounds for self.inputs()
|
|
|
|
pub fn sighash(
|
|
|
|
&self,
|
|
|
|
network_upgrade: NetworkUpgrade,
|
|
|
|
hash_type: sighash::HashType,
|
|
|
|
input: Option<(u32, transparent::Output)>,
|
|
|
|
) -> blake2b_simd::Hash {
|
|
|
|
sighash::SigHasher::new(self, hash_type, network_upgrade, input).sighash()
|
|
|
|
}
|
|
|
|
|
|
|
|
// header
|
|
|
|
|
|
|
|
/// Get this transaction's lock time.
|
|
|
|
pub fn lock_time(&self) -> LockTime {
|
|
|
|
match self {
|
|
|
|
Transaction::V1 { lock_time, .. } => *lock_time,
|
|
|
|
Transaction::V2 { lock_time, .. } => *lock_time,
|
|
|
|
Transaction::V3 { lock_time, .. } => *lock_time,
|
|
|
|
Transaction::V4 { lock_time, .. } => *lock_time,
|
|
|
|
Transaction::V5 { lock_time, .. } => *lock_time,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get this transaction's expiry height, if any.
|
|
|
|
pub fn expiry_height(&self) -> Option<block::Height> {
|
|
|
|
match self {
|
|
|
|
Transaction::V1 { .. } => None,
|
|
|
|
Transaction::V2 { .. } => None,
|
|
|
|
Transaction::V3 { expiry_height, .. } => Some(*expiry_height),
|
|
|
|
Transaction::V4 { expiry_height, .. } => Some(*expiry_height),
|
|
|
|
Transaction::V5 { expiry_height, .. } => Some(*expiry_height),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// transparent
|
|
|
|
|
2020-08-13 14:04:43 -07:00
|
|
|
/// Access the transparent inputs of this transaction, regardless of version.
|
2020-08-17 01:24:33 -07:00
|
|
|
pub fn inputs(&self) -> &[transparent::Input] {
|
2020-02-09 20:55:52 -08:00
|
|
|
match self {
|
2020-08-13 14:04:43 -07:00
|
|
|
Transaction::V1 { ref inputs, .. } => inputs,
|
|
|
|
Transaction::V2 { ref inputs, .. } => inputs,
|
|
|
|
Transaction::V3 { ref inputs, .. } => inputs,
|
|
|
|
Transaction::V4 { ref inputs, .. } => inputs,
|
2021-03-03 13:56:41 -08:00
|
|
|
Transaction::V5 { ref inputs, .. } => inputs,
|
2020-02-09 20:55:52 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-13 14:04:43 -07:00
|
|
|
/// Access the transparent outputs of this transaction, regardless of version.
|
2020-08-17 01:24:33 -07:00
|
|
|
pub fn outputs(&self) -> &[transparent::Output] {
|
2020-02-09 20:55:52 -08:00
|
|
|
match self {
|
2020-08-13 14:04:43 -07:00
|
|
|
Transaction::V1 { ref outputs, .. } => outputs,
|
|
|
|
Transaction::V2 { ref outputs, .. } => outputs,
|
|
|
|
Transaction::V3 { ref outputs, .. } => outputs,
|
|
|
|
Transaction::V4 { ref outputs, .. } => outputs,
|
2021-03-03 13:56:41 -08:00
|
|
|
Transaction::V5 { ref outputs, .. } => outputs,
|
2020-02-09 20:55:52 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-27 17:43:00 -07:00
|
|
|
/// Returns `true` if this transaction is a coinbase transaction.
|
|
|
|
pub fn is_coinbase(&self) -> bool {
|
|
|
|
self.inputs().len() == 1
|
|
|
|
&& matches!(
|
|
|
|
self.inputs().get(0),
|
|
|
|
Some(transparent::Input::Coinbase { .. })
|
|
|
|
)
|
2020-02-09 20:55:52 -08:00
|
|
|
}
|
|
|
|
|
2021-04-27 17:43:00 -07:00
|
|
|
/// Returns `true` if transaction contains any coinbase inputs.
|
|
|
|
pub fn contains_coinbase_input(&self) -> bool {
|
|
|
|
self.inputs()
|
|
|
|
.iter()
|
|
|
|
.any(|input| matches!(input, transparent::Input::Coinbase { .. }))
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns `true` if transaction contains any `PrevOut` inputs.
|
|
|
|
///
|
|
|
|
/// `PrevOut` inputs are also known as `transparent` inputs in the spec.
|
|
|
|
pub fn contains_prevout_input(&self) -> bool {
|
|
|
|
self.inputs()
|
|
|
|
.iter()
|
|
|
|
.any(|input| matches!(input, transparent::Input::PrevOut { .. }))
|
|
|
|
}
|
|
|
|
|
|
|
|
// sprout
|
|
|
|
|
|
|
|
/// Returns the number of `JoinSplit`s in this transaction, regardless of version.
|
|
|
|
pub fn joinsplit_count(&self) -> usize {
|
2020-02-09 20:55:52 -08:00
|
|
|
match self {
|
2021-04-27 17:43:00 -07:00
|
|
|
// JoinSplits with Bctv14 Proofs
|
|
|
|
Transaction::V2 {
|
|
|
|
joinsplit_data: Some(joinsplit_data),
|
|
|
|
..
|
|
|
|
}
|
|
|
|
| Transaction::V3 {
|
|
|
|
joinsplit_data: Some(joinsplit_data),
|
|
|
|
..
|
|
|
|
} => joinsplit_data.joinsplits().count(),
|
|
|
|
// JoinSplits with Groth Proofs
|
|
|
|
Transaction::V4 {
|
|
|
|
joinsplit_data: Some(joinsplit_data),
|
|
|
|
..
|
|
|
|
} => joinsplit_data.joinsplits().count(),
|
|
|
|
// No JoinSplits
|
|
|
|
Transaction::V1 { .. }
|
|
|
|
| Transaction::V2 {
|
|
|
|
joinsplit_data: None,
|
|
|
|
..
|
|
|
|
}
|
|
|
|
| Transaction::V3 {
|
|
|
|
joinsplit_data: None,
|
|
|
|
..
|
|
|
|
}
|
|
|
|
| Transaction::V4 {
|
|
|
|
joinsplit_data: None,
|
|
|
|
..
|
|
|
|
}
|
|
|
|
| Transaction::V5 { .. } => 0,
|
2020-02-09 20:55:52 -08:00
|
|
|
}
|
|
|
|
}
|
2020-07-09 16:13:44 -07:00
|
|
|
|
2020-10-23 15:29:52 -07:00
|
|
|
/// Access the sprout::Nullifiers in this transaction, regardless of version.
|
|
|
|
pub fn sprout_nullifiers(&self) -> Box<dyn Iterator<Item = &sprout::Nullifier> + '_> {
|
|
|
|
// This function returns a boxed iterator because the different
|
|
|
|
// transaction variants end up having different iterator types
|
2021-04-19 23:22:25 -07:00
|
|
|
// (we could extract bctv and groth as separate iterators, then chain
|
|
|
|
// them together, but that would be much harder to read and maintain)
|
2020-10-23 15:29:52 -07:00
|
|
|
match self {
|
|
|
|
// JoinSplits with Bctv14 Proofs
|
|
|
|
Transaction::V2 {
|
|
|
|
joinsplit_data: Some(joinsplit_data),
|
|
|
|
..
|
|
|
|
}
|
|
|
|
| Transaction::V3 {
|
|
|
|
joinsplit_data: Some(joinsplit_data),
|
|
|
|
..
|
2021-04-19 23:22:25 -07:00
|
|
|
} => Box::new(joinsplit_data.nullifiers()),
|
2020-10-23 15:29:52 -07:00
|
|
|
// JoinSplits with Groth Proofs
|
|
|
|
Transaction::V4 {
|
|
|
|
joinsplit_data: Some(joinsplit_data),
|
|
|
|
..
|
2021-04-19 23:22:25 -07:00
|
|
|
} => Box::new(joinsplit_data.nullifiers()),
|
2020-10-23 15:29:52 -07:00
|
|
|
// No JoinSplits
|
2021-03-03 13:56:41 -08:00
|
|
|
Transaction::V1 { .. }
|
|
|
|
| Transaction::V2 {
|
|
|
|
joinsplit_data: None,
|
|
|
|
..
|
|
|
|
}
|
|
|
|
| Transaction::V3 {
|
|
|
|
joinsplit_data: None,
|
|
|
|
..
|
|
|
|
}
|
|
|
|
| Transaction::V4 {
|
|
|
|
joinsplit_data: None,
|
|
|
|
..
|
2021-04-15 15:19:28 -07:00
|
|
|
}
|
|
|
|
| Transaction::V5 { .. } => Box::new(std::iter::empty()),
|
2020-10-23 15:29:52 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-27 17:43:00 -07:00
|
|
|
// sapling
|
|
|
|
|
|
|
|
/// Iterate over the sapling [`Spend`](sapling::Spend)s for this transaction,
|
|
|
|
/// returning `Spend<PerSpendAnchor>` regardless of the underlying
|
|
|
|
/// transaction version.
|
|
|
|
///
|
|
|
|
/// # Correctness
|
|
|
|
///
|
|
|
|
/// Do not use this function for serialization.
|
|
|
|
pub fn sapling_spends_per_anchor(
|
|
|
|
&self,
|
|
|
|
) -> Box<dyn Iterator<Item = sapling::Spend<sapling::PerSpendAnchor>> + '_> {
|
|
|
|
match self {
|
|
|
|
Transaction::V4 {
|
|
|
|
sapling_shielded_data: Some(sapling_shielded_data),
|
|
|
|
..
|
|
|
|
} => Box::new(sapling_shielded_data.spends_per_anchor()),
|
|
|
|
Transaction::V5 {
|
|
|
|
sapling_shielded_data: Some(sapling_shielded_data),
|
|
|
|
..
|
|
|
|
} => Box::new(sapling_shielded_data.spends_per_anchor()),
|
|
|
|
|
|
|
|
// No Spends
|
|
|
|
Transaction::V1 { .. }
|
|
|
|
| Transaction::V2 { .. }
|
|
|
|
| Transaction::V3 { .. }
|
|
|
|
| Transaction::V4 {
|
|
|
|
sapling_shielded_data: None,
|
|
|
|
..
|
|
|
|
}
|
|
|
|
| Transaction::V5 {
|
|
|
|
sapling_shielded_data: None,
|
|
|
|
..
|
|
|
|
} => Box::new(std::iter::empty()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Iterate over the sapling [`Output`](sapling::Output)s for this
|
|
|
|
/// transaction
|
|
|
|
pub fn sapling_outputs(&self) -> Box<dyn Iterator<Item = &sapling::Output> + '_> {
|
|
|
|
match self {
|
|
|
|
Transaction::V4 {
|
|
|
|
sapling_shielded_data: Some(sapling_shielded_data),
|
|
|
|
..
|
|
|
|
} => Box::new(sapling_shielded_data.outputs()),
|
|
|
|
Transaction::V5 {
|
|
|
|
sapling_shielded_data: Some(sapling_shielded_data),
|
|
|
|
..
|
|
|
|
} => Box::new(sapling_shielded_data.outputs()),
|
|
|
|
|
|
|
|
// No Outputs
|
|
|
|
Transaction::V1 { .. }
|
|
|
|
| Transaction::V2 { .. }
|
|
|
|
| Transaction::V3 { .. }
|
|
|
|
| Transaction::V4 {
|
|
|
|
sapling_shielded_data: None,
|
|
|
|
..
|
|
|
|
}
|
|
|
|
| Transaction::V5 {
|
|
|
|
sapling_shielded_data: None,
|
|
|
|
..
|
|
|
|
} => Box::new(std::iter::empty()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-23 15:29:52 -07:00
|
|
|
/// Access the sapling::Nullifiers in this transaction, regardless of version.
|
|
|
|
pub fn sapling_nullifiers(&self) -> Box<dyn Iterator<Item = &sapling::Nullifier> + '_> {
|
|
|
|
// This function returns a boxed iterator because the different
|
|
|
|
// transaction variants end up having different iterator types
|
|
|
|
match self {
|
2021-04-15 15:19:28 -07:00
|
|
|
// Spends with Groth Proofs
|
2020-10-23 15:29:52 -07:00
|
|
|
Transaction::V4 {
|
2021-03-31 14:34:25 -07:00
|
|
|
sapling_shielded_data: Some(sapling_shielded_data),
|
2020-10-23 15:29:52 -07:00
|
|
|
..
|
2021-03-31 14:34:25 -07:00
|
|
|
} => Box::new(sapling_shielded_data.nullifiers()),
|
2021-04-15 15:19:28 -07:00
|
|
|
Transaction::V5 {
|
|
|
|
sapling_shielded_data: Some(sapling_shielded_data),
|
|
|
|
..
|
|
|
|
} => Box::new(sapling_shielded_data.nullifiers()),
|
|
|
|
|
|
|
|
// No Spends
|
2021-03-03 13:56:41 -08:00
|
|
|
Transaction::V1 { .. }
|
|
|
|
| Transaction::V2 { .. }
|
|
|
|
| Transaction::V3 { .. }
|
|
|
|
| Transaction::V4 {
|
2021-03-31 14:34:25 -07:00
|
|
|
sapling_shielded_data: None,
|
2021-03-03 13:56:41 -08:00
|
|
|
..
|
2021-04-15 15:19:28 -07:00
|
|
|
}
|
|
|
|
| Transaction::V5 {
|
|
|
|
sapling_shielded_data: None,
|
|
|
|
..
|
2021-03-03 13:56:41 -08:00
|
|
|
} => Box::new(std::iter::empty()),
|
2020-10-23 15:29:52 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-27 17:43:00 -07:00
|
|
|
// TODO: orchard
|
2020-02-09 20:55:52 -08:00
|
|
|
}
|