ethspecify 0.3.7__tar.gz → 0.3.9__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ethspecify
3
- Version: 0.3.7
3
+ Version: 0.3.9
4
4
  Summary: A utility for processing Ethereum specification tags.
5
5
  Home-page: https://github.com/jtraglia/ethspecify
6
6
  Author: Justin Traglia
@@ -26,39 +26,132 @@ Dynamic: summary
26
26
 
27
27
  # ethspecify
28
28
 
29
- A tool for referencing the Ethereum specifications in clients.
29
+ A tool for referencing the Ethereum specifications in clients. This will:
30
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.
31
+ - Help developers keep track of specification changes.
32
+ - Help auditors find important functions in client implementations.
35
33
 
36
- ## Getting started
34
+ ## Table of contents
37
35
 
38
- ### Installation
36
+ - [Getting Started](#getting-started)
37
+ - [Install](#install)
38
+ - [Initialize](#initialize)
39
+ - [Inline References](#inline-references)
40
+ - [Centralized References](#centralized-references)
41
+ - [Configure](#configure)
42
+ - [Update](#update)
43
+ - [Map](#map)
44
+ - [Check](#check)
45
+ - [Exceptions](#exceptions)
46
+ - [Style Options](#style-options)
47
+ - [`hash`](#hash)
48
+ - [`full`](#full)
49
+ - [`link`](#link)
50
+ - [`diff`](#diff)
51
+
52
+ ## Getting Started
53
+
54
+ ### Install
39
55
 
40
56
  ```
41
57
  pipx install ethspecify
42
58
  ```
43
59
 
44
- ### Initialize specrefs
60
+ ### Initialize
45
61
 
46
- From the root of the client source directory, initialize a directory for specrefs:
62
+ Create a `.ethspecify.yml` config file:
47
63
 
48
64
  ```
49
65
  $ ethspecify init v1.6.0-beta.0
66
+ Successfully created .ethspecify.yml
67
+ ```
68
+
69
+ To also generate a `specrefs/` directory with a YAML file for each
70
+ specification category, use the `--specrefs` flag:
71
+
72
+ ```
73
+ $ ethspecify init v1.6.0-beta.0 --specrefs
50
74
  Initializing specrefs directory: v1.6.0-beta.0
51
- Successfully created specrefs directory at: specrefs
75
+ Successfully created .ethspecify.yml and specrefs/ directory
76
+ ```
77
+
78
+ > [!TIP]
79
+ > `nightly` is also a valid version, which tracks the latest
80
+ > development specifications.
81
+
82
+ ## Inline References
83
+
84
+ Add `<spec>` tags anywhere in the codebase to reference specification
85
+ items. For example:
86
+
87
+ ```
88
+ <spec fn="is_active_validator" fork="phase0" />
89
+ ```
90
+
91
+ Then run `ethspecify` to populate the tag body with the corresponding
92
+ specification content:
93
+
94
+ ```
95
+ <spec fn="is_active_validator" fork="phase0" hash="5765e850">
96
+ def is_active_validator(validator: Validator, epoch: Epoch) -> bool:
97
+ """
98
+ Check if ``validator`` is active.
99
+ """
100
+ return validator.activation_epoch <= epoch < validator.exit_epoch
101
+ </spec>
52
102
  ```
53
103
 
54
- This creates a `specrefs` directory with `.ethspecify.yml` and YAML files for each spec category
55
- (constants, configs, presets, functions, containers, dataclasses, types).
104
+ > [!NOTE]
105
+ > The indentation of the `<spec>` tag is preserved when populating
106
+ > the body. This means spec tags inside comments will have their
107
+ > content indented to match:
108
+ >
109
+ > ```java
110
+ > // <spec fn="is_active_validator" fork="phase0" hash="5765e850">
111
+ > // def is_active_validator(validator: Validator, epoch: Epoch) -> bool:
112
+ > // """
113
+ > // Check if ``validator`` is active.
114
+ > // """
115
+ > // return validator.activation_epoch <= epoch < validator.exit_epoch
116
+ > // </spec>
117
+ > ```
118
+
119
+ ## Centralized References
120
+
121
+ Specification references (specrefs) are YAML files that map
122
+ specification items (constants, functions, containers, etc.) to
123
+ their corresponding source file locations.
124
+
125
+ ### Configure
126
+
127
+ The following options can be set in the `specrefs` section of
128
+ `.ethspecify.yml`:
129
+
130
+ | Option | Default | Description |
131
+ |--------|---------|-------------|
132
+ | `search_root` | `.` | Root directory for resolving source file paths |
133
+ | `auto_standardize_names` | `false` | Rename entries to `item#fork` format |
134
+ | `auto_add_missing_entries` | `false` | Add missing specification items with empty sources |
135
+ | `require_exceptions_have_fork` | `false` | Require exceptions to use `item#fork` format |
136
+
137
+ ### Update
138
+
139
+ To update to a newer specification version, change the `version`
140
+ field in `.ethspecify.yml` and run:
56
141
 
57
- ### Map sources
142
+ ```
143
+ $ ethspecify
144
+ ```
145
+
146
+ This updates the specification content in the YAML files to match
147
+ the new version.
58
148
 
59
- Edit the YAML files to add sources for where each spec item is implemented.
149
+ ### Map
60
150
 
61
- If it's the entire file:
151
+ Edit the YAML files to add sources for where each specification
152
+ item is implemented.
153
+
154
+ If it is the entire file:
62
155
 
63
156
  ```yaml
64
157
  - name: BlobParameters
@@ -72,7 +165,7 @@ If it's the entire file:
72
165
  </spec>
73
166
  ```
74
167
 
75
- If it's multiple entire files:
168
+ If it is multiple entire files:
76
169
 
77
170
  ```yaml
78
171
  - name: BlobsBundleDeneb
@@ -90,7 +183,7 @@ If it's multiple entire files:
90
183
  </spec>
91
184
  ```
92
185
 
93
- If it's a specific part of a file:
186
+ If it is a specific part of a file:
94
187
 
95
188
  ```yaml
96
189
  - name: EFFECTIVE_BALANCE_INCREMENT
@@ -103,7 +196,7 @@ If it's a specific part of a file:
103
196
  </spec>
104
197
  ```
105
198
 
106
- You can also use regex in the searches if that is necessary:
199
+ Regex is also supported in searches:
107
200
 
108
201
  ```yaml
109
202
  - name: ATTESTATION_DUE_BPS
@@ -117,56 +210,57 @@ You can also use regex in the searches if that is necessary:
117
210
  </spec>
118
211
  ```
119
212
 
120
- ### Check specrefs
213
+ ### Check
121
214
 
122
- Run the check command in CI to verify all spec items are properly mapped:
215
+ Run the check command in CI to verify all specification items are
216
+ properly mapped:
123
217
 
124
218
  ```
125
- $ ethspecify check --path=specrefs
219
+ $ ethspecify check
126
220
  MISSING: constants.BLS_MODULUS#deneb
127
221
  ```
128
222
 
129
- ### Add exceptions
223
+ ### Exceptions
130
224
 
131
- Some spec items may not be implemented in your client. Add them to the exceptions list in
132
- `specrefs/.ethspecify.yml`:
225
+ Some specification items may not have a corresponding
226
+ implementation. Add them to the exceptions list in
227
+ `.ethspecify.yml`:
133
228
 
134
229
  ```yaml
135
230
  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
231
+ exceptions:
232
+ containers:
233
+ # Not defined, unnecessary
234
+ - Eth1Block
235
+
236
+ functions:
237
+ # No light client support
238
+ - is_valid_light_client_header
239
+ - process_light_client_update
150
240
  ```
151
241
 
152
242
  ## Style Options
153
243
 
154
- This attribute can be used to change how the specification content is shown.
244
+ This attribute can be used to change how the specification content
245
+ is shown.
155
246
 
156
- ### `hash` (default)
247
+ ### `hash`
157
248
 
158
- This style adds a hash of the specification content to the spec tag, without showing the content.
249
+ This style adds a hash of the specification content to the
250
+ `<spec>` tag, without showing the content.
159
251
 
160
252
  ```
161
253
  <spec fn="apply_deposit" fork="electra" hash="c723ce7b" />
162
254
  ```
163
255
 
164
256
  > [!NOTE]
165
- > The hash is the first 8 characters of the specification content's SHA256 digest.
257
+ > The hash is the first 8 characters of the specification
258
+ > content's SHA256 digest.
166
259
 
167
260
  ### `full`
168
261
 
169
- This style displays the whole content of this specification item, including comments.
262
+ This style displays the whole content of this specification item,
263
+ including comments.
170
264
 
171
265
  ```
172
266
  <spec fn="is_fully_withdrawable_validator" fork="deneb" style="full">
@@ -184,7 +278,8 @@ def is_fully_withdrawable_validator(validator: Validator, balance: Gwei, epoch:
184
278
 
185
279
  ### `link`
186
280
 
187
- This style displays an ethspec.tools link to the specification item.
281
+ This style displays an ethspec.tools link to the specification
282
+ item.
188
283
 
189
284
  ```
190
285
  <spec fn="apply_pending_deposit" fork="electra" style="link" hash="83ee9126">
@@ -194,7 +289,8 @@ https://ethspec.tools/#specs/v1.7.0-alpha.1/functions-apply_pending_deposit-elec
194
289
 
195
290
  ### `diff`
196
291
 
197
- This style displays a diff with the previous fork's version of the specification.
292
+ This style displays a diff with the previous fork's version of the
293
+ specification.
198
294
 
199
295
  ```
200
296
  <spec ssz_object="BeaconState" fork="electra" style="diff">
@@ -217,10 +313,12 @@ This style displays a diff with the previous fork's version of the specification
217
313
  ```
218
314
 
219
315
  > [!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.
316
+ > Comments are stripped from the specifications when the `diff`
317
+ > style is used. This is because they complicate the diff; the
318
+ > "[Modified in Fork]" comments are not valuable here.
222
319
 
223
- This can be used with any specification item, like functions too:
320
+ This can be used with any specification item, like functions
321
+ too:
224
322
 
225
323
  ```
226
324
  <spec fn="is_eligible_for_activation_queue" fork="electra" style="diff">
@@ -0,0 +1,309 @@
1
+ # ethspecify
2
+
3
+ A tool for referencing the Ethereum specifications in clients. This will:
4
+
5
+ - Help developers keep track of specification changes.
6
+ - Help auditors find important functions in client implementations.
7
+
8
+ ## Table of contents
9
+
10
+ - [Getting Started](#getting-started)
11
+ - [Install](#install)
12
+ - [Initialize](#initialize)
13
+ - [Inline References](#inline-references)
14
+ - [Centralized References](#centralized-references)
15
+ - [Configure](#configure)
16
+ - [Update](#update)
17
+ - [Map](#map)
18
+ - [Check](#check)
19
+ - [Exceptions](#exceptions)
20
+ - [Style Options](#style-options)
21
+ - [`hash`](#hash)
22
+ - [`full`](#full)
23
+ - [`link`](#link)
24
+ - [`diff`](#diff)
25
+
26
+ ## Getting Started
27
+
28
+ ### Install
29
+
30
+ ```
31
+ pipx install ethspecify
32
+ ```
33
+
34
+ ### Initialize
35
+
36
+ Create a `.ethspecify.yml` config file:
37
+
38
+ ```
39
+ $ ethspecify init v1.6.0-beta.0
40
+ Successfully created .ethspecify.yml
41
+ ```
42
+
43
+ To also generate a `specrefs/` directory with a YAML file for each
44
+ specification category, use the `--specrefs` flag:
45
+
46
+ ```
47
+ $ ethspecify init v1.6.0-beta.0 --specrefs
48
+ Initializing specrefs directory: v1.6.0-beta.0
49
+ Successfully created .ethspecify.yml and specrefs/ directory
50
+ ```
51
+
52
+ > [!TIP]
53
+ > `nightly` is also a valid version, which tracks the latest
54
+ > development specifications.
55
+
56
+ ## Inline References
57
+
58
+ Add `<spec>` tags anywhere in the codebase to reference specification
59
+ items. For example:
60
+
61
+ ```
62
+ <spec fn="is_active_validator" fork="phase0" />
63
+ ```
64
+
65
+ Then run `ethspecify` to populate the tag body with the corresponding
66
+ specification content:
67
+
68
+ ```
69
+ <spec fn="is_active_validator" fork="phase0" hash="5765e850">
70
+ def is_active_validator(validator: Validator, epoch: Epoch) -> bool:
71
+ """
72
+ Check if ``validator`` is active.
73
+ """
74
+ return validator.activation_epoch <= epoch < validator.exit_epoch
75
+ </spec>
76
+ ```
77
+
78
+ > [!NOTE]
79
+ > The indentation of the `<spec>` tag is preserved when populating
80
+ > the body. This means spec tags inside comments will have their
81
+ > content indented to match:
82
+ >
83
+ > ```java
84
+ > // <spec fn="is_active_validator" fork="phase0" hash="5765e850">
85
+ > // def is_active_validator(validator: Validator, epoch: Epoch) -> bool:
86
+ > // """
87
+ > // Check if ``validator`` is active.
88
+ > // """
89
+ > // return validator.activation_epoch <= epoch < validator.exit_epoch
90
+ > // </spec>
91
+ > ```
92
+
93
+ ## Centralized References
94
+
95
+ Specification references (specrefs) are YAML files that map
96
+ specification items (constants, functions, containers, etc.) to
97
+ their corresponding source file locations.
98
+
99
+ ### Configure
100
+
101
+ The following options can be set in the `specrefs` section of
102
+ `.ethspecify.yml`:
103
+
104
+ | Option | Default | Description |
105
+ |--------|---------|-------------|
106
+ | `search_root` | `.` | Root directory for resolving source file paths |
107
+ | `auto_standardize_names` | `false` | Rename entries to `item#fork` format |
108
+ | `auto_add_missing_entries` | `false` | Add missing specification items with empty sources |
109
+ | `require_exceptions_have_fork` | `false` | Require exceptions to use `item#fork` format |
110
+
111
+ ### Update
112
+
113
+ To update to a newer specification version, change the `version`
114
+ field in `.ethspecify.yml` and run:
115
+
116
+ ```
117
+ $ ethspecify
118
+ ```
119
+
120
+ This updates the specification content in the YAML files to match
121
+ the new version.
122
+
123
+ ### Map
124
+
125
+ Edit the YAML files to add sources for where each specification
126
+ item is implemented.
127
+
128
+ If it is the entire file:
129
+
130
+ ```yaml
131
+ - name: BlobParameters
132
+ sources:
133
+ - file: ethereum/spec/src/main/java/tech/pegasys/teku/spec/logic/versions/fulu/helpers/BlobParameters.java
134
+ spec: |
135
+ <spec dataclass="BlobParameters" fork="fulu" hash="a4575aa8">
136
+ class BlobParameters:
137
+ epoch: Epoch
138
+ max_blobs_per_block: uint64
139
+ </spec>
140
+ ```
141
+
142
+ If it is multiple entire files:
143
+
144
+ ```yaml
145
+ - name: BlobsBundleDeneb
146
+ sources:
147
+ - file: ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/execution/BlobsBundle.java
148
+ - file: ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/builder/BlobsBundleSchema.java
149
+ - file: ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/builder/versions/deneb/BlobsBundleDeneb.java
150
+ - file: ethereum/spec/src/main/java/tech/pegasys/teku/spec/datastructures/builder/versions/deneb/BlobsBundleSchemaDeneb.java
151
+ spec: |
152
+ <spec dataclass="BlobsBundle" fork="deneb" hash="8d6e7be6">
153
+ class BlobsBundle(object):
154
+ commitments: List[KZGCommitment, MAX_BLOB_COMMITMENTS_PER_BLOCK]
155
+ proofs: List[KZGProof, MAX_BLOB_COMMITMENTS_PER_BLOCK]
156
+ blobs: List[Blob, MAX_BLOB_COMMITMENTS_PER_BLOCK]
157
+ </spec>
158
+ ```
159
+
160
+ If it is a specific part of a file:
161
+
162
+ ```yaml
163
+ - name: EFFECTIVE_BALANCE_INCREMENT
164
+ sources:
165
+ - file: ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/presets/mainnet/phase0.yaml
166
+ search: "EFFECTIVE_BALANCE_INCREMENT:"
167
+ spec: |
168
+ <spec preset_var="EFFECTIVE_BALANCE_INCREMENT" fork="phase0" hash="23dfe52c">
169
+ EFFECTIVE_BALANCE_INCREMENT: Gwei = 1000000000
170
+ </spec>
171
+ ```
172
+
173
+ Regex is also supported in searches:
174
+
175
+ ```yaml
176
+ - name: ATTESTATION_DUE_BPS
177
+ sources:
178
+ - file: ethereum/spec/src/main/resources/tech/pegasys/teku/spec/config/configs/mainnet.yaml
179
+ search: "^ATTESTATION_DUE_BPS:"
180
+ regex: true
181
+ spec: |
182
+ <spec config_var="ATTESTATION_DUE_BPS" fork="phase0" hash="929dd1c9">
183
+ ATTESTATION_DUE_BPS: uint64 = 3333
184
+ </spec>
185
+ ```
186
+
187
+ ### Check
188
+
189
+ Run the check command in CI to verify all specification items are
190
+ properly mapped:
191
+
192
+ ```
193
+ $ ethspecify check
194
+ MISSING: constants.BLS_MODULUS#deneb
195
+ ```
196
+
197
+ ### Exceptions
198
+
199
+ Some specification items may not have a corresponding
200
+ implementation. Add them to the exceptions list in
201
+ `.ethspecify.yml`:
202
+
203
+ ```yaml
204
+ specrefs:
205
+ exceptions:
206
+ containers:
207
+ # Not defined, unnecessary
208
+ - Eth1Block
209
+
210
+ functions:
211
+ # No light client support
212
+ - is_valid_light_client_header
213
+ - process_light_client_update
214
+ ```
215
+
216
+ ## Style Options
217
+
218
+ This attribute can be used to change how the specification content
219
+ is shown.
220
+
221
+ ### `hash`
222
+
223
+ This style adds a hash of the specification content to the
224
+ `<spec>` tag, without showing the content.
225
+
226
+ ```
227
+ <spec fn="apply_deposit" fork="electra" hash="c723ce7b" />
228
+ ```
229
+
230
+ > [!NOTE]
231
+ > The hash is the first 8 characters of the specification
232
+ > content's SHA256 digest.
233
+
234
+ ### `full`
235
+
236
+ This style displays the whole content of this specification item,
237
+ including comments.
238
+
239
+ ```
240
+ <spec fn="is_fully_withdrawable_validator" fork="deneb" style="full">
241
+ def is_fully_withdrawable_validator(validator: Validator, balance: Gwei, epoch: Epoch) -> bool:
242
+ """
243
+ Check if ``validator`` is fully withdrawable.
244
+ """
245
+ return (
246
+ has_eth1_withdrawal_credential(validator)
247
+ and validator.withdrawable_epoch <= epoch
248
+ and balance > 0
249
+ )
250
+ </spec>
251
+ ```
252
+
253
+ ### `link`
254
+
255
+ This style displays an ethspec.tools link to the specification
256
+ item.
257
+
258
+ ```
259
+ <spec fn="apply_pending_deposit" fork="electra" style="link" hash="83ee9126">
260
+ https://ethspec.tools/#specs/v1.7.0-alpha.1/functions-apply_pending_deposit-electra
261
+ </spec>
262
+ ```
263
+
264
+ ### `diff`
265
+
266
+ This style displays a diff with the previous fork's version of the
267
+ specification.
268
+
269
+ ```
270
+ <spec ssz_object="BeaconState" fork="electra" style="diff">
271
+ --- deneb
272
+ +++ electra
273
+ @@ -27,3 +27,12 @@
274
+ next_withdrawal_index: WithdrawalIndex
275
+ next_withdrawal_validator_index: ValidatorIndex
276
+ historical_summaries: List[HistoricalSummary, HISTORICAL_ROOTS_LIMIT]
277
+ + deposit_requests_start_index: uint64
278
+ + deposit_balance_to_consume: Gwei
279
+ + exit_balance_to_consume: Gwei
280
+ + earliest_exit_epoch: Epoch
281
+ + consolidation_balance_to_consume: Gwei
282
+ + earliest_consolidation_epoch: Epoch
283
+ + pending_deposits: List[PendingDeposit, PENDING_DEPOSITS_LIMIT]
284
+ + pending_partial_withdrawals: List[PendingPartialWithdrawal, PENDING_PARTIAL_WITHDRAWALS_LIMIT]
285
+ + pending_consolidations: List[PendingConsolidation, PENDING_CONSOLIDATIONS_LIMIT]
286
+ </spec>
287
+ ```
288
+
289
+ > [!NOTE]
290
+ > Comments are stripped from the specifications when the `diff`
291
+ > style is used. This is because they complicate the diff; the
292
+ > "[Modified in Fork]" comments are not valuable here.
293
+
294
+ This can be used with any specification item, like functions
295
+ too:
296
+
297
+ ```
298
+ <spec fn="is_eligible_for_activation_queue" fork="electra" style="diff">
299
+ --- phase0
300
+ +++ electra
301
+ @@ -4,5 +4,5 @@
302
+ """
303
+ return (
304
+ validator.activation_eligibility_epoch == FAR_FUTURE_EPOCH
305
+ - and validator.effective_balance == MAX_EFFECTIVE_BALANCE
306
+ + and validator.effective_balance >= MIN_ACTIVATION_BALANCE
307
+ )
308
+ </spec>
309
+ ```
@@ -3,7 +3,7 @@ import json
3
3
  import os
4
4
  import sys
5
5
 
6
- from .core import grep, replace_spec_tags, get_pyspec, get_spec_item_history, load_config, run_checks, sort_specref_yaml, generate_specref_files
6
+ from .core import grep, replace_spec_tags, get_pyspec, get_spec_item_history, load_config, run_checks, sort_specref_yaml, generate_specref_files, generate_config_file
7
7
 
8
8
 
9
9
  def process(args):
@@ -200,25 +200,40 @@ def list_forks(args):
200
200
 
201
201
 
202
202
  def init(args):
203
- """Initialize a specrefs directory with basic configuration and empty source mappings."""
204
- output_dir = args.path or "specrefs"
203
+ """Initialize .ethspecify.yml and optionally a specrefs directory."""
205
204
  version = args.version
206
205
 
207
- # Check if output directory already exists
208
- if os.path.exists(output_dir):
209
- print(f"Error: directory {repr(output_dir)} already exists.")
210
- print("Please specify a different directory or remove the existing one.")
206
+ # Check if .ethspecify.yml already exists
207
+ if os.path.exists('.ethspecify.yml'):
208
+ print("Error: '.ethspecify.yml' already exists.")
209
+ print("Please remove the existing config file first.")
211
210
  return 1
212
211
 
213
- try:
214
- # Generate the specref files
215
- print(f"Initializing specrefs directory: {version}")
216
- generate_specref_files(output_dir, version, "mainnet")
217
- print(f"Successfully created specrefs directory at: {output_dir}")
218
- return 0
219
- except Exception as e:
220
- print(f"Error: {e}")
221
- return 1
212
+ if args.specrefs:
213
+ output_dir = args.path or "specrefs"
214
+
215
+ # Check if output directory already exists
216
+ if os.path.exists(output_dir):
217
+ print(f"Error: directory {repr(output_dir)} already exists.")
218
+ print("Please specify a different directory or remove the existing one.")
219
+ return 1
220
+
221
+ try:
222
+ print(f"Initializing specrefs directory: {version}")
223
+ generate_specref_files(output_dir, version, "mainnet")
224
+ print(f"Successfully created .ethspecify.yml and {output_dir}/ directory")
225
+ return 0
226
+ except Exception as e:
227
+ print(f"Error: {e}")
228
+ return 1
229
+ else:
230
+ try:
231
+ generate_config_file(version)
232
+ print(f"Successfully created .ethspecify.yml")
233
+ return 0
234
+ except Exception as e:
235
+ print(f"Error: {e}")
236
+ return 1
222
237
 
223
238
 
224
239
  def main():
@@ -296,13 +311,18 @@ def main():
296
311
  )
297
312
 
298
313
  # Parser for 'init' command
299
- init_parser = subparsers.add_parser("init", help="Initialize a specrefs directory")
314
+ init_parser = subparsers.add_parser("init", help="Initialize .ethspecify.yml")
300
315
  init_parser.set_defaults(func=init)
301
316
  init_parser.add_argument(
302
317
  "version",
303
318
  type=str,
304
319
  help="Specification version (e.g., 'nightly' or 'v1.6.0-alpha.5')",
305
320
  )
321
+ init_parser.add_argument(
322
+ "--specrefs",
323
+ action="store_true",
324
+ help="Also create a specrefs directory with YAML files for each spec category",
325
+ )
306
326
  init_parser.add_argument(
307
327
  "--path",
308
328
  type=str,
@@ -1656,8 +1656,8 @@ def run_checks(project_dir, config):
1656
1656
  source_root = os.path.join(project_dir, search_root_rel) if not os.path.isabs(search_root_rel) else search_root_rel
1657
1657
  source_root = os.path.abspath(source_root)
1658
1658
  else:
1659
- # Default behavior: parent directory
1660
- source_root = os.path.dirname(project_dir)
1659
+ # Default behavior: if we're in a specrefs directory, search in the parent directory
1660
+ source_root = os.path.dirname(project_dir) if os.path.basename(project_dir) == 'specrefs' else project_dir
1661
1661
 
1662
1662
  valid_count, total_count, source_errors = check_source_files(yaml_path, source_root, [])
1663
1663
 
@@ -1717,8 +1717,8 @@ def run_checks(project_dir, config):
1717
1717
  source_root = os.path.join(project_dir, search_root_rel) if not os.path.isabs(search_root_rel) else search_root_rel
1718
1718
  source_root = os.path.abspath(source_root)
1719
1719
  else:
1720
- # Default behavior: parent directory
1721
- source_root = os.path.dirname(project_dir)
1720
+ # Default behavior: if we're in a specrefs directory, search in the parent directory
1721
+ source_root = os.path.dirname(project_dir) if os.path.basename(project_dir) == 'specrefs' else project_dir
1722
1722
 
1723
1723
  valid_count, total_count, source_errors = check_source_files(yaml_path, source_root, all_exceptions)
1724
1724
 
@@ -2022,6 +2022,30 @@ def add_missing_spec_items_to_yaml_files(project_dir, config, specrefs_files):
2022
2022
  add_missing_entries_to_yaml(yaml_path, new_entries)
2023
2023
 
2024
2024
 
2025
+ def generate_config_file(version="nightly"):
2026
+ """Generate a .ethspecify.yml config file in the current directory."""
2027
+ config_path = '.ethspecify.yml'
2028
+ with open(config_path, 'w') as f:
2029
+ f.write(f'version: {version}\n')
2030
+ f.write('style: full\n')
2031
+ f.write('\n')
2032
+ f.write('specrefs:\n')
2033
+ f.write(' search_root: .\n')
2034
+ f.write(' auto_standardize_names: true\n')
2035
+ f.write(' auto_add_missing_entries: true\n')
2036
+ f.write(' require_exceptions_have_fork: true\n')
2037
+ f.write('\n')
2038
+ f.write(' exceptions:\n')
2039
+ f.write(' # Add any exceptions here\n')
2040
+
2041
+ # Strip trailing whitespace
2042
+ with open(config_path, 'r') as f:
2043
+ lines = f.readlines()
2044
+ with open(config_path, 'w') as f:
2045
+ for line in lines:
2046
+ f.write(line.rstrip() + '\n')
2047
+
2048
+
2025
2049
  def generate_specref_files(output_dir, version="nightly", preset="mainnet"):
2026
2050
  """
2027
2051
  Generate specref YAML files without sources for manual mapping.
@@ -2147,17 +2171,21 @@ def generate_specref_files(output_dir, version="nightly", preset="mainnet"):
2147
2171
  for line in entry['spec'].split('\n'):
2148
2172
  f.write(f' {line}\n')
2149
2173
 
2150
- # Create .ethspecify.yml config file
2151
- config_path = os.path.join(output_dir, '.ethspecify.yml')
2174
+ # Create .ethspecify.yml config file in current directory
2175
+ config_path = '.ethspecify.yml'
2152
2176
  with open(config_path, 'w') as f:
2153
2177
  f.write(f'version: {version}\n')
2154
2178
  f.write('style: full\n')
2155
2179
  f.write('\n')
2156
2180
  f.write('specrefs:\n')
2181
+ f.write(' search_root: .\n')
2182
+ f.write(' auto_standardize_names: true\n')
2183
+ f.write(' auto_add_missing_entries: true\n')
2184
+ f.write(' require_exceptions_have_fork: true\n')
2157
2185
  f.write(' files:\n')
2158
2186
  for category, (filename, _) in category_map.items():
2159
2187
  if items_by_category[category]:
2160
- f.write(f' - {filename}\n')
2188
+ f.write(f' - {output_dir}/{filename}\n')
2161
2189
  f.write('\n')
2162
2190
  f.write(' exceptions:\n')
2163
2191
  f.write(' # Add any exceptions here\n')
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ethspecify
3
- Version: 0.3.7
3
+ Version: 0.3.9
4
4
  Summary: A utility for processing Ethereum specification tags.
5
5
  Home-page: https://github.com/jtraglia/ethspecify
6
6
  Author: Justin Traglia
@@ -26,39 +26,132 @@ Dynamic: summary
26
26
 
27
27
  # ethspecify
28
28
 
29
- A tool for referencing the Ethereum specifications in clients.
29
+ A tool for referencing the Ethereum specifications in clients. This will:
30
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.
31
+ - Help developers keep track of specification changes.
32
+ - Help auditors find important functions in client implementations.
35
33
 
36
- ## Getting started
34
+ ## Table of contents
37
35
 
38
- ### Installation
36
+ - [Getting Started](#getting-started)
37
+ - [Install](#install)
38
+ - [Initialize](#initialize)
39
+ - [Inline References](#inline-references)
40
+ - [Centralized References](#centralized-references)
41
+ - [Configure](#configure)
42
+ - [Update](#update)
43
+ - [Map](#map)
44
+ - [Check](#check)
45
+ - [Exceptions](#exceptions)
46
+ - [Style Options](#style-options)
47
+ - [`hash`](#hash)
48
+ - [`full`](#full)
49
+ - [`link`](#link)
50
+ - [`diff`](#diff)
51
+
52
+ ## Getting Started
53
+
54
+ ### Install
39
55
 
40
56
  ```
41
57
  pipx install ethspecify
42
58
  ```
43
59
 
44
- ### Initialize specrefs
60
+ ### Initialize
45
61
 
46
- From the root of the client source directory, initialize a directory for specrefs:
62
+ Create a `.ethspecify.yml` config file:
47
63
 
48
64
  ```
49
65
  $ ethspecify init v1.6.0-beta.0
66
+ Successfully created .ethspecify.yml
67
+ ```
68
+
69
+ To also generate a `specrefs/` directory with a YAML file for each
70
+ specification category, use the `--specrefs` flag:
71
+
72
+ ```
73
+ $ ethspecify init v1.6.0-beta.0 --specrefs
50
74
  Initializing specrefs directory: v1.6.0-beta.0
51
- Successfully created specrefs directory at: specrefs
75
+ Successfully created .ethspecify.yml and specrefs/ directory
76
+ ```
77
+
78
+ > [!TIP]
79
+ > `nightly` is also a valid version, which tracks the latest
80
+ > development specifications.
81
+
82
+ ## Inline References
83
+
84
+ Add `<spec>` tags anywhere in the codebase to reference specification
85
+ items. For example:
86
+
87
+ ```
88
+ <spec fn="is_active_validator" fork="phase0" />
89
+ ```
90
+
91
+ Then run `ethspecify` to populate the tag body with the corresponding
92
+ specification content:
93
+
94
+ ```
95
+ <spec fn="is_active_validator" fork="phase0" hash="5765e850">
96
+ def is_active_validator(validator: Validator, epoch: Epoch) -> bool:
97
+ """
98
+ Check if ``validator`` is active.
99
+ """
100
+ return validator.activation_epoch <= epoch < validator.exit_epoch
101
+ </spec>
52
102
  ```
53
103
 
54
- This creates a `specrefs` directory with `.ethspecify.yml` and YAML files for each spec category
55
- (constants, configs, presets, functions, containers, dataclasses, types).
104
+ > [!NOTE]
105
+ > The indentation of the `<spec>` tag is preserved when populating
106
+ > the body. This means spec tags inside comments will have their
107
+ > content indented to match:
108
+ >
109
+ > ```java
110
+ > // <spec fn="is_active_validator" fork="phase0" hash="5765e850">
111
+ > // def is_active_validator(validator: Validator, epoch: Epoch) -> bool:
112
+ > // """
113
+ > // Check if ``validator`` is active.
114
+ > // """
115
+ > // return validator.activation_epoch <= epoch < validator.exit_epoch
116
+ > // </spec>
117
+ > ```
118
+
119
+ ## Centralized References
120
+
121
+ Specification references (specrefs) are YAML files that map
122
+ specification items (constants, functions, containers, etc.) to
123
+ their corresponding source file locations.
124
+
125
+ ### Configure
126
+
127
+ The following options can be set in the `specrefs` section of
128
+ `.ethspecify.yml`:
129
+
130
+ | Option | Default | Description |
131
+ |--------|---------|-------------|
132
+ | `search_root` | `.` | Root directory for resolving source file paths |
133
+ | `auto_standardize_names` | `false` | Rename entries to `item#fork` format |
134
+ | `auto_add_missing_entries` | `false` | Add missing specification items with empty sources |
135
+ | `require_exceptions_have_fork` | `false` | Require exceptions to use `item#fork` format |
136
+
137
+ ### Update
138
+
139
+ To update to a newer specification version, change the `version`
140
+ field in `.ethspecify.yml` and run:
56
141
 
57
- ### Map sources
142
+ ```
143
+ $ ethspecify
144
+ ```
145
+
146
+ This updates the specification content in the YAML files to match
147
+ the new version.
58
148
 
59
- Edit the YAML files to add sources for where each spec item is implemented.
149
+ ### Map
60
150
 
61
- If it's the entire file:
151
+ Edit the YAML files to add sources for where each specification
152
+ item is implemented.
153
+
154
+ If it is the entire file:
62
155
 
63
156
  ```yaml
64
157
  - name: BlobParameters
@@ -72,7 +165,7 @@ If it's the entire file:
72
165
  </spec>
73
166
  ```
74
167
 
75
- If it's multiple entire files:
168
+ If it is multiple entire files:
76
169
 
77
170
  ```yaml
78
171
  - name: BlobsBundleDeneb
@@ -90,7 +183,7 @@ If it's multiple entire files:
90
183
  </spec>
91
184
  ```
92
185
 
93
- If it's a specific part of a file:
186
+ If it is a specific part of a file:
94
187
 
95
188
  ```yaml
96
189
  - name: EFFECTIVE_BALANCE_INCREMENT
@@ -103,7 +196,7 @@ If it's a specific part of a file:
103
196
  </spec>
104
197
  ```
105
198
 
106
- You can also use regex in the searches if that is necessary:
199
+ Regex is also supported in searches:
107
200
 
108
201
  ```yaml
109
202
  - name: ATTESTATION_DUE_BPS
@@ -117,56 +210,57 @@ You can also use regex in the searches if that is necessary:
117
210
  </spec>
118
211
  ```
119
212
 
120
- ### Check specrefs
213
+ ### Check
121
214
 
122
- Run the check command in CI to verify all spec items are properly mapped:
215
+ Run the check command in CI to verify all specification items are
216
+ properly mapped:
123
217
 
124
218
  ```
125
- $ ethspecify check --path=specrefs
219
+ $ ethspecify check
126
220
  MISSING: constants.BLS_MODULUS#deneb
127
221
  ```
128
222
 
129
- ### Add exceptions
223
+ ### Exceptions
130
224
 
131
- Some spec items may not be implemented in your client. Add them to the exceptions list in
132
- `specrefs/.ethspecify.yml`:
225
+ Some specification items may not have a corresponding
226
+ implementation. Add them to the exceptions list in
227
+ `.ethspecify.yml`:
133
228
 
134
229
  ```yaml
135
230
  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
231
+ exceptions:
232
+ containers:
233
+ # Not defined, unnecessary
234
+ - Eth1Block
235
+
236
+ functions:
237
+ # No light client support
238
+ - is_valid_light_client_header
239
+ - process_light_client_update
150
240
  ```
151
241
 
152
242
  ## Style Options
153
243
 
154
- This attribute can be used to change how the specification content is shown.
244
+ This attribute can be used to change how the specification content
245
+ is shown.
155
246
 
156
- ### `hash` (default)
247
+ ### `hash`
157
248
 
158
- This style adds a hash of the specification content to the spec tag, without showing the content.
249
+ This style adds a hash of the specification content to the
250
+ `<spec>` tag, without showing the content.
159
251
 
160
252
  ```
161
253
  <spec fn="apply_deposit" fork="electra" hash="c723ce7b" />
162
254
  ```
163
255
 
164
256
  > [!NOTE]
165
- > The hash is the first 8 characters of the specification content's SHA256 digest.
257
+ > The hash is the first 8 characters of the specification
258
+ > content's SHA256 digest.
166
259
 
167
260
  ### `full`
168
261
 
169
- This style displays the whole content of this specification item, including comments.
262
+ This style displays the whole content of this specification item,
263
+ including comments.
170
264
 
171
265
  ```
172
266
  <spec fn="is_fully_withdrawable_validator" fork="deneb" style="full">
@@ -184,7 +278,8 @@ def is_fully_withdrawable_validator(validator: Validator, balance: Gwei, epoch:
184
278
 
185
279
  ### `link`
186
280
 
187
- This style displays an ethspec.tools link to the specification item.
281
+ This style displays an ethspec.tools link to the specification
282
+ item.
188
283
 
189
284
  ```
190
285
  <spec fn="apply_pending_deposit" fork="electra" style="link" hash="83ee9126">
@@ -194,7 +289,8 @@ https://ethspec.tools/#specs/v1.7.0-alpha.1/functions-apply_pending_deposit-elec
194
289
 
195
290
  ### `diff`
196
291
 
197
- This style displays a diff with the previous fork's version of the specification.
292
+ This style displays a diff with the previous fork's version of the
293
+ specification.
198
294
 
199
295
  ```
200
296
  <spec ssz_object="BeaconState" fork="electra" style="diff">
@@ -217,10 +313,12 @@ This style displays a diff with the previous fork's version of the specification
217
313
  ```
218
314
 
219
315
  > [!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.
316
+ > Comments are stripped from the specifications when the `diff`
317
+ > style is used. This is because they complicate the diff; the
318
+ > "[Modified in Fork]" comments are not valuable here.
222
319
 
223
- This can be used with any specification item, like functions too:
320
+ This can be used with any specification item, like functions
321
+ too:
224
322
 
225
323
  ```
226
324
  <spec fn="is_eligible_for_activation_queue" fork="electra" style="diff">
@@ -8,7 +8,7 @@ long_description = (this_directory / "README.md").read_text(encoding="utf-8")
8
8
 
9
9
  setup(
10
10
  name="ethspecify",
11
- version="0.3.7",
11
+ version="0.3.9",
12
12
  description="A utility for processing Ethereum specification tags.",
13
13
  long_description=long_description,
14
14
  long_description_content_type="text/markdown",
@@ -1,211 +0,0 @@
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 an ethspec.tools link to the specification item.
162
-
163
- ```
164
- <spec fn="apply_pending_deposit" fork="electra" style="link" hash="83ee9126">
165
- https://ethspec.tools/#specs/v1.7.0-alpha.1/functions-apply_pending_deposit-electra
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
- ```
File without changes
File without changes