cuprate_consensus/transactions/
free.rs

1use monero_oxide::{
2    ringct::{bulletproofs::Bulletproof, RctType},
3    transaction::{Input, Transaction},
4};
5
6use cuprate_consensus_rules::{transactions::TransactionError, ConsensusError};
7use cuprate_types::{CachedVerificationState, TransactionVerificationData, TxVersion};
8
9/// Creates a new [`TransactionVerificationData`] from a [`Transaction`].
10///
11/// # Errors
12///
13/// This function will return [`Err`] if the transaction is malformed, although returning [`Ok`] does
14/// not necessarily mean the tx is correctly formed.
15pub fn new_tx_verification_data(
16    tx: Transaction,
17) -> Result<TransactionVerificationData, ConsensusError> {
18    let tx_hash = tx.hash();
19    let tx_blob = tx.serialize();
20
21    let tx_weight = tx_weight(&tx, &tx_blob);
22
23    let fee = tx_fee(&tx)?;
24
25    Ok(TransactionVerificationData {
26        tx_hash,
27        version: TxVersion::from_raw(tx.version())
28            .ok_or(TransactionError::TransactionVersionInvalid)?,
29        tx_blob,
30        tx_weight,
31        fee,
32        cached_verification_state: CachedVerificationState::NotVerified,
33        tx,
34    })
35}
36
37/// Calculates the weight of a [`Transaction`].
38///
39/// This is more efficient that [`Transaction::weight`] if you already have the transaction blob.
40pub(crate) fn tx_weight(tx: &Transaction, tx_blob: &[u8]) -> usize {
41    // the tx weight is only different from the blobs length for bp(+) txs.
42
43    match &tx {
44        Transaction::V1 { .. } | Transaction::V2 { proofs: None, .. } => tx_blob.len(),
45        Transaction::V2 {
46            proofs: Some(proofs),
47            ..
48        } => match proofs.rct_type() {
49            RctType::AggregateMlsagBorromean | RctType::MlsagBorromean => tx_blob.len(),
50            RctType::MlsagBulletproofs
51            | RctType::MlsagBulletproofsCompactAmount
52            | RctType::ClsagBulletproof => {
53                tx_blob.len() + Bulletproof::calculate_clawback(false, tx.prefix().outputs.len()).0
54            }
55            RctType::ClsagBulletproofPlus => {
56                tx_blob.len() + Bulletproof::calculate_clawback(true, tx.prefix().outputs.len()).0
57            }
58        },
59    }
60}
61
62/// Calculates the fee of the [`Transaction`].
63pub(crate) fn tx_fee(tx: &Transaction) -> Result<u64, TransactionError> {
64    let mut fee = 0_u64;
65
66    match &tx {
67        Transaction::V1 { prefix, .. } => {
68            for input in &prefix.inputs {
69                if let Input::ToKey { amount, .. } = input {
70                    fee = fee
71                        .checked_add(amount.unwrap_or(0))
72                        .ok_or(TransactionError::InputsOverflow)?;
73                }
74            }
75
76            for output in &prefix.outputs {
77                fee = fee
78                    .checked_sub(output.amount.unwrap_or(0))
79                    .ok_or(TransactionError::OutputsTooHigh)?;
80            }
81        }
82        Transaction::V2 { proofs, .. } => {
83            fee = proofs
84                .as_ref()
85                .ok_or(TransactionError::TransactionVersionInvalid)?
86                .base
87                .fee;
88        }
89    }
90
91    Ok(fee)
92}