Skip to content

Commit 90d48e5

Browse files
authored
Merge pull request #3563 from etan-status/lc-branchtypes
Use types for representing LC Merkle branches
2 parents b594347 + 82143e1 commit 90d48e5

File tree

11 files changed

+64
-36
lines changed

11 files changed

+64
-36
lines changed

pysetup/helpers.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,11 @@ def collect_prev_forks(fork: str) -> list[str]:
2222
forks.append(fork)
2323

2424

25-
def is_byte_vector(value: str) -> bool:
26-
return value.startswith(('ByteVector'))
25+
def requires_mypy_type_ignore(value: str) -> bool:
26+
return (
27+
value.startswith(('ByteVector'))
28+
or (value.startswith(('Vector')) and 'floorlog2' in value)
29+
)
2730

2831

2932
def make_function_abstract(protocol_def: ProtocolDefinition, key: str):
@@ -41,7 +44,8 @@ def objects_to_spec(preset_name: str,
4144
new_type_definitions = (
4245
'\n\n'.join(
4346
[
44-
f"class {key}({value}):\n pass\n" if not is_byte_vector(value) else f"class {key}({value}): # type: ignore\n pass\n"
47+
f"class {key}({value}):\n pass\n" if not requires_mypy_type_ignore(value)
48+
else f"class {key}({value}): # type: ignore\n pass\n"
4549
for key, value in spec_object.custom_types.items()
4650
]
4751
)
@@ -108,7 +112,7 @@ def format_constant(name: str, vardef: VariableDefinition) -> str:
108112
if vardef.comment is not None:
109113
out += f' # {vardef.comment}'
110114
return out
111-
115+
112116
# Merge all constant objects
113117
hardcoded_ssz_dep_constants = reduce(lambda obj, builder: {**obj, **builder.hardcoded_ssz_dep_constants()}, builders, {})
114118
hardcoded_custom_type_dep_constants = reduce(lambda obj, builder: {**obj, **builder.hardcoded_custom_type_dep_constants(spec_object)}, builders, {})
@@ -131,12 +135,13 @@ def format_constant(name: str, vardef: VariableDefinition) -> str:
131135
imports,
132136
preparations,
133137
f"fork = \'{fork}\'\n",
138+
# The helper functions that some SSZ containers require. Need to be defined before `custom_type_dep_constants`
139+
CONSTANT_DEP_SUNDRY_CONSTANTS_FUNCTIONS,
134140
# The constants that some SSZ containers require. Need to be defined before `new_type_definitions`
135141
custom_type_dep_constants,
136-
new_type_definitions,
137-
CONSTANT_DEP_SUNDRY_CONSTANTS_FUNCTIONS,
138142
# The constants that some SSZ containers require. Need to be defined before `constants_spec`
139143
ssz_dep_constants,
144+
new_type_definitions,
140145
constant_vars_spec,
141146
preset_vars_spec,
142147
config_spec,

pysetup/spec_builders/capella.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,5 @@ def imports(cls, preset_name: str):
1616
@classmethod
1717
def hardcoded_ssz_dep_constants(cls) -> Dict[str, str]:
1818
return {
19-
'EXECUTION_PAYLOAD_INDEX': 'GeneralizedIndex(25)',
19+
'EXECUTION_PAYLOAD_GINDEX': 'GeneralizedIndex(25)',
2020
}

pysetup/spec_builders/whisk.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,6 @@ def hardcoded_custom_type_dep_constants(cls, spec_object) -> str:
2727
@classmethod
2828
def hardcoded_ssz_dep_constants(cls) -> Dict[str, str]:
2929
constants = {
30-
'EXECUTION_PAYLOAD_INDEX': 'GeneralizedIndex(41)',
30+
'EXECUTION_PAYLOAD_GINDEX': 'GeneralizedIndex(41)',
3131
}
3232
return {**super().hardcoded_ssz_dep_constants(), **constants}

setup.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ def _get_class_info_from_source(source: str) -> Tuple[str, Optional[str]]:
9393
base = class_def.bases[0]
9494
if isinstance(base, ast.Name):
9595
parent_class = base.id
96+
elif isinstance(base, ast.Subscript):
97+
parent_class = base.value.id
9698
else:
9799
# NOTE: SSZ definition derives from earlier phase...
98100
# e.g. `phase0.SignedBeaconBlock`

specs/altair/light-client/full-node.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,8 @@ def create_light_client_bootstrap(state: BeaconState,
7575
return LightClientBootstrap(
7676
header=block_to_light_client_header(block),
7777
current_sync_committee=state.current_sync_committee,
78-
current_sync_committee_branch=compute_merkle_proof(state, CURRENT_SYNC_COMMITTEE_GINDEX),
78+
current_sync_committee_branch=CurrentSyncCommitteeBranch(
79+
compute_merkle_proof(state, CURRENT_SYNC_COMMITTEE_GINDEX)),
7980
)
8081
```
8182

@@ -122,7 +123,8 @@ def create_light_client_update(state: BeaconState,
122123
# `next_sync_committee` is only useful if the message is signed by the current sync committee
123124
if update_attested_period == update_signature_period:
124125
update.next_sync_committee = attested_state.next_sync_committee
125-
update.next_sync_committee_branch = compute_merkle_proof(attested_state, NEXT_SYNC_COMMITTEE_GINDEX)
126+
update.next_sync_committee_branch = NextSyncCommitteeBranch(
127+
compute_merkle_proof(attested_state, NEXT_SYNC_COMMITTEE_GINDEX))
126128

127129
# Indicate finality whenever possible
128130
if finalized_block is not None:
@@ -131,7 +133,8 @@ def create_light_client_update(state: BeaconState,
131133
assert hash_tree_root(update.finalized_header.beacon) == attested_state.finalized_checkpoint.root
132134
else:
133135
assert attested_state.finalized_checkpoint.root == Bytes32()
134-
update.finality_branch = compute_merkle_proof(attested_state, FINALIZED_ROOT_GINDEX)
136+
update.finality_branch = FinalityBranch(
137+
compute_merkle_proof(attested_state, FINALIZED_ROOT_GINDEX))
135138

136139
update.sync_aggregate = block.message.body.sync_aggregate
137140
update.signature_slot = block.message.slot

specs/altair/light-client/sync-protocol.md

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
1010

1111
- [Introduction](#introduction)
12+
- [Custom types](#custom-types)
1213
- [Constants](#constants)
1314
- [Preset](#preset)
1415
- [Misc](#misc)
@@ -56,6 +57,14 @@ Additional documents describe how the light client sync protocol can be used:
5657
- [Light client](./light-client.md)
5758
- [Networking](./p2p-interface.md)
5859

60+
## Custom types
61+
62+
| Name | SSZ equivalent | Description |
63+
| - | - | - |
64+
| `FinalityBranch` | `Vector[Bytes32, floorlog2(FINALIZED_ROOT_GINDEX)]` | Merkle branch of `finalized_checkpoint.root` within `BeaconState` |
65+
| `CurrentSyncCommitteeBranch` | `Vector[Bytes32, floorlog2(CURRENT_SYNC_COMMITTEE_GINDEX)]` | Merkle branch of `current_sync_committee` within `BeaconState` |
66+
| `NextSyncCommitteeBranch` | `Vector[Bytes32, floorlog2(NEXT_SYNC_COMMITTEE_GINDEX)]` | Merkle branch of `next_sync_committee` within `BeaconState` |
67+
5968
## Constants
6069

6170
| Name | Value |
@@ -93,7 +102,7 @@ class LightClientBootstrap(Container):
93102
header: LightClientHeader
94103
# Current sync committee corresponding to `header.beacon.state_root`
95104
current_sync_committee: SyncCommittee
96-
current_sync_committee_branch: Vector[Bytes32, floorlog2(CURRENT_SYNC_COMMITTEE_GINDEX)]
105+
current_sync_committee_branch: CurrentSyncCommitteeBranch
97106
```
98107

99108
### `LightClientUpdate`
@@ -104,10 +113,10 @@ class LightClientUpdate(Container):
104113
attested_header: LightClientHeader
105114
# Next sync committee corresponding to `attested_header.beacon.state_root`
106115
next_sync_committee: SyncCommittee
107-
next_sync_committee_branch: Vector[Bytes32, floorlog2(NEXT_SYNC_COMMITTEE_GINDEX)]
116+
next_sync_committee_branch: NextSyncCommitteeBranch
108117
# Finalized header corresponding to `attested_header.beacon.state_root`
109118
finalized_header: LightClientHeader
110-
finality_branch: Vector[Bytes32, floorlog2(FINALIZED_ROOT_GINDEX)]
119+
finality_branch: FinalityBranch
111120
# Sync committee aggregate signature
112121
sync_aggregate: SyncAggregate
113122
# Slot at which the aggregate signature was created (untrusted)
@@ -122,7 +131,7 @@ class LightClientFinalityUpdate(Container):
122131
attested_header: LightClientHeader
123132
# Finalized header corresponding to `attested_header.beacon.state_root`
124133
finalized_header: LightClientHeader
125-
finality_branch: Vector[Bytes32, floorlog2(FINALIZED_ROOT_GINDEX)]
134+
finality_branch: FinalityBranch
126135
# Sync committee aggregate signature
127136
sync_aggregate: SyncAggregate
128137
# Slot at which the aggregate signature was created (untrusted)
@@ -174,14 +183,14 @@ def is_valid_light_client_header(header: LightClientHeader) -> bool:
174183

175184
```python
176185
def is_sync_committee_update(update: LightClientUpdate) -> bool:
177-
return update.next_sync_committee_branch != [Bytes32() for _ in range(floorlog2(NEXT_SYNC_COMMITTEE_GINDEX))]
186+
return update.next_sync_committee_branch != NextSyncCommitteeBranch()
178187
```
179188

180189
### `is_finality_update`
181190

182191
```python
183192
def is_finality_update(update: LightClientUpdate) -> bool:
184-
return update.finality_branch != [Bytes32() for _ in range(floorlog2(FINALIZED_ROOT_GINDEX))]
193+
return update.finality_branch != FinalityBranch()
185194
```
186195

187196
### `is_better_update`
@@ -493,7 +502,7 @@ def process_light_client_finality_update(store: LightClientStore,
493502
update = LightClientUpdate(
494503
attested_header=finality_update.attested_header,
495504
next_sync_committee=SyncCommittee(),
496-
next_sync_committee_branch=[Bytes32() for _ in range(floorlog2(NEXT_SYNC_COMMITTEE_GINDEX))],
505+
next_sync_committee_branch=NextSyncCommitteeBranch(),
497506
finalized_header=finality_update.finalized_header,
498507
finality_branch=finality_update.finality_branch,
499508
sync_aggregate=finality_update.sync_aggregate,
@@ -512,9 +521,9 @@ def process_light_client_optimistic_update(store: LightClientStore,
512521
update = LightClientUpdate(
513522
attested_header=optimistic_update.attested_header,
514523
next_sync_committee=SyncCommittee(),
515-
next_sync_committee_branch=[Bytes32() for _ in range(floorlog2(NEXT_SYNC_COMMITTEE_GINDEX))],
524+
next_sync_committee_branch=NextSyncCommitteeBranch(),
516525
finalized_header=LightClientHeader(),
517-
finality_branch=[Bytes32() for _ in range(floorlog2(FINALIZED_ROOT_GINDEX))],
526+
finality_branch=FinalityBranch(),
518527
sync_aggregate=optimistic_update.sync_aggregate,
519528
signature_slot=optimistic_update.signature_slot,
520529
)

specs/capella/light-client/full-node.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,15 @@ def block_to_light_client_header(block: SignedBeaconBlock) -> LightClientHeader:
4646
transactions_root=hash_tree_root(payload.transactions),
4747
withdrawals_root=hash_tree_root(payload.withdrawals),
4848
)
49-
execution_branch = compute_merkle_proof(block.message.body, EXECUTION_PAYLOAD_INDEX)
49+
execution_branch = ExecutionBranch(
50+
compute_merkle_proof(block.message.body, EXECUTION_PAYLOAD_GINDEX))
5051
else:
5152
# Note that during fork transitions, `finalized_header` may still point to earlier forks.
5253
# While Bellatrix blocks also contain an `ExecutionPayload` (minus `withdrawals_root`),
5354
# it was not included in the corresponding light client data. To ensure compatibility
5455
# with legacy data going through `upgrade_lc_header_to_capella`, leave out execution data.
5556
execution_header = ExecutionPayloadHeader()
56-
execution_branch = [Bytes32() for _ in range(floorlog2(EXECUTION_PAYLOAD_INDEX))]
57+
execution_branch = ExecutionBranch()
5758

5859
return LightClientHeader(
5960
beacon=BeaconBlockHeader(

specs/capella/light-client/sync-protocol.md

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
1010

1111
- [Introduction](#introduction)
12+
- [Custom types](#custom-types)
1213
- [Constants](#constants)
1314
- [Containers](#containers)
1415
- [Modified `LightClientHeader`](#modified-lightclientheader)
@@ -27,11 +28,17 @@ Additional documents describes the impact of the upgrade on certain roles:
2728
- [Full node](./full-node.md)
2829
- [Networking](./p2p-interface.md)
2930

31+
## Custom types
32+
33+
| Name | SSZ equivalent | Description |
34+
| - | - | - |
35+
| `ExecutionBranch` | `Vector[Bytes32, floorlog2(EXECUTION_PAYLOAD_GINDEX)]` | Merkle branch of `execution_payload` within `BeaconBlockBody` |
36+
3037
## Constants
3138

3239
| Name | Value |
3340
| - | - |
34-
| `EXECUTION_PAYLOAD_INDEX` | `get_generalized_index(BeaconBlockBody, 'execution_payload')` (= 25) |
41+
| `EXECUTION_PAYLOAD_GINDEX` | `get_generalized_index(BeaconBlockBody, 'execution_payload')` (= 25) |
3542

3643
## Containers
3744

@@ -43,7 +50,7 @@ class LightClientHeader(Container):
4350
beacon: BeaconBlockHeader
4451
# Execution payload header corresponding to `beacon.body_root` (from Capella onward)
4552
execution: ExecutionPayloadHeader
46-
execution_branch: Vector[Bytes32, floorlog2(EXECUTION_PAYLOAD_INDEX)]
53+
execution_branch: ExecutionBranch
4754
```
4855

4956
## Helper functions
@@ -69,14 +76,14 @@ def is_valid_light_client_header(header: LightClientHeader) -> bool:
6976
if epoch < CAPELLA_FORK_EPOCH:
7077
return (
7178
header.execution == ExecutionPayloadHeader()
72-
and header.execution_branch == [Bytes32() for _ in range(floorlog2(EXECUTION_PAYLOAD_INDEX))]
79+
and header.execution_branch == ExecutionBranch()
7380
)
7481

7582
return is_valid_merkle_branch(
7683
leaf=get_lc_execution_root(header),
7784
branch=header.execution_branch,
78-
depth=floorlog2(EXECUTION_PAYLOAD_INDEX),
79-
index=get_subtree_index(EXECUTION_PAYLOAD_INDEX),
85+
depth=floorlog2(EXECUTION_PAYLOAD_GINDEX),
86+
index=get_subtree_index(EXECUTION_PAYLOAD_GINDEX),
8087
root=header.beacon.body_root,
8188
)
8289
```

specs/deneb/light-client/full-node.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,15 @@ def block_to_light_client_header(block: SignedBeaconBlock) -> LightClientHeader:
5252
execution_header.blob_gas_used = payload.blob_gas_used
5353
execution_header.excess_blob_gas = payload.excess_blob_gas
5454

55-
execution_branch = compute_merkle_proof(block.message.body, EXECUTION_PAYLOAD_INDEX)
55+
execution_branch = ExecutionBranch(
56+
compute_merkle_proof(block.message.body, EXECUTION_PAYLOAD_GINDEX))
5657
else:
5758
# Note that during fork transitions, `finalized_header` may still point to earlier forks.
5859
# While Bellatrix blocks also contain an `ExecutionPayload` (minus `withdrawals_root`),
5960
# it was not included in the corresponding light client data. To ensure compatibility
6061
# with legacy data going through `upgrade_lc_header_to_capella`, leave out execution data.
6162
execution_header = ExecutionPayloadHeader()
62-
execution_branch = [Bytes32() for _ in range(floorlog2(EXECUTION_PAYLOAD_INDEX))]
63+
execution_branch = ExecutionBranch()
6364

6465
return LightClientHeader(
6566
beacon=BeaconBlockHeader(

specs/deneb/light-client/sync-protocol.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,14 +74,14 @@ def is_valid_light_client_header(header: LightClientHeader) -> bool:
7474
if epoch < CAPELLA_FORK_EPOCH:
7575
return (
7676
header.execution == ExecutionPayloadHeader()
77-
and header.execution_branch == [Bytes32() for _ in range(floorlog2(EXECUTION_PAYLOAD_INDEX))]
77+
and header.execution_branch == ExecutionBranch()
7878
)
7979

8080
return is_valid_merkle_branch(
8181
leaf=get_lc_execution_root(header),
8282
branch=header.execution_branch,
83-
depth=floorlog2(EXECUTION_PAYLOAD_INDEX),
84-
index=get_subtree_index(EXECUTION_PAYLOAD_INDEX),
83+
depth=floorlog2(EXECUTION_PAYLOAD_GINDEX),
84+
index=get_subtree_index(EXECUTION_PAYLOAD_GINDEX),
8585
root=header.beacon.body_root,
8686
)
8787
```

0 commit comments

Comments
 (0)