use std::{fmt, sync::Arc};
use crate::{
amount::{Amount, NonNegative},
block::Height,
serialization::ZcashSerialize,
transaction::{
AuthDigest, Hash,
Transaction::{self, *},
WtxId,
},
transparent,
};
use UnminedTxId::*;
#[cfg(any(test, feature = "proptest-impl"))]
use proptest_derive::Arbitrary;
#[allow(unused_imports)]
use crate::block::MAX_BLOCK_BYTES;
pub mod zip317;
pub const MEMPOOL_TRANSACTION_COST_THRESHOLD: u64 = 10_000;
const MEMPOOL_TRANSACTION_LOW_FEE_PENALTY: u64 = 40_000;
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
pub enum UnminedTxId {
Legacy(Hash),
Witnessed(WtxId),
}
impl fmt::Debug for UnminedTxId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Legacy(_hash) => f.debug_tuple("Legacy").field(&self.to_string()).finish(),
Self::Witnessed(_id) => f.debug_tuple("Witnessed").field(&self.to_string()).finish(),
}
}
}
impl fmt::Display for UnminedTxId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Legacy(_hash) => f
.debug_tuple("transaction::Hash")
.field(&"private")
.finish(),
Witnessed(_id) => f.debug_tuple("WtxId").field(&"private").finish(),
}
}
}
impl From<Transaction> for UnminedTxId {
fn from(transaction: Transaction) -> Self {
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()),
#[cfg(all(zcash_unstable = "nu7", feature = "tx_v6"))]
V6 { .. } => 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 UnminedTxId {
pub fn from_legacy_id(legacy_tx_id: Hash) -> UnminedTxId {
Legacy(legacy_tx_id)
}
pub fn mined_id(&self) -> Hash {
match self {
Legacy(legacy_id) => *legacy_id,
Witnessed(wtx_id) => wtx_id.id,
}
}
#[cfg(any(test, feature = "proptest-impl"))]
pub fn mined_id_mut(&mut self) -> &mut Hash {
match self {
Legacy(legacy_id) => legacy_id,
Witnessed(wtx_id) => &mut wtx_id.id,
}
}
pub fn auth_digest(&self) -> Option<AuthDigest> {
match self {
Legacy(_) => None,
Witnessed(wtx_id) => Some(wtx_id.auth_digest),
}
}
#[cfg(any(test, feature = "proptest-impl"))]
pub fn auth_digest_mut(&mut self) -> Option<&mut AuthDigest> {
match self {
Legacy(_) => None,
Witnessed(wtx_id) => Some(&mut wtx_id.auth_digest),
}
}
}
#[derive(Clone, Eq, PartialEq)]
pub struct UnminedTx {
pub transaction: Arc<Transaction>,
pub id: UnminedTxId,
pub size: usize,
pub conventional_fee: Amount<NonNegative>,
}
impl fmt::Debug for UnminedTx {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("UnminedTx").field(&"private").finish()
}
}
impl fmt::Display for UnminedTx {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("UnminedTx").field(&"private").finish()
}
}
impl From<Transaction> for UnminedTx {
fn from(transaction: Transaction) -> Self {
let size = transaction.zcash_serialized_size();
let conventional_fee = zip317::conventional_fee(&transaction);
#[allow(clippy::needless_borrow)]
Self {
id: (&transaction).into(),
size,
conventional_fee,
transaction: Arc::new(transaction),
}
}
}
impl From<&Transaction> for UnminedTx {
fn from(transaction: &Transaction) -> Self {
let size = transaction.zcash_serialized_size();
let conventional_fee = zip317::conventional_fee(transaction);
Self {
id: transaction.into(),
size,
conventional_fee,
transaction: Arc::new(transaction.clone()),
}
}
}
impl From<Arc<Transaction>> for UnminedTx {
fn from(transaction: Arc<Transaction>) -> Self {
let size = transaction.zcash_serialized_size();
let conventional_fee = zip317::conventional_fee(&transaction);
Self {
id: transaction.as_ref().into(),
size,
conventional_fee,
transaction,
}
}
}
impl From<&Arc<Transaction>> for UnminedTx {
fn from(transaction: &Arc<Transaction>) -> Self {
let size = transaction.zcash_serialized_size();
let conventional_fee = zip317::conventional_fee(transaction);
Self {
id: transaction.as_ref().into(),
size,
conventional_fee,
transaction: transaction.clone(),
}
}
}
#[derive(Clone, PartialEq)]
pub struct VerifiedUnminedTx {
pub transaction: UnminedTx,
pub miner_fee: Amount<NonNegative>,
pub legacy_sigop_count: u32,
pub conventional_actions: u32,
pub unpaid_actions: u32,
pub fee_weight_ratio: f32,
pub time: Option<chrono::DateTime<chrono::Utc>>,
pub height: Option<Height>,
pub spent_outputs: Arc<Vec<transparent::Output>>,
}
impl fmt::Debug for VerifiedUnminedTx {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("VerifiedUnminedTx")
.field(&"private")
.finish()
}
}
impl fmt::Display for VerifiedUnminedTx {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("VerifiedUnminedTx")
.field(&"private")
.finish()
}
}
impl VerifiedUnminedTx {
pub fn new(
transaction: UnminedTx,
miner_fee: Amount<NonNegative>,
legacy_sigop_count: u32,
spent_outputs: Arc<Vec<transparent::Output>>,
) -> Result<Self, zip317::Error> {
let fee_weight_ratio = zip317::conventional_fee_weight_ratio(&transaction, miner_fee);
let conventional_actions = zip317::conventional_actions(&transaction.transaction);
let unpaid_actions = zip317::unpaid_actions(&transaction, miner_fee);
zip317::mempool_checks(unpaid_actions, miner_fee, transaction.size)?;
Ok(Self {
transaction,
miner_fee,
legacy_sigop_count,
fee_weight_ratio,
conventional_actions,
unpaid_actions,
time: None,
height: None,
spent_outputs,
})
}
pub fn pays_conventional_fee(&self) -> bool {
self.miner_fee >= self.transaction.conventional_fee
}
pub fn cost(&self) -> u64 {
std::cmp::max(
u64::try_from(self.transaction.size).expect("fits in u64"),
MEMPOOL_TRANSACTION_COST_THRESHOLD,
)
}
pub fn eviction_weight(&self) -> u64 {
let mut cost = self.cost();
if !self.pays_conventional_fee() {
cost += MEMPOOL_TRANSACTION_LOW_FEE_PENALTY
}
cost
}
}