@@ -61,8 +61,8 @@ class StateTransitionTest(BaseConsensusFixture):
6161 """
6262 The filled Blocks, processed through the specs.
6363
64- This is a private attribute not part of the model schema. Tests cannot set this.
65- The framework populates it during make_fixture().
64+ This is a private attribute not part of the model schema. Tests cannot set
65+ this. The framework populates it during make_fixture().
6666 """
6767
6868 post : StateExpectation | None = None
@@ -76,6 +76,9 @@ class StateTransitionTest(BaseConsensusFixture):
7676 expect_exception : type [Exception ] | None = None
7777 """Expected exception type for invalid tests."""
7878
79+ expect_exception_message : str | None = None
80+ """Expected exception message for invalid tests."""
81+
7982 @field_serializer ("blocks" , when_used = "json" )
8083 def serialize_blocks (self , value : List [BlockSpec ]) -> List [dict [str , Any ]]:
8184 """
@@ -139,11 +142,15 @@ def make_fixture(self) -> "StateTransitionTest":
139142 filled_blocks .append (block )
140143
141144 # Use cached state if available, otherwise run state transition
142- state = (
143- cached_state
144- if cached_state is not None
145- else state .state_transition (block = block , valid_signatures = True )
146- )
145+ if cached_state is not None :
146+ state = cached_state
147+ elif getattr (block_spec , "skip_slot_processing" , False ):
148+ state = state .process_block (block )
149+ else :
150+ state = state .state_transition (
151+ block = block ,
152+ valid_signatures = True ,
153+ )
147154
148155 actual_post_state = state
149156 except (AssertionError , ValueError ) as e :
@@ -168,6 +175,12 @@ def make_fixture(self) -> "StateTransitionTest":
168175 f"Expected { self .expect_exception .__name__ } "
169176 f"but got { type (exception_raised ).__name__ } : { exception_raised } "
170177 )
178+ if self .expect_exception_message is not None :
179+ if str (exception_raised ) != self .expect_exception_message :
180+ raise AssertionError (
181+ f"Expected exception message '{ self .expect_exception_message } ' "
182+ f"but got '{ exception_raised } '"
183+ )
171184
172185 # Validate post-state expectations if provided
173186 if self .post is not None and actual_post_state is not None :
@@ -198,12 +211,16 @@ def _build_block_from_spec(self, spec: BlockSpec, state: State) -> tuple[Block,
198211 # Use provided proposer index or compute it
199212 proposer_index = spec .proposer_index or Uint64 (int (spec .slot ) % len (state .validators ))
200213
201- # Use provided parent root or compute it
214+ temp_state : State | None = None
215+ if not spec .skip_slot_processing :
216+ temp_state = state .process_slots (spec .slot )
217+
218+ # Use provided parent_root or compute it
202219 if spec .parent_root is not None :
203220 parent_root = spec .parent_root
204221 else :
205- temp_state = state . process_slots ( spec . slot )
206- parent_root = hash_tree_root (temp_state .latest_block_header )
222+ source_state = temp_state if temp_state is not None else state
223+ parent_root = hash_tree_root (source_state .latest_block_header )
207224
208225 # Extract attestations from body if provided
209226 aggregated_attestations = (
@@ -221,25 +238,24 @@ def _build_block_from_spec(self, spec: BlockSpec, state: State) -> tuple[Block,
221238 )
222239 return block , None
223240
224- # For invalid tests, return incomplete block without processing
225- if self . expect_exception is not None :
226- block = Block (
227- slot = spec . slot ,
228- proposer_index = proposer_index ,
229- parent_root = parent_root ,
230- state_root = Bytes32 . zero (),
231- body = spec . body or BlockBody ( attestations = aggregated_attestations ),
232- )
233- return block , None
241+ temp_block = Block (
242+ slot = spec . slot ,
243+ proposer_index = proposer_index ,
244+ parent_root = parent_root ,
245+ state_root = Bytes32 . zero () ,
246+ body = spec . body or BlockBody ( attestations = aggregated_attestations ) ,
247+ )
248+
249+ if self . expect_exception is not None or spec . skip_slot_processing :
250+ return temp_block , None
234251
235- # Build the block using the state for standard case
236- #
237252 # Convert aggregated attestations to plain attestations to build block
238253 plain_attestations = [
239254 Attestation (validator_id = vid , data = agg .data )
240255 for agg in aggregated_attestations
241256 for vid in agg .aggregation_bits .to_validator_indices ()
242257 ]
258+
243259 block , post_state , _ , _ = state .build_block (
244260 slot = spec .slot ,
245261 proposer_index = proposer_index ,
0 commit comments