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