Skip to main content

rhdl_ubft/
lib.rs

1//! # rhdl_ubft: ubft_new's consensus kernel, rewritten in RHDL
2//!
3//! # Building for Wolverine's U280
4//!
5//! The full consensus implementation does not yet have an established,
6//! accepted end-to-end U280 image flow. A successful Cargo build or RTL export
7//! is only a prerequisite; it does not establish routed timing or card behavior.
8//! Do not substitute `crates/dsig/tools/wolverine-ubft.sh`: that script builds
9//! the isolated DSig signature-service probe, not this consensus kernel.
10//!
11//! As recorded in `PROJECT_COORDINATION.md` on October 24, 2026, production
12//! export for source `d806f5bd` passed, but the subsequent child synthesis timed
13//! out without an accepted checkpoint. A later receiver fixture hit an Icarus
14//! parser input-buffer overflow. The ledger also recorded less than the 30 GiB
15//! free disk required for the planned full link. These are dated observations,
16//! not a live Wolverine inventory or a claim that the current source was built.
17//!
18//! Continue from the pinned source, configuration and evidence under
19//! `evidence/rhdl-ubft/card-bringup-20261024/`: resolve the lowering/parser and
20//! child-synthesis gates, confirm disk capacity, then package/link with
21//! Vivado/Vitis 2023.2 and the installed U280 platform. Require routed acceptance
22//! and host verification before programming or claiming performance. Preserve
23//! the existing evidence and match the host configuration to the exported kernel.
24//!
25//! Run commands from the workspace root. Before compilation or RTL generation,
26//! record ownership in `PROJECT_COORDINATION.md` and
27//! `/home/sahasta/Development/FPGA_COORDINATION.md`, and use the applicable
28//! nonblocking `local-heavy.lock`; `crates/ed25519/tools/bounded.sh` wraps local
29//! Cargo commands with that lock and resource limits. Reserve Wolverine builds
30//! in both ledgers too, then use `crates/dsig/tools/with-u280-lock.sh` to hold
31//! `u280.lock`. Do not bypass a busy lock.
32//!
33//! [ubft_new](https://github.com/YiJiang0908/ubft_new) (commit `a7537bd`)
34//! runs Byzantine fault-tolerant consensus on an Alveo U280. Each card is
35//! one replica. Replicas talk by RDMA-writing into each other's HBM over a
36//! RoCE stack. A key-value store sends every PUT through consensus before
37//! it touches its hash table. ubft_new's kernels are Vitis HLS C++.
38//! rhdl_ubft is its consensus kernel (`ubft`) in RHDL: the same ports, the
39//! related HBM layout and protocol, with targeted repairs and mechanics rebuilt
40//! for hardware. Certified Byzantine recovery and full integration remain open.
41//!
42//! **In numbers.** Not measured yet. The comparison against ubft_new's own
43//! RTL (HLS cosimulation, Vivado route and the card) is the next chapter of
44//! this page to be written. Until then this page states no speedup.
45//!
46//! # Contents
47//!
48//! 1. [What ubft_new does](#chapter-1-what-ubft_new-does)
49//! 2. [Why it is slow, and where it is wrong](#chapter-2-why-it-is-slow-and-where-it-is-wrong)
50//! 3. [The rewrite](#chapter-3-the-rewrite)
51//! 4. [Choosing a build](#chapter-4-choosing-a-build)
52//! 5. [What is proven](#chapter-5-what-is-proven)
53//! 6. [Where to go next](#where-to-go-next)
54//!
55//! # Chapter 1: What ubft_new does
56//!
57//! A slot is one consensus decision: sequence number `k`, value `m`. The
58//! leader proposes, and every replica votes by writing a word into the
59//! same mailbox address of every peer's HBM ([`layout`]). Replicas poll
60//! their own HBM to count votes.
61//!
62//! - **Fast path:** prepare, then two rounds of votes (`will_cert`,
63//!   `will_commit`). Each round needs a quorum of `2f + 1`, where uBFT
64//!   tolerates `f = (n − 1) / 2` faults among `n` replicas.
65//! - **Slow path**, after a timeout: every replica signs the slot's summary
66//!   with Ed25519, sends its 64-byte share to all, and verifies shares
67//!   until it holds `2f + 1`. Then one round of commit votes.
68//! - **View change**, after another timeout: the leader rotates, and the
69//!   new leader re-proposes the highest-numbered prepare anyone saw.
70//!
71//! # Chapter 2: Why it is slow, and where it is wrong
72//!
73//! **Slow.** The HLS kernel is one loop that does one state's work per
74//! iteration, and every mailbox word it reads is its own HBM transaction.
75//! Its Ed25519 calls are inline: a signature takes 3.07M cycles and a
76//! verification 10.4M (C/RTL cosimulation, measured by DSig), and the loop
77//! stops for each. That signer is also the routed design's worst path. The
78//! kernel clock misses 300 MHz by 2.866 ns, and the linker selects about
79//! 161 MHz. That is a timing-derived setting, not measured runtime frequency.
80//!
81//! **Wrong.** Reading the code, and running it as a C-simulated cluster,
82//! shows a protocol that cannot reach a decision as written:
83//!
84//! - a fast-path vote is sent to one queue pair, then the state moves on;
85//! - the leader never votes, so `2f + 1` out of `2f + 1` is unreachable;
86//! - the slow path never broadcasts the flag that tells peers its share is
87//!   there;
88//! - sends go to queue pairs `id·N + j`, but the RoCE stack programs `N −
89//!   1` connections at `(N − 1)·id + rank`;
90//! - `k = slot + 1` repeats, and nothing clears old votes;
91//! - shares go out as eight one-word packets;
92//! - the signed summary XORs its fields, and the library hashes only one
93//!   byte of it.
94//!
95//! [`protocol`] lists each fix next to the line it changes.
96//!
97//! # Chapter 3: The rewrite
98//!
99//! The kernel is small engines around one controller ([`replica`]):
100//!
101//! | Engine | Job | Replaces |
102//! |---|---|---|
103//! | [`control`] | ubft_new's register map, plus counters the host reads after a run | HLS `s_axilite` |
104//! | [`fetch`] | one burst per poll, any alignment | one HBM read per word |
105//! | [`broadcast`] | a write to every peer, one packet each, nothing dropped | `rdma_write*` loops |
106//! | [`writer`] | the replica's own copy of what it sent | (missing) |
107//! | [`share`] | a peer's share from HBM, realigned, into the verifier | 16 word reads |
108//! | [`setup`] + [`crypto`] | keys into a signature service on streams | inline xf Ed25519 |
109//! | [`protocol`] | the four machines | `ubft_machines.cpp` |
110//!
111//! # Chapter 4: Choosing a build
112//!
113//! | Choice | How | Values |
114//! |---|---|---|
115//! | Signature scheme | Cargo feature, then `RHDL_UBFT_SCHEME` if both are on | `ed25519` (default), `dsig` |
116//! | Most replicas | `RHDL_UBFT_NODES` | 3..=16 (ubft_new: 12) |
117//! | Window | `RHDL_UBFT_WINDOW` | 1..=8 (ubft_new: 3) |
118//!
119//! The run's cluster size is `node_num`, at most the build's. The Tuner writes supported values to `build-config.toml`; apply them with
120//! `apply_ubft_plan`. Saving TOML alone does not change a build.
121//!
122//! # Chapter 5: What is proven
123//!
124//! Nothing on hardware yet. In cycle simulation (feature `sim`: three real
125//! RHDL replicas, models of HBM, RoCE and the host, and a stand-in
126//! signature scheme that is **not secure**):
127//!
128//! | Run | Result |
129//! |---|---|
130//! | healthy cluster, 5 proposals | all decide on the fast path, in order, and agree; every write one framed packet to a programmed queue pair |
131//! | one replica's fast votes dropped | all decide on the slow path (2 shares verified each) |
132//! | equivocating leader | no conflicting decisions, but **nothing decided**: views drift apart and an all-`n` view change never completes (a known failure, an ignored test) |
133//!
134//! The replica lowers to Verilog (`export_rhdl_ubft`). Its view change is a
135//! value-binding prototype, not uBFT's certified recovery; the plan to bring
136//! that back is docs/rhdl_ubft-recovery-design.md.
137//!
138//! # Where to go next
139//!
140//! | You want to... | Read |
141//! |---|---|
142//! | see the protocol and its fixes | [`protocol`] |
143//! | see what crosses the wire | [`layout`], [`wire`] |
144//! | add a signature scheme | [`ports::CryptoIn`], [`ports::CryptoScheme`], [`crypto`] |
145//! | plug into the Vitis link | [`ports`] |
146
147#![warn(missing_docs)]
148
149pub mod broadcast;
150pub mod consensus;
151pub mod control;
152/// The one-card cluster: replicas, the on-chip fabric, the service (DSig).
153pub mod card;
154pub mod cert;
155pub mod checkpoint;
156pub mod crypto;
157pub mod ctb;
158pub mod fetch;
159// Atlas's key-value kernels (docs pending: docs/rhdl_ubft-kv-journey.md).
160#[allow(missing_docs)]
161pub mod kv;
162pub mod layout;
163pub mod ports;
164pub mod protocol;
165pub mod register;
166pub mod register_reader;
167#[cfg(feature = "replicated-register")]
168pub mod register_writer;
169#[cfg(feature = "replicated-register")]
170pub mod replicated_register;
171pub mod replica;
172pub mod rpc;
173pub mod setup;
174pub mod share;
175pub mod view_change;
176#[cfg(feature = "sim")]
177pub mod sim;
178pub mod wire;
179pub mod writer;
180
181/// Build variables (`build.rs`).
182pub mod build_config {
183    include!(concat!(env!("OUT_DIR"), "/build_config.rs"));
184}
185
186#[cfg(feature = "ed25519")]
187pub use replica::RhdlUbftEd25519;
188
189/// The replica selected at build time, including its actual service and shape.
190#[cfg(ubft_scheme = "ed25519")]
191pub type SelectedReplica = RhdlUbftEd25519<{ build_config::BUILD_NODES }, { build_config::BUILD_WINDOW }>;
192/// DSig selection preserves its named W4/Haraka/Blake3 profile.
193#[cfg(ubft_scheme = "dsig")]
194pub type SelectedReplica = crypto::dsig::RhdlUbftDsig<{ build_config::BUILD_NODES }, { build_config::BUILD_WINDOW }>;