Expand description
One complete slice: factory, subtree builder, signer and key cache.
§Why slices exist
Almost all of DSig’s work is hashing, and almost all of the hashing is
making one-time keys (68 W-OTS+ chains per key). The way to sign faster is
to make keys faster, and the way to do that within a LUT budget is to
build one compact unit that makes and spends keys on its own, then copy it.
That unit is a slice. Each slice makes one key per 68 clocks, so the
engine’s signing rate grows with the number of slices
(SLICES).
This is the unit the sustained engine repeats eight
times. Everything a signature needs is made and spent inside it, except the
batch certificate, which comes from the shared cert_unit.
┌──────────────────────── Slice ───────────────────────────┐
config ──────▶│ SliceFactory ──cache_write──▶ 4 × UltraRAM cache banks │
│ │ key_done ▲ Merkle/message slots │ nodes │
│ ▼ │ ▼ │
│ SubtreeBuilder ───┘ SliceSigner ◀── proof port │
│ │ subtree root │ ▲ certificate │
└───┼────────────────────────────┼───┼─────────────────────┘
▼ ▼ │
cert_unit (shared) frame beats ─▶ HBM writer| Part | Module | Role in the slice |
|---|---|---|
factory | crate::slice_factory | makes one key per 68 clocks; also hashes Merkle parents and messages for the others |
bank0..bank3 | crate::cache_bank | hold chain positions 0..3 of 512 keys until they are spent |
subtree | crate::slice_merkle | builds this slice’s subtree of every batch, keeps the nodes for proofs |
signer | crate::slice_signer | burns a key per message and writes a 1,642-byte signature frame |
This file contains almost no logic. It is wiring: it connects the parts
and computes one signal of its own, space (cache admission, below).
The same slice drawn as dataflow, with its clock budget and its routed LUT cost. The three chain lanes are what make it fast and what make it big:
§The life of one key
factory starts key n ───▶ 68 chains written to cache slot n mod 512
(needs `space`) │ key_done (pk_hash, nonces, batch, j)
├──▶ signer: metadata RAM
└──▶ subtree: leaf j → … → subtree root ─▶ cert unit
│
cert unit ─▶ certificate for the batch ─▶ signer FIFO ◀────────┘ (much later)
message arrives ─▶ signer BURNS key n (consumed += 1) ─▶ reads nodes ─▶ frameA key is spent (“burned”) the moment the signer accepts a message for it, before any output is produced. If a frame is lost afterwards, the key is still gone. The project rule is that a one-time key is never reused after admission, timeout, reset or an uncertain launch; burning first is what makes that true here, because the counter only moves forward.
§Cache admission
The factory assigns cache slots in a ring of
CACHE_SLOTS = 512 keys (32 batches).
Each bank stores them densely, 68 rows per key
(cache_address_kernel).
The factory may start key n only while started − consumed <
ADMIT_LIMIT = 512 − KEYS_PER_SLICE, so at least one whole batch of
slack remains:
- slot
n mod 512is free, because keyn − 512was burned long ago; - the subtree ring entry of batch
bis free, because every key of the batch that last used it has been burned. Leafjand its parents overwrite nodes that are siblings of other keys in the old batch, so freeing only keyn − 512would not be enough.
key numbers in one slice, 8-slice build (512 slots, 16 keys per batch)
… burned │ in flight: made, not burned │ may still start │ kept free │
─────────┼─────────────────────────────┼─────────────────┼───────────┼──▶ key n
consumed started consumed + 496 consumed + 512
start allowed while started − consumed < ADMIT_LIMIT = 512 − 16 = 496When the signer falls behind (HBM stalls, or certificates still in flight), the factory simply skips key starts: every compression of a skipped key becomes an idle slot.
Structs§
- Slice
- One slice of the sustained engine.
- Slice
Input - Slice inputs.
- Slice
Output - Slice outputs.
Constants§
- ADMIT_
LIMIT - Keys that may be started but not yet burned: the cache less one batch.
- SUBTREE_
SHIFT log2 KEYS_PER_SLICEas a shift amount:keys_started >> SUBTREE_SHIFTis the batch of the next key (RHDL shifts takeBits, notusize).
Functions§
- bank_
port_ kernel - One cache bank’s port: write position
pof a chain, read the signer’s address. - slice_
kernel - Pure wiring between the slice’s parts.