1use super::{Axis512, Latency, latency_kernel};
40use crate::cert_unit::{CertConfig, CertInput, CertUnit, SLICES};
41use crate::ed25519::ScalarWords;
42use crate::engine::{SliceControl, SliceReport};
43use crate::relay::{Delay, PipeIn, RelayPipe, ToggleLink};
44use crate::slice::{Slice, SliceInput};
45use crate::slice_factory::{FactoryConfig, TWO_BLOCK_MESSAGE};
46use crate::slice_signer::{Certificate, FrameBeat, Message};
47use rhdl::core::ReplicatedSynchronous;
48use rhdl::prelude::*;
49use rhdl_fpga::core::dff::DFF;
50
51#[derive(Clone, Copy, Debug, PartialEq, Digital)]
53pub struct SignConfig {
54 pub prefix_cv: [b32; 8],
56 pub epoch: b64,
58 pub first_batch: b64,
60 pub batches: b32,
62 pub seed: [b64; 4],
64}
65
66#[derive(Clone, Copy, Debug, PartialEq, Digital)]
68pub struct SignerIn {
69 pub config: SignConfig,
71 pub run: bool,
73 pub ticks: b64,
75 pub request: Axis512,
77 pub frame_ready: bool,
79 pub cert_ready: bool,
81}
82
83#[derive(Clone, Copy, Debug, PartialEq, Digital)]
85pub struct SignerOut {
86 pub request_ready: bool,
88 pub frame: Axis512,
90 pub cert: Axis512,
92 pub announced: b32,
94 pub taken: b32,
96 pub signed: b32,
98 pub certified: b32,
100 pub public_key: ScalarWords,
102 pub slice_fault: bool,
104 pub cert_fault: bool,
106 pub latency: Latency,
108}
109
110#[derive(Clone, Copy, Debug, PartialEq, Digital)]
112#[doc(hidden)]
113pub struct SignerState {
114 pending: [Message; SLICES],
116 next: b3,
118 locked: bool,
120 owner: b3,
122 next_owner: b3,
124 taken: b32,
126 signed: b32,
128 in_flight: b32,
130 latency: Latency,
132 ann_valid: bool,
134 ann_second: bool,
136 ann_commitment: [b8; 64],
138 ann_signature: [b8; 64],
140 announced: b32,
142}
143
144const CONTEXT_BYTES: usize = if TWO_BLOCK_MESSAGE { 48 } else { 0 };
146
147impl Default for SignerState {
148 fn default() -> Self {
150 Self {
151 pending: [Message {
152 valid: false,
153 bytes: [b8(0); crate::MESSAGE_BYTES],
154 index: b64(0),
155 }; SLICES],
156 next: b3(0),
157 locked: false,
158 owner: b3(0),
159 next_owner: b3(0),
160 taken: b32(0),
161 signed: b32(0),
162 in_flight: b32(0),
163 latency: Latency::default(),
164 ann_valid: false,
165 ann_second: false,
166 ann_commitment: [b8(0); 64],
167 ann_signature: [b8(0); 64],
168 announced: b32(0),
169 }
170 }
171}
172
173#[derive(Clone, Debug, Synchronous, SynchronousDQ)]
175pub struct UbftSigner<const TABLE_MASK: usize> {
176 state: DFF<SignerState>,
178 cert: CertUnit,
180 slices: ReplicatedSynchronous<Slice<TABLE_MASK>, SLICES>,
182 beats: [RelayPipe<FrameBeat>; SLICES],
184 messages: [ToggleLink<Message>; SLICES],
186 certificates: [ToggleLink<Certificate>; SLICES],
188 to_slice: [Delay<SliceControl>; SLICES],
190 from_slice: [Delay<SliceReport>; SLICES],
192}
193
194impl<const TABLE_MASK: usize> Default for UbftSigner<TABLE_MASK> {
195 fn default() -> Self {
197 Self {
198 state: DFF::new(SignerState::default()),
199 cert: CertUnit::default(),
200 slices: ReplicatedSynchronous::new(Slice::default()),
201 beats: core::array::from_fn(|_| RelayPipe::default()),
202 messages: core::array::from_fn(|_| ToggleLink::default()),
203 certificates: core::array::from_fn(|_| ToggleLink::default()),
204 to_slice: core::array::from_fn(|_| Delay::default()),
205 from_slice: core::array::from_fn(|_| Delay::default()),
206 }
207 }
208}
209
210impl<const TABLE_MASK: usize> SynchronousIO for UbftSigner<TABLE_MASK> {
211 type I = SignerIn;
212 type O = SignerOut;
213 type Kernel = ubft_signer_kernel<TABLE_MASK>;
214}
215
216#[allow(clippy::needless_range_loop, clippy::too_many_lines)]
218#[kernel]
219pub fn ubft_signer_kernel<const TABLE_MASK: usize>(
220 _clock_reset: ClockReset,
221 input: SignerIn,
222 q: UbftSignerQ<TABLE_MASK>,
223) -> (SignerOut, UbftSignerD<TABLE_MASK>) {
224 let mut s = q.state;
225 let run = input.run;
226
227 let mut pending = q.state.pending;
230 let mut message_taken = [false; SLICES];
231 for i in 0..SLICES {
232 message_taken[i] = q.state.pending[i].valid && q.messages[i].ready;
233 if message_taken[i] {
234 pending[i].valid = false;
235 }
236 }
237 let mut pick_found = false;
239 let mut pick = b3(0);
240 for j in 0..SLICES {
241 let i = SLICES - 1 - j;
242 if !q.state.pending[i].valid && b3(i as u128) >= q.state.next {
243 pick_found = true;
244 pick = b3(i as u128);
245 }
246 }
247 if !pick_found {
248 for j in 0..SLICES {
249 let i = SLICES - 1 - j;
250 if !q.state.pending[i].valid {
251 pick_found = true;
252 pick = b3(i as u128);
253 }
254 }
255 }
256 let request_ready = run && pick_found;
257 let accept = request_ready && input.request.valid;
258 if accept {
259 let d = input.request.data;
260 let mut bytes = [b8(0); crate::MESSAGE_BYTES];
264 let mut index = b64(0);
265 if TWO_BLOCK_MESSAGE {
266 for k in 0..CONTEXT_BYTES {
267 bytes[k] = d[8 + k];
268 }
269 for k in 0..8 {
270 index = index | (d[k].resize::<64>() << ((8 * k) as u128));
271 }
272 } else {
273 for k in 0..16 {
274 bytes[k] = d[k];
275 }
276 for k in 0..8 {
277 index = index | (d[16 + k].resize::<64>() << ((8 * k) as u128));
278 }
279 }
280 for i in 0..SLICES {
281 if pick == b3(i as u128) {
282 pending[i] = Message {
283 valid: true,
284 bytes,
285 index,
286 };
287 }
288 }
289 s.next = if pick == b3((SLICES - 1) as u128) { b3(0) } else { pick + b3(1) };
290 s.taken = q.state.taken + b32(1);
291 }
292 s.pending = pending;
293
294 let mut frame = Axis512 {
296 valid: false,
297 data: [b8(0); 64],
298 last: false,
299 };
300 let mut beat_taken = [false; SLICES];
301 let mut sent_last = false;
302 if q.state.locked {
303 for i in 0..SLICES {
304 if q.state.owner == b3(i as u128) {
305 frame = Axis512 {
306 valid: q.beats[i].valid,
307 data: q.beats[i].data.data,
308 last: q.beats[i].data.last,
309 };
310 beat_taken[i] = q.beats[i].valid && input.frame_ready;
311 }
312 }
313 if frame.valid && input.frame_ready && frame.last {
314 sent_last = true;
315 s.locked = false;
316 s.signed = q.state.signed + b32(1);
317 }
318 } else {
319 let mut found = false;
320 let mut owner = b3(0);
321 for j in 0..SLICES {
322 let i = SLICES - 1 - j;
323 if q.beats[i].valid && b3(i as u128) >= q.state.next_owner {
324 found = true;
325 owner = b3(i as u128);
326 }
327 }
328 if !found {
329 for j in 0..SLICES {
330 let i = SLICES - 1 - j;
331 if q.beats[i].valid {
332 found = true;
333 owner = b3(i as u128);
334 }
335 }
336 }
337 if found {
338 s.locked = true;
339 s.owner = owner;
340 s.next_owner = if owner == b3((SLICES - 1) as u128) { b3(0) } else { owner + b3(1) };
341 }
342 }
343
344 let begin = accept && q.state.in_flight == b32(0);
346 let end = sent_last && q.state.in_flight == b32(1);
347 let abandon = accept && q.state.in_flight != b32(0);
348 s.latency = latency_kernel(q.state.latency, input.ticks, begin, end, abandon);
349 s.in_flight = q.state.in_flight + (if accept { b32(1) } else { b32(0) }) - (if sent_last { b32(1) } else { b32(0) });
350
351 let cert_out = Axis512 {
353 valid: q.state.ann_valid,
354 data: if q.state.ann_second { q.state.ann_signature } else { q.state.ann_commitment },
355 last: q.state.ann_second,
356 };
357 if q.state.ann_valid && input.cert_ready {
358 if q.state.ann_second {
359 s.ann_valid = false;
360 s.ann_second = false;
361 s.announced = q.state.announced + b32(1);
362 } else {
363 s.ann_second = true;
364 }
365 }
366 let fresh = q.cert.certificates[0].valid && q.certificates[0].ready;
368 if fresh && !q.state.ann_valid {
369 s.ann_valid = true;
370 s.ann_second = false;
371 s.ann_commitment = q.cert.certificates[0].commitment;
372 s.ann_signature = q.cert.certificates[0].signature;
373 }
374
375 let cfg = input.config;
377 let mut roots = [q.from_slice[0].root; SLICES];
378 let mut cert_taken = [false; SLICES];
379 let mut slice_fault = false;
380 for i in 0..SLICES {
381 roots[i] = q.from_slice[i].root;
382 cert_taken[i] = q.cert.certificates[i].valid && q.certificates[i].ready;
383 slice_fault = slice_fault || q.from_slice[i].fault;
384 }
385 let mut to_slice = [SliceControl {
386 config: FactoryConfig {
387 prefix_cv: cfg.prefix_cv,
388 epoch: cfg.epoch,
389 first_batch: cfg.first_batch,
390 batches: cfg.batches,
391 slice: b3(0),
392 },
393 run,
394 roots_popped: q.cert.roots_popped,
395 }; SLICES];
396 let mut slices = [SliceInput {
397 config: q.to_slice[0].config,
398 run: q.to_slice[0].run,
399 message: q.messages[0].data,
400 certificate: q.certificates[0].data,
401 beat_ready: q.beats[0].ready,
402 roots_popped: q.to_slice[0].roots_popped,
403 }; SLICES];
404 let mut beats = [PipeIn::<FrameBeat> {
405 data: q.slices[0].beat,
406 valid: q.slices[0].beat.valid,
407 taken: beat_taken[0],
408 }; SLICES];
409 let mut messages = [PipeIn::<Message> {
410 data: q.state.pending[0],
411 valid: q.state.pending[0].valid,
412 taken: q.slices[0].message_taken,
413 }; SLICES];
414 let mut certificates = [PipeIn::<Certificate> {
415 data: q.cert.certificates[0],
416 valid: q.cert.certificates[0].valid,
417 taken: q.slices[0].certificate_taken,
418 }; SLICES];
419 let mut from_slice = [SliceReport {
420 root: q.slices[0].root,
421 keys_started: q.slices[0].keys_started,
422 fault: q.slices[0].fault,
423 }; SLICES];
424 for i in 0..SLICES {
425 to_slice[i].config.slice = b3(i as u128);
426 slices[i] = SliceInput {
427 config: q.to_slice[i].config,
428 run: q.to_slice[i].run,
429 message: q.messages[i].data,
430 certificate: q.certificates[i].data,
431 beat_ready: q.beats[i].ready,
432 roots_popped: q.to_slice[i].roots_popped,
433 };
434 slices[i].message.valid = q.messages[i].valid;
435 slices[i].certificate.valid = q.certificates[i].valid;
436 beats[i] = PipeIn::<FrameBeat> {
437 data: q.slices[i].beat,
438 valid: q.slices[i].beat.valid,
439 taken: beat_taken[i],
440 };
441 messages[i] = PipeIn::<Message> {
442 data: q.state.pending[i],
443 valid: q.state.pending[i].valid,
444 taken: q.slices[i].message_taken,
445 };
446 certificates[i] = PipeIn::<Certificate> {
447 data: q.cert.certificates[i],
448 valid: q.cert.certificates[i].valid,
449 taken: q.slices[i].certificate_taken,
450 };
451 from_slice[i] = SliceReport {
452 root: q.slices[i].root,
453 keys_started: q.slices[i].keys_started,
454 fault: q.slices[i].fault,
455 };
456 }
457 let d = UbftSignerD::<TABLE_MASK> {
458 state: s,
459 cert: CertInput {
460 config: CertConfig {
461 epoch: cfg.epoch,
462 first_batch: cfg.first_batch,
463 key_seed: ScalarWords { words: cfg.seed },
464 run,
465 },
466 roots,
467 taken: cert_taken,
468 },
469 slices,
470 beats,
471 messages,
472 certificates,
473 to_slice,
474 from_slice,
475 };
476 (
477 SignerOut {
478 request_ready,
479 frame,
480 cert: cert_out,
481 announced: q.state.announced,
482 taken: q.state.taken,
483 signed: q.state.signed,
484 certified: q.cert.certified,
485 public_key: q.cert.public_key,
486 slice_fault,
487 cert_fault: q.cert.fault,
488 latency: q.state.latency,
489 },
490 d,
491 )
492}