Skip to main content

cuprate_txpool/
types.rs

1//! Tx-pool types.
2//!
3//! This module contains all types used by the database tables,
4//! and aliases for common  types that use the same underlying
5//! primitive type.
6//!
7//! <!-- FIXME: Add schema here or a link to it when complete -->
8use bytemuck::{Pod, Zeroable};
9use monero_oxide::transaction::Timelock;
10
11use cuprate_types::{CachedVerificationState, HardFork};
12
13/// An inputs key image.
14pub type KeyImage = [u8; 32];
15
16/// A transaction hash.
17pub type TransactionHash = [u8; 32];
18
19/// A transaction blob hash.
20pub type TransactionBlobHash = [u8; 32];
21
22bitflags::bitflags! {
23    /// Flags representing the state of the transaction in the pool.
24    #[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Pod, Zeroable)]
25    #[repr(transparent)]
26    pub struct TxStateFlags: u8 {
27        /// A flag for if the transaction is in the stem state.
28        const STATE_STEM   = 0b0000_0001;
29        /// A flag for if we have seen another tx double spending this tx.
30        const DOUBLE_SPENT = 0b0000_0010;
31    }
32}
33
34impl TxStateFlags {
35    pub const fn private(&self) -> bool {
36        self.contains(Self::STATE_STEM)
37    }
38}
39
40/// Information on a tx-pool transaction.
41#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Pod, Zeroable)]
42#[repr(C)]
43pub struct TransactionInfo {
44    /// The transaction's fee.
45    pub fee: u64,
46    /// The transaction's weight.
47    pub weight: usize,
48    /// The UNIX timestamp of when this tx was received.
49    pub received_at: u64,
50    pub cached_verification_state: RawCachedVerificationState,
51    /// [`TxStateFlags`] of this transaction.
52    pub flags: TxStateFlags,
53    #[expect(clippy::pub_underscore_fields)]
54    /// Explicit padding so that we have no implicit padding bytes in `repr(C)`.
55    ///
56    /// Allows potential future expansion of this type.
57    pub _padding: [u8; 6],
58}
59
60/// [`CachedVerificationState`] in a format that can be stored into the database.
61///
62/// This type impls [`Into`] & [`From`] [`CachedVerificationState`].
63#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Pod, Zeroable)]
64#[repr(C)]
65pub struct RawCachedVerificationState {
66    /// The raw hash, will be all `0`s if there is no block hash that this is valid for.
67    raw_valid_at_hash: [u8; 32],
68    /// The raw hard-fork, will be `0` if there is no hf this was validated at.
69    raw_hf: u8,
70    /// The raw [`u64`] timestamp as little endian bytes ([`u64::to_le_bytes`]).
71    ///
72    /// This will be `0` if there is no timestamp that needs to be passed for this to
73    /// be valid.
74    ///
75    /// Not a [`u64`] as if it was this type would have an alignment requirement.
76    raw_valid_past_timestamp: [u8; 8],
77}
78
79impl From<RawCachedVerificationState> for CachedVerificationState {
80    fn from(value: RawCachedVerificationState) -> Self {
81        // if the hash is all `0`s then there is no hash this is valid at.
82        if value.raw_valid_at_hash == [0; 32] {
83            if value.raw_hf != 0 {
84                return Self::OnlySemantic(
85                    HardFork::from_version(value.raw_hf)
86                        .expect("hard-fork values stored in the DB should always be valid"),
87                );
88            }
89
90            return Self::NotVerified;
91        }
92
93        let raw_valid_past_timestamp = u64::from_le_bytes(value.raw_valid_past_timestamp);
94
95        // if the timestamp is 0, there is no timestamp that needs to be passed.
96        if raw_valid_past_timestamp == 0 {
97            return Self::ValidAtHashAndHF {
98                block_hash: value.raw_valid_at_hash,
99                hf: HardFork::from_version(value.raw_hf)
100                    .expect("hard-fork values stored in the DB should always be valid"),
101            };
102        }
103
104        Self::ValidAtHashAndHFWithTimeBasedLock {
105            block_hash: value.raw_valid_at_hash,
106            hf: HardFork::from_version(value.raw_hf)
107                .expect("hard-fork values stored in the DB should always be valid"),
108            time_lock: Timelock::Time(raw_valid_past_timestamp),
109        }
110    }
111}
112
113#[expect(clippy::fallible_impl_from, reason = "only panics in invalid states")]
114impl From<CachedVerificationState> for RawCachedVerificationState {
115    fn from(value: CachedVerificationState) -> Self {
116        match value {
117            CachedVerificationState::NotVerified => Self {
118                raw_valid_at_hash: [0; 32],
119                raw_hf: 0,
120                raw_valid_past_timestamp: [0; 8],
121            },
122            CachedVerificationState::OnlySemantic(hf) => Self {
123                raw_valid_at_hash: [0; 32],
124                raw_hf: hf.as_u8(),
125                raw_valid_past_timestamp: [0; 8],
126            },
127            CachedVerificationState::ValidAtHashAndHF { block_hash, hf } => Self {
128                raw_valid_at_hash: block_hash,
129                raw_hf: hf.as_u8(),
130                raw_valid_past_timestamp: [0; 8],
131            },
132            CachedVerificationState::ValidAtHashAndHFWithTimeBasedLock {
133                block_hash,
134                hf,
135                time_lock,
136            } => {
137                let Timelock::Time(time) = time_lock else {
138                    panic!("ValidAtHashAndHFWithTimeBasedLock timelock was not time-based");
139                };
140
141                Self {
142                    raw_valid_at_hash: block_hash,
143                    raw_hf: hf.as_u8(),
144                    raw_valid_past_timestamp: time.to_le_bytes(),
145                }
146            }
147        }
148    }
149}