cuprate_txpool/
types.rs

1//! Tx-pool [table](crate::tables) 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    /// [`TxStateFlags`] of this transaction.
51    pub flags: TxStateFlags,
52    #[expect(clippy::pub_underscore_fields)]
53    /// Explicit padding so that we have no implicit padding bytes in `repr(C)`.
54    ///
55    /// Allows potential future expansion of this type.
56    pub _padding: [u8; 7],
57}
58
59/// [`CachedVerificationState`] in a format that can be stored into the database.
60///
61/// This type impls [`Into`] & [`From`] [`CachedVerificationState`].
62#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Pod, Zeroable)]
63#[repr(C)]
64pub struct RawCachedVerificationState {
65    /// The raw hash, will be all `0`s if there is no block hash that this is valid for.
66    raw_valid_at_hash: [u8; 32],
67    /// The raw hard-fork, will be `0` if there is no hf this was validated at.
68    raw_hf: u8,
69    /// The raw [`u64`] timestamp as little endian bytes ([`u64::to_le_bytes`]).
70    ///
71    /// This will be `0` if there is no timestamp that needs to be passed for this to
72    /// be valid.
73    ///
74    /// Not a [`u64`] as if it was this type would have an alignment requirement.
75    raw_valid_past_timestamp: [u8; 8],
76}
77
78impl From<RawCachedVerificationState> for CachedVerificationState {
79    fn from(value: RawCachedVerificationState) -> Self {
80        // if the hash is all `0`s then there is no hash this is valid at.
81        if value.raw_valid_at_hash == [0; 32] {
82            if value.raw_hf != 0 {
83                return Self::OnlySemantic(
84                    HardFork::from_version(value.raw_hf)
85                        .expect("hard-fork values stored in the DB should always be valid"),
86                );
87            }
88
89            return Self::NotVerified;
90        }
91
92        let raw_valid_past_timestamp = u64::from_le_bytes(value.raw_valid_past_timestamp);
93
94        // if the timestamp is 0, there is no timestamp that needs to be passed.
95        if raw_valid_past_timestamp == 0 {
96            return Self::ValidAtHashAndHF {
97                block_hash: value.raw_valid_at_hash,
98                hf: HardFork::from_version(value.raw_hf)
99                    .expect("hard-fork values stored in the DB should always be valid"),
100            };
101        }
102
103        Self::ValidAtHashAndHFWithTimeBasedLock {
104            block_hash: value.raw_valid_at_hash,
105            hf: HardFork::from_version(value.raw_hf)
106                .expect("hard-fork values stored in the DB should always be valid"),
107            time_lock: Timelock::Time(raw_valid_past_timestamp),
108        }
109    }
110}
111
112#[expect(clippy::fallible_impl_from, reason = "only panics in invalid states")]
113impl From<CachedVerificationState> for RawCachedVerificationState {
114    fn from(value: CachedVerificationState) -> Self {
115        match value {
116            CachedVerificationState::NotVerified => Self {
117                raw_valid_at_hash: [0; 32],
118                raw_hf: 0,
119                raw_valid_past_timestamp: [0; 8],
120            },
121            CachedVerificationState::OnlySemantic(hf) => Self {
122                raw_valid_at_hash: [0; 32],
123                raw_hf: hf.as_u8(),
124                raw_valid_past_timestamp: [0; 8],
125            },
126            CachedVerificationState::ValidAtHashAndHF { block_hash, hf } => Self {
127                raw_valid_at_hash: block_hash,
128                raw_hf: hf.as_u8(),
129                raw_valid_past_timestamp: [0; 8],
130            },
131            CachedVerificationState::ValidAtHashAndHFWithTimeBasedLock {
132                block_hash,
133                hf,
134                time_lock,
135            } => {
136                let Timelock::Time(time) = time_lock else {
137                    panic!("ValidAtHashAndHFWithTimeBasedLock timelock was not time-based");
138                };
139
140                Self {
141                    raw_valid_at_hash: block_hash,
142                    raw_hf: hf.as_u8(),
143                    raw_valid_past_timestamp: time.to_le_bytes(),
144                }
145            }
146        }
147    }
148}