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;
1011use cuprate_types::{CachedVerificationState, HardFork};
1213/// An inputs key image.
14pub type KeyImage = [u8; 32];
1516/// A transaction hash.
17pub type TransactionHash = [u8; 32];
1819/// A transaction blob hash.
20pub type TransactionBlobHash = [u8; 32];
2122bitflags::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)]
26pub struct TxStateFlags: u8 {
27/// A flag for if the transaction is in the stem state.
28const STATE_STEM = 0b0000_0001;
29/// A flag for if we have seen another tx double spending this tx.
30const DOUBLE_SPENT = 0b0000_0010;
31 }
32}
3334/// 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.
39pub fee: u64,
40/// The transaction's weight.
41pub weight: usize,
42/// [`TxStateFlags`] of this transaction.
43pub 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.
48pub _padding: [u8; 7],
49}
5051/// [`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.
58raw_valid_at_hash: [u8; 32],
59/// The raw hard-fork, will be `0` if there is no hf this was validated at.
60raw_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.
67raw_valid_past_timestamp: [u8; 8],
68}
6970impl From<RawCachedVerificationState> for CachedVerificationState {
71fn from(value: RawCachedVerificationState) -> Self {
72// if the hash is all `0`s then there is no hash this is valid at.
73if value.raw_valid_at_hash == [0; 32] {
74if value.raw_hf != 0 {
75return Self::OnlySemantic(
76 HardFork::from_version(value.raw_hf)
77 .expect("hard-fork values stored in the DB should always be valid"),
78 );
79 }
8081return Self::NotVerified;
82 }
8384let raw_valid_past_timestamp = u64::from_le_bytes(value.raw_valid_past_timestamp);
8586// if the timestamp is 0, there is no timestamp that needs to be passed.
87if raw_valid_past_timestamp == 0 {
88return 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 }
9495Self::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}
103104#[expect(clippy::fallible_impl_from, reason = "only panics in invalid states")]
105impl From<CachedVerificationState> for RawCachedVerificationState {
106fn from(value: CachedVerificationState) -> Self {
107match 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 } => {
128let Timelock::Time(time) = time_lock else {
129panic!("ValidAtHashAndHFWithTimeBasedLock timelock was not time-based");
130 };
131132Self {
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}