Skip to main content

Module slice_schedule

Module slice_schedule 

Source
Expand description

The fixed timetable that lets one BLAKE3 lane serve a whole slice.

This module is pure compile-time arithmetic: no hardware. It answers one question for crate::slice_factory: “in which cycle does each BLAKE3 compression of a key run?” The factory then turns the answer into small constant tables (SLOTS, WINDOW_LOADS) that a 0..=67 counter indexes.

§The problem

A slice starts one key every KEY_PERIOD = 68 cycles, because its chain engine accepts one of the key’s 68 chains per cycle. Each key needs 46 BLAKE3 compressions (seed, two nonces, 20 XOF blocks, a 21-block endpoint commitment, a Merkle share and a message hash), and they all share one pipelined BLAKE3 lane that accepts one compression per cycle. Several keys are in flight at once, and some compressions depend on others, up to 16 in a strict chain.

(The “21-block endpoint commitment” counts its 20 data blocks plus the parent that joins its two chunks: 3 + 20 + 21 + 2 = 46 slots, out of 68.)

§The trick: a modulo schedule

Give every compression of a key a fixed offset from the cycle T when that key’s chain 0 enters the chain engine. Key k starts at T₀ + 68k, so its compression with offset x issues at T₀ + 68k + x. Two keys collide on the lane only if two offsets are equal mod 68. So if every offset in the table has a different residue mod 68, no two compressions ever collide, however many keys overlap.

  cycle mod 68:  0  1  2  3  4  5  …                              67
  op issued:     X7 C0 ·  P  X8 C1 …     one op (or idle) per residue
                  │  └ chunk-0 block of the key that started 1 period ago
                  └ XOF block 7 of the key that starts next period

The hardware is then a 68-entry table (Schedule::residue) and a counter, with no arbiter. To stall, a slice simply does not start a key in some period; every compression of that key becomes an idle slot.

Words used below:

TermMeaning
Tthe cycle at which the key’s chain 0 enters the chain engine
offsetan op’s issue cycle minus T; negative = before chain 0
residuea cycle number mod 68; the hardware’s counter value
period68 consecutive cycles, residue 0 to 67
lagoffset div 68: in which period, relative to the key’s own, the op runs
DSchedule::use_delay: cycles from issue until a result can be used

§One key’s life on the timeline

  offset:  ≪0 ───────────────── 0 ────────── 67 ─────────────────────────▶
           Seed ─D─▶ PkNonce, Nonce
                └D─▶ Xof(0..19), each D before the first chain that needs it
                                chains 0..67 enter, one per cycle
                                          chain ends emerge (end_delay later)
                                             Chunk0(0) ─≥D─▶ Chunk0(1) … Chunk0(15)
                                                  Chunk1(0) ─≥D─▶ … Chunk1(3)
                                                                 ─D─▶ Parent → pk_hash

Preparation ops are placed as late as possible (so their results wait in storage for the shortest time), and commitment ops as early as possible (so the key finishes soon). Each op then lands on the nearest residue no other op has taken.

§Constraints the solver enforces

A result is usable Schedule::use_delay cycles after its compression issues: the BLAKE3 latency, one output register, then a write into and a registered read from the slice’s per-key context RAM (latency + 4).

  • Seed first. Nonces and XOF blocks hash the seed: each issues ≥ D after it.
  • XOF in time. Chain i needs bytes 18i..18i+18 of the XOF stream when it issues at T + i, so each XOF block is ready before its first chain.
  • Salt in time. pk_nonce salts every chain, so it is ready before T.
  • Commitment follows the chain ends. The commitment hashes pk_nonce ‖ end₀ ‖ … ‖ end₆₇ (1,240 bytes) as 20 blocks: chunk 0 is blocks 0–15, chunk 1 is blocks 16–19. Block j needs every chain end that touches it, and each chunk’s blocks chain through their chaining values, so consecutive blocks are ≥ D apart.
  • Parent last. The pk_hash parent combines both chunks.

The Merkle-share and message slots are reserved residues whose data comes from elsewhere; their readiness is checked by the controllers that use them. tests/slice_schedule.rs re-verifies every constraint independently.

§How the solver works

solve is a greedy, compile-time search. A table of 68 “used” flags records which residues are taken.

  1. Chunk 0 first: 16 blocks, each needing the previous one’s chaining value, is the hardest fit. solve_chunk tries every start base and every spacing gap ≥ D (within one period of the earliest choice) and keeps the placement that finishes earliest without collisions.
  2. Chunk 1 the same way, around what chunk 0 took.
  3. The parent at the first free residue D after both chunks.
  4. XOF blocks, last to first, each at the latest free residue that is still D before its first chain.
  5. The two nonces at the latest free residue D before T.
  6. The seed at the latest free residue D before its earliest user.
  7. The Merkle and message slots at the first free residues from offset 0.

If any step finds no room, solve fails at compile time, so a bad timetable can never reach hardware.

Structs§

Schedule
A complete, collision-free timetable for one slice.

Enums§

Op
One kind of compression in the per-key timetable.

Constants§

CHUNK0_BLOCKS
Endpoint-commitment blocks in the first 1,024-byte chunk.
CHUNK1_BLOCKS
Endpoint-commitment blocks in the 216-byte second chunk.
COMMITMENT_BYTES
Bytes hashed by the endpoint commitment: pk_nonce and 68 chain ends.
KEY_PERIOD
Cycles between consecutive key starts in a slice (one chain per cycle).
OPS
Number of scheduled compressions or reserved slots per key.
TAIL_SLACK
Cycles between a message result leaving the lane and its tail request being ready (result register, signer latch, request register, margin).
XOF_BLOCKS
XOF output blocks holding the 68 eighteen-byte chain secrets.

Functions§

block_ready
First cycle (relative to T) at which commitment block block is complete.
first_chain_of_xof
First chain that reads XOF block block.
op_at
The op stored at position index of Schedule::offsets.
op_index
Index of an op in Schedule::offsets; the inverse of op_at.
solve
Build the timetable for a BLAKE3 latency and a chain-end delay.