Skip to content

Vector-of-Felts storage on top of SMT #2085

@partylikeits1983

Description

@partylikeits1983

Summary

Add a minimal scheme to store a Vec<Felt> under a 4-Felt KEY in the existing SMT. The value is chunked into 4-Felt words and written to per-chunk keys derived from the parent key and the chunk index; length is tracked separately for fast reads. Provide four methods: set(KEY, start_ptr, end_ptr), delete(KEY), get_length(KEY), and get(KEY, mem_ptr).

Concretely useful for storing NoteScripts inside an SMT and making them accessible in MASM.


Storage layout

Key derivation

  • Parent length entry:
    KEY[len, 0, 0, 0]
    (Canonical length in Felts, not chunks.)

  • Chunk entries:
    For chunk index N = 0, 1, 2, …, store the next 4 Felts at:
    HASHN = H(KEY || N)[f0, f1, f2, f3]
    where:

    • H is the platform hash (same as SMT).
    • Concatenation follows project-standard domain separation/encoding.
    • If the final chunk has <4 remaining Felts, pad its tail with zeros.

Keys depend on (KEY, N) so reads/writes are index-addressable without knowing values.

Example

For KEY = [1,1,1,1] and vector [0,1,2,3,4,5,6,7,8,9,10]:

  • Parent length: [1,1,1,1][11,0,0,0]

  • Chunks (4 Felts each):

    • HASH0 = H([1,1,1,1] || 0)[0,1,2,3]
    • HASH1 = H([1,1,1,1] || 1)[4,5,6,7]
    • HASH2 = H([1,1,1,1] || 2)[8,9,10,0]

No other metadata required.


Method definitions (behavioral spec)

  1. set(KEY, start_ptr, end_ptr)

    • Compute len = end_ptr - start_ptr (in Felts).

    • Store KEY → [len,0,0,0].

    • Iterate the memory range [start_ptr, end_ptr) in 4-Felt chunks. For each chunk index N:

      • Compute HASHN = H(KEY || N).
      • Write HASHN → [f0,f1,f2,f3], zero-padding the last chunk if needed.
    • If previously stored data had more chunks than the new ceil(len/4), they may remain; readers ignore them because all reads clamp to len which is stored in the first key value pair.

  2. delete(KEY)

  • Read len from KEY and compute chunk_count = ceil(len / 4).
  • For each N = 0 .. chunk_count - 1, overwrite the chunk: H(KEY || N) → [0,0,0,0].
  • Finally, set KEY → [0,0,0,0] to zero the canonical length.

This fully wipes the current vector’s storage (length and all populated chunks).

  1. get_length(KEY) → returns len

    • Read KEY and return the first Felt as the canonical length (Felts).
  2. get(KEY, mem_ptr) → returns len

    • Read len from KEY.

    • For N = 0 .. ceil(len/4) - 1:

      • Load chunk from H(KEY||N)[f0,f1,f2,f3].
      • Write up to 4 Felts into memory starting at mem_ptr + 4*N, but only emit the first len Felts in total (ignore padded zeros beyond len).
    • Return len


Acceptance criteria

  • Storage keys/values follow the exact layout above.
  • set(KEY, start_ptr, end_ptr), delete(KEY), get_length(KEY), and get(KEY, mem_ptr) behave as specified and are gas/constraint documented.
  • Last-chunk zero-padding is handled correctly; get never emits padded zeros past len.
  • Works for any len ≥ 0; scalability only bounded by index encoding and SMT limits.
  • Clear developer docs with the example mapping shown here.

Note: not 100% if this should go in Miden base or Miden VM stdlib.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions