1use bytemuck::{Pod, Zeroable};
9use monero_serai::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
34#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Pod, Zeroable)]
36#[repr(C)]
37pub struct TransactionInfo {
38 pub fee: u64,
40 pub weight: usize,
42 pub flags: TxStateFlags,
44 #[expect(clippy::pub_underscore_fields)]
45 pub _padding: [u8; 7],
49}
50
51#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Pod, Zeroable)]
55#[repr(C)]
56pub struct RawCachedVerificationState {
57 raw_valid_at_hash: [u8; 32],
59 raw_hf: u8,
61 raw_valid_past_timestamp: [u8; 8],
68}
69
70impl From<RawCachedVerificationState> for CachedVerificationState {
71 fn from(value: RawCachedVerificationState) -> Self {
72 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 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}