Skip to content

feat(felt): size-based compact CBOR codec on Typed[K] - #4230

Merged
EgeCaner merged 10 commits into
mainfrom
feat/felt/compact-cbor-on-typed
Nov 13, 2026
Merged

EgeCaner merged 10 commits into
mainfrom
feat/felt/compact-cbor-on-typed

Conversation

@EgeCaner

Copy link
Copy Markdown
Contributor

What changes

Every felt stored through CBOR was written as a CBOR array of its four Montgomery limbs: 37 bytes for any
non-zero value, whether it was a nonce of 1 or a 32-byte hash. This PR replaces that with a size-based encoding on
Typed[K], so every felt kind encodes identically:

value wire form size
below 24 CBOR uint, the header byte is the value 1 B
up to 30 significant bytes byte string of the trimmed big-endian value 2–32 B
31 or 32 significant bytes (hashes, addresses) 32-byte string of the Montgomery limbs 34 B

The length alone identifies the form, and each value has exactly one valid encoding: non-minimal headers, leading
zeros, byte strings below 24, 31-byte bodies and values at or above the modulus are rejected, and a rejected input
never modifies the destination. Hashes keep their limbs so decoding them is a copy plus a modulus check; small values
pay one canonical-to-Montgomery multiplication on decode, except values below 24, which come from a precomputed table.

Legacy rows still decode. The decoder recognises the old array shape and takes the limb path, and anything that shape
check does not cover falls back to the generic decoder as before, so a node reads a mixed database. New writes are
compact from the first block after upgrade. Rewriting existing rows is a separate PR (feat/felt/compact-felt-migration);
the size numbers below come from that migration, run on a full mainnet node with this codec.

Also in this PR:

  • Slice[F] encodes and decodes element by element with the same codec and the same legacy fallback.
  • MinimalBytes / SetMinimalBytes on Typed[K]: the trimmed big-endian value without CBOR framing, for buckets whose
    whole value is one felt (nonces, class hashes, storage values). Strict like SetBytesCanonical but accepting 1 to 32
    bytes. Used by the migration PR; no callers here.
  • Tests: exact wire bytes at every size boundary, canonical-form rejections, truncation of every prefix, legacy decode
    on 100+ values and 20 malformed legacy shapes (carried over from the old fast-path tests), mixed legacy and compact
    elements in one slice, all eight kinds encoding identically, map key ordering, and two fuzzers. The CBOR compatibility
    goldens are regenerated for the new form and the old file is kept verbatim as golden_bytes_legacy.json, decode-only.
    The typed-bucket test keeps its real mainnet rows as the read side and adds the compact bytes as the write side.
  • Benchmarks: felt-level encode/decode per wire size, slice decode compact vs legacy both directly and through the CBOR
    library, and a struct-level read benchmark on the mainnet fixtures.

Disk size

Full mainnet DB at height 14,733,615. Per-bucket bytes are the live SST sizes from a pebble manifest dump,
attributed to buckets by key prefix (the totals match du). "Before" is the pristine snapshot as synced by main
(26,064 tables, all L6, median 4 MiB). "After" is after the migration rewrote every felt-bearing bucket and
juno db compact ran with the node's zstd3 options (9,283 tables, median 63 MiB): the rewrite alone leaves old and new
SSTs overlapping until compaction (504 → 496 GiB), so only the compacted number is meaningful. The base was not
re-compacted the same way, so the total includes whatever a full compaction would have reclaimed on its own; buckets
the migration did not touch moved 0 to -1.5%, which bounds that share for the large buckets.

bucket before after Δ
BlockTransactions 250.46 GiB 202.70 GiB -19.1%
StateUpdatesByBlockNumber 96.17 GiB 78.96 GiB -17.9%
Class 6.41 GiB 4.13 GiB -35.6%
BlockHeadersByNumber 3.51 GiB 2.80 GiB -20.2%
DeprecatedContractNonceHistory 2.22 GiB 1.75 GiB -21.2%
everything else (tries, hash indexes, bloom filters, commitments) 145.40 GiB 141.33 GiB -2.8%
total 504.17 GiB 431.67 GiB -14.4%

Raw CBOR shrinks more than that (BlockTransactions -48%, Class -54%, StateUpdates -38% per record); zstd was already
recovering part of the padding, so the on-disk gain is the compressed one.

RPC throughput (k6)

Interleaved A/B on the same laptop (M3 Max, 36 GiB, NVMe): main binary on the pristine snapshot vs this branch on
the migrated DB. Each arm's DB is restored from a verified copy before every block and the page cache is purged before
each profile. sequential = 1 VU, high = 500 VUs.

Warm corpus, 5,000 requests per method, 3 trials, medians:

method seq req/s Δ seq p50 ms Δ high req/s Δ
getBlockWithTxs 901 → 1,095 +22% 0.64 → 0.51 -20% 8,930 → 9,779 +10%
getBlockWithReceipts 479 → 540 +13% 0.88 → 0.77 -12% 4,461 → 4,808 +8%
getTransactionByHash 3,597 → 4,487 +25% 0.14 → 0.11 -21% 37,605 → 43,819 +17%
getTransactionReceipt 2,823 → 3,600 +28% 0.19 → 0.14 -26% 29,128 → 33,832 +16%
getStateUpdate 2,504 → 2,648 +6% 0.18 → 0.17 -6% 19,472 → 20,506 +5%
getEvents 139 → 155 +11% 4.00 → 3.29 -18% 1,003 → 1,072 +7%
getClass 124 → 144 +16% 4.94 → 3.77 -24% 903 → 1,006 +11%
getCompiledCasm 71 → 77 +8% 5.74 → 4.63 -19% 435 → 462 +6%
getStorageAt 8,063 → 8,471 +5% 0.07 → 0.06 -14% 68,645 → 71,833 +5%
blockNumber (control) 12,414 → 12,516 +1% 0.05 → 0.05 0% 92,817 → 93,007 0%

Cold corpus, 1,000,000 distinct requests per method drawn uniformly over the whole chain (mostly NVMe reads):

method seq req/s Δ seq p95 ms Δ high req/s Δ high p95 ms Δ
getBlockWithTxs 1,013 → 1,109 +9% 1.53 → 1.30 -15% 10,198 → 11,361 +11% 105 → 77 -27%
getBlockWithReceipts 580 → 643 +11% 3.24 → 2.70 -17% 3,590 → 5,080 +41% 306 → 176 -42%
getBlockWithTxHashes 1,853 → 2,461 +33% 0.95 → 0.64 -33% 15,763 → 32,108 +104% 76 → 48 -37%
getTransactionByHash 2,040 → 2,289 +12% 0.63 → 0.53 -16% 23,244 → 31,892 +37% 38 → 32 -15%
getTransactionReceipt 1,697 → 1,985 +17% 0.78 → 0.64 -18% 20,756 → 25,133 +21% 69 → 55 -20%
getTransactionStatus 2,205 → 2,475 +12% 0.60 → 0.50 -17% 30,771 → 35,492 +15% 31 → 29 -7%

No failed requests in any run. A golden replay of the warm corpus (241.5k requests) returned the same responses from
both arms, modulo map ordering and L1 finality state.

Micro benchmarks

Felt level (BenchmarkMarshalCBOR, BenchmarkUnmarshalCBOR, 5 runs):

value bytes encode ns decode ns legacy decode ns
zero / below 24 1 8.0 3.5 10.0
one byte (24..255) 2 8.0 19.7 4.4
uint64 9 11.4 17.4 4.4
hash / address 34 10.2 5.1 4.4

Slice of 5,000 felts (BenchmarkSliceCBOR), decoded directly and through fxamacker, which is how struct fields are
decoded. "short" = values 0..4095, the worst case for compact; "long" = random hashes:

input bytes/felt direct ns/felt via library ns/felt
compact, short 2.9 22.5 46.6
legacy, short 37.0 4.7 66.9
compact, long 34.0 6.1 32.9
legacy, long 37.0 4.8 67.1

Direct, small values are slower to decode by the one multiplication. Through the library compact wins both cases: a
compact felt is one CBOR item the library delimits in one header read, a legacy felt is a nested five-item array it has
to walk first.

Struct level on the mainnet fixtures, cbor.Marshal p50 (p95): Transactions 26.1 → 19.2 µs (150.8 → 92.1), Receipts
41.0 → 30.5 µs (141.9 → 98.2), StateUpdate 14.1 → 12.9 µs, Header 1.7 → 1.5 µs. Decode per record on a read-only
mainnet sample: BlockTransactions 268 → 191 µs (-29%), transaction hashes only 88 → 33 µs (-63%), Class 327 → 205 µs
(-37%), Headers 2.1 → 1.8 µs. Decoding an unmigrated row with the new decoder is within ±3% of the old decoder.

@EgeCaner EgeCaner added the disable-deploy-test We don't want to run deploy tests with this PR because it might affect our development environment. label Oct 27, 2026
@github-actions

github-actions Bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Claude finished @EgeCaner's task in 14m 54s —— View job


Reviewing PR #4230

  • Gather context (read AGENTS.md, diff, changed files)
  • Understand the request (code review of this PR)
  • Review core/felt/cbor.go encode/decode logic
  • Review core/felt/felt.go additions (MinimalBytes/SetMinimalBytes)
  • Review core/felt/slice.go
  • Review tests for coverage/quality
  • Post findings as inline comments + summary

Summary

This PR is clean — I didn't find any correctness, concurrency, or security issues worth flagging.

Specifics I checked closely given the risk profile of a wire-format change like this:

  • Decode safety on untrusted input (core/felt/cbor.go): all length/offset arithmetic in decodeCompact/decodeByteStringHeader/decodeByteStringBody is bounds-checked before slicing (length > feltByteLen, then len(data)-offset < length), so a malicious length byte can't cause an out-of-bounds slice or panic. The modulus check (belowModulus) correctly rejects Montgomery-form byte strings at or above the field modulus, and canonical byte strings under 31 bytes can't exceed the modulus by construction (max 2^240-1 < p), so the extra fp.BigEndian.Element range check there is just defense in depth.
  • No partial writes on rejected input: both UnmarshalCBOR and decodeLegacyLimbs only write to z/value after validation succeeds, matching the stated invariant and verified by TestCBORDecodeRejects's sentinel check and the truncation fuzz test.
  • Legacy/compact interop: isCompactCBOR cleanly routes by major type (uint/byte-string → compact, everything else → legacy/generic), and Slice[F].UnmarshalCBOR's per-element fallback to unmarshalGeneric correctly handles mixed legacy+compact slices (confirmed by TestCBORDecodeMixedFormats).
  • No new goroutines/shared mutable state: smallMontgomery and modulusLimbs are computed once at package init via immediately-invoked literals and only read afterward — no data race potential.
  • Test coverage is thorough: exact wire-byte assertions at every size boundary, canonical-form rejection cases, truncation of every prefix, legacy-corpus equivalence against the generic decoder, two fuzzers (round-trip and decode-equivalence), and golden-byte compatibility tests including a dedicated legacy-decode-only golden file.

One non-blocking observation (not filed inline since it's not a bug): MinimalBytes/SetMinimalBytes in core/felt/felt.go have no callers in this PR — as the description notes, they're for the follow-up migration PR (feat/felt/compact-felt-migration), so this is expected, not dead code.

@github-actions

github-actions Bot commented Oct 27, 2026 •

Copy link
Copy Markdown
Contributor

Title

(Describe updated until commit 94363b8)

feat(felt): size-based compact CBOR codec on Typed[K]


PR Type

Enhancement, Tests


Description

  • Introduce a size-based compact CBOR codec for felt (1 to 34 bytes), replacing the fixed 37-byte Montgomery-limb encoding.

  • Gate the new compact encoder behind the compactfelt build tag, while allowing the decoder to read both legacy and compact forms in any build.

  • Implement a compactfelt migration to record the first block written with the new codec and block legacy builds from opening compact databases.

  • Introduce MinimalBytes and StoredBytes methods to standardise reading and writing trimmed bytes.

  • Update tests, test fixtures, and CI jobs to cover both legacy and compact logic.


File Walkthrough

Relevant files
Tests
23 files
typed_buckets_test.go
update bucket test utilities for compact and legacy codec support
+71/-14 
cbor_test.go
add tests for compact felt CBOR encoding and map key ordering
+285/-0 
cbor_decode_test.go
add decoding tests and strict edge case validations for felt
+215/-0 
cases_test.go
add larger test coverage for felts and related type aliases
+44/-5   
value_test.go
extend serializer tests to assert legacy and compact hex values
+49/-14 
slice_test.go
update slice decoding tests to account for new CBOR logic
+31/-4   
cbor_legacy_test.go
add legacy encoding tests ensuring backward compatibility
+152/-0 
cbor_helpers_test.go
add utility helpers and compact CBOR encoding fixtures     
+119/-0 
felt_test.go
test MinimalBytes and SetMinimalBytes implementations       
+102/-0 
node_test.go
add trie node encoding and decoding tests                               
+80/-0   
felt_benchmark_test.go
update inputs for felt CBOR unmarshalling benchmarks         
+6/-42   
slice_bench_compact_test.go
benchmark legacy and compact slice decoding                           
+71/-0   
cbor_bench_legacy_test.go
add legacy unmarshal benchmark tests based on wire sizes 
+48/-0   
cbor_bench_compact_test.go
add compact unmarshal benchmark tests based on wire sizes
+51/-0   
golden_test.go
test both legacy and compact golden cases                               
+18/-2   
migrator_test.go
add unit tests for compactfelt migrator                                   
+43/-0   
runner_test.go
assert updated runner error message for opt-out constraints
+6/-3     
registry_test.go
verify updated enable hint array operations                           
+9/-9     
fxamacker_test.go
use correct golden JSON based on the felt form configuration
+15/-5   
bucket_test.go
expand invalid value slice to exceed standard felt size   
+2/-1     
golden_bytes.json
update golden bytes vectors for compact felt codec             
+33/-21 
golden_bytes_legacy.json
add golden bytes vectors for legacy felt codec                     
+57/-0   
cbor_fastpath_test.go
delete redundant test file                                                             
+0/-239 
Enhancement
11 files
cbor.go
refactor UnmarshalCBOR to support both legacy and compact formats
+223/-84
slice.go
refactor slice CBOR marshalling and unmarshalling               
+16/-20 
runner.go
update opt-out error message with dynamic enable hints     
+12/-12 
felt.go
implement MinimalBytes and SetMinimalBytes                             
+56/-0   
cbor_legacy.go
define legacy MarshalCBOR bound by !compactfelt build tag
+76/-0   
node.go
update trie node to write and read minimal bytes for leaves
+19/-5   
cbor_compact.go
define compact MarshalCBOR bound by compactfelt build tag
+46/-0   
migrator.go
introduce compactfelt migrator to record height of first compact block
+50/-0   
buckets.go
add CompactFeltHeight bucket                                                         
+3/-0     
ctors.go
add FromMinimalBytes constructor                                                 
+7/-0     
typed_buckets.go
declare CompactFeltHeightBucket                                                   
+7/-0     
Miscellaneous
2 files
buckets_enumer.go
regenerate buckets enum to include CompactFeltHeight         
+8/-4     
value.go
update serializer documentation                                                   
+1/-0     
Refactoring
8 files
accessors.go
update deprecated state accessors to use MinimalBytes and StoredBytes
+7/-9     
registry.go
replace optional flags array with an enable hints array   
+14/-16 
state.go
read deprecated states with SetMinimalBytes                           
+3/-3     
contract.go
write deprecated state contracts with StoredBytes               
+2/-2     
ingestor.go
update ingestor to unmarshal nodes using SetMinimalBytes 
+10/-4   
accessors.go
use StoredBytes in core state accessors                                   
+3/-3     
felt.go
update felt bytes serializer logic with MinimalBytes and StoredBytes
+6/-4     
state_reader.go
use SetMinimalBytes to read historic state values               
+1/-2     
Configuration changes
4 files
migration.go
register compactfelt migrator                                                       
+9/-3     
Makefile
add linting and testing targets for compactfelt                   
+9/-2     
juno-lint.yml
add lint steps for the compactfelt tag                                     
+12/-0   
juno-test.yml
add compactfelt test command                                                         
+3/-0     

@codecov

codecov Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.06987% with 9 lines in your changes missing coverage. Please review.
✅ Project coverage is 81.61%. Comparing base (74a70d1) to head (94363b8).

Files with missing lines Patch % Lines
core/felt/cbor.go 97.00% 3 Missing ⚠️
migration/compactfelt/migrator.go 80.00% 2 Missing ⚠️
migration/state/newstate/internal/trie/ingestor.go 66.66% 2 Missing ⚠️
core/trie/node.go 88.88% 1 Missing ⚠️
migration/runner.go 88.88% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #4230      +/-   ##
==========================================
+ Coverage   81.56%   81.61%   +0.05%     
==========================================
  Files         505      507       +2     
  Lines       36877    37005     +128     
==========================================
+ Hits        30078    30202     +124     
- Misses       6793     6797       +4     
  Partials        6        6              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions

github-actions Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Claude finished @EgeCaner's task in 9m 44s —— View job


Reviewing PR #4230

  • Gather context (diff vs origin/main, changed files)
  • Review core/felt/cbor.go, cbor_compact.go, cbor_legacy.go
  • Review core/felt/felt.go additions (MinimalBytes/SetMinimalBytes)
  • Review core/felt/slice.go
  • Review CI workflow / Makefile changes
  • Review tests for coverage/quality
  • Check repo-wide for hardcoded felt-CBOR byte fixtures that could break under -tags compactfelt
  • Post findings

Summary

Clean — no correctness, concurrency, or resource-leak issues found in the two new commits since the last review (da5c8852c gating the compact codec behind the compactfelt build tag, 0ee2d52de making both felt forms decode in every build).

What I checked:

  • Build-tag split is sound: core/felt/cbor_compact.go (//go:build compactfelt) and core/felt/cbor_legacy.go (//go:build !compactfelt) each define MarshalCBOR/appendCBOR/maxCBORFeltLen under mutually exclusive tags, so there's no symbol collision and exactly one implementation links per build. The default (no-tag) build still writes byte-for-byte what main writes (verified via cbor_legacy_test.go's requireMarshalEquivalent against the generic encoder), so existing nodes see no behavior change until they're explicitly built with the new tag.
  • Decode path is shared and tag-independent: UnmarshalCBOR/decodeCBOR/decodeCompact/decodeLegacyLimbs in core/felt/cbor.go have no build tag, so every build — tagged or not — accepts both wire forms. This is the key correctness property for a mixed-format database and it's exercised by TestCBORDecodeLegacy, TestCBORDecodeMixedFormats, and the fuzzers in both cbor_test.go (compactfelt) and cbor_decode_test.go (tag-independent).
  • Test/CI wiring for the new tag: Makefile's new test-compactfelt target and the .github/workflows/juno-test.yml/juno-lint.yml additions correctly pass -tags compactfelt (and --build-tags compactfelt for lint) so CI exercises both builds. I had a background check sweep the rest of the repo (migration/, p2p/, rpc/, mempool/, sync/, consensus/, db/) for other hardcoded felt-CBOR byte fixtures that could silently break when built with -tags compactfelt; it found none — every other cbor.Marshal/Unmarshal test in the tree round-trips through Go values rather than asserting raw bytes, so they're insensitive to which felt codec is linked in.
  • Golden/fixture updates are consistent: core/typed_buckets_test.go, db/typed/value/value_test.go, and utils/cbor/compatibility/{cases,golden,fxamacker}_test.go all gate on a runtime compactFeltWrites check (len(felt.Zero.MarshalCBOR()) == 1) rather than a build tag, and carry both valueHex/compactHex (or golden_bytes.json/golden_bytes_legacy.json) pairs so the same test binary validates whichever form it links.

Nit-level (not blocking, no inline comment since it's not a bug): .github/workflows/juno-lint.yml's new "golangci-lint-diffs (compactfelt)" step omits the ./core/felt/... path restriction that the plain "golangci-lint (compactfelt)" step has, so it re-lints the whole repo under the tag instead of just the package the tag affects — harmless given only-new-issues: true, just a bit more CI time than necessary.

@github-actions

github-actions Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Reviewing PR #4230

  • Gather context (diff vs origin/main, changed files since last review)
  • Review core/felt/cbor.go, cbor_compact.go, cbor_legacy.go
  • Review core/felt/felt.go, slice.go, ctors.go
  • Review core/trie/node.go changes
  • Review migration/compactfelt/migrator.go
  • Review db/typed/value/felt.go, value.go
  • Review CI workflow / Makefile changes
  • Post findings

@EgeCaner

EgeCaner commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor Author

Update since the description: the write side is now behind the compactfelt build tag, so a default build writes exactly what main writes today. This covers both the CBOR felt codec and the raw felt rows (state history, nonces, typed felt buckets, trie1 leaves), which are now trimmed under the tag via StoredBytes. The read side is shared: every build decodes both the legacy and the compact forms, and both the padded and the trimmed raw rows. A marker migration records the block where compact writes started and sets a schema bit, so an older Juno refuses the database instead of failing on felt decoding. That guards against version downgrades, but as a side effect it also blocks switching a node back from the compact build to the default one, even though reads support both forms at the moment.

@github-actions

github-actions Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Claude finished @EgeCaner's task in 12m 22s —— View job


Reviewing PR #4230

  • Gather context (diff vs origin/main, changes since last review)
  • Review new commits since last review (2e7439dc2 trim raw felt rows under compactfelt, 008f2e0b7 migration marker, 781c2b8d9 test fixture fix)
  • Review core/trie/node.go leaf encoding and migration/state/newstate/internal/trie/ingestor.go
  • Review migration/compactfelt/migrator.go, node/migration.go, schema-version/opt-out interaction
  • Review db/typed/value/felt.go and callers (core/deprecatedstate, core/state)
  • Repo-wide sweep for other raw single-felt storage call sites that might need the same treatment
  • Post findings

Summary

Clean — nothing new to flag in the commits added since the last review pass.

What I verified:

  • StoredBytes/SetMinimalBytes split (core/felt/cbor_compact.go, cbor_legacy.go, felt.go): the compact build's StoredBytes returns MinimalBytes() (trimmed, min 1 byte), the default build's returns Marshal() (full 32 bytes, byte-for-byte what main writes today), and SetMinimalBytes accepts both widths in every build — bounds-checked (len(data) > feltByteLen → errFeltTooLong) and modulus-checked via fp.BigEndian.Element, with no partial write to z before validation succeeds.
  • core/trie/node.go: isLeaf() correctly keys off Left == nil && LeftHash == nil && RightHash == nil, which matches every real construction site I checked (trie.go:514,570 build genuine leaves with only Value set; NewPartialStorageNode sets Left/Right to emptyBitArray, not nil, so it's correctly treated as non-leaf). UnmarshalBinary take the min(len(data), felt.Bytes) prefix as the value, which correctly handles both the 1-32 byte trimmed form and the legacy 32-byte form, and the node correctly clears Left/Right/LeftHash/RightHash when re-decoding a reused node into a leaf shape (covered by the new "reused node decoding a leaf drops previous children" test case in node_test.go).
  • migration/state/newstate/internal/trie/ingestor.go: same trimmed-or-full leaf decode logic as core/trie/node.go, kept consistent between the two trie implementations.
  • migration/compactfelt/migrator.go: records chain height + 1 (or 0 for an empty DB) as the first compact block; registered via WithOptional(..., felt.CompactWrites, ...) so a legacy build never sets this bit, and the registry's existing validateNoOptOut/validateNoVersionDowngrade logic (unmodified) naturally blocks downgrading from a compact-writing build back to a legacy one once the migration has run — matches the PR description's stated tradeoff.
  • db/typed/value/felt.go: feltBytesSerializer.Marshal/Unmarshal now go through StoredBytes/SetMinimalBytes instead of Marshal/SetBytesCanonical; all direct callers of the old .Marshal() pattern for standalone felt rows (core/deprecatedstate/accessors.go, contract.go, core/state/accessors.go) were updated to .StoredBytes(), and the corresponding readers to SetMinimalBytes/FromMinimalBytes.
  • Repo-wide sweep: remaining .Marshal() call sites on felt values (core/state/contract.go, core/class.go, mempool/db_utils.go, adapters/core2p2p/felt.go, etc.) are either multi-field fixed-size struct encodings or P2P wire types outside this PR's scope (single standalone felt rows) — correctly left untouched.
  • Test fixture fix (781c2b8d9): insertInvalidValue in db/typed/prefix/bucket_test.go now writes felt.Bytes+1 zero bytes instead of the literal string "invalid felt" (12 bytes) — the old fixture was coincidentally a length that could be misread as a valid trimmed felt by the shared SetMinimalBytes reader; felt.Bytes+1 is unambiguously invalid at either stored width, which is the right fix.

No blockers or nits found in this round.
(branch: feat/felt/compact-cbor-on-typed)

@EgeCaner EgeCaner removed the disable-deploy-test We don't want to run deploy tests with this PR because it might affect our development environment. label Nov 3, 2026
@EgeCaner
EgeCaner force-pushed the feat/felt/compact-cbor-on-typed branch from 781c2b8 to 6896899 Compare November 4, 2026 14:21
Comment thread core/felt/cbor.go Resolved
Comment thread core/felt/cbor_decode_test.go Resolved
Comment thread core/trie/node.go Resolved

@danielntmd danielntmd left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm, found no correctness issues, won't block on this since it is gated by build tag anyways.

Comment thread node/migration.go Resolved
@EgeCaner
EgeCaner force-pushed the feat/felt/compact-cbor-on-typed branch from 6896899 to 82b4a7d Compare November 6, 2026 09:47
@EgeCaner
EgeCaner force-pushed the feat/felt/compact-cbor-on-typed branch 2 times, most recently from 741a497 to b5a080a Compare November 12, 2026 11:44
Comment thread core/deprecatedstate/accessors.go Resolved
Comment thread core/felt/slice.go Resolved
@github-actions

Copy link
Copy Markdown
Contributor

Reviewed PR #4230 (size-based compact CBOR felt codec). Posted 2 inline comments on GitHub:

  1. Important (core/deprecatedstate/accessors.go:39): WriteContractClassHash/GetContractClassHash (and setClassHash in contract.go) were not converted to StoredBytes()/SetMinimalBytes() like every sibling accessor (nonce, storage history, nonce/class-hash history) in the same files. Self-consistent (not a correctness bug) but leaves the still-default DeprecatedContractClassHash bucket uncompacted and unmentioned in the disk-savings table — looks like an oversight.

  2. Nit (core/felt/slice.go:67): Slice CBOR decode's malicious-size guard now divides by minCBORFeltLen=1 (was 5 for legacy-only), allowing ~32x allocation amplification vs ~6.4x before for a corrupted array header. Likely low risk since this path is mostly DB-internal, but flagged for awareness.

Core codec logic (compact/legacy CBOR encode/decode, MinimalBytes/SetMinimalBytes, trie leaf trimming, migration bookkeeping, canonical-form validation, modulus checks, truncation handling, legacy/compact interop, and extensive test/golden vectors) was reviewed in depth and found correct.

@EgeCaner
EgeCaner force-pushed the feat/felt/compact-cbor-on-typed branch from b5a080a to ed493a6 Compare November 13, 2026 09:32
@github-actions

Copy link
Copy Markdown
Contributor

Launched three parallel review forks (core felt codec, migration/db code, tests) for PR 4230; awaiting their completion notifications before compiling the final review.

@EgeCaner
EgeCaner deployed to Development November 13, 2026 09:58 — with GitHub Actions Active
@EgeCaner
EgeCaner force-pushed the feat/felt/compact-cbor-on-typed branch from ed493a6 to 94363b8 Compare November 13, 2026 11:44
@github-actions

Copy link
Copy Markdown
Contributor

Launched four parallel review forks for PR 4230 (NethermindEth/juno, size-based compact CBOR felt codec): (1) core/felt CBOR encode/decode correctness and panic-safety, (2) migration/bucket wiring for the compact-felt switch and legacy-build gating, (3) backward-compatibility of deprecatedstate/state call sites using the new StoredBytes/SetMinimalBytes/FromMinimalBytes APIs against pre-existing DB rows, and (4) test quality/coverage across the new felt codec and migration tests. All four are still running; I scheduled a wakeup to compile their findings into inline PR comments plus a severity-grouped summary once they finish. No results are available yet to report.

@EgeCaner
EgeCaner deployed to Development November 13, 2026 12:17 — with GitHub Actions Active
@EgeCaner
EgeCaner merged commit 0ee4356 into main Nov 13, 2026
32 of 34 checks passed
@EgeCaner
EgeCaner deleted the feat/felt/compact-cbor-on-typed branch November 13, 2026 14:23

This branch was successfully deployed

1 active deployment
Development — 94363b83 Deployed Nov 13, 2026 by EgeCaner via dev_deploy_test / deploy #6524
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants