From 631b21fe14b52f452be4ba7db2de4f3be2d1d398 Mon Sep 17 00:00:00 2001 From: Potuz Date: Fri, 2 Jan 2026 14:35:15 -0300 Subject: [PATCH] Add unit tests for handleStateRootError recursion limit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../prysm/v1alpha1/validator/proposer_test.go | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_test.go b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_test.go index 6455882424..30a4bf17ff 100644 --- a/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_test.go +++ b/beacon-chain/rpc/prysm/v1alpha1/validator/proposer_test.go @@ -1313,6 +1313,59 @@ func TestProposer_ComputeStateRoot_OK(t *testing.T) { require.NoError(t, err) } +func TestHandleStateRootError_MaxAttemptsReached(t *testing.T) { + // Test that handleStateRootError returns an error when max attempts is reached + // instead of recursing infinitely. + ctx := t.Context() + vs := &Server{} + + // Create a minimal block for testing + blk := util.NewBeaconBlock() + wsb, err := blocks.NewSignedBeaconBlock(blk) + require.NoError(t, err) + + // Pre-seed the context with max attempts already reached + ctx = context.WithValue(ctx, computeStateRootAttemptsKey, maxComputeStateRootAttempts) + + // Call handleStateRootError with a retryable error + _, err = vs.handleStateRootError(ctx, wsb, transition.ErrAttestationsSignatureInvalid) + + // Should return an error about max attempts instead of recursing + require.ErrorContains(t, "attempted max compute state root attempts", err) +} + +func TestHandleStateRootError_IncrementsAttempts(t *testing.T) { + // Test that handleStateRootError properly increments the attempts counter + // and eventually fails after max attempts. + db := dbutil.SetupDB(t) + ctx := t.Context() + + beaconState, parentRoot, _ := util.DeterministicGenesisStateWithGenesisBlock(t, ctx, db, 100) + + stateGen := stategen.New(db, doublylinkedtree.New()) + vs := &Server{ + StateGen: stateGen, + } + + // Create a block that will trigger retries + blk := util.NewBeaconBlock() + blk.Block.ParentRoot = parentRoot[:] + blk.Block.Slot = 1 + wsb, err := blocks.NewSignedBeaconBlock(blk) + require.NoError(t, err) + + // Add a state for the parent root so StateByRoot succeeds + require.NoError(t, stateGen.SaveState(ctx, parentRoot, beaconState)) + + // Call handleStateRootError with a retryable error - it will recurse + // but eventually hit the max attempts limit since CalculateStateRoot + // will keep failing (no valid attestations, randao, etc.) + _, err = vs.handleStateRootError(ctx, wsb, transition.ErrAttestationsSignatureInvalid) + + // Should eventually fail - either with max attempts or another error + require.NotNil(t, err) +} + func TestProposer_PendingDeposits_Eth1DataVoteOK(t *testing.T) { ctx := t.Context()