Skip to main content

rhdl_ubft/
view_change.rs

1//! The view change (W4 steps 3–4; docs/rhdl_ubft-view-change.md): uBFT
2//! Algorithm 5 with every NEWVIEW check upstream leaves out.
3//!
4//! **Problem.** ubft_new's views are local only and never synchronize
5//! (UN-F3); its recovery takes the highest self-reported proposal (N14). The
6//! original accepts a NEWVIEW from anyone (U02), at any time and more than
7//! once (U03, U06), with one replica's history counted repeatedly (U04),
8//! from any sealed view (U05), and never starts a view change by itself
9//! (U13).
10//!
11//! **Idea.** A step kernel beside the consensus kernel ([`crate::consensus`]):
12//! 1. **Records.** For every replica `r` it keeps, per window slot, the
13//!    latest COMMIT `r` CTB-broadcast (view, instance, digest, value), with
14//!    a flag set once that COMMIT's certificate is held here.
15//! 2. **Seal.** On the consensus kernel's `stalled`, or SEALVIEW from
16//!    `f + 1` others for this view, it asks the consensus kernel to seal
17//!    (the bridge certifies our fast commits) and then CTB-broadcasts
18//!    `SEALVIEW(v)`, the view named explicitly.
19//! 3. **History shares.** When `r`'s SEALVIEW(v) is delivered and every one
20//!    of `r`'s records is certified, it digests `r`'s records (a hash chain,
21//!    one SHA-512 job per record) and signs `HISTORY(v, r, digest)`
22//!    (role 4). A replica whose records never all certify is never vouched
23//!    for (upstream busy-waits, then crashes on an invalid one).
24//! 4. **NEWVIEW.** `leader(v + 1)` broadcasts `NEWVIEW(v + 1, subjects)` once
25//!    `q` subjects' histories are certified here. A receiver takes it only
26//!    from `leader(v + 1)` (U02), only for `v + 1` and only once (U03, U06,
27//!    UE-C6), with exactly `q` distinct in-range subjects (U04, UE-C8), and
28//!    only when each subject's certificate is for sealed view `v` and **its
29//!    own** digest of that subject's records (U05, U01). Until then it waits.
30//! 5. **Safe values.** Per slot, the record with the highest view among the
31//!    subjects; two different digests at the highest view refuse the
32//!    NEWVIEW. The consensus kernel installs them.
33use rhdl::prelude::*;
34
35use crate::cert::{ROLE_HISTORY, share_context};
36use crate::cert::collector::popcount16;
37use crate::consensus::{Constraint, ConsMsg, Install, SlotView, empty_constraint, leader_of};
38use crate::ctb::bswap64;
39
40/// CTB message kinds of the view change (after the consensus kinds).
41pub mod kind {
42    pub const COMMIT: u128 = 2;
43    pub const SEALVIEW: u128 = 5;
44    pub const NEWVIEW: u128 = 6;
45}
46
47/// `"RUB2-HIS"` as a little-endian word.
48pub const HIS_DOMAIN: u128 = 0x5349_482D_3242_5552;
49
50/// Configuration.
51#[derive(Clone, Copy, Debug, PartialEq, Digital, Default)]
52pub struct VcConfig {
53    pub init: bool,
54    pub n: b5,
55    pub me: b5,
56    pub session: b64,
57    /// Cycles to wait, after our SEALVIEW, for the next leader's NEWVIEW
58    /// before entering the next view without one (and sealing it in turn).
59    pub nv_deadline: b32,
60}
61
62/// One record of replica `r`'s commits.
63#[derive(Clone, Copy, Debug, PartialEq, Digital)]
64pub struct Record<const P: usize> {
65    pub valid: bool,
66    pub certified: bool,
67    pub view: b32,
68    pub instance: b32,
69    pub digest: [b64; 4],
70    pub len: b5,
71    pub value: [b32; P],
72}
73
74/// A digest job of the history chain, for `(subject, j)`.
75#[derive(Clone, Copy, Debug, PartialEq, Digital, Default)]
76pub struct VcDigestReq {
77    pub valid: bool,
78    pub epoch: b32,
79    pub subject: b5,
80    pub j: b4,
81    /// The 128-byte block (big-endian SHA-512 words); the job adds the
82    /// constant padding block.
83    pub block: [b64; 16],
84}
85
86/// Its answer (the first 32 bytes, little-endian words).
87#[derive(Clone, Copy, Debug, PartialEq, Digital, Default)]
88pub struct VcDigest {
89    pub valid: bool,
90    pub epoch: b32,
91    pub subject: b5,
92    pub j: b4,
93    pub digest: [b64; 4],
94}
95
96/// Sign `HISTORY(v, subject, digest)`.
97#[derive(Clone, Copy, Debug, PartialEq, Digital, Default)]
98pub struct VcSignReq {
99    pub valid: bool,
100    pub epoch: b32,
101    pub subject: b5,
102    pub context: [b64; 6],
103}
104
105/// Our signature for `subject`'s history.
106#[derive(Clone, Copy, Debug, PartialEq, Digital, Default)]
107pub struct VcSigned {
108    pub valid: bool,
109    pub epoch: b32,
110    pub subject: b5,
111    pub sig: [b64; 8],
112}
113
114/// A history-collector key (`instance` is the subject), or our share.
115#[derive(Clone, Copy, Debug, PartialEq, Digital, Default)]
116pub struct VcKey {
117    pub valid: bool,
118    pub view: b32,
119    pub subject: b5,
120    pub digest: [b64; 4],
121    pub sig: [b64; 8],
122}
123
124/// Inputs.
125#[derive(Clone, Copy, Debug, PartialEq, Digital)]
126pub struct VcIn<const G: usize, const P: usize> {
127    pub cfg: VcConfig,
128    /// A CTB delivery, and whether it is taken this cycle (by both this
129    /// kernel and the consensus kernel).
130    pub ctb: ConsMsg<P>,
131    pub ctb_taken: bool,
132    /// Broadcasters whose CTB stream delivered a gap beat this cycle (R-25):
133    /// their records here may lack a message others have, so their history
134    /// is never vouched for again (until a checkpoint covers it).
135    pub gap_from: b16,
136    /// From the consensus kernel.
137    pub view: b32,
138    /// Its next undecided instance: everything below is decided here.
139    pub low: b32,
140    pub stalled: bool,
141    pub bridge_done: bool,
142    pub slots: [SlotView<P>; G],
143    /// The prepare collector's per-slot certificate status (current keys).
144    pub certified: [bool; G],
145    /// The history collector's per-subject status (`instance = subject`).
146    pub hist_certified: [bool; 16],
147    pub digest: VcDigest,
148    pub signed: VcSigned,
149    pub ctb_ready: bool,
150    pub digest_ready: bool,
151    pub sign_ready: bool,
152    pub key_ready: bool,
153    pub share_ready: bool,
154}
155
156/// Outputs (requests are holding registers).
157#[derive(Clone, Copy, Debug, PartialEq, Digital)]
158pub struct VcOut<const G: usize, const P: usize> {
159    /// This kernel can take the CTB delivery now (a NEWVIEW waits).
160    pub ctb_ready: bool,
161    /// Ask the consensus kernel to seal (the bridge).
162    pub sealing: bool,
163    pub ctb_send: ConsMsg<P>,
164    pub digest_req: VcDigestReq,
165    pub sign_req: VcSignReq,
166    /// Open a history-collector key, or give it our own share.
167    pub open: VcKey,
168    pub own: VcKey,
169    /// Send our history share to every replica.
170    pub share_send: VcKey,
171    pub install: Install<G, P>,
172    pub refused: b32,
173}
174
175/// State.
176#[derive(Clone, Copy, Debug, PartialEq, Digital)]
177pub struct VcState<const N: usize, const G: usize, const P: usize> {
178    pub configured: bool,
179    pub epoch: b32,
180    pub n: b5,
181    pub me: b5,
182    pub q: b5,
183    pub session: b64,
184    pub view: b32,
185    pub rec: [[Record<P>; G]; N],
186    /// SEALVIEW(view) seen from these replicas (a seal of a later view
187    /// counts: that replica has left this view, R-28).
188    pub sealed_from: b16,
189    /// Per replica, one plus the highest view it sealed (0: none). Kept
190    /// across views so a replica that ran ahead still counts (R-28).
191    pub seal_hi: [b32; N],
192    /// R-28: the view we left, for subjects that seal it after we did. Each
193    /// such subject's view-`p_view` history is complete exactly at its own
194    /// SEALVIEW(p_view) (CTB is FIFO): its records are copied that cycle
195    /// into `p_rec` and chained and shared from the copy only.
196    pub p_view: b32,
197    pub p_open: b16,
198    pub p_busy: bool,
199    pub p_subject: b5,
200    pub p_rec: [Record<P>; G],
201    pub p_j: b4,
202    pub p_sent: bool,
203    pub p_acc: [b64; 4],
204    pub p_have: bool,
205    pub p_sign_out: bool,
206    pub p_signed: bool,
207    pub p_sig: [b64; 8],
208    /// Our own key and share for the late subject go into our own history
209    /// collector too (so a late NEWVIEW naming it can be judged here).
210    pub p_key_out: bool,
211    pub p_keyed: bool,
212    pub p_own_given: bool,
213    pub p_shared: bool,
214    /// R-28 late install: we issued a skip; we entered this view by skip;
215    /// a late NEWVIEW for it was taken; the previous view's opened keys.
216    pub skip_pending: bool,
217    pub in_skipped: bool,
218    pub late_done: bool,
219    pub prev_open_done: b16,
220    /// R-31: subjects whose records changed since their history digest was
221    /// taken (set on every record write; cleared when a chain or a late
222    /// copy starts from the current records).
223    pub dirty: b16,
224    /// Subjects with a gap in their CTB stream here: never vouched for.
225    pub tainted: b16,
226    pub sealing: bool,
227    pub seal_sent: bool,
228    /// History digests: chain progress per subject, and done flags.
229    pub chain_busy: bool,
230    pub chain_subject: b5,
231    pub chain_j: b4,
232    pub chain_sent: bool,
233    pub chain_acc: [b64; 4],
234    pub hdone: b16,
235    pub hdigest: [[b64; 4]; N],
236    pub opened: b16,
237    /// The history collector took our key for this view (only then does its
238    /// status speak for this view, not a previous one).
239    pub open_done: b16,
240    pub sign_sent: b16,
241    pub signed: b16,
242    pub hsig: [[b64; 8]; N],
243    pub own_given: b16,
244    pub shared: b16,
245    pub nv_sent: bool,
246    pub clock: b32,
247    pub seal_at: b32,
248    pub nv_deadline: b32,
249    /// The next view's NEWVIEW was accepted.
250    pub installed_next: bool,
251    pub h_ctb: ConsMsg<P>,
252    pub h_dig: VcDigestReq,
253    pub h_sign: VcSignReq,
254    pub h_open: VcKey,
255    pub h_own: VcKey,
256    pub h_share: VcKey,
257    pub h_inst: Install<G, P>,
258    pub refused: b32,
259}
260
261/// An empty record, for kernels.
262#[kernel]
263pub fn empty_record<const P: usize>() -> Record<P> {
264    Record::<P> { valid: false, certified: false, view: b32(0), instance: b32(0), digest: [b64(0); 4], len: b5(0), value: [b32(0); P] }
265}
266
267/// An idle CTB message, for kernels.
268#[kernel]
269pub fn vc_idle_msg<const P: usize>() -> ConsMsg<P> {
270    ConsMsg::<P> { valid: false, from: b5(0), kind: b4(0), view: b32(0), instance: b32(0), digest: [b64(0); 4], len: b5(0), value: [b32(0); P] }
271}
272
273/// An idle install, for kernels.
274#[kernel]
275pub fn idle_install<const G: usize, const P: usize>() -> Install<G, P> {
276    Install::<G, P> { valid: false, skip: false, late: false, view: b32(0), c: [empty_constraint::<P>(); G] }
277}
278
279/// Which link of which history: the sealed view, the subject, the record.
280#[derive(Clone, Copy, Debug, PartialEq, Digital, Default)]
281pub struct Link {
282    pub view: b32,
283    pub subject: b5,
284    pub j: b4,
285}
286
287/// One link of a history chain: 128 bytes, `"RUB2-HIS" ‖ session ‖ view ‖
288/// subject ‖ j ‖ flags ‖ h_j ‖ record view ‖ record instance ‖ record
289/// digest ‖ len ‖ value (P ≤ 4 words) ‖ 0`, as big-endian SHA-512 words.
290/// `flags` is 1 for a valid record, 0 for an empty slot.
291#[kernel]
292pub fn history_block<const P: usize>(session: b64, link: Link, acc: [b64; 4], rec: Record<P>) -> [b64; 16] {
293    let mut le = [b64(0); 16];
294    le[0] = b64(HIS_DOMAIN);
295    le[1] = session;
296    le[2] = link.view.resize::<64>() | (link.subject.resize::<64>() << b64(32));
297    let flags: b64 = if rec.valid { b64(1) } else { b64(0) };
298    le[3] = link.j.resize::<64>() | (flags << b64(32));
299    for k in 0..4 {
300        le[4 + k] = acc[k];
301    }
302    if rec.valid {
303        le[8] = rec.view.resize::<64>() | (rec.instance.resize::<64>() << b64(32));
304        for k in 0..4 {
305            le[9 + k] = rec.digest[k];
306        }
307        le[13] = rec.len.resize::<64>();
308        for k in 0..P {
309            let word = if b5(k as u128) < rec.len { rec.value[k] } else { b32(0) };
310            let w = (k >> 1) + 14;
311            let sh = (k & 1) * 32;
312            le[w] = le[w] | (word.resize::<64>() << b64(sh as u128));
313        }
314    }
315    let mut be = [b64(0); 16];
316    for k in 0..16 {
317        be[k] = bswap64(le[k]);
318    }
319    be
320}
321
322impl<const N: usize, const G: usize, const P: usize> VcState<N, G, P> {
323    pub fn idle() -> Self {
324        Self {
325            configured: false,
326            epoch: b32(0),
327            n: b5(0),
328            me: b5(0),
329            q: b5(0),
330            session: b64(0),
331            view: b32(0),
332            rec: [[empty_record::<P>(); G]; N],
333            sealed_from: b16(0),
334            seal_hi: [b32(0); N],
335            p_view: b32(0),
336            p_open: b16(0),
337            p_busy: false,
338            p_subject: b5(0),
339            p_rec: [empty_record::<P>(); G],
340            p_j: b4(0),
341            p_sent: false,
342            p_acc: [b64(0); 4],
343            p_have: false,
344            p_sign_out: false,
345            p_signed: false,
346            p_sig: [b64(0); 8],
347            p_key_out: false,
348            p_keyed: false,
349            p_own_given: false,
350            p_shared: false,
351            skip_pending: false,
352            in_skipped: false,
353            late_done: false,
354            prev_open_done: b16(0),
355            dirty: b16(0),
356            tainted: b16(0),
357            sealing: false,
358            seal_sent: false,
359            chain_busy: false,
360            chain_subject: b5(0),
361            chain_j: b4(0),
362            chain_sent: false,
363            chain_acc: [b64(0); 4],
364            hdone: b16(0),
365            hdigest: [[b64(0); 4]; N],
366            opened: b16(0),
367            open_done: b16(0),
368            sign_sent: b16(0),
369            signed: b16(0),
370            hsig: [[b64(0); 8]; N],
371            own_given: b16(0),
372            shared: b16(0),
373            nv_sent: false,
374            clock: b32(0),
375            seal_at: b32(0),
376            nv_deadline: b32(0),
377            installed_next: false,
378            h_ctb: vc_idle_msg::<P>(),
379            h_dig: VcDigestReq::default(),
380            h_sign: VcSignReq::default(),
381            h_open: VcKey::default(),
382            h_own: VcKey::default(),
383            h_share: VcKey::default(),
384            h_inst: idle_install::<G, P>(),
385            refused: b32(0),
386        }
387    }
388}
389
390impl<const N: usize, const G: usize, const P: usize> Default for VcState<N, G, P> {
391    fn default() -> Self {
392        Self::idle()
393    }
394}
395
396impl<const G: usize, const P: usize> Default for VcIn<G, P> {
397    fn default() -> Self {
398        Self {
399            cfg: VcConfig::default(),
400            ctb: vc_idle_msg::<P>(),
401            ctb_taken: false,
402            gap_from: b16(0),
403            view: b32(0),
404            low: b32(0),
405            stalled: false,
406            bridge_done: false,
407            slots: [SlotView::<P> { active: false, instance: b32(0), dig_have: false, digest: [b64(0); 4], len: b5(0), value: [b32(0); P], kview: b32(0) }; G],
408            certified: [false; G],
409            hist_certified: [false; 16],
410            digest: VcDigest::default(),
411            signed: VcSigned::default(),
412            ctb_ready: false,
413            digest_ready: false,
414            sign_ready: false,
415            key_ready: false,
416            share_ready: false,
417        }
418    }
419}
420
421/// One cycle of the view change.
422#[kernel]
423pub fn vc_step<const N: usize, const G: usize, const P: usize>(i: VcIn<G, P>, r: VcState<N, G, P>) -> (VcOut<G, P>, VcState<N, G, P>) {
424    let mut s = r;
425    let gmask = b32(G as u128) - b32(1);
426    if i.cfg.init {
427        s = VcState::<N, G, P> {
428            configured: i.cfg.n >= b5(3) && i.cfg.n <= b5(N as u128) && i.cfg.me < i.cfg.n && i.cfg.session != b64(0),
429            epoch: r.epoch + b32(1),
430            n: i.cfg.n,
431            me: i.cfg.me,
432            q: (i.cfg.n >> b5(1)) + b5(1),
433            session: i.cfg.session,
434            view: b32(0),
435            rec: [[empty_record::<P>(); G]; N],
436            sealed_from: b16(0),
437            seal_hi: [b32(0); N],
438            p_view: b32(0),
439            p_open: b16(0),
440            p_busy: false,
441            p_subject: b5(0),
442            p_rec: [empty_record::<P>(); G],
443            p_j: b4(0),
444            p_sent: false,
445            p_acc: [b64(0); 4],
446            p_have: false,
447            p_sign_out: false,
448            p_signed: false,
449            p_sig: [b64(0); 8],
450            p_key_out: false,
451            p_keyed: false,
452            p_own_given: false,
453            p_shared: false,
454            skip_pending: false,
455            in_skipped: false,
456            late_done: false,
457            prev_open_done: b16(0),
458            dirty: b16(0),
459            tainted: b16(0),
460            sealing: false,
461            seal_sent: false,
462            chain_busy: false,
463            chain_subject: b5(0),
464            chain_j: b4(0),
465            chain_sent: false,
466            chain_acc: [b64(0); 4],
467            hdone: b16(0),
468            hdigest: [[b64(0); 4]; N],
469            opened: b16(0),
470            open_done: b16(0),
471            sign_sent: b16(0),
472            signed: b16(0),
473            hsig: [[b64(0); 8]; N],
474            own_given: b16(0),
475            shared: b16(0),
476            nv_sent: false,
477            clock: b32(0),
478            seal_at: b32(0),
479            nv_deadline: b32(0),
480            installed_next: false,
481            h_ctb: vc_idle_msg::<P>(),
482            h_dig: VcDigestReq::default(),
483            h_sign: VcSignReq::default(),
484            h_open: VcKey::default(),
485            h_own: VcKey::default(),
486            h_share: VcKey::default(),
487            h_inst: idle_install::<G, P>(),
488            refused: b32(0),
489        };
490    }
491    if i.cfg.init {
492        s.nv_deadline = i.cfg.nv_deadline;
493        s.configured = s.configured && i.cfg.nv_deadline != b32(0);
494    }
495    let run = r.configured && !i.cfg.init;
496    s.clock = r.clock + b32(1);
497    let next = r.view + b32(1);
498    let next_leader = leader_of(next, r.n);
499
500    // ---------------------------------------------------- NEWVIEW checks
501    // A NEWVIEW is taken only when it can be judged now; otherwise it waits.
502    let m = i.ctb;
503    let is_nv = m.valid && m.kind == b4(kind::NEWVIEW);
504    let subjects: b16 = m.value[0].resize();
505    let mut all_cert = true;
506    for k in 0..16 {
507        if ((subjects >> b16(k as u128)) & b16(1)) != b16(0) {
508            // Certified here, for sealed view `view`, over our own digest.
509            if !(i.hist_certified[k] && ((r.open_done >> b16(k as u128)) & b16(1)) != b16(0)) {
510                all_cert = false;
511            }
512        }
513    }
514    // A NEWVIEW one view further ahead than we can install now waits
515    // (R-23); further still, or one we already took, is dropped.
516    let nv_ahead = is_nv && m.view == next + b32(1);
517    // R-28: a NEWVIEW for the view we entered by skip, not yet sealed, whose
518    // named subjects' collector slots hold no key of this view yet (slots are
519    // per subject, so they still hold the previous view's keys its
520    // certificates are over; another subject's current key does not matter).
521    let nv_late = is_nv && m.view == r.view && r.in_skipped && !r.late_done && !r.seal_sent && (r.opened & subjects) == b16(0);
522    let mut late_cert = true;
523    for k in 0..16 {
524        if ((subjects >> b16(k as u128)) & b16(1)) != b16(0) {
525            if !(i.hist_certified[k] && ((r.prev_open_done >> b16(k as u128)) & b16(1)) != b16(0)) {
526                late_cert = false;
527            }
528        }
529    }
530    let nv_stale = is_nv && !nv_ahead && !nv_late && (m.view != next || r.installed_next);
531    // The sender must lead the NEWVIEW's own view (for one held a view
532    // ahead, that is leader(next + 1), not leader(next)).
533    let nv_shape = is_nv && m.from == leader_of(m.view, r.n) && popcount16(subjects) == r.q && (subjects & !(r_all(r.n))) == b16(0);
534    // Wait while our own seal is not out, or certificates are missing.
535    let nv_wait = nv_ahead || (is_nv && !nv_late && !nv_stale && nv_shape && (!r.seal_sent || !all_cert)) || (nv_late && nv_shape && !late_cert);
536    // A subject's SEALVIEW of the view we left (R-28): taken only when the
537    // previous-view chain is free, so its records are copied that cycle.
538    let late_seal = m.valid && m.kind == b4(kind::SEALVIEW) && m.from < r.n && m.view == r.p_view && ((r.p_open >> m.from.resize::<16>()) & b16(1)) != b16(0);
539    let late_hold = late_seal && r.p_busy;
540
541    let mut o = VcOut::<G, P> {
542        ctb_ready: run && !nv_wait && !late_hold,
543        sealing: r.sealing,
544        ctb_send: r.h_ctb,
545        digest_req: r.h_dig,
546        sign_req: r.h_sign,
547        open: r.h_open,
548        own: r.h_own,
549        share_send: r.h_share,
550        install: r.h_inst,
551        refused: r.refused,
552    };
553    if i.cfg.init {
554        o.ctb_send.valid = false;
555        o.digest_req.valid = false;
556        o.sign_req.valid = false;
557        o.open.valid = false;
558        o.own.valid = false;
559        o.share_send.valid = false;
560        o.install.valid = false;
561    }
562    if !i.cfg.init {
563        if r.h_ctb.valid && i.ctb_ready {
564            s.h_ctb.valid = false;
565        }
566        if r.h_dig.valid && i.digest_ready {
567            s.h_dig.valid = false;
568        }
569        if r.h_sign.valid && i.sign_ready {
570            s.h_sign.valid = false;
571        }
572        if r.h_open.valid && i.key_ready {
573            s.h_open.valid = false;
574            if r.p_key_out {
575                // The previous view's key for the late subject.
576                s.prev_open_done = r.prev_open_done | (b16(1) << r.h_open.subject.resize::<16>());
577                s.p_key_out = false;
578                s.p_keyed = true;
579            } else {
580                s.open_done = r.open_done | (b16(1) << r.h_open.subject.resize::<16>());
581            }
582        }
583        if r.h_own.valid && i.key_ready && !r.h_open.valid {
584            s.h_own.valid = false;
585        }
586        if r.h_share.valid && i.share_ready {
587            s.h_share.valid = false;
588        }
589        // The install is a one-cycle command.
590        s.h_inst.valid = false;
591    }
592    if r.h_open.valid {
593        o.own.valid = false;
594    }
595
596    // ---------------------------------------------------- CTB deliveries
597    if run && m.valid && i.ctb_taken && m.from < r.n {
598        let fk: b4 = m.from.resize();
599        // One plus the highest view this sender sealed, whatever view we are
600        // in (no commit of a view counts once its sender sealed it).
601        let mut from_hi = b32(0);
602        for x in 0..N {
603            if m.from == b5(x as u128) {
604                from_hi = r.seal_hi[x];
605                if m.kind == b4(kind::SEALVIEW) && m.view + b32(1) > r.seal_hi[x] {
606                    s.seal_hi[x] = m.view + b32(1);
607                }
608            }
609        }
610        // A commit of this or an earlier view whose sender has not sealed
611        // that view belongs to its history (R-28).
612        if m.kind == b4(kind::COMMIT) && m.view <= r.view && from_hi <= m.view {
613            // `r`'s latest commit for this slot (no later commit counts once
614            // `r` has sealed this view).
615            let k: b3 = (m.instance & gmask).resize();
616            let e = r.rec[fk][k];
617            let newer = !e.valid || m.instance > e.instance || (m.instance == e.instance && m.view > e.view);
618            // A later instance in the same slot replaces `r`'s record only
619            // once that record's instance is decided here: otherwise a
620            // faulty `r` could erase its own commit of a value it helped
621            // decide elsewhere, without a gap (R-25 by overwrite).
622            let replaceable = !e.valid || m.instance == e.instance || e.instance < i.low;
623            if newer && replaceable {
624                s.rec[fk][k] = Record::<P> { valid: true, certified: false, view: m.view, instance: m.instance, digest: m.digest, len: m.len, value: m.value };
625                s.dirty = s.dirty | (b16(1) << m.from.resize::<16>());
626            }
627        } else if m.kind == b4(kind::SEALVIEW) && m.view >= r.view {
628            // CTB is FIFO per broadcaster, so all of `r`'s messages of this
629            // view arrived before its seal of this or any later view.
630            s.sealed_from = r.sealed_from | (b16(1) << m.from.resize::<16>());
631        } else if late_seal && !r.p_busy {
632            // Its view-`p_view` history is complete now: copy it this cycle.
633            s.p_open = r.p_open & !(b16(1) << m.from.resize::<16>());
634            s.p_busy = true;
635            s.p_subject = m.from;
636            // The sender's row, by constant indices.
637            for x in 0..N {
638                if m.from == b5(x as u128) {
639                    for k in 0..G {
640                        s.p_rec[k] = r.rec[x][k];
641                    }
642                }
643            }
644            s.p_j = b4(0);
645            s.p_sent = false;
646            s.p_acc = [b64(0); 4];
647            s.p_have = false;
648            s.p_sign_out = false;
649            s.p_signed = false;
650            s.p_key_out = false;
651            s.p_keyed = false;
652            s.p_own_given = false;
653            s.p_shared = false;
654            // The copy equals the current records (a write this cycle, after
655            // the copy, keeps the bit).
656            let cb = b16(1) << m.from.resize::<16>();
657            s.dirty = (s.dirty & !cb) | (s.dirty & !r.dirty & cb);
658        } else if is_nv && !nv_stale && !nv_wait {
659            if !nv_shape {
660                s.refused = r.refused + b32(1);
661            } else {
662                // Safe values: per slot, the highest-view record among the
663                // subjects; two digests at that view refuse the NEWVIEW.
664                let mut inst = idle_install::<G, P>();
665                inst.valid = true;
666                inst.view = if nv_late { r.view } else { next };
667                inst.late = nv_late;
668                let mut conflict = false;
669                for k in 0..G {
670                    let mut best = empty_record::<P>();
671                    for x in 0..N {
672                        let bit = ((subjects >> b16(x as u128)) & b16(1)) != b16(0);
673                        let e = r.rec[x][k];
674                        if bit && e.valid {
675                            if !best.valid || e.instance > best.instance || (e.instance == best.instance && e.view > best.view) {
676                                best = e;
677                            } else if e.instance == best.instance && e.view == best.view && e.digest != best.digest {
678                                conflict = true;
679                            }
680                        }
681                    }
682                    if best.valid {
683                        inst.c[k] = Constraint::<P> { set: true, instance: best.instance, digest: best.digest, len: best.len, value: best.value };
684                    }
685                    // R-31: a late install judges certificates over our
686                    // digests of the named subjects' view-(w-1) histories,
687                    // but after the skip their records stay writable (a
688                    // view-w COMMIT replaces a lower-view one). A record that
689                    // is uncertified or names a view >= w cannot be what the
690                    // certificate covered, and the record it replaced is
691                    // gone: refuse rather than install from it.
692                    for x in 0..N {
693                        let bit = ((subjects >> b16(x as u128)) & b16(1)) != b16(0);
694                        let e = r.rec[x][k];
695                        if nv_late && bit && e.valid && (!e.certified || e.view >= r.view) {
696                            conflict = true;
697                        }
698                    }
699                    // A late install never drops a value we ourselves
700                    // committed (fluxability-5d): our own record must be
701                    // carried, or outranked by a later view or instance.
702                    // "Outranked" is always by a certified record: a
703                    // subject's history is digested only once all its
704                    // records are certified (at their own view), and this
705                    // NEWVIEW is judged only once every named history's
706                    // certificate verified against our own digest.
707                    for x in 0..N {
708                        let own = r.rec[x][k];
709                        if nv_late && r.me == b5(x as u128) && own.valid && own.instance >= i.low {
710                            let kept = best.valid && (best.instance > own.instance || (best.instance == own.instance && (best.digest == own.digest || best.view > own.view)));
711                            if !kept {
712                                conflict = true;
713                            }
714                        }
715                    }
716                }
717                // R-31 (exact binding): a named subject whose records changed
718                // since its digest was taken.
719                if nv_late && (r.dirty & subjects) != b16(0) {
720                    conflict = true;
721                }
722                if conflict {
723                    s.refused = r.refused + b32(1);
724                } else if nv_late {
725                    s.h_inst = inst;
726                    s.late_done = true;
727                } else {
728                    s.h_inst = inst;
729                    s.installed_next = true;
730                    s.skip_pending = false;
731                }
732            }
733        }
734    }
735
736    // ---------------------------------------------------- gaps (R-25)
737    // CTB lets a faulty subject withhold a message from us and send later
738    // ones: we then hold a gap, not its message, and must never vouch for its
739    // history (an empty-looking history could hide a certified COMMIT).
740    if run {
741        s.tainted = r.tainted | i.gap_from;
742    }
743
744    // ---------------------------------------------------- certification of records
745    // A record becomes certified when the prepare collector holds its key.
746    for x in 0..N {
747        for k in 0..G {
748            let e = s.rec[x][k];
749            let sv = i.slots[k];
750            // Against a certificate of the record's own view, never another
751            // (else a value could be re-tagged with a higher view: R-28).
752            if e.valid && !e.certified && sv.kview == e.view && sv.active && sv.dig_have && sv.instance == e.instance && sv.digest == e.digest && i.certified[k] {
753                s.rec[x][k].certified = true;
754            }
755        }
756    }
757    for k in 0..G {
758        let e = s.p_rec[k];
759        let sv = i.slots[k];
760        if e.valid && !e.certified && sv.kview == e.view && sv.active && sv.dig_have && sv.instance == e.instance && sv.digest == e.digest && i.certified[k] {
761            s.p_rec[k].certified = true;
762        }
763    }
764
765    // ---------------------------------------------------- seal
766    let others_sealed = popcount16(r.sealed_from & !(b16(1) << r.me.resize::<16>()));
767    // Seal on our own deadline, on SEALVIEW from f + 1 = q others (one of
768    // them is honest), or on a well-formed NEWVIEW for the next view.
769    // Only a NEWVIEW for exactly the next view prompts sealing.
770    let nv_seen = is_nv && !nv_stale && !nv_ahead && m.view == next && nv_shape;
771    if run && !r.sealing && (i.stalled || others_sealed >= r.q || nv_seen) {
772        s.sealing = true;
773    }
774    if run && r.sealing && i.bridge_done && !r.seal_sent && !r.h_ctb.valid {
775        s.h_ctb = ConsMsg::<P> { valid: true, from: r.me, kind: b4(kind::SEALVIEW), view: r.view, instance: b32(0), digest: [b64(0); 4], len: b5(0), value: [b32(0); P] };
776        s.seal_sent = true;
777        s.seal_at = r.clock;
778    }
779    // The next leader never delivered a NEWVIEW in time (it may be the
780    // faulty one, or quarantined here): enter the next view without one.
781    // Nothing can be prepared there; it times out and is sealed in turn.
782    // Only with f + 1 = q replicas (ourselves included) having sealed this
783    // view, so a replica never runs ahead through views alone (R-24): every
784    // honest replica eventually times out and seals, so this still
785    // progresses.
786    let quorum_sealed = popcount16(r.sealed_from) >= r.q;
787    if run && r.seal_sent && quorum_sealed && !r.installed_next && !r.h_inst.valid && r.clock - r.seal_at >= r.nv_deadline {
788        let mut skip = idle_install::<G, P>();
789        skip.valid = true;
790        skip.skip = true;
791        skip.view = next;
792        s.h_inst = skip;
793        s.installed_next = true;
794        s.skip_pending = true;
795    }
796
797    // ---------------------------------------------------- history digests
798    if run && i.digest.valid && i.digest.epoch == r.epoch && r.chain_busy && r.chain_sent && i.digest.subject == r.chain_subject && i.digest.j == r.chain_j {
799        s.chain_acc = i.digest.digest;
800        s.chain_sent = false;
801        if r.chain_j == b4((G - 1) as u128) {
802            s.chain_busy = false;
803            let sb = r.chain_subject;
804            let sk: b4 = sb.resize();
805            s.hdone = r.hdone | (b16(1) << sb.resize::<16>());
806            s.hdigest[sk] = i.digest.digest;
807        } else {
808            s.chain_j = r.chain_j + b4(1);
809        }
810    }
811    if run && i.digest.valid && i.digest.epoch == r.epoch && r.p_busy && r.p_sent && !r.chain_sent && i.digest.subject == r.p_subject && i.digest.j == r.p_j {
812        s.p_acc = i.digest.digest;
813        s.p_sent = false;
814        if r.p_j == b4((G - 1) as u128) {
815            s.p_have = true;
816        } else {
817            s.p_j = r.p_j + b4(1);
818        }
819    }
820    // A tainted subject is never vouched for, late or not.
821    if run && r.p_busy && ((r.tainted >> r.p_subject.resize::<16>()) & b16(1)) != b16(0) {
822        s.p_busy = false;
823    }
824    // A copied previous-view history goes first (its subject's stream may be
825    // held): the current chain starts only once it is shared, so the hold is
826    // bounded by at most one current chain in flight plus one previous one.
827    if run && !r.chain_busy && !r.p_sent && !(r.p_busy && !r.p_have) {
828        // The next sealed subject whose records are all certified.
829        let mut found = false;
830        for x in 0..N {
831            let bit = b16(1) << b16(x as u128);
832            let mut ready = (r.sealed_from & bit) != b16(0) && (r.hdone & bit) == b16(0) && (r.tainted & bit) == b16(0);
833            for k in 0..G {
834                if r.rec[x][k].valid && !r.rec[x][k].certified {
835                    ready = false;
836                }
837            }
838            if !found && ready {
839                found = true;
840                s.chain_busy = true;
841                s.chain_subject = b5(x as u128);
842                s.chain_j = b4(0);
843                s.chain_sent = false;
844                s.chain_acc = [b64(0); 4];
845                // The chain reads the current records (a write this cycle
846                // keeps the bit).
847                s.dirty = (s.dirty & !bit) | (s.dirty & !r.dirty & bit);
848            }
849        }
850    }
851    if run && r.chain_busy && !r.chain_sent && !r.h_dig.valid {
852        let sk: b4 = r.chain_subject.resize();
853        let jk: b3 = r.chain_j.resize();
854        s.h_dig = VcDigestReq { valid: true, epoch: r.epoch, subject: r.chain_subject, j: r.chain_j, block: history_block::<P>(r.session, Link { view: r.view, subject: r.chain_subject, j: r.chain_j }, r.chain_acc, r.rec[sk][jk]) };
855        s.chain_sent = true;
856    }
857    // The previous-view chain runs only when the current one is idle (one
858    // digest outstanding at a time), over the copy, once it is all certified.
859    let mut p_ready = r.p_busy && !r.p_have && !r.p_sent && !r.chain_busy && !s.chain_busy && !r.h_dig.valid;
860    for k in 0..G {
861        if r.p_rec[k].valid && !r.p_rec[k].certified {
862            p_ready = false;
863        }
864    }
865    if run && p_ready {
866        let jk: b3 = r.p_j.resize();
867        s.h_dig = VcDigestReq { valid: true, epoch: r.epoch, subject: r.p_subject, j: r.p_j, block: history_block::<P>(r.session, Link { view: r.p_view, subject: r.p_subject, j: r.p_j }, r.p_acc, r.p_rec[jk]) };
868        s.p_sent = true;
869    }
870
871    // ---------------------------------------------------- history shares
872    let mut k_done = r.h_open.valid;
873    let mut g_done = r.h_sign.valid || (r.p_sign_out && !r.p_signed);
874    let mut w_done = r.h_own.valid || r.h_open.valid;
875    let mut x_done = r.h_share.valid;
876    for x in 0..N {
877        let bit = b16(1) << b16(x as u128);
878        let xs = b5(x as u128);
879        if run && (r.hdone & bit) != b16(0) {
880            if !k_done && (r.opened & bit) == b16(0) {
881                k_done = true;
882                s.h_open = VcKey { valid: true, view: r.view, subject: xs, digest: r.hdigest[x], sig: [b64(0); 8] };
883                s.opened = s.opened | bit;
884            }
885            if !g_done && (r.sign_sent & bit) == b16(0) {
886                g_done = true;
887                s.h_sign = VcSignReq { valid: true, epoch: r.epoch, subject: xs, context: share_context(b8(ROLE_HISTORY), r.view, xs.resize(), r.hdigest[x]) };
888                s.sign_sent = s.sign_sent | bit;
889            }
890            if !w_done && (r.signed & bit) != b16(0) && (r.open_done & bit) != b16(0) && (r.own_given & bit) == b16(0) {
891                w_done = true;
892                s.h_own = VcKey { valid: true, view: r.view, subject: xs, digest: r.hdigest[x], sig: r.hsig[x] };
893                s.own_given = s.own_given | bit;
894            }
895            if !x_done && (r.signed & bit) != b16(0) && (r.shared & bit) == b16(0) {
896                x_done = true;
897                s.h_share = VcKey { valid: true, view: r.view, subject: xs, digest: r.hdigest[x], sig: r.hsig[x] };
898                s.shared = s.shared | bit;
899            }
900        }
901    }
902    // The previous-view share: signed once nothing of the current view is
903    // out for signing, then sent.
904    let cur_out = (r.sign_sent & !r.signed) != b16(0);
905    if run && r.p_busy && r.p_have && !r.p_sign_out && !g_done && !cur_out {
906        g_done = true;
907        s.h_sign = VcSignReq { valid: true, epoch: r.epoch, subject: r.p_subject, context: share_context(b8(ROLE_HISTORY), r.p_view, r.p_subject.resize(), r.p_acc) };
908        s.p_sign_out = true;
909    }
910    // Our key for (late subject, p_view) into our collector, unless we
911    // already opened that subject's slot for the current view (never replace
912    // a newer key with an older one; the share still goes out).
913    let p_bit = b16(1) << r.p_subject.resize::<16>();
914    let p_slot_free = (r.opened & p_bit) == b16(0);
915    if run && r.p_busy && r.p_have && p_slot_free && !r.p_keyed && !r.p_key_out && !k_done {
916        k_done = true;
917        s.h_open = VcKey { valid: true, view: r.p_view, subject: r.p_subject, digest: r.p_acc, sig: [b64(0); 8] };
918        s.p_key_out = true;
919    }
920    if run && r.p_busy && r.p_signed && r.p_keyed && !r.p_own_given && !w_done {
921        w_done = true;
922        s.h_own = VcKey { valid: true, view: r.p_view, subject: r.p_subject, digest: r.p_acc, sig: r.p_sig };
923        s.p_own_given = true;
924    }
925    if run && r.p_busy && r.p_signed && !r.p_shared && !x_done {
926        x_done = true;
927        s.h_share = VcKey { valid: true, view: r.p_view, subject: r.p_subject, digest: r.p_acc, sig: r.p_sig };
928        s.p_shared = true;
929    }
930    // Done once shared, and our own share given (or our slot was taken).
931    if run && r.p_busy && r.p_shared && (r.p_own_given || !p_slot_free) && !r.p_key_out {
932        s.p_busy = false;
933    }
934    if run && i.signed.valid && i.signed.epoch == r.epoch {
935        if r.p_sign_out && !r.p_signed && i.signed.subject == r.p_subject {
936            s.p_signed = true;
937            s.p_sig = i.signed.sig;
938        } else {
939            let sk: b4 = i.signed.subject.resize();
940            s.signed = s.signed | (b16(1) << i.signed.subject.resize::<16>());
941            s.hsig[sk] = i.signed.sig;
942        }
943    }
944
945    // ---------------------------------------------------- the next leader
946    if run && r.me == next_leader && r.seal_sent && !r.nv_sent && !r.h_ctb.valid {
947        let mut mask = b16(0);
948        let mut count = b5(0);
949        for x in 0..N {
950            let bit = b16(1) << b16(x as u128);
951            if count < r.q && i.hist_certified[x] && (r.open_done & bit) != b16(0) {
952                mask = mask | bit;
953                count = count + b5(1);
954            }
955        }
956        if count == r.q {
957            let mut v = [b32(0); P];
958            v[0] = mask.resize();
959            s.h_ctb = ConsMsg::<P> { valid: true, from: r.me, kind: b4(kind::NEWVIEW), view: next, instance: b32(0), digest: [b64(0); 4], len: b5(1), value: v };
960            s.nv_sent = true;
961        }
962    }
963
964    // ---------------------------------------------------- entering the next view
965    // The consensus kernel moved to `view + 1`: start the next view change.
966    if run && i.view == next {
967        s.view = next;
968        // Replicas already sealed at or beyond the new view still count.
969        let mut carried = b16(0);
970        for x in 0..N {
971            if s.seal_hi[x] > next {
972                carried = carried | (b16(1) << b16(x as u128));
973            }
974        }
975        s.sealed_from = carried;
976        // Subjects that have not sealed the view we leave may still do so;
977        // an unfinished previous-view share is dropped, never sent partial.
978        let mut late = b16(0);
979        for x in 0..N {
980            if b5(x as u128) < r.n && b5(x as u128) != r.me && s.seal_hi[x] <= r.view {
981                late = late | (b16(1) << b16(x as u128));
982            }
983        }
984        s.p_view = r.view;
985        s.p_open = late;
986        s.in_skipped = r.skip_pending || s.skip_pending;
987        s.skip_pending = false;
988        s.late_done = false;
989        s.prev_open_done = r.open_done;
990        s.p_busy = false;
991        s.p_sent = false;
992        s.p_have = false;
993        s.p_sign_out = false;
994        s.p_signed = false;
995        // A previous-view key still waiting to be taken is cancelled, so it
996        // is never taken as a key of the view we now enter.
997        if r.p_key_out || s.p_key_out {
998            s.h_open.valid = false;
999        }
1000        s.p_key_out = false;
1001        s.p_keyed = false;
1002        s.p_own_given = false;
1003        s.p_shared = false;
1004        s.sealing = false;
1005        s.seal_sent = false;
1006        s.chain_busy = false;
1007        s.hdone = b16(0);
1008        s.opened = b16(0);
1009        s.open_done = b16(0);
1010        s.sign_sent = b16(0);
1011        s.signed = b16(0);
1012        s.own_given = b16(0);
1013        s.shared = b16(0);
1014        s.nv_sent = false;
1015        s.installed_next = false;
1016        s.epoch = r.epoch + b32(1);
1017    }
1018    (o, s)
1019}
1020
1021/// The mask of ids below `n`.
1022#[kernel]
1023pub fn r_all(n: b5) -> b16 {
1024    let mut all = b16(0);
1025    for k in 0..16 {
1026        if b5(k as u128) < n {
1027            all = all | (b16(1) << b16(k as u128));
1028        }
1029    }
1030    all
1031}