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