Commitment v3 replaces each bundled branch row with one record per trie edge; leaves are records too, not fields of the parent row. The design note predates the implementation, and the cited code lines in awskii/commitment-v3 are the specification.
Uses #17838 as a part of scheme
Format
| Item |
Encoding or rule |
| Node key |
Whole nibble pairs are packed two per byte. An even-length path ends with the terminator 0x00; for an odd-length path the trailing nibble is not packed, it rides in the terminator as 0xf0|nibble. Length is floor(n/2)+1; no parity byte or pad nibble. nibbles_v2.go:24, nibbles_v3.go:32-51 |
| Record key |
nodeKey(parent) || (0x80|childNibble), length floor(depth/2)+2. Siblings occupy [nodeKey||0x80, nodeKey||0x90). nibbles_v3.go:85-93, nibbles_v3.go:112-115, nibbles_v3.go:127-135 |
| State key |
0x00, also the empty-path root node key; value shape distinguishes state from records. branch_cache.go:40, branch_cache.go:104-107, commitment_context.go:1328-1330 |
| Flags |
0x01 leaf, 0x02 odd extension, 0x04 storage leaf, 0x08 account has storage, 0x10 hash present, 0x20 hoisted slot. For a branch child, 0x10 is always set and the 32-byte hash is mandatory; for a leaf, it marks an optional 32-byte state hash directly after the flags byte. record.go:31-39 |
| Branch child |
[flags:1][childMask:2 BE][hash:32][extension:tail]; 0x10 is set. record.go:154-168 |
| Storage leaf |
[flags:1][stateHash:32?][storageSlot:32]; no extension. record.go:220-221, record.go:394 |
| Hoisted slot |
[flags:1][stateHash:32?][storageMask:2 BE][accountAddr:20][storageSlot:32][extension:tail]. record.go:222-228 |
| Account with storage |
[flags:1][stateHash:32?][storageRoot:32][storageMask:2 BE][accountAddr:20][extension:tail]. record.go:229-235 |
| Plain account |
[flags:1][stateHash:32?][accountAddr:20][extension:tail?]. record.go:236-240 |
| Extension |
Packed two nibbles per byte; length follows from record length, 0x02 marks odd count, and the pad nibble is zero. record.go:383-433 |
| Masks |
A non-root node’s mask is stored in its incoming edge record. RootMask lives in the state blob. Unknown-mask reads request all 16 children. record.go:126-168, hex_patricia_hashed.go:3120-3123, hex_patricia_hashed.go:3169, commitment_records.go:44-57 |
| Deletion |
Write a zero-length value at the cleared child’s record key; mask-driven reads omit it, while merges drop it. commitment.go:604-626 |
| Version gate |
Edge .kv files use v3.0; bundled rows use v2.2, or v2.1 with references. A file is edge-record format iff version ≥3.0; merges derive output format from inputs. state_schema.go:185-210 |
| Configuration |
COMMITMENT_EDGE_RECORDS is read once, defaults true, and has no CLI or erigondb.toml setting. Existing commitment .kv files persist the datadir choice and override the default; mixed formats are rejected. state_schema.go:217-220, commitment_format_resolve.go:33-71, aggregator.go:396 |
| Compatibility |
Record reads skip files below v3.0. erigon commitment convert changes legacy key encoding and .kv squeeze state only, refuses edge-record files, and does not migrate history or accessors. commitment_records.go:136-139, commitment_convert.go:84, commitment_convert.go:390, commitment_convert.go:404 |
| History |
Each entry holds the previous value of one edge-record key, 34 B per entry in the raw step-44 file on hoodi; v2 holds the previous bundled row. Unchanged values are not written. commitment.go:625, domain_shared.go:1856-1866, temporal_mem_batch.go:151-161 |
Results
Two identical AMD EPYC 4344P hosts with 16 threads and 125 GiB RAM ran hoodi from wiped chaindata with no state snapshot files to block 1M using 3.7.0-dev-d7a9d63f, --chain=hoodi --prune.mode=full --prune.include-commitment-history --exec.serial --experimental.parallel-commitment, and KV_READ_METRICS=true; the v2 arm set COMMITMENT_EDGE_RECORDS=false. Wall clock was 22,389 s for v2 and 20,609 s for v3: v3 was 8.0% faster and led every 100k-block range.
| On disk |
v2 |
v3 |
v3/v2 |
.kv |
9,609,434,637 B |
7,536,176,763 B |
0.78 |
.bt |
52,560,944 B |
150,406,752 B |
2.86 |
.kvei |
36,260,880 B |
116,359,184 B |
3.21 |
.v history |
28,704,912,438 B |
17,586,615,076 B |
0.61 |
.vi |
1,954,684,787 B |
2,548,136,820 B |
1.30 |
.ef |
1,818,839,737 B |
4,480,275,804 B |
2.46 |
.efi |
160,629,568 B |
606,486,055 B |
3.78 |
| All commitment files |
42,337,322,991 B |
33,024,456,454 B |
0.78 |
| chaindata (mdbx file size, never shrinks) |
36,674,994,176 B |
19,864,223,744 B |
0.54 |
The v3 format saves 13.2 GB of data, adds 3.9 GB of index, and saves 9.3 GB net; every per-key index grows 2.5–3.8x. The v2 arm’s unmerged step file inflates its history total. Between-cycle flush falls from 2,748 s to 1,209 s, including MDBX sync from 984 s to 159 s; storage writes fall from 1,133 GB to 502 GB. The v3 format pays 1.93x in commitment compute and 17.6x in warmup file-record reads for +3.5% process CPU.
#23199 reports 110–140 MiB/block of MDBX copy-on-write writes at mainnet tip; v3 cuts mdbx cow page ops 4.0x and storage writes 2.26x on this from-0 hoodi run, while tip behaviour remains unmeasured.
Open
- Whether the per-record cost is acceptable: keys 2.14x at step 44 (3.03x at step 0), history entries 1.30x, every per-key index 2.5-3.8x; the alternative is bundled rows for the trunk and edge records only inside storage subtrees.
- Reproduce the mainnet-tip write profile on v3.
- Measure unknown-mask reads across a deep file stack using the branch counters; the rig binary predates them.
- Add history and accessor migration before changing any published snapshot set.
Commitment v3 replaces each bundled branch row with one record per trie edge; leaves are records too, not fields of the parent row. The design note predates the implementation, and the cited code lines in
awskii/commitment-v3are the specification.Uses #17838 as a part of scheme
Format
0x00; for an odd-length path the trailing nibble is not packed, it rides in the terminator as0xf0|nibble. Length isfloor(n/2)+1; no parity byte or pad nibble.nibbles_v2.go:24,nibbles_v3.go:32-51nodeKey(parent) || (0x80|childNibble), lengthfloor(depth/2)+2. Siblings occupy[nodeKey||0x80, nodeKey||0x90).nibbles_v3.go:85-93,nibbles_v3.go:112-115,nibbles_v3.go:127-1350x00, also the empty-path root node key; value shape distinguishes state from records.branch_cache.go:40,branch_cache.go:104-107,commitment_context.go:1328-13300x01leaf,0x02odd extension,0x04storage leaf,0x08account has storage,0x10hash present,0x20hoisted slot. For a branch child,0x10is always set and the 32-byte hash is mandatory; for a leaf, it marks an optional 32-byte state hash directly after the flags byte.record.go:31-39[flags:1][childMask:2 BE][hash:32][extension:tail];0x10is set.record.go:154-168[flags:1][stateHash:32?][storageSlot:32]; no extension.record.go:220-221,record.go:394[flags:1][stateHash:32?][storageMask:2 BE][accountAddr:20][storageSlot:32][extension:tail].record.go:222-228[flags:1][stateHash:32?][storageRoot:32][storageMask:2 BE][accountAddr:20][extension:tail].record.go:229-235[flags:1][stateHash:32?][accountAddr:20][extension:tail?].record.go:236-2400x02marks odd count, and the pad nibble is zero.record.go:383-433RootMasklives in the state blob. Unknown-mask reads request all 16 children.record.go:126-168,hex_patricia_hashed.go:3120-3123,hex_patricia_hashed.go:3169,commitment_records.go:44-57commitment.go:604-626.kvfiles use v3.0; bundled rows use v2.2, or v2.1 with references. A file is edge-record format iff version ≥3.0; merges derive output format from inputs.state_schema.go:185-210COMMITMENT_EDGE_RECORDSis read once, defaults true, and has no CLI orerigondb.tomlsetting. Existing commitment.kvfiles persist the datadir choice and override the default; mixed formats are rejected.state_schema.go:217-220,commitment_format_resolve.go:33-71,aggregator.go:396erigon commitment convertchanges legacy key encoding and.kvsqueeze state only, refuses edge-record files, and does not migrate history or accessors.commitment_records.go:136-139,commitment_convert.go:84,commitment_convert.go:390,commitment_convert.go:404commitment.go:625,domain_shared.go:1856-1866,temporal_mem_batch.go:151-161Results
Two identical AMD EPYC 4344P hosts with 16 threads and 125 GiB RAM ran hoodi from wiped chaindata with no state snapshot files to block 1M using
3.7.0-dev-d7a9d63f,--chain=hoodi --prune.mode=full --prune.include-commitment-history --exec.serial --experimental.parallel-commitment, andKV_READ_METRICS=true; the v2 arm setCOMMITMENT_EDGE_RECORDS=false. Wall clock was 22,389 s for v2 and 20,609 s for v3: v3 was 8.0% faster and led every 100k-block range..kv.bt.kvei.vhistory.vi.ef.efiThe v3 format saves 13.2 GB of data, adds 3.9 GB of index, and saves 9.3 GB net; every per-key index grows 2.5–3.8x. The v2 arm’s unmerged step file inflates its history total. Between-cycle flush falls from 2,748 s to 1,209 s, including MDBX sync from 984 s to 159 s; storage writes fall from 1,133 GB to 502 GB. The v3 format pays 1.93x in commitment compute and 17.6x in warmup file-record reads for +3.5% process CPU.
#23199 reports 110–140 MiB/block of MDBX copy-on-write writes at mainnet tip; v3 cuts mdbx cow page ops 4.0x and storage writes 2.26x on this from-0 hoodi run, while tip behaviour remains unmeasured.
Open