1
+ # beacon_chain
2
+ # Copyright (c) 2025 Status Research & Development GmbH
3
+ # Licensed and distributed under either of
4
+ # * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
5
+ # * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
6
+ # at your option. This file may not be copied, modified, or distributed except according to those terms.
7
+
8
+ {.push raises: [].}
9
+ {.used.}
10
+
11
+ import
12
+ # Utilities
13
+ chronicles,
14
+ unittest2,
15
+ # Beacon chain internals
16
+ ../../../ beacon_chain/ spec/ state_transition_block,
17
+ ../../../ beacon_chain/ spec/ datatypes/ fulu,
18
+ # Test utilities
19
+ ../../ testutil,
20
+ ../ fixtures_utils, ../ os_ops,
21
+ ../../ helpers/ debug_state
22
+
23
+ from std/ sequtils import anyIt, mapIt, toSeq
24
+ from std/ strutils import contains
25
+ from ../../../ beacon_chain/ spec/ beaconstate import
26
+ get_base_reward_per_increment, get_state_exit_queue_info,
27
+ get_total_active_balance, latest_block_root, process_attestation
28
+
29
+ const
30
+ OpDir = SszTestsDir/ const_preset/ " fulu" / " operations"
31
+ OpAttestationsDir = OpDir/ " attestation"
32
+ OpAttSlashingDir = OpDir/ " attester_slashing"
33
+ OpBlockHeaderDir = OpDir/ " block_header"
34
+ OpBlsToExecutionChangeDir = OpDir/ " bls_to_execution_change"
35
+ OpConsolidationRequestDir = OpDir/ " consolidation_request"
36
+ OpDepositRequestDir = OpDir/ " deposit_request"
37
+ OpDepositsDir = OpDir/ " deposit"
38
+ OpWithdrawalRequestDir = OpDir/ " withdrawal_request"
39
+ OpExecutionPayloadDir = OpDir/ " execution_payload"
40
+ OpProposerSlashingDir = OpDir/ " proposer_slashing"
41
+ OpSyncAggregateDir = OpDir/ " sync_aggregate"
42
+ OpVoluntaryExitDir = OpDir/ " voluntary_exit"
43
+ OpWithdrawalsDir = OpDir/ " withdrawals"
44
+
45
+ baseDescription = " EF - Fulu - Operations - "
46
+
47
+
48
+ const testDirs = toHashSet([
49
+ OpAttestationsDir, OpAttSlashingDir, OpBlockHeaderDir,
50
+ OpBlsToExecutionChangeDir, OpConsolidationRequestDir, OpDepositRequestDir,
51
+ OpDepositsDir, OpWithdrawalRequestDir, OpExecutionPayloadDir,
52
+ OpProposerSlashingDir, OpSyncAggregateDir, OpVoluntaryExitDir,
53
+ OpWithdrawalsDir])
54
+
55
+ doAssert toHashSet(
56
+ mapIt(toSeq(walkDir(OpDir, relative = false )), it.path)) == testDirs
57
+
58
+ proc runTest[T, U](
59
+ testSuiteDir, suiteName, opName, applyFile: string ,
60
+ applyProc: U, identifier: string ) =
61
+ let testDir = testSuiteDir / "pyspec_tests" / identifier
62
+
63
+ let prefix =
64
+ if fileExists(testDir/"post.ssz_snappy"):
65
+ "[Valid] "
66
+ else:
67
+ "[Invalid] "
68
+
69
+ test prefix & baseDescription & opName & " - " & identifier:
70
+ let preState = newClone(
71
+ parseTest(testDir/"pre.ssz_snappy", SSZ, fulu.BeaconState))
72
+ let done = applyProc(
73
+ preState[], parseTest(testDir/(applyFile & ".ssz_snappy"), SSZ, T))
74
+
75
+ if fileExists(testDir/"post.ssz_snappy"):
76
+ let postState =
77
+ newClone(parseTest(
78
+ testDir/"post.ssz_snappy", SSZ, fulu.BeaconState))
79
+
80
+ reportDiff(preState, postState)
81
+ check:
82
+ done.isOk()
83
+ preState[].hash_tree_root() == postState[].hash_tree_root()
84
+ else:
85
+ check: done.isErr() # No post state = processing should fail
86
+
87
+ suite baseDescription & "Attestation " & preset():
88
+ proc applyAttestation(
89
+ preState: var fulu.BeaconState, attestation: electra.Attestation):
90
+ Result[void , cstring ] =
91
+ var cache: StateCache
92
+ let
93
+ total_active_balance = get_total_active_balance(preState, cache)
94
+ base_reward_per_increment =
95
+ get_base_reward_per_increment(total_active_balance)
96
+
97
+ # This returns the proposer reward for including the attestation, which
98
+ # isn't tested here.
99
+ discard ? process_attestation(
100
+ preState, attestation, {strictVerification}, base_reward_per_increment, cache)
101
+ ok()
102
+
103
+ for path in walkTests(OpAttestationsDir):
104
+ runTest[electra.Attestation, typeof applyAttestation](
105
+ OpAttestationsDir, suiteName, "Attestation", "attestation",
106
+ applyAttestation, path)
107
+
108
+ suite baseDescription & "Attester Slashing " & preset():
109
+ proc applyAttesterSlashing(
110
+ preState: var fulu.BeaconState,
111
+ attesterSlashing: electra.AttesterSlashing): Result[void , cstring ] =
112
+ var cache: StateCache
113
+ doAssert (? process_attester_slashing(
114
+ defaultRuntimeConfig, preState, attesterSlashing, {},
115
+ get_state_exit_queue_info(preState), cache))[ 0] > 0 .Gwei
116
+ ok()
117
+
118
+ for path in walkTests(OpAttSlashingDir):
119
+ runTest[electra.AttesterSlashing, typeof applyAttesterSlashing](
120
+ OpAttSlashingDir, suiteName, "Attester Slashing", "attester_slashing",
121
+ applyAttesterSlashing, path)
122
+
123
+ suite baseDescription & "Block Header " & preset():
124
+ proc applyBlockHeader(
125
+ preState: var fulu.BeaconState, blck: fulu.BeaconBlock):
126
+ Result[void , cstring ] =
127
+ if blck.is_execution_block:
128
+ check blck.body.execution_payload.block_hash ==
129
+ blck.compute_execution_block_hash()
130
+ var cache: StateCache
131
+ process_block_header(preState, blck, {}, cache)
132
+
133
+ for path in walkTests(OpBlockHeaderDir):
134
+ runTest[fulu.BeaconBlock, typeof applyBlockHeader](
135
+ OpBlockHeaderDir, suiteName, "Block Header", "block",
136
+ applyBlockHeader, path)
137
+
138
+ from ../../../beacon_chain/spec/datatypes/capella import
139
+ SignedBLSToExecutionChange
140
+
141
+ suite baseDescription & "BLS to execution change " & preset():
142
+ proc applyBlsToExecutionChange(
143
+ preState: var fulu.BeaconState,
144
+ signed_address_change: SignedBLSToExecutionChange):
145
+ Result[void , cstring ] =
146
+ process_bls_to_execution_change(
147
+ defaultRuntimeConfig, preState, signed_address_change)
148
+
149
+ for path in walkTests(OpBlsToExecutionChangeDir):
150
+ runTest[SignedBLSToExecutionChange, typeof applyBlsToExecutionChange](
151
+ OpBlsToExecutionChangeDir, suiteName, "BLS to execution change", "address_change",
152
+ applyBlsToExecutionChange, path)
153
+
154
+ from ".."/".."/".."/beacon_chain/validator_bucket_sort import
155
+ sortValidatorBuckets
156
+
157
+ suite baseDescription & "Consolidation Request " & preset():
158
+ proc applyConsolidationRequest(
159
+ preState: var fulu.BeaconState,
160
+ consolidation_request: ConsolidationRequest): Result[void , cstring ] =
161
+ var cache: StateCache
162
+ process_consolidation_request(
163
+ defaultRuntimeConfig, preState,
164
+ sortValidatorBuckets(preState.validators.asSeq)[],
165
+ consolidation_request, cache)
166
+ ok()
167
+
168
+ for path in walkTests(OpConsolidationRequestDir):
169
+ runTest[ConsolidationRequest, typeof applyConsolidationRequest](
170
+ OpConsolidationRequestDir, suiteName, "Consolidation Request",
171
+ "consolidation_request", applyConsolidationRequest, path)
172
+
173
+ suite baseDescription & "Deposit " & preset():
174
+ func applyDeposit(
175
+ preState: var fulu.BeaconState, deposit: Deposit):
176
+ Result[void , cstring ] =
177
+ process_deposit(
178
+ defaultRuntimeConfig, preState,
179
+ sortValidatorBuckets(preState.validators.asSeq)[], deposit, {})
180
+
181
+ for path in walkTests(OpDepositsDir):
182
+ runTest[Deposit, typeof applyDeposit](
183
+ OpDepositsDir, suiteName, "Deposit", "deposit", applyDeposit, path)
184
+
185
+ suite baseDescription & "Deposit Request " & preset():
186
+ func applyDepositRequest(
187
+ preState: var fulu.BeaconState, depositRequest: DepositRequest):
188
+ Result[void , cstring ] =
189
+ process_deposit_request(
190
+ defaultRuntimeConfig, preState, depositRequest, {})
191
+
192
+ for path in walkTests(OpDepositRequestDir):
193
+ runTest[DepositRequest, typeof applyDepositRequest](
194
+ OpDepositRequestDir, suiteName, "Deposit Request", "deposit_request",
195
+ applyDepositRequest, path)
196
+
197
+ suite baseDescription & "Execution Payload " & preset():
198
+ func makeApplyExecutionPayloadCb(path: string ): auto =
199
+ return proc(
200
+ preState: var fulu.BeaconState, body: fulu.BeaconBlockBody):
201
+ Result[void , cstring ] {.raises: [IOError].} =
202
+ let payloadValid = os_ops.readFile(
203
+ OpExecutionPayloadDir/"pyspec_tests"/path/"execution.yaml"
204
+ ).contains("execution_valid: true")
205
+ if payloadValid and body.is_execution_block and
206
+ not body.execution_payload.transactions.anyIt(it.len == 0):
207
+ let expectedOk = (path != "incorrect_block_hash")
208
+ check expectedOk == (body.execution_payload.block_hash ==
209
+ body.compute_execution_block_hash(
210
+ preState.latest_block_root(
211
+ assignClone(preState)[].hash_tree_root())))
212
+ func executePayload(_: fulu.ExecutionPayload): bool = payloadValid
213
+ process_execution_payload(
214
+ defaultRuntimeConfig, preState, body, executePayload)
215
+
216
+ for path in walkTests(OpExecutionPayloadDir):
217
+ let applyExecutionPayload = makeApplyExecutionPayloadCb(path)
218
+ runTest[fulu.BeaconBlockBody, typeof applyExecutionPayload](
219
+ OpExecutionPayloadDir, suiteName, "Execution Payload", "body",
220
+ applyExecutionPayload, path)
221
+
222
+ suite baseDescription & "Withdrawal Request " & preset():
223
+ func applyWithdrawalRequest(
224
+ preState: var fulu.BeaconState, withdrawalRequest: WithdrawalRequest):
225
+ Result[void , cstring ] =
226
+ var cache: StateCache
227
+ process_withdrawal_request(
228
+ defaultRuntimeConfig, preState,
229
+ sortValidatorBuckets(preState.validators.asSeq)[], withdrawalRequest,
230
+ cache)
231
+ ok()
232
+
233
+ for path in walkTests(OpWithdrawalRequestDir):
234
+ runTest[WithdrawalRequest, typeof applyWithdrawalRequest](
235
+ OpWithdrawalRequestDir, suiteName, "Withdrawal Request",
236
+ "withdrawal_request", applyWithdrawalRequest, path)
237
+
238
+ suite baseDescription & "Proposer Slashing " & preset():
239
+ proc applyProposerSlashing(
240
+ preState: var fulu.BeaconState, proposerSlashing: ProposerSlashing):
241
+ Result[void , cstring ] =
242
+ var cache: StateCache
243
+ doAssert (? process_proposer_slashing(
244
+ defaultRuntimeConfig, preState, proposerSlashing, {},
245
+ get_state_exit_queue_info(preState), cache))[ 0] > 0 .Gwei
246
+ ok()
247
+
248
+ for path in walkTests(OpProposerSlashingDir):
249
+ runTest[ProposerSlashing, typeof applyProposerSlashing](
250
+ OpProposerSlashingDir, suiteName, "Proposer Slashing", "proposer_slashing",
251
+ applyProposerSlashing, path)
252
+
253
+ suite baseDescription & "Sync Aggregate " & preset():
254
+ proc applySyncAggregate(
255
+ preState: var fulu.BeaconState, syncAggregate: SyncAggregate):
256
+ Result[void , cstring ] =
257
+ var cache: StateCache
258
+ discard ? process_sync_aggregate(
259
+ preState, syncAggregate, get_total_active_balance(preState, cache),
260
+ {}, cache)
261
+ ok()
262
+
263
+ for path in walkTests(OpSyncAggregateDir):
264
+ runTest[SyncAggregate, typeof applySyncAggregate](
265
+ OpSyncAggregateDir, suiteName, "Sync Aggregate", "sync_aggregate",
266
+ applySyncAggregate, path)
267
+
268
+ suite baseDescription & "Voluntary Exit " & preset():
269
+ proc applyVoluntaryExit(
270
+ preState: var fulu.BeaconState, voluntaryExit: SignedVoluntaryExit):
271
+ Result[void , cstring ] =
272
+ var cache: StateCache
273
+ if process_voluntary_exit(
274
+ defaultRuntimeConfig, preState, voluntaryExit, {},
275
+ get_state_exit_queue_info(preState), cache).isOk:
276
+ ok()
277
+ else:
278
+ err("")
279
+
280
+ for path in walkTests(OpVoluntaryExitDir):
281
+ runTest[SignedVoluntaryExit, typeof applyVoluntaryExit](
282
+ OpVoluntaryExitDir, suiteName, "Voluntary Exit", "voluntary_exit",
283
+ applyVoluntaryExit, path)
284
+
285
+ suite baseDescription & "Withdrawals " & preset():
286
+ func applyWithdrawals(
287
+ preState: var fulu.BeaconState,
288
+ executionPayload: fulu.ExecutionPayload): Result[void , cstring ] =
289
+ process_withdrawals(preState, executionPayload)
290
+
291
+ for path in walkTests(OpWithdrawalsDir):
292
+ runTest[fulu.ExecutionPayload, typeof applyWithdrawals](
293
+ OpWithdrawalsDir, suiteName, "Withdrawals", "execution_payload",
294
+ applyWithdrawals, path)
0 commit comments