Slot Builder Module Specification

1. Purpose and Scope

Purpose

The slot builder module partitions dataset blocks into slots, builds individual Merkle trees for each slot enabling cell-level proof generation, and constructs a root verification tree over all slot roots.

Scope

  • Build block-level digest trees from raw data
  • Construct slot trees from block digests
  • Create verification trees from slot roots
  • Manage empty blocks and padding
  • Store cryptographic proofs in the block store

Boundaries and Limitations

  • Requires protected manifests
  • Block size must be divisible by cell size
  • Number of blocks must be divisible by number of slots
  • Slot cells must be padded to the nearest power of two

2. Interfaces

InterfaceDescriptionInputOutput
new[T, H]()Creates a new SlotsBuilder instancestore: BlockStore, manifest: Manifest, strategy: IndexingStrategy, cellSize: NBytes?!SlotsBuilder[T, H]
buildSlot(slotIndex)Builds a single slot tree and stores proofsslotIndex: NaturalFuture[?!H] (slot root hash)
buildSlots()Builds all slot trees for the datasetNoneFuture[?!void]
buildManifest()Builds a new verifiable manifest with slot rootsNoneFuture[?!Manifest]
buildBlockTree(blkIdx, slotPos)Builds digest tree for a specific blockblkIdx: Natural, slotPos: NaturalFuture[?!(seq[byte], T)]
buildSlotTree(slotIndex)Constructs slot tree from block hashesslotIndex: NaturalFuture[?!T]
getCellHashes(slotIndex)Retrieves cell hashes for a slotslotIndex: NaturalFuture[?!seq[H]]
buildVerifyTree(slotRoots)Creates verification tree from slot rootsslotRoots: openArray[H]?!T

3. Functional Requirements

3.1 Slot Tree Construction

  • Construct a Poseidon2-based Merkle tree for each slot from block digest hashes
  • Insert padding blocks with empty data for incomplete slots
  • Ensure slot trees have power-of-two number of leaves
  • Apply position-aware hashing using compression keys (bottom layer, odd position)

3.2 Verification Tree Generation

  • Build a root verification tree from all slot roots using Poseidon2
  • Compare stored verification roots against reconstructed roots to validate integrity

3.3 Proof Storage

  • Store Poseidon2 Merkle inclusion proofs for each cell in the block store
  • Associate proofs with corresponding tree CIDs
  • Provide proof retrieval functionality for verification requests
  • Encode proofs into a serializable format for persistent storage

3.4 Manifest Generation

  • Generate updated manifests containing:
    • Slot roots
    • Verification roots
    • Cell size
    • Strategy type
  • Preserve all original manifest metadata
  • Generate CIDs for slot and verification trees using codec identifiers

4. Non-Functional Requirements

4.1 Reliability

  • Error Handling: Proper error propagation through Result types

4.2 Scalability

  • Large Datasets: Handle datasets of arbitrary size - limited only by storage

4.3 Security

  • Cryptographic Integrity: From all data cells, construct a Merkle tree using Poseidon2 hash function over the BN254 curve
  • Proof Verification: Use Poseidon2-based Merkle proofs to verify data cells in commited root
  • Tamper Detection: Detect data modification by comparing stored and recomputed root hash
  • Position-Aware Hashing: Incorporate compression keys based on tree position, to prevent structural collisions and enhance Merkle tree security

5. Internal Behavior

5.1 Slot Building Workflow

flowchart TD
    Start([Initialize SlotsBuilder]) --> ValidateManifest{Manifest Protected?}
    ValidateManifest -->|No| Error1[Return Error]
    ValidateManifest -->|Yes| InitParams[Set parameters:<br/>- cellSize<br/>- indexing strategy<br/>- empty blocks]
    
    InitParams --> SlotLoop{For each slot<br/>i = 0 to numSlots-1}
    SlotLoop -->|Next Slot| GetIndices[Get block indices<br/>using strategy]
    
    GetIndices --> BlockLoop{For each block}
    BlockLoop -->|Next Block| CheckPadding{Is padding block?}
    CheckPadding -->|Yes| UseEmpty[Use empty block<br/>& empty tree]
    CheckPadding -->|No| RetrieveBlock[Retrieve block<br/>from store]
    
    RetrieveBlock --> CheckEmpty{Block empty?}
    CheckEmpty -->|Yes| UseEmpty
    CheckEmpty -->|No| BuildDigest[Build block<br/>digest tree]
    
    UseEmpty --> ExtractHash[Extract root hash]
    BuildDigest --> ExtractHash
    
    ExtractHash --> MoreBlocks{More blocks?}
    MoreBlocks -->|Yes| BlockLoop
    MoreBlocks -->|No| BuildSlotTree[Construct slot tree<br/>from block hashes]
    
    BuildSlotTree --> StoreProofs[Store proofs<br/>for each cell]
    StoreProofs --> MoreSlots{More slots?}
    MoreSlots -->|Yes| SlotLoop
    MoreSlots -->|No| BuildVerifyTree[Build verification tree<br/>from slot roots]
    
    BuildVerifyTree --> GenerateManifest[Generate new manifest<br/>with roots and CIDs]
    GenerateManifest --> End([Complete])

5.2 Block Tree Construction

flowchart TD
    StartBlock([Process Block]) --> CheckPosition{Block position ><br/>numSlotBlocks?}
    CheckPosition -->|Yes| ReturnEmpty[Return:<br/>- Empty block data<br/>- Pre-computed empty tree]
    CheckPosition -->|No| FetchBlock[Retrieve block<br/>from store]
    
    FetchBlock --> CheckData{Is block empty?}
    CheckData -->|Yes| ReturnEmpty
    CheckData -->|No| DivideCells[Divide block<br/>into cells]
    
    DivideCells --> BuildTree[Build Poseidon2 tree<br/>from cell hashes]
    BuildTree --> ReturnFull[Return:<br/>- Block data<br/>- Digest tree]
    
    ReturnEmpty --> EndBlock([End])
    ReturnFull --> EndBlock

Poseidon2 Compression Details

The Poseidon2-based Merkle tree uses position-aware compression keys to prevent structural collisions:

  • Default (KeyNone, 0x0): Used for even-indexed (non-leaf) nodes
  • Bottom Layer (KeyBottomLayer, 0x1): Used for even-indexed leaves when compressing at the bottom layer
  • Odd Positions (KeyOdd, 0x2): Used for odd-indexed internal (non-leaf) nodes
  • Odd + Bottom Layer (KeyOddAndBottomLayer, 0x3): Used for odd-indexed leaves in the bottom layer

By binding the compression key to both node type (leaf or internal) and position (even or odd), this scheme ensures that identical child hashes in different positions cannot produce the same parent hash, thereby eliminating cross-branch collisions and strengthening Merkle tree security.

5.3 Helper Functions

FunctionPurposeLocation
slotIndicesIter(slot)Returns iterator for slot block indicesSlotsBuilder
slotIndices(slot)Returns sequence of slot block indicesSlotsBuilder
digestTree(data, cellSize)Creates Poseidon2 tree from block data divided into cellsTree implementation
toSlotCids()Converts slot roots to CIDsConverters
toEncodableProof()Converts proof to storable formatProof types
compress(x, y, key)Poseidon2 compression with position-aware keyPoseidon2 module
merkleTreeWorker()Builds tree layers recursivelyMerkleTree

6. Dependencies

Internal Components

ComponentPurposeInterface
BlockStoreBlock storage and retrievalgetBlock(treeCid, index), putCidAndProof(treeCid, index, cellCid, proof)
ManifestManifest metadataVerifiable/Protected/unprotected manifests with slot configuration
MerkleTreeGeneric merkle tree operationsinit(leaves), getProof(index), root(), verify()
IndexingStrategyBlock organization patternsgetIndices(slot), init(strategy, firstIndex, lastIndex, iterations)
ConvertersType conversion utilitiestoSlotCid(), fromSlotCid(), toVerifyCid(), fromVerifyCid(), toCellCid()
AsyncIterAsynchronous iteration supportIterator protocol for async block retrieval
Poseidon2 modulePoseidon2 cryptographic primitivescompress(x, y, key), tree initialization, proof generation
RNGRandom number generationUsed for tree verification sampling

7. Data Models

7.1 Core Types

SlotsBuilder Object

type SlotsBuilder*[T, H] = ref object of RootObj
  store: BlockStore              # Storage backend for blocks
  manifest: Manifest             # Current dataset manifest
  strategy: IndexingStrategy     # Block indexing strategy
  cellSize: NBytes               # Size of each cell in bytes
  numSlotBlocks: Natural         # Blocks per slot (including padding)
  slotRoots: seq[H]              # Computed slot root hashes
  emptyBlock: seq[byte]          # Pre-allocated empty block data
  verifiableTree: ?T             # Optional verification tree
  emptyDigestTree: T             # Pre-computed empty block tree

Type Aliases

type Poseidon2Builder* = SlotsBuilder[Poseidon2Tree, Poseidon2Hash]
  # Specialized builder using Poseidon2 hash function
  # - Poseidon2Tree: MerkleTree with BN254 prime field elements
  # - Poseidon2Hash: BN254 prime field element (Bn254Fr)

7.2 Proof Types

Sample

type Sample*[H] = object
  cellData*: seq[H]              # Cell hash data
  merklePaths*: seq[H]           # Merkle proof paths

PublicInputs

type PublicInputs*[H] = object
  slotIndex*: int                # Slot identifier
  datasetRoot*: H                # Root hash of entire dataset
  entropy*: H                    # Randomness for sampling

ProofInputs

type ProofInputs*[H] = object
  entropy*: H                    # Randomness value
  datasetRoot*: H                # Dataset root hash
  slotIndex*: Natural            # Slot identifier
  slotRoot*: H                   # Root hash of slot
  nCellsPerSlot*: Natural        # Cell count per slot
  nSlotsPerDataSet*: Natural     # Total slot count
  slotProof*: seq[H]             # Inclusion proof for slot in dataset
  samples*: seq[Sample[H]]       # Cell inclusion proofs

Poseidon2Proof

type Poseidon2Proof* = MerkleProof[Poseidon2Hash, PoseidonKeysEnum]

7.3 Configuration Parameters

Cell and Block Sizes

const
  DefaultCellSize* = 2048.NBytes   # Default 2KB cells
  DefaultBlockSize* = 65536.NBytes # Default 64KB blocks

7.4 Supporting Types

Verifiable Manifest Metadata

# Manifest fields when verifiable = true
type Manifest* = object
  #...
  case verifiable*: bool
  of true:
    verifyRoot*: Cid                  # Root of verification tree
    slotRoots*: seq[Cid]              # Individual slot roots
    cellSize*: NBytes                 # Size of verification cells
    verifiableStrategy*: StrategyType # Verification strategy

7.5 Tree Types

Generic Merkle Tree

type MerkleTree*[H, K] = ref object of RootObj
  layers*: seq[seq[H]]          # Tree layers from leaves to root
  compress*: CompressFn[H, K]   # Hash compression function
  zero*: H                      # Zero/empty value

Poseidon2 Hash Types

type
  Bn254Fr* = F                  # Field element from BN254 curve
  Poseidon2Hash* = Bn254Fr       # Hash output is a field element
  
  PoseidonKeysEnum* = enum       # Compression key selection
    KeyNone                      # 0x0 - default key
    KeyBottomLayer               # 0x1 - bottom layer of tree
    KeyOdd                       # 0x2 - odd position
    KeyOddAndBottomLayer         # 0x3 - odd position at bottom

Poseidon2 Trees

type
  Poseidon2Tree* = MerkleTree[Poseidon2Hash, PoseidonKeysEnum]
    # Merkle tree using Poseidon2 hash with position-aware compression
  
  Poseidon2Proof* = MerkleProof[Poseidon2Hash, PoseidonKeysEnum]
    # Merkle proof for Poseidon2 trees

Poseidon2 Constants

const
  Poseidon2Zero* = zero
  KeyNoneF = F.fromHex("0x0")
  KeyBottomLayerF = F.fromHex("0x1")
  KeyOddF = F.fromHex("0x2")
  KeyOddAndBottomLayerF = F.fromHex("0x3")

Tree Initialization

func init*(T: type Poseidon2Tree, leaves: openArray[Poseidon2Hash]): ?!Poseidon2Tree
  # Initialize tree from hash leaves
  
func init*(T: type Poseidon2Tree, leaves: openArray[array[31, byte]]): ?!Poseidon2Tree
  # Initialize tree from byte array leaves
  
proc fromNodes*(T: type Poseidon2Tree, nodes: openArray[Poseidon2Hash], nleaves: int): ?!Poseidon2Tree
  # Reconstruct tree from all nodes (for verification)

7.6 CID Types and Codecs

Supported CID Codecs

const
  SlotRootCodec* = multiCodec("codex-slot-root")           # Codec for slot root CIDs
  SlotProvingRootCodec* = multiCodec("codex-proving-root") # Codec for proving root CIDs  
  CodexSlotCellCodec* = multiCodec("codex-slot-cell")      # Codec for slot cell CIDs

CID Conversion Functions

proc toSlotCid*(root: H): ?!Cid
  # Convert slot root hash to CID
  
proc fromSlotCid*(cid: Cid): ?!H
  # Extract slot root hash from CID
  
proc toVerifyCid*(root: H): ?!Cid
  # Convert verification root to CID
  
proc fromVerifyCid*(cid: Cid): ?!H
  # Extract verification root from CID