1use core::{fmt, marker::PhantomData};
10
11use hashsigs_types::{
12 CHAIN_COUNT, HASH_BYTES, MESSAGE_BYTES, MessageDigest, Profile, PublicKey, SIGNATURE_BYTES,
13 Signature,
14};
15use rhdl::prelude::*;
16
17use crate::blocks::HashBytes;
18
19pub const VERIFY_BEAT_BYTES: usize = 64;
21pub const VERIFY_INPUT_BYTES: usize = SIGNATURE_BYTES + 2 * HASH_BYTES + MESSAGE_BYTES;
23pub const VERIFY_INPUT_BEATS: usize = VERIFY_INPUT_BYTES / VERIFY_BEAT_BYTES;
25pub const VERIFY_FULL_KEEP: u64 = u64::MAX;
27
28const _: () = {
29 assert!(SIGNATURE_BYTES == 2_144);
30 assert!(VERIFY_INPUT_BYTES == 2_240);
31 assert!(VERIFY_INPUT_BYTES.is_multiple_of(VERIFY_BEAT_BYTES));
32 assert!(VERIFY_INPUT_BEATS == 35);
33};
34
35#[derive(Clone, Copy, Debug, Eq, PartialEq)]
37pub struct VerifyFrameBeat {
38 pub data: [u8; VERIFY_BEAT_BYTES],
40 pub keep: u64,
42 pub last: bool,
44 pub job_id: u32,
46}
47
48impl Default for VerifyFrameBeat {
49 fn default() -> Self {
50 Self {
51 data: [0; VERIFY_BEAT_BYTES],
52 keep: 0,
53 last: false,
54 job_id: 0,
55 }
56 }
57}
58
59#[derive(Clone, Debug, Eq, PartialEq)]
68pub struct VerifyFrame<P: Profile> {
69 beats: [VerifyFrameBeat; VERIFY_INPUT_BEATS],
70 profile: PhantomData<fn() -> P>,
71}
72
73impl<P: Profile> VerifyFrame<P> {
74 #[must_use]
81 pub const fn from_beats(beats: [VerifyFrameBeat; VERIFY_INPUT_BEATS]) -> Self {
82 Self {
83 beats,
84 profile: PhantomData,
85 }
86 }
87
88 #[must_use]
90 pub const fn beats(&self) -> &[VerifyFrameBeat; VERIFY_INPUT_BEATS] {
91 &self.beats
92 }
93
94 #[must_use]
96 pub const fn into_beats(self) -> [VerifyFrameBeat; VERIFY_INPUT_BEATS] {
97 self.beats
98 }
99}
100
101#[derive(Clone, Copy, Debug, Eq, PartialEq)]
103pub enum VerifyFrameError {
104 Keep {
106 beat: usize,
108 actual: u64,
110 },
111 Last {
113 beat: usize,
115 actual: bool,
117 },
118 JobId {
120 beat: usize,
122 expected: u32,
124 actual: u32,
126 },
127}
128
129impl fmt::Display for VerifyFrameError {
130 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
131 match *self {
132 Self::Keep { beat, actual } => {
133 write!(
134 formatter,
135 "beat {beat} has noncanonical keep {actual:#018x}"
136 )
137 }
138 Self::Last { beat, actual } => {
139 write!(formatter, "beat {beat} has noncanonical last={actual}")
140 }
141 Self::JobId {
142 beat,
143 expected,
144 actual,
145 } => write!(
146 formatter,
147 "beat {beat} changed job ID from {expected:#010x} to {actual:#010x}"
148 ),
149 }
150 }
151}
152
153impl std::error::Error for VerifyFrameError {}
154
155#[must_use]
157pub fn pack_verify_input<P: Profile>(
158 signature: &Signature<P>,
159 public_key: &PublicKey<P>,
160 message: &MessageDigest,
161 job_id: u32,
162) -> VerifyFrame<P> {
163 let signature = signature.to_bytes();
164 let public_key = public_key.to_bytes();
165 let message = message.as_bytes();
166 let mut stream = [0_u8; VERIFY_INPUT_BYTES];
167 stream[..SIGNATURE_BYTES].copy_from_slice(&signature);
168 stream[SIGNATURE_BYTES..SIGNATURE_BYTES + 2 * HASH_BYTES].copy_from_slice(&public_key);
169 stream[SIGNATURE_BYTES + 2 * HASH_BYTES..].copy_from_slice(message);
170
171 let beats = core::array::from_fn(|beat| {
172 let offset = beat * VERIFY_BEAT_BYTES;
173 let mut data = [0_u8; VERIFY_BEAT_BYTES];
174 data.copy_from_slice(&stream[offset..offset + VERIFY_BEAT_BYTES]);
175 VerifyFrameBeat {
176 data,
177 keep: VERIFY_FULL_KEEP,
178 last: beat + 1 == VERIFY_INPUT_BEATS,
179 job_id,
180 }
181 });
182 VerifyFrame::from_beats(beats)
183}
184
185pub fn unpack_verify_input<P: Profile>(
196 frame: &VerifyFrame<P>,
197) -> Result<(Signature<P>, PublicKey<P>, MessageDigest, u32), VerifyFrameError> {
198 let beats = frame.beats();
199 let job_id = beats[0].job_id;
200 let mut stream = [0_u8; VERIFY_INPUT_BYTES];
201 for (beat_index, beat) in beats.iter().enumerate() {
202 if beat.keep != VERIFY_FULL_KEEP {
203 return Err(VerifyFrameError::Keep {
204 beat: beat_index,
205 actual: beat.keep,
206 });
207 }
208 let expected_last = beat_index + 1 == VERIFY_INPUT_BEATS;
209 if beat.last != expected_last {
210 return Err(VerifyFrameError::Last {
211 beat: beat_index,
212 actual: beat.last,
213 });
214 }
215 if beat.job_id != job_id {
216 return Err(VerifyFrameError::JobId {
217 beat: beat_index,
218 expected: job_id,
219 actual: beat.job_id,
220 });
221 }
222 let offset = beat_index * VERIFY_BEAT_BYTES;
223 stream[offset..offset + VERIFY_BEAT_BYTES].copy_from_slice(&beat.data);
224 }
225
226 let signature = Signature::from_slice(&stream[..SIGNATURE_BYTES])
227 .expect("the fixed verifier frame contains exactly 2,144 signature bytes");
228 let public_key = PublicKey::from_slice(&stream[SIGNATURE_BYTES..SIGNATURE_BYTES + 64])
229 .expect("the fixed verifier frame contains exactly 64 public-key bytes");
230 let message = MessageDigest::from_slice(&stream[SIGNATURE_BYTES + 64..])
231 .expect("the fixed verifier frame contains exactly 32 message bytes");
232 Ok((signature, public_key, message, job_id))
233}
234
235#[derive(Clone, Copy, Debug, Digital, Eq, PartialEq)]
237pub struct VerifyStreamBeat {
238 pub data: [b8; VERIFY_BEAT_BYTES],
240 pub keep: b64,
242 pub last: bool,
244 pub job_id: b32,
246}
247
248impl Default for VerifyStreamBeat {
249 fn default() -> Self {
250 Self {
251 data: [b8(0); VERIFY_BEAT_BYTES],
252 keep: b64(0),
253 last: false,
254 job_id: b32(0),
255 }
256 }
257}
258
259#[derive(Clone, Copy, Debug, Default, Digital, Eq, PartialEq)]
261pub struct VerifyBeatHalves {
262 pub first: HashBytes,
264 pub second: HashBytes,
266}
267
268#[kernel]
270pub fn verify_beat_halves_kernel(data: [b8; VERIFY_BEAT_BYTES]) -> VerifyBeatHalves {
271 let mut first = [b8(0); HASH_BYTES];
272 let mut second = [b8(0); HASH_BYTES];
273 for index in 0..HASH_BYTES {
274 first[index] = data[index];
275 second[index] = data[index + HASH_BYTES];
276 }
277 VerifyBeatHalves { first, second }
278}
279
280#[allow(clippy::assign_op_pattern)]
282#[kernel]
283pub fn verify_hash_equal_kernel(left: HashBytes, right: HashBytes) -> bool {
284 let mut difference = b8(0);
285 for index in 0..HASH_BYTES {
286 difference = difference | (left[index] ^ right[index]);
287 }
288 difference == b8(0)
289}
290
291const _: () = {
292 assert!(CHAIN_COUNT == 67);
293};