Skip to main content

wots_rhdl/verify/
sha.rs

1//! Fine-grained, single-context SHA-256 WOTS verifier.
2//!
3//! The context loads one exact 35-beat input frame, stores signature segments
4//! directly in two mutable parity banks, derives all sixteen current-oracle
5//! masks, advances only real chain steps, hashes the 67 endpoints, and retains a
6//! small verification result under backpressure. It emits no task for a bypassed
7//! chain position.
8//!
9//! Tasks may issue in any dependency-correct order. Every accepted response
10//! must carry a canonical verifier-subset tag for this context and generation
11//! and name an actually in-flight task. Rejected responses perform no state
12//! write and latch a fail-closed transport poison that prevents context reuse
13//! until shared reset.
14//! Reset suppresses requests, response acceptance, memory writes, and retained
15//! results; the enclosing lane must share that reset.
16//!
17//! The functional compression-lane contract is exact 64-cycle latency with
18//! tag preservation. A 64-bit due bitmap enforces response cardinality and
19//! timing locally, but is not an independent detector for a perfect swap of two
20//! canonical in-flight tags. The enclosing verifier transport supplies that
21//! stronger expected-tag delay line and converts every global integrity failure
22//! into `transport_abort`. This context exposes ordinary request backpressure;
23//! explicit outbound rejection and unowned lane returns are cluster-fatal rather
24//! than being represented as an indefinitely deasserted `request_ready`.
25//! Message digits are intentionally not retained after loading: the segment
26//! stages and exact expected-task count capture every downstream dependency,
27//! avoiding 268 bits of redundant logical register state.
28//!
29//! Logical RAM geometry is 36,864 bits per context. This is source geometry, not
30//! synthesis, mapping, resource, timing, route, or hardware evidence.
31
32use rhdl::prelude::*;
33use rhdl_fpga::core::{
34    dff::DFF,
35    ram::synchronous::{In as SyncBramIn, SyncBRAM, Write as SyncBramWrite},
36};
37use rhdl_primitives::NoResetDff;
38use sha256_rhdl::lane::{CompressionInput, CompressionOutput};
39
40use crate::{
41    blocks::{
42        ChainBlockInput, EndpointPairInput, HashBytes, HashWords, PrfBlockInput,
43        chain_block_kernel, endpoint_final_block_kernel, endpoint_pair_block_kernel,
44        hash_words_to_bytes_kernel, initial_state_words_kernel, prf_block_kernel,
45    },
46    digits::message_digits_kernel,
47    engine::{
48        ENGINE_SEGMENTS, MASK_ADDRESS_BITS, MASK_STORAGE_BITS, SEGMENT_GROUPS,
49        STATE_BANK_ADDRESS_BITS, STATE_STORAGE_BITS, SegmentGroups, SegmentStages,
50        exclude_segment_groups_kernel, public_key_endpoints_ready_kernel, segment_bit_kernel,
51        segment_required_mask_kernel, select_public_key_focus_segment_kernel,
52        select_ready_segment_kernel, set_segment_bit_kernel, state_pair_exclusion_kernel,
53        union_segment_groups_kernel, wake_segments_for_mask_kernel,
54    },
55    tag::{TagFields, decode_tag_kernel, encode_tag_kernel},
56    verify::{
57        framing::{VerifyStreamBeat, verify_beat_halves_kernel, verify_hash_equal_kernel},
58        tasks::{
59            verification_chain_fields_kernel, verification_sha_task_count_kernel,
60            verification_tag_is_well_formed_kernel,
61        },
62    },
63};
64
65/// Successful protocol execution; `verified` carries the signature decision.
66pub const VERIFY_ERROR_NONE: u128 = 0;
67/// Noncanonical keep, last, job-ID, context, or generation framing.
68pub const VERIFY_ERROR_FRAME: u128 = 1;
69/// Malformed, stale, duplicate, or unexpected compression response.
70pub const VERIFY_ERROR_TRANSPORT: u128 = 2;
71/// Completion disagreed with exact task/dependency accounting.
72pub const VERIFY_ERROR_AUDIT: u128 = 3;
73
74const VERIFY_FULL_KEEP_BITS: u128 = u64::MAX as u128;
75const VERIFY_DUE_INSERT_BITS: u128 = 1_u128 << 63;
76const VERIFY_REMOVED_MESSAGE_DIGIT_STATE_BITS: usize = ENGINE_SEGMENTS * 4;
77
78/// Logical RAM bits authored by one SHA verifier context.
79pub const VERIFY_CONTEXT_LOGICAL_RAM_BITS: usize = STATE_STORAGE_BITS + MASK_STORAGE_BITS;
80/// Exact functional latency required from the tag-preserving SHA lane.
81pub const VERIFY_LANE_LATENCY: usize = 64;
82
83const _: () = {
84    assert!(ENGINE_SEGMENTS == 67);
85    assert!(VERIFY_CONTEXT_LOGICAL_RAM_BITS == 36_864);
86    assert!(VERIFY_LANE_LATENCY == 64);
87    assert!(VERIFY_REMOVED_MESSAGE_DIGIT_STATE_BITS == 268);
88};
89
90/// Resettable frame, dependency, ownership, and result state.
91#[allow(clippy::struct_excessive_bools)]
92#[derive(Clone, Copy, Debug, Digital, Eq, PartialEq)]
93pub struct VerifyContextControl {
94    /// A 35-beat frame is being loaded.
95    pub loading: bool,
96    /// Cryptographic verification work is active.
97    pub active: bool,
98    /// One retained result awaits transfer.
99    pub result_valid: bool,
100    /// Sticky compression-transport poison, cleared only by shared reset.
101    pub fault: bool,
102    /// Sticky framing failure during the current load.
103    pub frame_fault: bool,
104    /// Retained signature decision; meaningful only with no error.
105    pub result_verified: bool,
106    /// Retained three-bit result error code.
107    pub result_error: b3,
108    /// Next fixed frame beat while loading.
109    pub load_beat: b6,
110    /// Eligible mask PRFs.
111    pub mask_ready: b16,
112    /// Issued but unretired mask PRFs.
113    pub mask_inflight: b16,
114    /// Retired mask PRFs.
115    pub mask_done: b16,
116    /// Eligible chain tasks.
117    pub segment_ready: SegmentGroups,
118    /// Issued but unretired chain tasks.
119    pub segment_inflight: SegmentGroups,
120    /// Current chain stage, or endpoint sentinel 18, for every segment.
121    pub segment_stage: SegmentStages,
122    /// Signature values already at final chain position fifteen.
123    pub endpoint_done: SegmentGroups,
124    /// Rotating general chain selector cursor.
125    pub segment_group_cursor: b4,
126    /// Serialized endpoint-hash block currently eligible or in flight.
127    pub public_key_block: b6,
128    /// One endpoint-hash block is in flight.
129    pub public_key_inflight: bool,
130    /// Prior endpoint-hash chaining state is available.
131    pub public_key_state_ready: bool,
132    /// Accepted compression requests.
133    pub total_issued: b11,
134    /// Accepted compression responses.
135    pub total_retired: b11,
136    /// Request-cardinality bits due to return in exactly 1 through 64 cycles.
137    pub response_due: b64,
138    /// Exact message-dependent task count.
139    pub expected_tasks: b11,
140    /// A synchronous-memory prefetch awaits payload construction.
141    pub prefetch_pending: bool,
142    /// Complete tag captured for the prefetch.
143    pub prefetch_tag: b32,
144    /// Stable compression request is presented to transport.
145    pub request_valid: bool,
146    /// Local transport context captured on beat zero.
147    pub context: b4,
148    /// Stale-response generation captured on beat zero.
149    pub generation: b8,
150}
151
152impl Default for VerifyContextControl {
153    fn default() -> Self {
154        Self {
155            loading: false,
156            active: false,
157            result_valid: false,
158            fault: false,
159            frame_fault: false,
160            result_verified: false,
161            result_error: b3(0),
162            load_beat: b6(0),
163            mask_ready: b16(0),
164            mask_inflight: b16(0),
165            mask_done: b16(0),
166            segment_ready: [b8(0); SEGMENT_GROUPS],
167            segment_inflight: [b8(0); SEGMENT_GROUPS],
168            segment_stage: [b5(18); ENGINE_SEGMENTS],
169            endpoint_done: [b8(0); SEGMENT_GROUPS],
170            segment_group_cursor: b4(0),
171            public_key_block: b6(0),
172            public_key_inflight: false,
173            public_key_state_ready: false,
174            total_issued: b11(0),
175            total_retired: b11(0),
176            response_due: b64(0),
177            expected_tasks: b11(0),
178            prefetch_pending: false,
179            prefetch_tag: b32(0),
180            request_valid: false,
181            context: b4(0),
182            generation: b8(0),
183        }
184    }
185}
186
187/// Input ports for one SHA verifier context.
188#[allow(clippy::struct_excessive_bools)]
189#[derive(Clone, Copy, Debug, Default, Digital, Eq, PartialEq)]
190pub struct VerifyContextInput {
191    /// A verifier stream beat is present.
192    pub stream_valid: bool,
193    /// Complete 512-bit stream payload and sideband.
194    pub stream: VerifyStreamBeat,
195    /// Local context assigned by the enclosing cluster.
196    pub context: b4,
197    /// The enclosing cluster confirms this context exists.
198    pub context_enabled: bool,
199    /// Generation assigned to this context reuse.
200    pub generation: b8,
201    /// Compression transport accepted the stable request.
202    pub request_ready: bool,
203    /// Routed fixed-latency compression response.
204    pub response: CompressionOutput,
205    /// Consumer accepts the retained verification result.
206    pub result_ready: bool,
207    /// Enclosing transport observed a lane-global integrity failure.
208    ///
209    /// Loading, active, and clean retained jobs become held transport errors.
210    /// An idle context and an already-retained per-job framing error are left
211    /// untouched. Only shared reset clears the resulting poison.
212    pub transport_abort: bool,
213}
214
215/// Output ports from one SHA verifier context.
216#[allow(clippy::struct_excessive_bools)]
217#[derive(Clone, Copy, Debug, Default, Digital, Eq, PartialEq)]
218pub struct VerifyContextOutput {
219    /// The presented stream beat can transfer.
220    pub stream_ready: bool,
221    /// Frame loading is in progress.
222    pub loading: bool,
223    /// Cryptographic work is in progress.
224    pub active: bool,
225    /// Stable request for the SHA transport.
226    pub request: CompressionInput,
227    /// Registered request before local response-failure output gating.
228    ///
229    /// The enclosing expected-tag transport uses this payload to avoid a
230    /// combinational route/request-valid loop on a cycle that both launches a
231    /// held request and returns another. The cluster still suppresses the
232    /// actual launch and acknowledgement on every immediate fatal edge.
233    pub transport_request: CompressionInput,
234    /// A response passed all canonical/in-flight checks.
235    pub response_ready: bool,
236    /// A response was rejected without a state write.
237    pub response_rejected: bool,
238    /// Sticky transport fault for the current operation.
239    pub fault: bool,
240    /// Registered transport/audit poison before current-cycle response checks.
241    pub fault_latched: bool,
242    /// One-cycle pulse when a protocol or cryptographic result is completed.
243    pub done: bool,
244    /// A retained result is present.
245    pub result_valid: bool,
246    /// One-cycle result transfer pulse.
247    pub result_transfer: bool,
248    /// Retained external job identifier.
249    pub result_job_id: b32,
250    /// Retained signature decision; forced false for every protocol error.
251    pub verified: bool,
252    /// Retained error code.
253    pub error: b3,
254    /// Exact expected SHA task count.
255    pub expected_tasks: b11,
256    /// Accepted requests.
257    pub total_issued: b11,
258    /// Accepted responses before the current edge.
259    pub total_retired: b11,
260}
261
262/// Inputs needed after one synchronous state/mask-memory prefetch.
263#[derive(Clone, Copy, Debug, Default, Digital, Eq, PartialEq)]
264pub struct VerifyPrepareRequestInput {
265    /// Canonical verifier-subset fields.
266    pub fields: TagFields,
267    /// Public seed used by mask PRFs.
268    pub public_seed: HashBytes,
269    /// Prior serialized endpoint-hash chaining value.
270    pub public_key_state: HashWords,
271    /// Prefetched even state/endpoint.
272    pub state_even: HashWords,
273    /// Prefetched odd state/endpoint.
274    pub state_odd: HashWords,
275    /// Prefetched one-based chain mask.
276    pub mask: HashWords,
277}
278
279/// Build a SHA request for exactly Mask, Chain, or `PublicKey` work.
280#[kernel]
281pub fn prepare_verify_request_kernel(input: VerifyPrepareRequestInput) -> CompressionInput {
282    let initial = initial_state_words_kernel(b1(0));
283    let state_words = if input.fields.segment & b7(1) == b7(0) {
284        input.state_even
285    } else {
286        input.state_odd
287    };
288    let mut chaining = initial;
289    let mut block = prf_block_kernel(PrfBlockInput {
290        seed: input.public_seed,
291        index: input.fields.segment.resize(),
292    });
293    let mut valid = input.fields.kind == b3(2);
294
295    if input.fields.kind == b3(6) {
296        valid = true;
297        block = chain_block_kernel(ChainBlockInput {
298            state: hash_words_to_bytes_kernel(state_words),
299            mask: hash_words_to_bytes_kernel(input.mask),
300        });
301    } else if input.fields.kind == b3(7) {
302        valid = true;
303        if input.fields.block_index != b6(0) {
304            chaining = input.public_key_state;
305        }
306        if input.fields.block_index == b6(33) {
307            block = endpoint_final_block_kernel(hash_words_to_bytes_kernel(input.state_even));
308        } else {
309            block = endpoint_pair_block_kernel(EndpointPairInput {
310                first: hash_words_to_bytes_kernel(input.state_even),
311                second: hash_words_to_bytes_kernel(input.state_odd),
312            });
313        }
314    }
315
316    CompressionInput {
317        chaining,
318        block,
319        tag: encode_tag_kernel(input.fields),
320        valid,
321    }
322}
323
324/// Complete one-context SHA verifier.
325#[derive(Clone, Debug, Synchronous, SynchronousDQ)]
326#[rhdl(dq_no_prefix)]
327pub struct VerifySingleContextSha {
328    control: DFF<VerifyContextControl>,
329    request: NoResetDff<CompressionInput>,
330    public_seed: NoResetDff<HashBytes>,
331    expected_hash: NoResetDff<HashBytes>,
332    public_key_state: NoResetDff<HashWords>,
333    job_id: NoResetDff<b32>,
334    state_even: SyncBRAM<HashWords, STATE_BANK_ADDRESS_BITS>,
335    state_odd: SyncBRAM<HashWords, STATE_BANK_ADDRESS_BITS>,
336    masks: SyncBRAM<HashWords, MASK_ADDRESS_BITS>,
337}
338
339impl Default for VerifySingleContextSha {
340    fn default() -> Self {
341        Self {
342            control: DFF::new(VerifyContextControl::default()),
343            request: NoResetDff::new(),
344            public_seed: NoResetDff::new(),
345            expected_hash: NoResetDff::new(),
346            public_key_state: NoResetDff::new(),
347            job_id: NoResetDff::new(),
348            state_even: SyncBRAM::default(),
349            state_odd: SyncBRAM::default(),
350            masks: SyncBRAM::default(),
351        }
352    }
353}
354
355impl SynchronousIO for VerifySingleContextSha {
356    type I = VerifyContextInput;
357    type O = VerifyContextOutput;
358    type Kernel = verify_single_context_sha_kernel;
359}
360
361/// State transition and memory connectivity for [`VerifySingleContextSha`].
362#[allow(
363    clippy::assign_op_pattern,
364    clippy::comparison_chain,
365    clippy::match_same_arms,
366    clippy::needless_range_loop
367)]
368#[kernel]
369pub fn verify_single_context_sha_kernel(
370    clock_reset: ClockReset,
371    input: VerifyContextInput,
372    q: Q,
373) -> (VerifyContextOutput, D) {
374    let resetting = clock_reset.reset.any();
375    let zero_words = [b32(0); 8];
376    let halves = verify_beat_halves_kernel(input.stream.data);
377    let mut first_words = [b32(0); 8];
378    let mut second_words = [b32(0); 8];
379    for word in 0..8 {
380        let first_byte = word * 4;
381        let a: b32 = halves.first[first_byte].resize();
382        let b: b32 = halves.first[first_byte + 1].resize();
383        let c: b32 = halves.first[first_byte + 2].resize();
384        let d: b32 = halves.first[first_byte + 3].resize();
385        first_words[word] = (a << 24) | (b << 16) | (c << 8) | d;
386        let e: b32 = halves.second[first_byte].resize();
387        let f: b32 = halves.second[first_byte + 1].resize();
388        let g: b32 = halves.second[first_byte + 2].resize();
389        let h: b32 = halves.second[first_byte + 3].resize();
390        second_words[word] = (e << 24) | (f << 16) | (g << 8) | h;
391    }
392
393    let mut control = q.control;
394    let mut next_request = q.request;
395    let mut next_public_seed = q.public_seed;
396    let mut next_expected_hash = q.expected_hash;
397    let mut next_public_key_state = q.public_key_state;
398    let mut next_job_id = q.job_id;
399    let mut state_read_addr = b6(0);
400    let mut mask_read_addr = b4(0);
401    let mut state_even_write = SyncBramWrite::<HashWords, STATE_BANK_ADDRESS_BITS> {
402        addr: b6(0),
403        value: zero_words,
404        enable: false,
405    };
406    let mut state_odd_write = SyncBramWrite::<HashWords, STATE_BANK_ADDRESS_BITS> {
407        addr: b6(0),
408        value: zero_words,
409        enable: false,
410    };
411    let mut mask_write = SyncBramWrite::<HashWords, MASK_ADDRESS_BITS> {
412        addr: b4(0),
413        value: zero_words,
414        enable: false,
415    };
416    let mut done = false;
417
418    let idle =
419        !q.control.loading && !q.control.active && !q.control.result_valid && !q.control.fault;
420    let transport_abort_live = !resetting
421        && input.transport_abort
422        && (q.control.loading
423            || q.control.active
424            || (q.control.result_valid
425                && !q.control.fault
426                && q.control.result_error == b3(VERIFY_ERROR_NONE)));
427    let stream_ready = !resetting
428        && !input.transport_abort
429        && (q.control.loading || (idle && input.context_enabled));
430    let stream_accepted = input.stream_valid && stream_ready;
431    let frame_start = stream_accepted && !q.control.loading;
432    let beat_index = if q.control.loading {
433        q.control.load_beat
434    } else {
435        b6(0)
436    };
437
438    let result_transfer_requested =
439        !resetting && !transport_abort_live && q.control.result_valid && input.result_ready;
440    if result_transfer_requested {
441        control.result_valid = false;
442        control.result_verified = false;
443        control.result_error = b3(VERIFY_ERROR_NONE);
444        control.frame_fault = false;
445    }
446
447    if frame_start {
448        control.loading = true;
449        control.active = false;
450        control.result_valid = false;
451        control.fault = false;
452        control.frame_fault = false;
453        control.result_verified = false;
454        control.result_error = b3(VERIFY_ERROR_NONE);
455        control.load_beat = b6(0);
456        control.mask_ready = b16(0);
457        control.mask_inflight = b16(0);
458        control.mask_done = b16(0);
459        control.segment_ready = [b8(0); SEGMENT_GROUPS];
460        control.segment_inflight = [b8(0); SEGMENT_GROUPS];
461        control.segment_stage = [b5(18); ENGINE_SEGMENTS];
462        control.endpoint_done = [b8(0); SEGMENT_GROUPS];
463        control.segment_group_cursor = b4(0);
464        control.public_key_block = b6(0);
465        control.public_key_inflight = false;
466        control.public_key_state_ready = false;
467        control.total_issued = b11(0);
468        control.total_retired = b11(0);
469        control.response_due = b64(0);
470        control.expected_tasks = b11(0);
471        control.prefetch_pending = false;
472        control.request_valid = false;
473        control.context = input.context;
474        control.generation = input.generation;
475        next_job_id = input.stream.job_id;
476        next_public_key_state = zero_words;
477    }
478
479    if stream_accepted {
480        let expected_last = beat_index == b6(34);
481        let identity_matches = frame_start
482            || (input.stream.job_id == q.job_id
483                && input.context == q.control.context
484                && input.generation == q.control.generation);
485        let beat_fault = input.stream.keep != b64(VERIFY_FULL_KEEP_BITS)
486            || input.stream.last != expected_last
487            || !identity_matches;
488        control.frame_fault = control.frame_fault || beat_fault;
489
490        if beat_index < b6(33) {
491            state_even_write = SyncBramWrite::<HashWords, STATE_BANK_ADDRESS_BITS> {
492                addr: beat_index,
493                value: first_words,
494                enable: true,
495            };
496            state_odd_write = SyncBramWrite::<HashWords, STATE_BANK_ADDRESS_BITS> {
497                addr: beat_index,
498                value: second_words,
499                enable: true,
500            };
501        } else if beat_index == b6(33) {
502            state_even_write = SyncBramWrite::<HashWords, STATE_BANK_ADDRESS_BITS> {
503                addr: b6(33),
504                value: first_words,
505                enable: true,
506            };
507            next_public_seed = halves.second;
508        } else {
509            next_expected_hash = halves.first;
510            let digits = message_digits_kernel(halves.second);
511            control.loading = false;
512            if control.frame_fault {
513                control.result_valid = true;
514                control.result_verified = false;
515                control.result_error = b3(VERIFY_ERROR_FRAME);
516                done = true;
517            } else {
518                control.active = true;
519                control.mask_ready = b16(0xffff);
520                control.public_key_state_ready = true;
521                control.expected_tasks = verification_sha_task_count_kernel(digits);
522                for segment in 0..ENGINE_SEGMENTS {
523                    let segment_index = b7(segment as u128);
524                    let digit = digits[segment];
525                    if digit == b4(15) {
526                        control.endpoint_done =
527                            set_segment_bit_kernel(control.endpoint_done, segment_index, true);
528                        control.segment_stage[segment] = b5(18);
529                    } else {
530                        let digit_wide: b5 = digit.resize();
531                        control.segment_stage[segment] = digit_wide + b5(3);
532                    }
533                }
534            }
535        }
536        if beat_index < b6(34) {
537            control.load_beat = beat_index + b6(1);
538        }
539    }
540
541    let response_fields = decode_tag_kernel(input.response.tag);
542    let response_common = !resetting
543        && !transport_abort_live
544        && q.control.active
545        && input.response.valid
546        && verification_tag_is_well_formed_kernel(input.response.tag)
547        && response_fields.context == q.control.context
548        && response_fields.generation == q.control.generation;
549    let bounded_segment = if response_fields.segment < b7(67) {
550        response_fields.segment
551    } else {
552        b7(0)
553    };
554    let bounded_mask = if response_fields.segment < b7(16) {
555        response_fields.segment
556    } else {
557        b7(0)
558    };
559    let mask_bit = b16(1) << bounded_mask;
560    let mask_response = response_common
561        && response_fields.kind == b3(2)
562        && q.control.mask_inflight & mask_bit != b16(0);
563    let expected_chain = verification_chain_fields_kernel(
564        q.control.segment_stage[bounded_segment],
565        bounded_segment,
566        q.control.context,
567        q.control.generation,
568    );
569    let chain_response = response_common
570        && response_fields.kind == b3(6)
571        && segment_bit_kernel(q.control.segment_inflight, bounded_segment)
572        && input.response.tag == encode_tag_kernel(expected_chain);
573    let public_key_response = response_common
574        && response_fields.kind == b3(7)
575        && q.control.public_key_inflight
576        && response_fields.block_index == q.control.public_key_block;
577    let response_due_now = q.control.response_due & b64(1) != b64(0);
578    let operation_live = q.control.loading
579        || q.control.active
580        || frame_start
581        || (q.control.result_valid && !q.control.fault);
582    let response_payload_accepted = mask_response || chain_response || public_key_response;
583    let response_accepted = response_due_now && response_payload_accepted;
584    let response_timing_matches = (q.control.active && response_due_now) == input.response.valid;
585    let local_terminal_transport = !resetting
586        && !transport_abort_live
587        && operation_live
588        && (!response_timing_matches || (input.response.valid && !response_accepted));
589    let terminal_transport = transport_abort_live || local_terminal_transport;
590    let response_rejected = local_terminal_transport && input.response.valid;
591    let mut response_cycle_excluded = [b8(0); SEGMENT_GROUPS];
592    let mut public_key_response_guard = false;
593
594    if q.control.active {
595        control.response_due = q.control.response_due >> 1;
596    }
597
598    let request_accepted = !terminal_transport
599        && !resetting
600        && q.control.active
601        && q.control.request_valid
602        && input.request_ready;
603    if request_accepted {
604        let issued_fields = decode_tag_kernel(q.request.tag);
605        control.request_valid = false;
606        control.total_issued = q.control.total_issued + b11(1);
607        control.response_due = control.response_due | b64(VERIFY_DUE_INSERT_BITS);
608        if issued_fields.kind == b3(2) {
609            let bit = b16(1) << issued_fields.segment;
610            control.mask_ready = q.control.mask_ready & !bit;
611            control.mask_inflight = q.control.mask_inflight | bit;
612        } else if issued_fields.kind == b3(6) {
613            control.segment_ready =
614                set_segment_bit_kernel(q.control.segment_ready, issued_fields.segment, false);
615            control.segment_inflight =
616                set_segment_bit_kernel(q.control.segment_inflight, issued_fields.segment, true);
617        } else if issued_fields.kind == b3(7) {
618            control.public_key_inflight = true;
619            control.public_key_state_ready = false;
620        } else {
621            control.fault = true;
622        }
623    }
624    if response_accepted {
625        control.total_retired = q.control.total_retired + b11(1);
626    }
627
628    if mask_response {
629        control.mask_inflight = control.mask_inflight & !mask_bit;
630        control.mask_done = control.mask_done | mask_bit;
631        mask_write = SyncBramWrite::<HashWords, MASK_ADDRESS_BITS> {
632            addr: response_fields.segment.resize(),
633            value: input.response.digest,
634            enable: true,
635        };
636        let ready_before = control.segment_ready;
637        control.segment_ready = wake_segments_for_mask_kernel(
638            control.segment_stage,
639            control.segment_ready,
640            control.segment_inflight,
641            response_fields.segment.resize(),
642        );
643        response_cycle_excluded = union_segment_groups_kernel(
644            response_cycle_excluded,
645            exclude_segment_groups_kernel(control.segment_ready, ready_before),
646        );
647    }
648
649    if chain_response {
650        let segment = response_fields.segment;
651        let pair_addr: b6 = (segment >> 1).resize();
652        control.segment_inflight = set_segment_bit_kernel(control.segment_inflight, segment, false);
653        response_cycle_excluded = union_segment_groups_kernel(
654            response_cycle_excluded,
655            state_pair_exclusion_kernel(segment),
656        );
657        if segment & b7(1) == b7(0) {
658            state_even_write = SyncBramWrite::<HashWords, STATE_BANK_ADDRESS_BITS> {
659                addr: pair_addr,
660                value: input.response.digest,
661                enable: true,
662            };
663        } else {
664            state_odd_write = SyncBramWrite::<HashWords, STATE_BANK_ADDRESS_BITS> {
665                addr: pair_addr,
666                value: input.response.digest,
667                enable: true,
668            };
669        }
670
671        let current_stage = q.control.segment_stage[segment];
672        if current_stage < b5(17) {
673            let next_stage = current_stage + b5(1);
674            control.segment_stage[segment] = next_stage;
675            let required = segment_required_mask_kernel(next_stage);
676            if control.mask_done & (b16(1) << required) != b16(0) {
677                control.segment_ready =
678                    set_segment_bit_kernel(control.segment_ready, segment, true);
679                response_cycle_excluded =
680                    set_segment_bit_kernel(response_cycle_excluded, segment, true);
681            }
682        } else {
683            control.segment_stage[segment] = b5(18);
684            control.endpoint_done = set_segment_bit_kernel(control.endpoint_done, segment, true);
685            public_key_response_guard = true;
686        }
687    }
688
689    if public_key_response {
690        next_public_key_state = input.response.digest;
691        control.public_key_inflight = false;
692        control.public_key_state_ready = true;
693        if q.control.public_key_block == b6(33) {
694            let endpoints_complete = q.control.endpoint_done[0] == b8(0xff)
695                && q.control.endpoint_done[1] == b8(0xff)
696                && q.control.endpoint_done[2] == b8(0xff)
697                && q.control.endpoint_done[3] == b8(0xff)
698                && q.control.endpoint_done[4] == b8(0xff)
699                && q.control.endpoint_done[5] == b8(0xff)
700                && q.control.endpoint_done[6] == b8(0xff)
701                && q.control.endpoint_done[7] == b8(0xff)
702                && q.control.endpoint_done[8] == b8(0x07);
703            let exact_final = q.control.total_issued == q.control.expected_tasks
704                && q.control.total_retired + b11(1) == q.control.expected_tasks
705                && q.control.mask_done == b16(0xffff)
706                && endpoints_complete;
707            control.active = false;
708            control.result_valid = true;
709            control.prefetch_pending = false;
710            control.request_valid = false;
711            control.result_verified = false;
712            if !exact_final {
713                control.result_error = b3(VERIFY_ERROR_AUDIT);
714                control.fault = true;
715            } else if control.fault {
716                control.result_error = b3(VERIFY_ERROR_TRANSPORT);
717            } else {
718                control.result_error = b3(VERIFY_ERROR_NONE);
719                control.result_verified = verify_hash_equal_kernel(
720                    hash_words_to_bytes_kernel(input.response.digest),
721                    q.expected_hash,
722                );
723            }
724            done = true;
725        } else {
726            control.public_key_block = q.control.public_key_block + b6(1);
727        }
728    }
729
730    if !resetting && control.active && q.control.prefetch_pending && !q.control.request_valid {
731        next_request = prepare_verify_request_kernel(VerifyPrepareRequestInput {
732            fields: decode_tag_kernel(q.control.prefetch_tag),
733            public_seed: q.public_seed,
734            public_key_state: q.public_key_state,
735            state_even: q.state_even,
736            state_odd: q.state_odd,
737            mask: q.masks,
738        });
739        control.prefetch_pending = false;
740        control.request_valid = true;
741    }
742
743    let request_slot_free = !control.request_valid && !control.prefetch_pending;
744    let visible_ready =
745        exclude_segment_groups_kernel(control.segment_ready, response_cycle_excluded);
746    let segment_selection =
747        select_ready_segment_kernel(visible_ready, control.segment_group_cursor);
748    let focus_selection = select_public_key_focus_segment_kernel(
749        visible_ready,
750        control.public_key_block,
751        control.public_key_inflight,
752    );
753    let public_key_ready = !public_key_response_guard
754        && !control.public_key_inflight
755        && control.public_key_state_ready
756        && public_key_endpoints_ready_kernel(control.endpoint_done, control.public_key_block);
757    let mut selected_valid = false;
758    let mut selected_fields = TagFields {
759        context: control.context,
760        generation: control.generation,
761        ..TagFields::default()
762    };
763
764    if control.active && request_slot_free {
765        if public_key_ready {
766            selected_valid = true;
767            selected_fields.kind = b3(7);
768            selected_fields.block_index = control.public_key_block;
769        } else if control.mask_ready != b16(0) {
770            selected_valid = true;
771            selected_fields.kind = b3(2);
772            if control.mask_ready & b16(0x0001) != b16(0) {
773                selected_fields.segment = b7(0);
774            } else if control.mask_ready & b16(0x0002) != b16(0) {
775                selected_fields.segment = b7(1);
776            } else if control.mask_ready & b16(0x0004) != b16(0) {
777                selected_fields.segment = b7(2);
778            } else if control.mask_ready & b16(0x0008) != b16(0) {
779                selected_fields.segment = b7(3);
780            } else if control.mask_ready & b16(0x0010) != b16(0) {
781                selected_fields.segment = b7(4);
782            } else if control.mask_ready & b16(0x0020) != b16(0) {
783                selected_fields.segment = b7(5);
784            } else if control.mask_ready & b16(0x0040) != b16(0) {
785                selected_fields.segment = b7(6);
786            } else if control.mask_ready & b16(0x0080) != b16(0) {
787                selected_fields.segment = b7(7);
788            } else if control.mask_ready & b16(0x0100) != b16(0) {
789                selected_fields.segment = b7(8);
790            } else if control.mask_ready & b16(0x0200) != b16(0) {
791                selected_fields.segment = b7(9);
792            } else if control.mask_ready & b16(0x0400) != b16(0) {
793                selected_fields.segment = b7(10);
794            } else if control.mask_ready & b16(0x0800) != b16(0) {
795                selected_fields.segment = b7(11);
796            } else if control.mask_ready & b16(0x1000) != b16(0) {
797                selected_fields.segment = b7(12);
798            } else if control.mask_ready & b16(0x2000) != b16(0) {
799                selected_fields.segment = b7(13);
800            } else if control.mask_ready & b16(0x4000) != b16(0) {
801                selected_fields.segment = b7(14);
802            } else {
803                selected_fields.segment = b7(15);
804            }
805        } else if focus_selection.valid {
806            selected_valid = true;
807            selected_fields = verification_chain_fields_kernel(
808                control.segment_stage[focus_selection.segment],
809                focus_selection.segment,
810                control.context,
811                control.generation,
812            );
813        } else if segment_selection.valid {
814            selected_valid = true;
815            selected_fields = verification_chain_fields_kernel(
816                control.segment_stage[segment_selection.segment],
817                segment_selection.segment,
818                control.context,
819                control.generation,
820            );
821            control.segment_group_cursor = segment_selection.next_group;
822        }
823    }
824
825    if selected_valid {
826        control.prefetch_pending = true;
827        control.prefetch_tag = encode_tag_kernel(selected_fields);
828        if selected_fields.kind == b3(6) {
829            state_read_addr = (selected_fields.segment >> 1).resize();
830            mask_read_addr = selected_fields.chain_step;
831        } else if selected_fields.kind == b3(7) {
832            state_read_addr = selected_fields.block_index;
833        }
834    }
835
836    if terminal_transport {
837        control.loading = false;
838        control.active = false;
839        control.result_valid = true;
840        control.fault = true;
841        control.result_verified = false;
842        control.result_error = b3(VERIFY_ERROR_TRANSPORT);
843        control.prefetch_pending = false;
844        control.request_valid = false;
845        control.response_due = b64(0);
846        next_request.valid = false;
847        state_even_write.enable = false;
848        state_odd_write.enable = false;
849        mask_write.enable = false;
850        done = true;
851    }
852
853    if resetting {
854        state_even_write.enable = false;
855        state_odd_write.enable = false;
856        mask_write.enable = false;
857    }
858
859    let mut transport_request = q.request;
860    transport_request.valid = !resetting && q.control.request_valid;
861    let mut request = transport_request;
862    request.valid = transport_request.valid && !terminal_transport;
863    let result_transfer = result_transfer_requested && !terminal_transport;
864    let output = VerifyContextOutput {
865        stream_ready,
866        loading: !resetting && q.control.loading,
867        active: !resetting && q.control.active,
868        request,
869        transport_request,
870        response_ready: response_accepted,
871        response_rejected,
872        fault: !resetting && (q.control.fault || terminal_transport),
873        fault_latched: !resetting && q.control.fault,
874        done,
875        result_valid: !resetting && q.control.result_valid,
876        result_transfer,
877        result_job_id: q.job_id,
878        verified: !terminal_transport
879            && !resetting
880            && q.control.result_valid
881            && q.control.result_error == b3(VERIFY_ERROR_NONE)
882            && q.control.result_verified,
883        error: if terminal_transport {
884            b3(VERIFY_ERROR_TRANSPORT)
885        } else {
886            q.control.result_error
887        },
888        expected_tasks: q.control.expected_tasks,
889        total_issued: q.control.total_issued,
890        total_retired: q.control.total_retired,
891    };
892
893    let d = D {
894        control,
895        request: next_request,
896        public_seed: next_public_seed,
897        expected_hash: next_expected_hash,
898        public_key_state: next_public_key_state,
899        job_id: next_job_id,
900        state_even: SyncBramIn::<HashWords, STATE_BANK_ADDRESS_BITS> {
901            read_addr: state_read_addr,
902            write: state_even_write,
903        },
904        state_odd: SyncBramIn::<HashWords, STATE_BANK_ADDRESS_BITS> {
905            read_addr: state_read_addr,
906            write: state_odd_write,
907        },
908        masks: SyncBramIn::<HashWords, MASK_ADDRESS_BITS> {
909            read_addr: mask_read_addr,
910            write: mask_write,
911        },
912    };
913    (output, d)
914}