194 lines
6.0 KiB
Rust
194 lines
6.0 KiB
Rust
//! Unmined Zcash transaction identifiers and transactions.
|
|
//!
|
|
//! Transaction version 5 is uniquely identified by [`WtxId`] when unmined,
|
|
//! and [`Hash`] in the blockchain. The effects of a v5 transaction (spends and outputs)
|
|
//! are uniquely identified by the same [`Hash`] in both cases.
|
|
//!
|
|
//! Transaction versions 1-4 are uniquely identified by legacy [`Hash`] transaction IDs,
|
|
//! whether they have been mined or not. So Zebra, and the Zcash network protocol,
|
|
//! don't use witnessed transaction IDs for them.
|
|
//!
|
|
//! Zebra's [`UnminedTxId`] and [`UnminedTx`] enums provide the correct unique ID for
|
|
//! unmined transactions. They can be used to handle transactions regardless of version,
|
|
//! and get the [`WtxId`] or [`Hash`] when required.
|
|
|
|
use std::{fmt, sync::Arc};
|
|
|
|
#[cfg(any(test, feature = "proptest-impl"))]
|
|
use proptest_derive::Arbitrary;
|
|
|
|
use super::{
|
|
AuthDigest, Hash,
|
|
Transaction::{self, *},
|
|
WtxId,
|
|
};
|
|
|
|
use UnminedTxId::*;
|
|
|
|
/// A unique identifier for an unmined transaction, regardless of version.
|
|
///
|
|
/// "The transaction ID of a version 4 or earlier transaction is the SHA-256d hash
|
|
/// of the transaction encoding in the pre-v5 format described above.
|
|
///
|
|
/// The transaction ID of a version 5 transaction is as defined in [ZIP-244].
|
|
///
|
|
/// A v5 transaction also has a wtxid (used for example in the peer-to-peer protocol)
|
|
/// as defined in [ZIP-239]."
|
|
/// [Spec: Transaction Identifiers]
|
|
///
|
|
/// [ZIP-239]: https://zips.z.cash/zip-0239
|
|
/// [ZIP-244]: https://zips.z.cash/zip-0244
|
|
/// [Spec: Transaction Identifiers]: https://zips.z.cash/protocol/protocol.pdf#txnidentifiers
|
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
|
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
|
|
pub enum UnminedTxId {
|
|
/// A legacy unmined transaction identifier.
|
|
///
|
|
/// Used to uniquely identify unmined version 1-4 transactions.
|
|
/// (After v1-4 transactions are mined, they can be uniquely identified
|
|
/// using the same [`transaction::Hash`].)
|
|
Legacy(Hash),
|
|
|
|
/// A witnessed unmined transaction identifier.
|
|
///
|
|
/// Used to uniquely identify unmined version 5 transactions.
|
|
/// (After v5 transactions are mined, they can be uniquely identified
|
|
/// using only the [`transaction::Hash`] in their `WtxId.id`.)
|
|
///
|
|
/// For more details, see [`WtxId`].
|
|
Witnessed(WtxId),
|
|
}
|
|
|
|
impl From<Transaction> for UnminedTxId {
|
|
fn from(transaction: Transaction) -> Self {
|
|
// use the ref implementation, to avoid cloning the transaction
|
|
UnminedTxId::from(&transaction)
|
|
}
|
|
}
|
|
|
|
impl From<&Transaction> for UnminedTxId {
|
|
fn from(transaction: &Transaction) -> Self {
|
|
match transaction {
|
|
V1 { .. } | V2 { .. } | V3 { .. } | V4 { .. } => Legacy(transaction.into()),
|
|
V5 { .. } => Witnessed(transaction.into()),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<Arc<Transaction>> for UnminedTxId {
|
|
fn from(transaction: Arc<Transaction>) -> Self {
|
|
transaction.as_ref().into()
|
|
}
|
|
}
|
|
|
|
impl From<WtxId> for UnminedTxId {
|
|
fn from(wtx_id: WtxId) -> Self {
|
|
Witnessed(wtx_id)
|
|
}
|
|
}
|
|
|
|
impl From<&WtxId> for UnminedTxId {
|
|
fn from(wtx_id: &WtxId) -> Self {
|
|
(*wtx_id).into()
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for UnminedTxId {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self {
|
|
Legacy(hash) => hash.fmt(f),
|
|
Witnessed(id) => id.fmt(f),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl UnminedTxId {
|
|
/// Create a new `UnminedTxId` using a v1-v4 legacy transaction ID.
|
|
///
|
|
/// # Correctness
|
|
///
|
|
/// This method must only be used for v1-v4 transaction IDs.
|
|
/// [`Hash`] does not uniquely identify unmined v5 transactions.
|
|
#[allow(dead_code)]
|
|
pub fn from_legacy_id(legacy_tx_id: Hash) -> UnminedTxId {
|
|
Legacy(legacy_tx_id)
|
|
}
|
|
|
|
/// Return the unique ID that will be used if this transaction gets mined into a block.
|
|
///
|
|
/// # Correctness
|
|
///
|
|
/// For v1-v4 transactions, this method returns an ID which changes
|
|
/// if this transaction's effects (spends and outputs) change, or
|
|
/// if its authorizing data changes (signatures, proofs, and scripts).
|
|
///
|
|
/// But for v5 transactions, this ID uniquely identifies the transaction's effects.
|
|
#[allow(dead_code)]
|
|
pub fn mined_id(&self) -> Hash {
|
|
match self {
|
|
Legacy(legacy_id) => *legacy_id,
|
|
Witnessed(wtx_id) => wtx_id.id,
|
|
}
|
|
}
|
|
|
|
/// Return the digest of this transaction's authorizing data,
|
|
/// (signatures, proofs, and scripts), if it is a v5 transaction.
|
|
#[allow(dead_code)]
|
|
pub fn auth_digest(&self) -> Option<AuthDigest> {
|
|
match self {
|
|
Legacy(_) => None,
|
|
Witnessed(wtx_id) => Some(wtx_id.auth_digest),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// An unmined transaction, and its pre-calculated unique identifying ID.
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
|
|
pub struct UnminedTx {
|
|
/// A unique identifier for this unmined transaction.
|
|
pub id: UnminedTxId,
|
|
|
|
/// The unmined transaction itself.
|
|
pub transaction: Arc<Transaction>,
|
|
}
|
|
|
|
// Each of these conversions is implemented slightly differently,
|
|
// to avoid cloning the transaction where possible.
|
|
|
|
impl From<Transaction> for UnminedTx {
|
|
fn from(transaction: Transaction) -> Self {
|
|
Self {
|
|
id: (&transaction).into(),
|
|
transaction: Arc::new(transaction),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<&Transaction> for UnminedTx {
|
|
fn from(transaction: &Transaction) -> Self {
|
|
Self {
|
|
id: transaction.into(),
|
|
transaction: Arc::new(transaction.clone()),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<Arc<Transaction>> for UnminedTx {
|
|
fn from(transaction: Arc<Transaction>) -> Self {
|
|
Self {
|
|
id: transaction.as_ref().into(),
|
|
transaction,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<&Arc<Transaction>> for UnminedTx {
|
|
fn from(transaction: &Arc<Transaction>) -> Self {
|
|
Self {
|
|
id: transaction.as_ref().into(),
|
|
transaction: transaction.clone(),
|
|
}
|
|
}
|
|
}
|