Skip to content

feat: add progressive types#528

Open
wemeetagain wants to merge 4 commits into
masterfrom
cayman/progressives
Open

feat: add progressive types#528
wemeetagain wants to merge 4 commits into
masterfrom
cayman/progressives

Conversation

@wemeetagain
Copy link
Copy Markdown
Member

Motivation

Description

  • Add ProgressiveList, ProgressiveBitlist, ProgressiveContainer, CompatibleUnion
  • Add spec tests for the same

@wemeetagain wemeetagain requested a review from a team as a code owner May 11, 2026 15:16
@github-actions github-actions Bot added the ssz label May 11, 2026
@nazarhussain nazarhussain self-requested a review May 11, 2026 15:17
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: dca3c9673b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +305 to +306
case "data":
return this.representativeType;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Avoid using representative type for data path typing

Returning this.representativeType for getPropertyType("data") makes nested proof-path resolution incorrect when a CompatibleUnion is embedded in parents that still use getPathInfo traversal (e.g. StableContainerType.tree_createProofGindexes in packages/ssz/src/type/stableContainer.ts:434). In that flow, a path like ["unionField", "data", "newField"] is validated against the representative option instead of the active selector, so proofs for selector-specific fields throw Unknown ... property or resolve the wrong subtree even though CompatibleUnion.tree_createProofGindexes itself supports selector-aware traversal.

Useful? React with 👍 / 👎.

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.

Agree the bug is real, but I'd locate the fix elsewhere.

The PR already updates CompositeType.tree_createProofGindexes to a per-prop recursive dispatch via the new private tree_createProofGindexesForPath (packages/ssz/src/type/composite.ts:290-329), which correctly routes child gindex resolution through childType.tree_createProofGindexes. Because CompatibleUnion overrides that method to dispatch by the selector read off the node (compatibleUnion.ts:251-299), proof generation works correctly when CompatibleUnion is embedded in a default-CompositeType parent (e.g. plain Container, ProgressiveContainer — see the existing valid.test.ts:75-95 cases).

The break is specifically the StableContainerType.tree_createProofGindexes override (stableContainer.ts:434), which goes through this.getPathInfo(jsonPath). getPathInfo walks getPropertyType step-by-step with no node context, so it descends through CompatibleUnion.getPropertyType("data") → representativeType and then keeps resolving against the representative's schema. Concretely:

  • path ["u","data","fieldOnlyInB"] on a union whose active selector is BrepresentativeType.getPropertyGindex(...) throws Unknown ... property.
  • path ["u","data"] → returned type is the representative; tree_getLeafGindices(gindex, getNode(node, gindex)) walks representative's field list against a node whose layout is B, yielding phantom leaves at B's zero positions.

Changing getPropertyType("data") doesn't really help — getPathInfo is fundamentally node-less, so returning anything selector-dependent is impossible. Returning this just punts the failure one level deeper (CompatibleUnion.getPropertyGindex("fieldOnlyInB") still throws). Throwing here breaks legitimate "give me a representative type" callers.

Minimal fix: align StableContainerType.tree_createProofGindexes with the new recursive-dispatch pattern the PR just introduced in the base — keep the inactive-optional-field skip at the top level, then for each path delegate to childType.tree_createProofGindexes(childNode, [remainingPath]) (with concatGindices([childGindex, g])) instead of getPathInfo. That makes selector-aware traversal work without touching CompatibleUnion.

Worth adding a test that wraps CompatibleUnion in a StableContainer and proves both a shared-field path and a selector-specific path round-trip through createProof / createFromProof.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 11, 2026

Performance Report

✔️ no performance regression detected

Full benchmark results
Benchmark suite Current: 5ffc212 Previous: e8c3edb Ratio
digestTwoHashObjects 50023 times 29.016 ms/op 38.529 ms/op 0.75
digest2Bytes32 50023 times 31.682 ms/op 42.641 ms/op 0.74
digest 50023 times 31.334 ms/op 41.682 ms/op 0.75
input length 32 743.00 ns/op 995.00 ns/op 0.75
input length 64 828.00 ns/op 1.0850 us/op 0.76
input length 128 1.3670 us/op 1.8710 us/op 0.73
input length 256 2.0840 us/op 2.6590 us/op 0.78
input length 512 3.3900 us/op 4.4270 us/op 0.77
input length 1024 6.4080 us/op 8.4930 us/op 0.75
digest 1000000 times 467.60 ms/op 660.03 ms/op 0.71
hashObjectToByteArray 50023 times 850.36 us/op 983.03 us/op 0.87
byteArrayToHashObject 50023 times 978.30 us/op 1.1030 ms/op 0.89
digest64 200092 times 125.21 ms/op 168.58 ms/op 0.74
hash 200092 times using batchHash4UintArray64s 133.26 ms/op 173.41 ms/op 0.77
digest64HashObjects 200092 times 115.61 ms/op 154.08 ms/op 0.75
hash 200092 times using batchHash4HashObjectInputs 116.48 ms/op 156.01 ms/op 0.75
getGindicesAtDepth 2.6540 us/op 3.3120 us/op 0.80
iterateAtDepth 4.8300 us/op 6.0910 us/op 0.79
getGindexBits 328.00 ns/op 423.00 ns/op 0.78
gindexIterator 717.00 ns/op 876.00 ns/op 0.82
HashComputationLevel.push then loop 22.656 ms/op 26.270 ms/op 0.86
HashComputation[] push then loop 34.390 ms/op 40.769 ms/op 0.84
hash 2 32 bytes Uint8Array 500000 times - hashtree 114.34 ms/op 147.36 ms/op 0.78
batch hash 16 x 64 Uint8Array 31250 times - hashtree 20.727 ms/op 24.897 ms/op 0.83
hashTwoObjects 500000 times - hashtree 126.71 ms/op 163.16 ms/op 0.78
executeHashComputations - hashtree 7.1609 ms/op 8.5197 ms/op 0.84
hash 2 32 bytes Uint8Array 500000 times - as-sha256 294.74 ms/op 392.17 ms/op 0.75
batch hash 16 x 64 Uint8Array 31250 times - as-sha256 267.72 ms/op 394.52 ms/op 0.68
hashTwoObjects 500000 times - as-sha256 303.93 ms/op 405.87 ms/op 0.75
executeHashComputations - as-sha256 22.189 ms/op 30.946 ms/op 0.72
hash 2 32 bytes Uint8Array 500000 times - noble 734.04 ms/op 918.42 ms/op 0.80
batch hash 16 x 64 Uint8Array 31250 times - noble 717.88 ms/op 893.10 ms/op 0.80
hashTwoObjects 500000 times - noble 1.2188 s/op 1.3864 s/op 0.88
executeHashComputations - noble 21.943 ms/op 28.789 ms/op 0.76
getHashComputations 1.3095 ms/op 1.5376 ms/op 0.85
executeHashComputations 6.5405 ms/op 7.6237 ms/op 0.86
get root 9.6242 ms/op 12.531 ms/op 0.77
getNodeH() x7812.5 avg hindex 10.608 us/op 12.598 us/op 0.84
getNodeH() x7812.5 index 0 3.8560 us/op 4.8150 us/op 0.80
getNodeH() x7812.5 index 7 3.9330 us/op 4.7570 us/op 0.83
getNodeH() x7812.5 index 7 with key array 3.9100 us/op 4.8560 us/op 0.81
new LeafNode() x7812.5 208.63 us/op 262.77 us/op 0.79
getHashComputations 250000 nodes 9.1955 ms/op 10.680 ms/op 0.86
batchHash 250000 nodes 71.241 ms/op 67.281 ms/op 1.06
get root 250000 nodes 70.736 ms/op 94.173 ms/op 0.75
getHashComputations 500000 nodes 22.641 ms/op 18.913 ms/op 1.20
batchHash 500000 nodes 139.46 ms/op 147.65 ms/op 0.94
get root 500000 nodes 142.87 ms/op 185.88 ms/op 0.77
getHashComputations 1000000 nodes 52.075 ms/op 65.078 ms/op 0.80
batchHash 1000000 nodes 255.42 ms/op 261.73 ms/op 0.98
get root 1000000 nodes 284.24 ms/op 374.99 ms/op 0.76
multiproof - depth 15, 1 requested leaves 5.5760 us/op 7.1310 us/op 0.78
tree offset multiproof - depth 15, 1 requested leaves 12.398 us/op 15.962 us/op 0.78
compact multiproof - depth 15, 1 requested leaves 2.6280 us/op 2.6320 us/op 1.00
multiproof - depth 15, 2 requested leaves 7.9300 us/op 10.045 us/op 0.79
tree offset multiproof - depth 15, 2 requested leaves 14.633 us/op 19.044 us/op 0.77
compact multiproof - depth 15, 2 requested leaves 2.7220 us/op 2.6540 us/op 1.03
multiproof - depth 15, 3 requested leaves 11.059 us/op 14.415 us/op 0.77
tree offset multiproof - depth 15, 3 requested leaves 18.715 us/op 24.716 us/op 0.76
compact multiproof - depth 15, 3 requested leaves 2.8000 us/op 2.9930 us/op 0.94
multiproof - depth 15, 4 requested leaves 14.496 us/op 19.150 us/op 0.76
tree offset multiproof - depth 15, 4 requested leaves 23.027 us/op 30.573 us/op 0.75
compact multiproof - depth 15, 4 requested leaves 3.2340 us/op 3.4780 us/op 0.93
packedRootsBytesToLeafNodes bytes 4000 offset 0 4.1350 us/op 4.8950 us/op 0.84
packedRootsBytesToLeafNodes bytes 4000 offset 1 4.1460 us/op 4.8630 us/op 0.85
packedRootsBytesToLeafNodes bytes 4000 offset 2 4.0770 us/op 4.8510 us/op 0.84
packedRootsBytesToLeafNodes bytes 4000 offset 3 4.0920 us/op 4.8650 us/op 0.84
subtreeFillToContents depth 40 count 250000 14.989 ms/op 19.338 ms/op 0.78
setRoot - gindexBitstring 14.113 ms/op 17.861 ms/op 0.79
setRoot - gindex 14.416 ms/op 19.461 ms/op 0.74
getRoot - gindexBitstring 1.5191 ms/op 2.0258 ms/op 0.75
getRoot - gindex 1.8069 ms/op 2.7133 ms/op 0.67
getHashObject then setHashObject 15.218 ms/op 19.843 ms/op 0.77
setNodeWithFn 13.974 ms/op 17.754 ms/op 0.79
getNodeAtDepth depth 0 x100000 183.34 us/op 213.34 us/op 0.86
setNodeAtDepth depth 0 x100000 1.2535 ms/op 1.9396 ms/op 0.65
getNodesAtDepth depth 0 x100000 229.52 us/op 280.87 us/op 0.82
setNodesAtDepth depth 0 x100000 611.18 us/op 727.20 us/op 0.84
getNodeAtDepth depth 1 x100000 246.53 us/op 281.02 us/op 0.88
setNodeAtDepth depth 1 x100000 5.5613 ms/op 6.5966 ms/op 0.84
getNodesAtDepth depth 1 x100000 331.57 us/op 405.67 us/op 0.82
setNodesAtDepth depth 1 x100000 4.7831 ms/op 5.6508 ms/op 0.85
getNodeAtDepth depth 2 x100000 493.17 us/op 717.75 us/op 0.69
setNodeAtDepth depth 2 x100000 10.457 ms/op 13.089 ms/op 0.80
getNodesAtDepth depth 2 x100000 11.837 ms/op 16.906 ms/op 0.70
setNodesAtDepth depth 2 x100000 15.928 ms/op 19.786 ms/op 0.81
tree.getNodesAtDepth - gindexes 10.281 ms/op 8.4717 ms/op 1.21
tree.getNodesAtDepth - push all nodes 1.1489 ms/op 1.2990 ms/op 0.88
tree.getNodesAtDepth - navigation 136.74 us/op 156.99 us/op 0.87
tree.setNodesAtDepth - indexes 470.24 us/op 636.27 us/op 0.74
set at depth 8 539.00 ns/op 732.00 ns/op 0.74
set at depth 16 814.00 ns/op 1.0440 us/op 0.78
set at depth 32 1.5820 us/op 1.7180 us/op 0.92
iterateNodesAtDepth 8 256 10.187 us/op 12.485 us/op 0.82
getNodesAtDepth 8 256 2.5360 us/op 3.3340 us/op 0.76
iterateNodesAtDepth 16 65536 3.1847 ms/op 3.8844 ms/op 0.82
getNodesAtDepth 16 65536 830.01 us/op 1.0060 ms/op 0.83
iterateNodesAtDepth 32 250000 11.545 ms/op 14.608 ms/op 0.79
getNodesAtDepth 32 250000 3.1004 ms/op 3.7368 ms/op 0.83
iterateNodesAtDepth 40 250000 11.663 ms/op 14.498 ms/op 0.80
getNodesAtDepth 40 250000 3.1158 ms/op 3.7541 ms/op 0.83
250000 validators root getter 75.645 ms/op 97.114 ms/op 0.78
250000 validators batchHash() 61.779 ms/op 60.497 ms/op 1.02
250000 validators hashComputations 11.521 ms/op 12.746 ms/op 0.90
bitlist bytes to struct (120,90) 309.00 ns/op 387.00 ns/op 0.80
bitlist bytes to tree (120,90) 1.7560 us/op 1.8420 us/op 0.95
bitlist bytes to struct (2048,2048) 904.00 ns/op 986.00 ns/op 0.92
bitlist bytes to tree (2048,2048) 3.2790 us/op 3.7350 us/op 0.88
ByteListType - deserialize 7.9633 ms/op 8.7482 ms/op 0.91
BasicListType - deserialize 5.6657 ms/op 6.8250 ms/op 0.83
ByteListType - serialize 8.7202 ms/op 8.3282 ms/op 1.05
BasicListType - serialize 10.866 ms/op 12.848 ms/op 0.85
BasicListType - tree_convertToStruct 17.539 ms/op 17.733 ms/op 0.99
List[uint8, 68719476736] len 300000 ViewDU.getAll() + iterate 3.4324 ms/op 4.0543 ms/op 0.85
List[uint8, 68719476736] len 300000 ViewDU.get(i) 3.2248 ms/op 3.6718 ms/op 0.88
Array.push len 300000 empty Array - number 4.3620 ms/op 4.5717 ms/op 0.95
Array.set len 300000 from new Array - number 1.0374 ms/op 1.0901 ms/op 0.95
Array.set len 300000 - number 4.4259 ms/op 4.6656 ms/op 0.95
Uint8Array.set len 300000 207.12 us/op 287.39 us/op 0.72
Uint32Array.set len 300000 298.95 us/op 412.54 us/op 0.72
Container({a: uint8, b: uint8}) getViewDU x300000 28.022 ms/op 30.357 ms/op 0.92
ContainerNodeStruct({a: uint8, b: uint8}) getViewDU x300000 7.6653 ms/op 9.0934 ms/op 0.84
List(Container) len 300000 ViewDU.getAllReadonly() + iterate 97.729 ms/op 109.42 ms/op 0.89
List(Container) len 300000 ViewDU.getAllReadonlyValues() + iterate 204.34 ms/op 209.67 ms/op 0.97
List(Container) len 300000 ViewDU.get(i) 7.8823 ms/op 7.4059 ms/op 1.06
List(Container) len 300000 ViewDU.getReadonly(i) 8.5948 ms/op 9.9429 ms/op 0.86
List(ContainerNodeStruct) len 300000 ViewDU.getAllReadonly() + iterate 13.185 ms/op 14.509 ms/op 0.91
List(ContainerNodeStruct) len 300000 ViewDU.getAllReadonlyValues() + iterate 4.4796 ms/op 4.7374 ms/op 0.95
List(ContainerNodeStruct) len 300000 ViewDU.get(i) 8.3995 ms/op 8.2033 ms/op 1.02
List(ContainerNodeStruct) len 300000 ViewDU.getReadonly(i) 7.0383 ms/op 8.1593 ms/op 0.86
Array.push len 300000 empty Array - object 4.4193 ms/op 4.7624 ms/op 0.93
Array.set len 300000 from new Array - object 1.1151 ms/op 1.2068 ms/op 0.92
Array.set len 300000 - object 4.5016 ms/op 5.1690 ms/op 0.87
cachePermanentRootStruct no cache 2.2970 us/op 2.7360 us/op 0.84
cachePermanentRootStruct with cache 146.00 ns/op 176.00 ns/op 0.83
epochParticipation len 250000 rws 7813 1.7875 ms/op 2.2741 ms/op 0.79
Deneb BeaconBlock.hashTreeRoot(), numTransaction=200 3.3021 ms/op 4.1675 ms/op 0.79
BeaconState ViewDU batchHashTreeRoot vc=200000 mod=100000 176.46 ms/op 198.04 ms/op 0.89
BeaconState ViewDU batchHashTreeRoot - commit step vc=200000 mod=100000 147.72 ms/op 159.25 ms/op 0.93
BeaconState ViewDU batchHashTreeRoot - hash step vc=200000 mod=100000 28.347 ms/op 31.404 ms/op 0.90
BeaconState ViewDU hashTreeRoot() vc=200000 mod=100000 538.56 ms/op 644.07 ms/op 0.84
BeaconState ViewDU hashTreeRoot - commit step vc=200000 mod=100000 40.434 ms/op 55.375 ms/op 0.73
BeaconState ViewDU hashTreeRoot - validator tree creation vc=100000 mod=100000 197.39 ms/op 239.86 ms/op 0.82
deserialize Attestation - tree 2.6240 us/op 3.1460 us/op 0.83
deserialize Attestation - struct 1.4460 us/op 1.6900 us/op 0.86
deserialize Attestation - struct (reuse bytes) 1.0270 us/op 1.2820 us/op 0.80
deserialize SignedAggregateAndProof - tree 3.6120 us/op 4.2830 us/op 0.84
deserialize SignedAggregateAndProof - struct 2.5700 us/op 2.7560 us/op 0.93
deserialize SignedAggregateAndProof - struct (reuse bytes) 1.3960 us/op 1.6880 us/op 0.83
deserialize SyncCommitteeMessage - tree 1.0290 us/op 1.2480 us/op 0.82
deserialize SyncCommitteeMessage - struct 893.00 ns/op 1.0060 us/op 0.89
deserialize SyncCommitteeMessage - struct (reuse bytes) 488.00 ns/op 660.00 ns/op 0.74
deserialize SignedContributionAndProof - tree 2.1550 us/op 2.6080 us/op 0.83
deserialize SignedContributionAndProof - struct 2.1600 us/op 2.1910 us/op 0.99
deserialize SignedContributionAndProof - struct (reuse bytes) 878.00 ns/op 1.0720 us/op 0.82
deserialize SignedBeaconBlock - tree 200.48 us/op 246.85 us/op 0.81
deserialize SignedBeaconBlock - struct 94.595 us/op 109.11 us/op 0.87
deserialize SignedBeaconBlock - struct (reuse bytes) 53.579 us/op 67.760 us/op 0.79
BeaconState vc 300000 - deserialize tree 388.66 ms/op 442.52 ms/op 0.88
BeaconState vc 300000 - serialize tree 74.120 ms/op 93.807 ms/op 0.79
BeaconState.historicalRoots vc 300000 - deserialize tree 700.00 ns/op 769.00 ns/op 0.91
BeaconState.historicalRoots vc 300000 - serialize tree 711.00 ns/op 640.00 ns/op 1.11
BeaconState.validators vc 300000 - deserialize tree 335.69 ms/op 377.50 ms/op 0.89
BeaconState.validators vc 300000 - serialize tree 39.145 ms/op 44.552 ms/op 0.88
BeaconState.balances vc 300000 - deserialize tree 8.4279 ms/op 9.1885 ms/op 0.92
BeaconState.balances vc 300000 - serialize tree 6.9783 ms/op 10.730 ms/op 0.65
BeaconState.previousEpochParticipation vc 300000 - deserialize tree 627.23 us/op 728.20 us/op 0.86
BeaconState.previousEpochParticipation vc 300000 - serialize tree 577.89 us/op 1.0110 ms/op 0.57
BeaconState.currentEpochParticipation vc 300000 - deserialize tree 624.72 us/op 729.64 us/op 0.86
BeaconState.currentEpochParticipation vc 300000 - serialize tree 616.33 us/op 962.99 us/op 0.64
BeaconState.inactivityScores vc 300000 - deserialize tree 8.1225 ms/op 9.2125 ms/op 0.88
BeaconState.inactivityScores vc 300000 - serialize tree 5.7414 ms/op 8.2336 ms/op 0.70
hashTreeRoot Attestation - struct 6.6630 us/op 8.0880 us/op 0.82
hashTreeRoot Attestation - tree 5.5930 us/op 7.1160 us/op 0.79
hashTreeRoot SignedAggregateAndProof - struct 8.9100 us/op 11.564 us/op 0.77
hashTreeRoot SignedAggregateAndProof - tree 8.4440 us/op 10.745 us/op 0.79
hashTreeRoot SyncCommitteeMessage - struct 2.2480 us/op 2.7820 us/op 0.81
hashTreeRoot SyncCommitteeMessage - tree 2.1570 us/op 2.6380 us/op 0.82
hashTreeRoot SignedContributionAndProof - struct 5.5620 us/op 7.0970 us/op 0.78
hashTreeRoot SignedContributionAndProof - tree 5.7880 us/op 7.3720 us/op 0.79
hashTreeRoot SignedBeaconBlock - struct 526.81 us/op 691.32 us/op 0.76
hashTreeRoot SignedBeaconBlock - tree 493.76 us/op 643.37 us/op 0.77
hashTreeRoot Validator - struct 2.8560 us/op 3.6490 us/op 0.78
hashTreeRoot Validator - tree 4.3550 us/op 5.4730 us/op 0.80
BeaconState vc 300000 - hashTreeRoot tree 1.5003 s/op 1.7443 s/op 0.86
BeaconState vc 300000 - batchHashTreeRoot tree 3.0261 s/op 3.3927 s/op 0.89
BeaconState.historicalRoots vc 300000 - hashTreeRoot tree 687.00 ns/op 846.00 ns/op 0.81
BeaconState.validators vc 300000 - hashTreeRoot tree 1.4433 s/op 1.7139 s/op 0.84
BeaconState.balances vc 300000 - hashTreeRoot tree 20.689 ms/op 27.048 ms/op 0.76
BeaconState.previousEpochParticipation vc 300000 - hashTreeRoot tree 2.5581 ms/op 3.3816 ms/op 0.76
BeaconState.currentEpochParticipation vc 300000 - hashTreeRoot tree 2.5992 ms/op 3.3740 ms/op 0.77
BeaconState.inactivityScores vc 300000 - hashTreeRoot tree 20.564 ms/op 27.079 ms/op 0.76
hash64 x18 5.1750 us/op 6.5370 us/op 0.79
hashTwoObjects x18 5.0160 us/op 6.2400 us/op 0.80
hash64 x1740 459.86 us/op 581.73 us/op 0.79
hashTwoObjects x1740 453.68 us/op 573.51 us/op 0.79
hash64 x2700000 706.55 ms/op 903.76 ms/op 0.78
hashTwoObjects x2700000 710.56 ms/op 890.24 ms/op 0.80
get_exitEpoch - ContainerType 153.00 ns/op 186.00 ns/op 0.82
get_exitEpoch - ContainerNodeStructType 145.00 ns/op 202.00 ns/op 0.72
set_exitEpoch - ContainerType 193.00 ns/op 225.00 ns/op 0.86
set_exitEpoch - ContainerNodeStructType 163.00 ns/op 210.00 ns/op 0.78
get_pubkey - ContainerType 786.00 ns/op 827.00 ns/op 0.95
get_pubkey - ContainerNodeStructType 135.00 ns/op 189.00 ns/op 0.71
hashTreeRoot - ContainerType 292.00 ns/op 330.00 ns/op 0.88
hashTreeRoot - ContainerNodeStructType 245.00 ns/op 321.00 ns/op 0.76
createProof - ContainerType 2.7060 us/op 3.4730 us/op 0.78
createProof - ContainerNodeStructType 13.979 us/op 17.763 us/op 0.79
serialize - ContainerType 1.3090 us/op 1.7290 us/op 0.76
serialize - ContainerNodeStructType 1.0580 us/op 1.0720 us/op 0.99
set_exitEpoch_and_hashTreeRoot - ContainerType 1.9610 us/op 2.3480 us/op 0.84
set_exitEpoch_and_hashTreeRoot - ContainerNodeStructType 5.1450 us/op 5.9640 us/op 0.86
doBatchHashTreeRootValidators 2.8280 us/op 3.4280 us/op 0.82
ContainerNodeStructViewDU hashTreeRoot 17.140 us/op 20.718 us/op 0.83
Array - for of 5.0460 us/op 4.8380 us/op 1.04
Array - for(;;) 4.4240 us/op 4.0590 us/op 1.09
basicListValue.readonlyValuesArray() 2.9509 ms/op 3.6071 ms/op 0.82
basicListValue.readonlyValuesArray() + loop all 3.0360 ms/op 3.5047 ms/op 0.87
compositeListValue.readonlyValuesArray() 10.477 ms/op 14.224 ms/op 0.74
compositeListValue.readonlyValuesArray() + loop all 10.415 ms/op 13.181 ms/op 0.79
Number64UintType - get balances list 2.3620 ms/op 2.7574 ms/op 0.86
Number64UintType - set balances list 7.3607 ms/op 10.083 ms/op 0.73
Number64UintType - get and increase 10 then set 26.506 ms/op 30.891 ms/op 0.86
Number64UintType - increase 10 using applyDelta 11.864 ms/op 14.802 ms/op 0.80
Number64UintType - increase 10 using applyDeltaInBatch 11.752 ms/op 14.755 ms/op 0.80
tree_newTreeFromUint64Deltas 11.121 ms/op 14.228 ms/op 0.78
unsafeUint8ArrayToTree 16.356 ms/op 21.801 ms/op 0.75
bitLength(50) 187.00 ns/op 217.00 ns/op 0.86
bitLengthStr(50) 183.00 ns/op 212.00 ns/op 0.86
bitLength(8000) 178.00 ns/op 218.00 ns/op 0.82
bitLengthStr(8000) 201.00 ns/op 255.00 ns/op 0.79
bitLength(250000) 191.00 ns/op 212.00 ns/op 0.90
bitLengthStr(250000) 239.00 ns/op 289.00 ns/op 0.83
merkleize 32 chunks 8.8110 us/op 11.484 us/op 0.77
merkleizeBlocksBytes 32 chunks 2.2350 us/op 2.7900 us/op 0.80
merkleizeBlockArray 32 chunks 4.0980 us/op 5.0440 us/op 0.81
merkleize 128 chunks 34.436 us/op 45.338 us/op 0.76
merkleizeBlocksBytes 128 chunks 5.8080 us/op 6.7870 us/op 0.86
merkleizeBlockArray 128 chunks 11.841 us/op 14.740 us/op 0.80
merkleize 512 chunks 138.17 us/op 183.03 us/op 0.75
merkleizeBlocksBytes 512 chunks 18.301 us/op 21.675 us/op 0.84
merkleizeBlockArray 512 chunks 40.513 us/op 50.865 us/op 0.80
merkleize 1024 chunks 273.14 us/op 364.96 us/op 0.75
merkleizeBlocksBytes 1024 chunks 34.974 us/op 41.191 us/op 0.85
merkleizeBlockArray 1024 chunks 78.613 us/op 98.269 us/op 0.80
floor - Math.floor (53) 0.30728 ns/op 0.35073 ns/op 0.88
floor - << 0 (53) 0.30724 ns/op 0.35047 ns/op 0.88
floor - Math.floor (512) 0.30729 ns/op 0.35039 ns/op 0.88
floor - << 0 (512) 0.30807 ns/op 0.35035 ns/op 0.88
fnIf(0) 1.2460 ns/op 1.4288 ns/op 0.87
fnSwitch(0) 1.9858 ns/op 2.4933 ns/op 0.80
fnObj(0) 0.30722 ns/op 0.35055 ns/op 0.88
fnArr(0) 0.30742 ns/op 0.35049 ns/op 0.88
fnIf(4) 1.9102 ns/op 2.1796 ns/op 0.88
fnSwitch(4) 1.9055 ns/op 2.1849 ns/op 0.87
fnObj(4) 0.30775 ns/op 0.35043 ns/op 0.88
fnArr(4) 0.30770 ns/op 0.35064 ns/op 0.88
fnIf(9) 2.5927 ns/op 3.1136 ns/op 0.83
fnSwitch(9) 1.9055 ns/op 2.1785 ns/op 0.87
fnObj(9) 0.30736 ns/op 0.35056 ns/op 0.88
fnArr(9) 0.30723 ns/op 0.35052 ns/op 0.88
Container {a,b,vec} - as struct x100000 30.840 us/op 35.225 us/op 0.88
Container {a,b,vec} - as tree x100000 493.10 us/op 654.07 us/op 0.75
Container {a,vec,b} - as struct x100000 68.493 us/op 78.050 us/op 0.88
Container {a,vec,b} - as tree x100000 765.13 us/op 998.46 us/op 0.77
get 2 props x1000000 - rawObject 272.97 us/op 311.37 us/op 0.88
get 2 props x1000000 - proxy 63.472 ms/op 77.704 ms/op 0.82
get 2 props x1000000 - customObj 273.10 us/op 315.83 us/op 0.86
Simple object binary -> struct 361.00 ns/op 465.00 ns/op 0.78
Simple object binary -> tree_backed 850.00 ns/op 1.0450 us/op 0.81
Simple object struct -> tree_backed 1.3760 us/op 1.5400 us/op 0.89
Simple object tree_backed -> struct 1.1240 us/op 1.3190 us/op 0.85
Simple object struct -> binary 723.00 ns/op 836.00 ns/op 0.86
Simple object tree_backed -> binary 955.00 ns/op 1.1090 us/op 0.86
aggregationBits binary -> struct 325.00 ns/op 408.00 ns/op 0.80
aggregationBits binary -> tree_backed 1.6520 us/op 1.8510 us/op 0.89
aggregationBits struct -> tree_backed 2.0760 us/op 2.1950 us/op 0.95
aggregationBits tree_backed -> struct 797.00 ns/op 888.00 ns/op 0.90
aggregationBits struct -> binary 604.00 ns/op 656.00 ns/op 0.92
aggregationBits tree_backed -> binary 713.00 ns/op 763.00 ns/op 0.93
List(uint8) 100000 binary -> struct 680.07 us/op 770.19 us/op 0.88
List(uint8) 100000 binary -> tree_backed 209.67 us/op 238.29 us/op 0.88
List(uint8) 100000 struct -> tree_backed 1.2268 ms/op 1.5589 ms/op 0.79
List(uint8) 100000 tree_backed -> struct 771.19 us/op 887.42 us/op 0.87
List(uint8) 100000 struct -> binary 1.2394 ms/op 1.3549 ms/op 0.91
List(uint8) 100000 tree_backed -> binary 180.51 us/op 242.59 us/op 0.74
List(uint64Number) 100000 binary -> struct 911.71 us/op 1.1158 ms/op 0.82
List(uint64Number) 100000 binary -> tree_backed 2.0614 ms/op 2.4100 ms/op 0.86
List(uint64Number) 100000 struct -> tree_backed 3.3403 ms/op 3.9271 ms/op 0.85
List(uint64Number) 100000 tree_backed -> struct 1.7508 ms/op 2.1243 ms/op 0.82
List(uint64Number) 100000 struct -> binary 2.2492 ms/op 2.8184 ms/op 0.80
List(uint64Number) 100000 tree_backed -> binary 771.30 us/op 986.99 us/op 0.78
List(Uint64Bigint) 100000 binary -> struct 1.7966 ms/op 2.0762 ms/op 0.87
List(Uint64Bigint) 100000 binary -> tree_backed 1.9032 ms/op 2.2223 ms/op 0.86
List(Uint64Bigint) 100000 struct -> tree_backed 3.0723 ms/op 3.7158 ms/op 0.83
List(Uint64Bigint) 100000 tree_backed -> struct 2.3774 ms/op 2.9216 ms/op 0.81
List(Uint64Bigint) 100000 struct -> binary 2.0034 ms/op 2.5374 ms/op 0.79
List(Uint64Bigint) 100000 tree_backed -> binary 719.63 us/op 971.64 us/op 0.74
Vector(Root) 100000 binary -> struct 11.562 ms/op 11.623 ms/op 0.99
Vector(Root) 100000 binary -> tree_backed 14.042 ms/op 17.225 ms/op 0.82
Vector(Root) 100000 struct -> tree_backed 15.652 ms/op 19.923 ms/op 0.79
Vector(Root) 100000 tree_backed -> struct 14.128 ms/op 16.621 ms/op 0.85
Vector(Root) 100000 struct -> binary 3.5973 ms/op 4.7069 ms/op 0.76
Vector(Root) 100000 tree_backed -> binary 4.4077 ms/op 5.5674 ms/op 0.79
List(Validator) 100000 binary -> struct 53.463 ms/op 66.348 ms/op 0.81
List(Validator) 100000 binary -> tree_backed 189.22 ms/op 225.24 ms/op 0.84
List(Validator) 100000 struct -> tree_backed 216.55 ms/op 262.07 ms/op 0.83
List(Validator) 100000 tree_backed -> struct 146.99 ms/op 168.86 ms/op 0.87
List(Validator) 100000 struct -> binary 33.084 ms/op 45.128 ms/op 0.73
List(Validator) 100000 tree_backed -> binary 79.250 ms/op 83.170 ms/op 0.95
List(Validator-NS) 100000 binary -> struct 53.564 ms/op 65.893 ms/op 0.81
List(Validator-NS) 100000 binary -> tree_backed 95.429 ms/op 108.03 ms/op 0.88
List(Validator-NS) 100000 struct -> tree_backed 107.78 ms/op 127.95 ms/op 0.84
List(Validator-NS) 100000 tree_backed -> struct 83.975 ms/op 100.75 ms/op 0.83
List(Validator-NS) 100000 struct -> binary 8.6994 ms/op 11.226 ms/op 0.77
List(Validator-NS) 100000 tree_backed -> binary 11.068 ms/op 12.611 ms/op 0.88
get epochStatuses - MutableVector 90.700 us/op 116.63 us/op 0.78
get epochStatuses - ViewDU 138.01 us/op 175.85 us/op 0.78
set epochStatuses - ListTreeView 1.4760 ms/op 2.0246 ms/op 0.73
set epochStatuses - ListTreeView - set() 368.48 us/op 496.83 us/op 0.74
set epochStatuses - ListTreeView - commit() 613.34 us/op 737.71 us/op 0.83
bitstring 379.02 ns/op 461.32 ns/op 0.82
bit mask 6.3794 ns/op 7.2050 ns/op 0.89
struct - increase slot to 1000000 307.30 us/op 350.51 us/op 0.88
UintNumberType - increase slot to 1000000 19.658 ms/op 29.405 ms/op 0.67
UintBigintType - increase slot to 1000000 124.80 ms/op 169.58 ms/op 0.74
UintBigint8 x 100000 tree_deserialize 4.1942 ms/op 5.1096 ms/op 0.82
UintBigint8 x 100000 tree_serialize 1.0242 ms/op 1.2812 ms/op 0.80
UintBigint16 x 100000 tree_deserialize 4.2194 ms/op 5.1370 ms/op 0.82
UintBigint16 x 100000 tree_serialize 1.0499 ms/op 1.3241 ms/op 0.79
UintBigint32 x 100000 tree_deserialize 3.9502 ms/op 5.0214 ms/op 0.79
UintBigint32 x 100000 tree_serialize 1.3798 ms/op 1.6471 ms/op 0.84
UintBigint64 x 100000 tree_deserialize 4.3478 ms/op 5.4962 ms/op 0.79
UintBigint64 x 100000 tree_serialize 1.3465 ms/op 1.6649 ms/op 0.81
UintBigint8 x 100000 value_deserialize 329.23 us/op 407.21 us/op 0.81
UintBigint8 x 100000 value_serialize 421.09 us/op 395.25 us/op 1.07
UintBigint16 x 100000 value_deserialize 356.82 us/op 467.12 us/op 0.76
UintBigint16 x 100000 value_serialize 443.67 us/op 382.93 us/op 1.16
UintBigint32 x 100000 value_deserialize 446.32 us/op 468.45 us/op 0.95
UintBigint32 x 100000 value_serialize 478.80 us/op 431.96 us/op 1.11
UintBigint64 x 100000 value_deserialize 409.67 us/op 531.64 us/op 0.77
UintBigint64 x 100000 value_serialize 561.05 us/op 563.20 us/op 1.00
UintBigint8 x 100000 deserialize 2.0694 ms/op 2.1399 ms/op 0.97
UintBigint8 x 100000 serialize 1.3763 ms/op 1.5607 ms/op 0.88
UintBigint16 x 100000 deserialize 2.2224 ms/op 2.2777 ms/op 0.98
UintBigint16 x 100000 serialize 1.4822 ms/op 1.9474 ms/op 0.76
UintBigint32 x 100000 deserialize 2.1676 ms/op 2.2401 ms/op 0.97
UintBigint32 x 100000 serialize 2.1969 ms/op 2.7520 ms/op 0.80
UintBigint64 x 100000 deserialize 2.0639 ms/op 2.2903 ms/op 0.90
UintBigint64 x 100000 serialize 1.0284 ms/op 1.2355 ms/op 0.83
UintBigint128 x 100000 deserialize 2.2076 ms/op 2.4949 ms/op 0.88
UintBigint128 x 100000 serialize 9.3492 ms/op 12.076 ms/op 0.77
UintBigint256 x 100000 deserialize 2.9238 ms/op 3.4073 ms/op 0.86
UintBigint256 x 100000 serialize 29.963 ms/op 38.109 ms/op 0.79
Slice from Uint8Array x25000 778.35 us/op 964.56 us/op 0.81
Slice from ArrayBuffer x25000 12.246 ms/op 16.167 ms/op 0.76
Slice from ArrayBuffer x25000 + new Uint8Array 14.260 ms/op 20.185 ms/op 0.71
Copy Uint8Array 100000 iterate 824.32 us/op 1.0655 ms/op 0.77
Copy Uint8Array 100000 slice 125.31 us/op 163.76 us/op 0.77
Copy Uint8Array 100000 Uint8Array.prototype.slice.call 95.162 us/op 126.48 us/op 0.75
Copy Buffer 100000 Uint8Array.prototype.slice.call 134.28 us/op 136.49 us/op 0.98
Copy Uint8Array 100000 slice + set 241.20 us/op 276.71 us/op 0.87
Copy Uint8Array 100000 subarray + set 112.99 us/op 143.34 us/op 0.79
Copy Uint8Array 100000 slice arrayBuffer 107.87 us/op 156.33 us/op 0.69
Uint64 deserialize 100000 - iterate Uint8Array 1.4724 ms/op 1.4619 ms/op 1.01
Uint64 deserialize 100000 - by Uint32A 1.4951 ms/op 1.4215 ms/op 1.05
Uint64 deserialize 100000 - by DataView.getUint32 x2 1.4906 ms/op 1.5211 ms/op 0.98
Uint64 deserialize 100000 - by DataView.getBigUint64 3.3078 ms/op 3.9088 ms/op 0.85
Uint64 deserialize 100000 - by byte 12.092 ms/op 15.676 ms/op 0.77

by benchmarkbot/action

@wemeetagain
Copy link
Copy Markdown
Member Author

@lodekeeper please review

Copy link
Copy Markdown
Contributor

@lodekeeper lodekeeper left a comment

Choose a reason for hiding this comment

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

Thanks @wemeetagain. Read through the diff at d3bcb2f. Overall the new types look solid and the spec-test wiring is in good shape. One real blocker before merge, plus a couple of smaller things.

Blocker

CompatibleUnion embedded in StableContainer produces wrong proofs. Detailed in the inline reply to the Codex P1 (#528 (comment)) — TL;DR the PR correctly refactored the base CompositeType.tree_createProofGindexes to a per-prop recursive dispatch, so CompatibleUnion's selector-aware override is reached when the parent is a default-CompositeType (e.g. Container, ProgressiveContainer). But StableContainerType.tree_createProofGindexes (stableContainer.ts:434) still drives off this.getPathInfo(jsonPath), which is node-less and walks through CompatibleUnion.getPropertyType("data") → representativeType. That mis-resolves selector-specific paths (throws on fields not in the representative; silently returns phantom leaves for paths that fall back to tree_getLeafGindices on the representative type).

Suggested minimal fix: align StableContainerType.tree_createProofGindexes with the new recursive-dispatch pattern (keep the inactive-optional skip at the top level, then delegate to childType.tree_createProofGindexes(childNode, [remainingPath]) and concatGindices([childGindex, g])).

Plus a test like the existing valid.test.ts "creates nested selected data proofs using the active selector type" case but with a StableContainer wrapper — would have caught this. Worth covering both:

  • a shared/common field path (["u", "data", "common"])
  • a selector-specific field path against the non-representative selector (["u", "data", "fieldOnlyInB"])

Smaller observations

  1. ProgressiveListBasicTreeViewDU.push (progressiveList.ts:634-638) and ProgressiveListCompositeTreeViewDU.push (progressiveList.ts:765-769) do a full tree_toValuevalue_toTree round-trip on every push. That's O(N) per push and O(N²) for N pushes. ListBasicTreeViewDU/ListCompositeTreeViewDU have incremental push paths; worth at least a TODO since these are advertised on the public API. Not a correctness issue.

  2. CompatibleUnion reuses getLengthFromRootNode / addLengthNode to stash the selector in the right-leaf "length" slot (compatibleUnion.ts:184, 189, 199, 201, 253, 323, 404, 432). Clever, but the naming will confuse readers a year from now — a one-line comment in the class header explaining "the right leaf encodes the selector via the existing length-node helpers" would help.

  3. bitList.ts — the new end <= start guard in deserializeUint8ArrayBitListFromBytes is a nice defensive addition. Worth a test that exercises it (e.g. BitListType.deserialize(new Uint8Array(0))) — without it, that input falls through to data[end-1] returning undefined and produces a confusing error.

  4. Spec-test versioning bumps to v1.7.0-alpha.5 and the new compatible_unions / progressive_containers cases in generic/types.ts look correct.

Disclosure

🤖 Reviewed with AI assistance (Codex-CLI cross-check).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants