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 flags: TxStateFlags,
52 #[expect(clippy::pub_underscore_fields)]
53 pub _padding: [u8; 7],
57}
58
59#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Pod, Zeroable)]
63#[repr(C)]
64pub struct RawCachedVerificationState {
65 raw_valid_at_hash: [u8; 32],
67 raw_hf: u8,
69 raw_valid_past_timestamp: [u8; 8],
76}
77
78impl From<RawCachedVerificationState> for CachedVerificationState {
79 fn from(value: RawCachedVerificationState) -> Self {
80 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 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}