ethspecify 0.2.9__tar.gz → 0.3.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of ethspecify might be problematic. Click here for more details.

@@ -0,0 +1,237 @@
1
+ Metadata-Version: 2.4
2
+ Name: ethspecify
3
+ Version: 0.3.1
4
+ Summary: A utility for processing Ethereum specification tags.
5
+ Home-page: https://github.com/jtraglia/ethspecify
6
+ Author: Justin Traglia
7
+ Author-email: jtraglia@pm.me
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Requires-Python: >=3.6
12
+ Description-Content-Type: text/markdown
13
+ License-File: LICENSE
14
+ Requires-Dist: requests==2.32.3
15
+ Requires-Dist: PyYAML>=6.0
16
+ Dynamic: author
17
+ Dynamic: author-email
18
+ Dynamic: classifier
19
+ Dynamic: description
20
+ Dynamic: description-content-type
21
+ Dynamic: home-page
22
+ Dynamic: license-file
23
+ Dynamic: requires-dist
24
+ Dynamic: requires-python
25
+ Dynamic: summary
26
+
27
+ # ethspecify
28
+
29
+ A tool for referencing the Ethereum specifications in clients.
30
+
31
+ The idea is that ethspecify will help developers keep track of when the specification changes. It
32
+ will also help auditors verify that the client implementations match the specifications. Ideally,
33
+ this is configured as a CI check which notifies client developers when the specification changes.
34
+ When that happens, they can update the implementations appropriately.
35
+
36
+ ## Getting started
37
+
38
+ ### Installation
39
+
40
+ ```
41
+ pipx install ethspecify
42
+ ```
43
+
44
+ ### Initialize specrefs
45
+
46
+ From the root of the client source directory, initialize a directory for specrefs:
47
+
48
+ ```
49
+ $ ethspecify init v1.6.0-beta.0
50
+ Initializing specrefs directory: v1.6.0-beta.0
51
+ Successfully created specrefs directory at: specrefs
52
+ ```
53
+
54
+ This creates a `specrefs` directory with `.ethspecify.yml` and YAML files for each spec category
55
+ (constants, configs, presets, functions, containers, dataclasses, types).
56
+
57
+ ### Map sources
58
+
59
+ Edit the YAML files to add sources for where each spec item is implemented.
60
+
61
+ If it's the entire file:
62
+
63
+ ```yaml
64
+ - name: BlobParameters
65
+ sources:
66
+ - file: ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/fulu/helpers/BlobParameters.java
67
+ spec: |
68
+ <spec dataclass="BlobParameters" fork="fulu" hash="a4575aa8">
69
+ class BlobParameters:
70
+ epoch: Epoch
71
+ max_blobs_per_block: uint64
72
+ </spec>
73
+ ```
74
+
75
+ If it's multiple entire files:
76
+
77
+ ```yaml
78
+ - name: BlobsBundleDeneb
79
+ sources:
80
+ - file: ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/execution/BlobsBundle.java
81
+ - file: ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/builder/BlobsBundleSchema.java
82
+ - file: ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/builder/versions/deneb/BlobsBundleDeneb.java
83
+ - file: ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/builder/versions/deneb/BlobsBundleSchemaDeneb.java
84
+ spec: |
85
+ <spec dataclass="BlobsBundle" fork="deneb" hash="8d6e7be6">
86
+ class BlobsBundle(object):
87
+ commitments: List[KZGCommitment, MAX_BLOB_COMMITMENTS_PER_BLOCK]
88
+ proofs: List[KZGProof, MAX_BLOB_COMMITMENTS_PER_BLOCK]
89
+ blobs: List[Blob, MAX_BLOB_COMMITMENTS_PER_BLOCK]
90
+ </spec>
91
+ ```
92
+
93
+ If it's a specific part of a file:
94
+
95
+ ```yaml
96
+ - name: EFFECTIVE_BALANCE_INCREMENT
97
+ sources:
98
+ - file: ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/presets/mainnet/phase0.yaml
99
+ search: "EFFECTIVE_BALANCE_INCREMENT:"
100
+ spec: |
101
+ <spec preset_var="EFFECTIVE_BALANCE_INCREMENT" fork="phase0" hash="23dfe52c">
102
+ EFFECTIVE_BALANCE_INCREMENT: Gwei = 1000000000
103
+ </spec>
104
+ ```
105
+
106
+ You can also use regex in the searches if that is necessary:
107
+
108
+ ```yaml
109
+ - name: ATTESTATION_DUE_BPS
110
+ sources:
111
+ - file: ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/configs/mainnet.yaml
112
+ search: "^ATTESTATION_DUE_BPS:"
113
+ regex: true
114
+ spec: |
115
+ <spec config_var="ATTESTATION_DUE_BPS" fork="phase0" hash="929dd1c9">
116
+ ATTESTATION_DUE_BPS: uint64 = 3333
117
+ </spec>
118
+ ```
119
+
120
+ ### Check specrefs
121
+
122
+ Run the check command in CI to verify all spec items are properly mapped:
123
+
124
+ ```
125
+ $ ethspecify check --path=specrefs
126
+ MISSING: constants.BLS_MODULUS#deneb
127
+ ```
128
+
129
+ ### Add exceptions
130
+
131
+ Some spec items may not be implemented in your client. Add them to the exceptions list in
132
+ `specrefs/.ethspecify.yml`:
133
+
134
+ ```yaml
135
+ specrefs:
136
+ files:
137
+ - containers.yml
138
+ - functions.yml
139
+ # ...
140
+
141
+ exceptions:
142
+ containers:
143
+ # Not defined, unnecessary
144
+ - Eth1Block
145
+
146
+ functions:
147
+ # No light client support
148
+ - is_valid_light_client_header
149
+ - process_light_client_update
150
+ ```
151
+
152
+ ## Style Options
153
+
154
+ This attribute can be used to change how the specification content is shown.
155
+
156
+ ### `hash` (default)
157
+
158
+ This style adds a hash of the specification content to the spec tag, without showing the content.
159
+
160
+ ```
161
+ <spec fn="apply_deposit" fork="electra" hash="c723ce7b" />
162
+ ```
163
+
164
+ > [!NOTE]
165
+ > The hash is the first 8 characters of the specification content's SHA256 digest.
166
+
167
+ ### `full`
168
+
169
+ This style displays the whole content of this specification item, including comments.
170
+
171
+ ```
172
+ <spec fn="is_fully_withdrawable_validator" fork="deneb" style="full">
173
+ def is_fully_withdrawable_validator(validator: Validator, balance: Gwei, epoch: Epoch) -> bool:
174
+ """
175
+ Check if ``validator`` is fully withdrawable.
176
+ """
177
+ return (
178
+ has_eth1_withdrawal_credential(validator)
179
+ and validator.withdrawable_epoch <= epoch
180
+ and balance > 0
181
+ )
182
+ </spec>
183
+ ```
184
+
185
+ ### `link`
186
+
187
+ This style displays a GitHub link to the specification item.
188
+
189
+ ```
190
+ <spec fn="apply_pending_deposit" fork="electra" style="link" hash="83ee9126">
191
+ https://github.com/ethereum/consensus-specs/blob/dev/specs/electra/beacon-chain.md#new-apply_pending_deposit
192
+ </spec>
193
+ ```
194
+
195
+ ### `diff`
196
+
197
+ This style displays a diff with the previous fork's version of the specification.
198
+
199
+ ```
200
+ <spec ssz_object="BeaconState" fork="electra" style="diff">
201
+ --- deneb
202
+ +++ electra
203
+ @@ -27,3 +27,12 @@
204
+ next_withdrawal_index: WithdrawalIndex
205
+ next_withdrawal_validator_index: ValidatorIndex
206
+ historical_summaries: List[HistoricalSummary, HISTORICAL_ROOTS_LIMIT]
207
+ + deposit_requests_start_index: uint64
208
+ + deposit_balance_to_consume: Gwei
209
+ + exit_balance_to_consume: Gwei
210
+ + earliest_exit_epoch: Epoch
211
+ + consolidation_balance_to_consume: Gwei
212
+ + earliest_consolidation_epoch: Epoch
213
+ + pending_deposits: List[PendingDeposit, PENDING_DEPOSITS_LIMIT]
214
+ + pending_partial_withdrawals: List[PendingPartialWithdrawal, PENDING_PARTIAL_WITHDRAWALS_LIMIT]
215
+ + pending_consolidations: List[PendingConsolidation, PENDING_CONSOLIDATIONS_LIMIT]
216
+ </spec>
217
+ ```
218
+
219
+ > [!NOTE]
220
+ > Comments are stripped from the specifications when the `diff` style is used. We do this because
221
+ > these complicate the diff; the "[Modified in Fork]" comments aren't valuable here.
222
+
223
+ This can be used with any specification item, like functions too:
224
+
225
+ ```
226
+ <spec fn="is_eligible_for_activation_queue" fork="electra" style="diff">
227
+ --- phase0
228
+ +++ electra
229
+ @@ -4,5 +4,5 @@
230
+ """
231
+ return (
232
+ validator.activation_eligibility_epoch == FAR_FUTURE_EPOCH
233
+ - and validator.effective_balance == MAX_EFFECTIVE_BALANCE
234
+ + and validator.effective_balance >= MIN_ACTIVATION_BALANCE
235
+ )
236
+ </spec>
237
+ ```
@@ -0,0 +1,211 @@
1
+ # ethspecify
2
+
3
+ A tool for referencing the Ethereum specifications in clients.
4
+
5
+ The idea is that ethspecify will help developers keep track of when the specification changes. It
6
+ will also help auditors verify that the client implementations match the specifications. Ideally,
7
+ this is configured as a CI check which notifies client developers when the specification changes.
8
+ When that happens, they can update the implementations appropriately.
9
+
10
+ ## Getting started
11
+
12
+ ### Installation
13
+
14
+ ```
15
+ pipx install ethspecify
16
+ ```
17
+
18
+ ### Initialize specrefs
19
+
20
+ From the root of the client source directory, initialize a directory for specrefs:
21
+
22
+ ```
23
+ $ ethspecify init v1.6.0-beta.0
24
+ Initializing specrefs directory: v1.6.0-beta.0
25
+ Successfully created specrefs directory at: specrefs
26
+ ```
27
+
28
+ This creates a `specrefs` directory with `.ethspecify.yml` and YAML files for each spec category
29
+ (constants, configs, presets, functions, containers, dataclasses, types).
30
+
31
+ ### Map sources
32
+
33
+ Edit the YAML files to add sources for where each spec item is implemented.
34
+
35
+ If it's the entire file:
36
+
37
+ ```yaml
38
+ - name: BlobParameters
39
+ sources:
40
+ - file: ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/fulu/helpers/BlobParameters.java
41
+ spec: |
42
+ <spec dataclass="BlobParameters" fork="fulu" hash="a4575aa8">
43
+ class BlobParameters:
44
+ epoch: Epoch
45
+ max_blobs_per_block: uint64
46
+ </spec>
47
+ ```
48
+
49
+ If it's multiple entire files:
50
+
51
+ ```yaml
52
+ - name: BlobsBundleDeneb
53
+ sources:
54
+ - file: ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/execution/BlobsBundle.java
55
+ - file: ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/builder/BlobsBundleSchema.java
56
+ - file: ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/builder/versions/deneb/BlobsBundleDeneb.java
57
+ - file: ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/builder/versions/deneb/BlobsBundleSchemaDeneb.java
58
+ spec: |
59
+ <spec dataclass="BlobsBundle" fork="deneb" hash="8d6e7be6">
60
+ class BlobsBundle(object):
61
+ commitments: List[KZGCommitment, MAX_BLOB_COMMITMENTS_PER_BLOCK]
62
+ proofs: List[KZGProof, MAX_BLOB_COMMITMENTS_PER_BLOCK]
63
+ blobs: List[Blob, MAX_BLOB_COMMITMENTS_PER_BLOCK]
64
+ </spec>
65
+ ```
66
+
67
+ If it's a specific part of a file:
68
+
69
+ ```yaml
70
+ - name: EFFECTIVE_BALANCE_INCREMENT
71
+ sources:
72
+ - file: ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/presets/mainnet/phase0.yaml
73
+ search: "EFFECTIVE_BALANCE_INCREMENT:"
74
+ spec: |
75
+ <spec preset_var="EFFECTIVE_BALANCE_INCREMENT" fork="phase0" hash="23dfe52c">
76
+ EFFECTIVE_BALANCE_INCREMENT: Gwei = 1000000000
77
+ </spec>
78
+ ```
79
+
80
+ You can also use regex in the searches if that is necessary:
81
+
82
+ ```yaml
83
+ - name: ATTESTATION_DUE_BPS
84
+ sources:
85
+ - file: ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/configs/mainnet.yaml
86
+ search: "^ATTESTATION_DUE_BPS:"
87
+ regex: true
88
+ spec: |
89
+ <spec config_var="ATTESTATION_DUE_BPS" fork="phase0" hash="929dd1c9">
90
+ ATTESTATION_DUE_BPS: uint64 = 3333
91
+ </spec>
92
+ ```
93
+
94
+ ### Check specrefs
95
+
96
+ Run the check command in CI to verify all spec items are properly mapped:
97
+
98
+ ```
99
+ $ ethspecify check --path=specrefs
100
+ MISSING: constants.BLS_MODULUS#deneb
101
+ ```
102
+
103
+ ### Add exceptions
104
+
105
+ Some spec items may not be implemented in your client. Add them to the exceptions list in
106
+ `specrefs/.ethspecify.yml`:
107
+
108
+ ```yaml
109
+ specrefs:
110
+ files:
111
+ - containers.yml
112
+ - functions.yml
113
+ # ...
114
+
115
+ exceptions:
116
+ containers:
117
+ # Not defined, unnecessary
118
+ - Eth1Block
119
+
120
+ functions:
121
+ # No light client support
122
+ - is_valid_light_client_header
123
+ - process_light_client_update
124
+ ```
125
+
126
+ ## Style Options
127
+
128
+ This attribute can be used to change how the specification content is shown.
129
+
130
+ ### `hash` (default)
131
+
132
+ This style adds a hash of the specification content to the spec tag, without showing the content.
133
+
134
+ ```
135
+ <spec fn="apply_deposit" fork="electra" hash="c723ce7b" />
136
+ ```
137
+
138
+ > [!NOTE]
139
+ > The hash is the first 8 characters of the specification content's SHA256 digest.
140
+
141
+ ### `full`
142
+
143
+ This style displays the whole content of this specification item, including comments.
144
+
145
+ ```
146
+ <spec fn="is_fully_withdrawable_validator" fork="deneb" style="full">
147
+ def is_fully_withdrawable_validator(validator: Validator, balance: Gwei, epoch: Epoch) -> bool:
148
+ """
149
+ Check if ``validator`` is fully withdrawable.
150
+ """
151
+ return (
152
+ has_eth1_withdrawal_credential(validator)
153
+ and validator.withdrawable_epoch <= epoch
154
+ and balance > 0
155
+ )
156
+ </spec>
157
+ ```
158
+
159
+ ### `link`
160
+
161
+ This style displays a GitHub link to the specification item.
162
+
163
+ ```
164
+ <spec fn="apply_pending_deposit" fork="electra" style="link" hash="83ee9126">
165
+ https://github.com/ethereum/consensus-specs/blob/dev/specs/electra/beacon-chain.md#new-apply_pending_deposit
166
+ </spec>
167
+ ```
168
+
169
+ ### `diff`
170
+
171
+ This style displays a diff with the previous fork's version of the specification.
172
+
173
+ ```
174
+ <spec ssz_object="BeaconState" fork="electra" style="diff">
175
+ --- deneb
176
+ +++ electra
177
+ @@ -27,3 +27,12 @@
178
+ next_withdrawal_index: WithdrawalIndex
179
+ next_withdrawal_validator_index: ValidatorIndex
180
+ historical_summaries: List[HistoricalSummary, HISTORICAL_ROOTS_LIMIT]
181
+ + deposit_requests_start_index: uint64
182
+ + deposit_balance_to_consume: Gwei
183
+ + exit_balance_to_consume: Gwei
184
+ + earliest_exit_epoch: Epoch
185
+ + consolidation_balance_to_consume: Gwei
186
+ + earliest_consolidation_epoch: Epoch
187
+ + pending_deposits: List[PendingDeposit, PENDING_DEPOSITS_LIMIT]
188
+ + pending_partial_withdrawals: List[PendingPartialWithdrawal, PENDING_PARTIAL_WITHDRAWALS_LIMIT]
189
+ + pending_consolidations: List[PendingConsolidation, PENDING_CONSOLIDATIONS_LIMIT]
190
+ </spec>
191
+ ```
192
+
193
+ > [!NOTE]
194
+ > Comments are stripped from the specifications when the `diff` style is used. We do this because
195
+ > these complicate the diff; the "[Modified in Fork]" comments aren't valuable here.
196
+
197
+ This can be used with any specification item, like functions too:
198
+
199
+ ```
200
+ <spec fn="is_eligible_for_activation_queue" fork="electra" style="diff">
201
+ --- phase0
202
+ +++ electra
203
+ @@ -4,5 +4,5 @@
204
+ """
205
+ return (
206
+ validator.activation_eligibility_epoch == FAR_FUTURE_EPOCH
207
+ - and validator.effective_balance == MAX_EFFECTIVE_BALANCE
208
+ + and validator.effective_balance >= MIN_ACTIVATION_BALANCE
209
+ )
210
+ </spec>
211
+ ```
@@ -3,11 +3,11 @@ import json
3
3
  import os
4
4
  import sys
5
5
 
6
- from .core import grep, replace_spec_tags, get_pyspec, get_latest_fork, get_spec_item_history, load_config, run_checks
6
+ from .core import grep, replace_spec_tags, get_pyspec, get_latest_fork, get_spec_item_history, load_config, run_checks, sort_specref_yaml, generate_specref_files
7
7
 
8
8
 
9
9
  def process(args):
10
- """Process all spec tags."""
10
+ """Process all spec tags and sort specref YAML files."""
11
11
  project_dir = os.path.abspath(os.path.expanduser(args.path))
12
12
  if not os.path.isdir(project_dir):
13
13
  print(f"Error: The directory {repr(project_dir)} does not exist.")
@@ -16,10 +16,26 @@ def process(args):
16
16
  # Load config once from the project directory
17
17
  config = load_config(project_dir)
18
18
 
19
+ # Process spec tags in files
19
20
  for f in grep(project_dir, r"<spec\b.*?>", args.exclude):
20
21
  print(f"Processing file: {f}")
21
22
  replace_spec_tags(f, config)
22
23
 
24
+ # Sort specref YAML files if they exist in config
25
+ specrefs_config = config.get('specrefs', {})
26
+ if isinstance(specrefs_config, dict):
27
+ specrefs_files = specrefs_config.get('files', [])
28
+ elif isinstance(specrefs_config, list):
29
+ specrefs_files = specrefs_config
30
+ else:
31
+ specrefs_files = []
32
+
33
+ for yaml_file in specrefs_files:
34
+ yaml_path = os.path.join(project_dir, yaml_file)
35
+ if os.path.exists(yaml_path):
36
+ if not sort_specref_yaml(yaml_path):
37
+ print(f"Error sorting: {yaml_file}")
38
+
23
39
  return 0
24
40
 
25
41
 
@@ -166,6 +182,28 @@ def list_forks(args):
166
182
  return 0
167
183
 
168
184
 
185
+ def init(args):
186
+ """Initialize a specrefs directory with basic configuration and empty source mappings."""
187
+ output_dir = args.path or "specrefs"
188
+ version = args.version
189
+
190
+ # Check if output directory already exists
191
+ if os.path.exists(output_dir):
192
+ print(f"Error: directory {repr(output_dir)} already exists.")
193
+ print("Please specify a different directory or remove the existing one.")
194
+ return 1
195
+
196
+ try:
197
+ # Generate the specref files
198
+ print(f"Initializing specrefs directory: {version}")
199
+ generate_specref_files(output_dir, version, "mainnet")
200
+ print(f"Successfully created specrefs directory at: {output_dir}")
201
+ return 0
202
+ except Exception as e:
203
+ print(f"Error: {e}")
204
+ return 1
205
+
206
+
169
207
  def main():
170
208
  parser = argparse.ArgumentParser(
171
209
  description="Process files containing <spec> tags."
@@ -240,6 +278,21 @@ def main():
240
278
  help="Output format (text or json)",
241
279
  )
242
280
 
281
+ # Parser for 'init' command
282
+ init_parser = subparsers.add_parser("init", help="Initialize a specrefs directory")
283
+ init_parser.set_defaults(func=init)
284
+ init_parser.add_argument(
285
+ "version",
286
+ type=str,
287
+ help="Specification version (e.g., 'nightly' or 'v1.6.0-alpha.5')",
288
+ )
289
+ init_parser.add_argument(
290
+ "--path",
291
+ type=str,
292
+ help="Output directory for specrefs (default: specrefs)",
293
+ default="specrefs",
294
+ )
295
+
243
296
  # Default to 'process' if no args are provided
244
297
  if len(sys.argv) == 1:
245
298
  sys.argv.insert(1, "process")