Skip to main content

Module slice_signer

Module slice_signer 

Source
Expand description

A slice’s signer: one complete 1,642-byte signature every 68 cycles.

§Where this fits

By the time a message reaches the signer, almost all the work of signing it is already done. The key factory made the one-time key in advance and wrote its chain nodes to the key cache. The subtree builder hashed the key into its batch’s Merkle tree. The shared cert unit signed the batch root with Ed25519. What is left is to pick a key, hash the message, look up the right nodes, and write it all out in the wire format. That is this module.

The slice_factory fills the cache with keys; this block spends them. For every message it:

  1. burns the slice’s next certified key (before anything else, so a lost frame can never lead to key reuse) and asks the factory’s message slot for BLAKE3(pk_hash ‖ nonce ‖ message);
  2. turns the 64-byte XOF into 68 base-4 digits (64 message digits and a 4-digit complement checksum, as in reference::digits_from_xof);
  3. prepares the frame: reads four Merkle siblings, writes the six header beats, and computes the 34 header bytes that share a beat with the first node;
  4. streams the frame: one cached node per cycle, each read from the cache bank its digit selects, packed into beats.

Preparation of frame n + 1 overlaps the streaming of frame n, so in steady state a slice finishes one frame every 68 cycles: exactly one key per KEY_PERIOD, matching the factory.

 message ─▶ [burn+hash] ─▶ factory slot ─▶ [digits] ─▶ [prep] ─▶ [stream] ─▶ frame buffer ─▶ writer
               │                                         │           │        (4 frames)
         key metadata RAM ◀── key_done          proof port   cache banks
         certificate FIFO ◀── shared top

(The “four Merkle siblings” are the 8-slice count, SUBTREE_LEVELS; the other TOP_LEVELS siblings arrive inside the certificate.)

§Why the stages overlap

Streaming is the only stage that has to take 68 cycles (one node per chain, one cache read per cycle). Everything else is shorter, so it is done for the next frame while the current one streams. A work item moves through a chain of holding registers in SignerControl:

  queue[0..2] ──▶ prep ──▶ ready ──▶ stream ──▶ cache reads ──▶ packer ──▶ frame buffer ──▶ drain
  (burned,        (siblings, (prepared, (68 node    (token rides  (beats      (4 slots of     (26 beats
   digits soon)    header)    waiting)   addresses)  with data)    6..25)      32 beats)       to HBM)

  time ─▶     |──── frame n streams (68) ────|──── frame n+1 streams (68) ────|
              |hash n+1|prep n+1| (waits)     |hash n+2|prep n+2| (waits)

A new stream may begin addressing on the cycle right after the previous stream addressed chain 67, so streams run back to back with no gap.

§Frame layout (26 × 64-byte beats = 1,664 bytes)

Bytes 0..1642 are exactly DsigSignature::to_bytes:

BytesField
0..64certificate commitment (DSIGFP01 envelope)
64..128Ed25519 signature
128..130key index within the batch, LE16
130..162pk_hash
162..178pk_nonce
178..194private nonce
194..418seven 32-byte Merkle siblings, bottom-up
418..164268 revealed 18-byte nodes

Bytes 1642..1650 carry the message index (LE64) so the host can match a frame to its message; the remaining padding is zero.

The same layout seen as beats, and who writes each one:

  beat 0       commitment                                  ← preparation (from certificate)
  beat 1       Ed25519 signature                           ← preparation (from certificate)
  beats 2..5   frame bytes 128..384 (index, pk_hash, …)    ← preparation (header_tail_kernel)
  beat 6       bytes 384..418 (34 B) │ node 0 │ node 1 …    ← packer (34 B are its prefix)
  beats 7..24  nodes                                       ← packer
  beat 25      … node 67 │ msg index (B 42..50) │ zeros     ← packer + index patch
  beats 26..31 unused (each frame slot is 32 beats)

§Ordering rules

  • Keys are spent in cache-slot order, which is the order the factory made them, so “key k is finished” is the counter test done > consumed.
  • A key is eligible only once its batch certificate is present.
  • A certificate is retired after the last frame that needs it (this slice’s j = 15 key of that batch) has been prepared.

§The key-reuse rule

A one-time key must never sign twice. The signer increments consumed (the burn) in the same cycle the factory accepts the message hash, before any node is read. consumed only moves forward and the factory may not overwrite a slot until its key is burned (see crate::slice). So even if a frame is later lost, delayed or the run is reset, the key it used is gone for good.

Structs§

Certificate
One batch certificate as delivered by the shared top.
FrameBeat
One frame beat for the HBM writer.
KeyMeta
Per-key metadata held until the key is spent.
Message
A message to sign.
NodeRead
Everything needed to use one cache read when its data arrives.
SignerControl
Resettable control.
SignerData
Resetless datapath registers.
SignerInput
Signer inputs.
SignerOutput
Signer outputs.
SliceSigner
The slice signer.
Work
A burned key travelling through digits, preparation and streaming.

Constants§

FRAME_BEATS
Beats per frame.
FRAME_SLOTS
Frame slots in the frame buffer.
HEADER_BEATS
Header beats written by preparation.

Functions§

cleared_work
Kernel-side idle_work: clears every field except the don’t-care meta, which it keeps from stale (kernels cannot call dont_care()).
digits_kernel
68 digits from the 64-byte message XOF (low two bits, then checksum).
frame_address_kernel
Frame-buffer address of beat beat in frame slot frame.
header_tail_kernel
The 290 header bytes after the signature (frame bytes 128..418).
sibling_address_kernel
Subtree sibling address for level of key j in batch ring ring: the node next to j’s ancestor at that level.
slice_signer_kernel
The signer’s wiring.