1use bytemuck::{Pod, Zeroable};
9use monero_oxide::transaction::Timelock;
10
11use cuprate_types::{CachedVerificationState, HardFork};
12
13pub type KeyImage = [u8; 32];
15
16pub type TransactionHash = [u8; 32];
18
19pub type TransactionBlobHash = [u8; 32];
21
22bitflags::bitflags! {
23 #[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Pod, Zeroable)]
25 #[repr(transparent)]
26 pub struct TxStateFlags: u8 {
27 const STATE_STEM = 0b0000_0001;
29 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#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Pod, Zeroable)]
42#[repr(C)]
43pub struct TransactionInfo {
44 pub fee: u64,
46 pub weight: usize,
48 pub received_at: u64,
50 pub cached_verification_state: RawCachedVerificationState,
51 pub flags: TxStateFlags,
53 #[expect(clippy::pub_underscore_fields)]
54 pub _padding: [u8; 6],
58}
59
60#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Pod, Zeroable)]
64#[repr(C)]
65pub struct RawCachedVerificationState {
66 raw_valid_at_hash: [u8; 32],
68 raw_hf: u8,
70 raw_valid_past_timestamp: [u8; 8],
77}
78
79impl From<RawCachedVerificationState> for CachedVerificationState {
80 fn from(value: RawCachedVerificationState) -> Self {
81 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 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}