mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 15:37:56 -05:00
e2e test
This commit is contained in:
@@ -649,6 +649,19 @@ func (f *ForkChoice) DependentRootForEpoch(root [32]byte, epoch primitives.Epoch
|
||||
return f.store.finalizedDependentRoot, nil
|
||||
}
|
||||
}
|
||||
|
||||
// E2E TEST: Log the dependent root result for verification
|
||||
// The returned root should be from epoch-1, not from epoch or later
|
||||
returnedEpoch := slots.ToEpoch(node.slot)
|
||||
if returnedEpoch >= epoch {
|
||||
// BUG DETECTED: returning a root from the wrong epoch
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"requestedEpoch": epoch,
|
||||
"returnedSlot": node.slot,
|
||||
"returnedEpoch": returnedEpoch,
|
||||
"returnedRoot": fmt.Sprintf("%#x", node.root),
|
||||
}).Error("E2E_DEPENDENT_ROOT_BUG: dependent root is from wrong epoch")
|
||||
}
|
||||
return node.root, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -94,6 +94,7 @@ go_test(
|
||||
"component_handler_test.go",
|
||||
"endtoend_setup_test.go",
|
||||
"endtoend_test.go",
|
||||
"minimal_dependent_root_e2e_test.go",
|
||||
"minimal_e2e_test.go",
|
||||
"minimal_slashing_e2e_test.go",
|
||||
"slasher_simulator_e2e_test.go",
|
||||
|
||||
@@ -6,6 +6,7 @@ go_library(
|
||||
srcs = [
|
||||
"builder.go",
|
||||
"data.go",
|
||||
"dependent_root.go",
|
||||
"execution_engine.go",
|
||||
"fee_recipient.go",
|
||||
"finality.go",
|
||||
|
||||
95
testing/endtoend/evaluators/dependent_root.go
Normal file
95
testing/endtoend/evaluators/dependent_root.go
Normal file
@@ -0,0 +1,95 @@
|
||||
package evaluators
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
|
||||
e2e "github.com/OffchainLabs/prysm/v7/testing/endtoend/params"
|
||||
"github.com/OffchainLabs/prysm/v7/testing/endtoend/policies"
|
||||
"github.com/OffchainLabs/prysm/v7/testing/endtoend/types"
|
||||
"github.com/pkg/errors"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
// DependentRootEvaluators returns evaluators for testing dependent root handling.
|
||||
// These evaluators verify that the dependent root bug does NOT occur:
|
||||
// - When a block at the first slot of an epoch becomes finalized and its parent pruned
|
||||
// - The dependent root query should still return correct data (from the previous epoch)
|
||||
//
|
||||
// The evaluator checks for "E2E_DEPENDENT_ROOT_BUG" error log which indicates
|
||||
// a dependent root was returned from the wrong epoch.
|
||||
//
|
||||
// WITH the fix: No bug log appears → test PASSES
|
||||
// WITHOUT the fix: Bug log appears (wrong epoch data) → test FAILS
|
||||
func DependentRootEvaluators(afterEpoch primitives.Epoch) []types.Evaluator {
|
||||
return []types.Evaluator{
|
||||
{
|
||||
Name: "no_dependent_root_bug_epoch_%d",
|
||||
Policy: policies.AfterNthEpoch(afterEpoch + 2), // Allow time for finalization
|
||||
Evaluation: checkNoDependentRootBug,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// checkNoDependentRootBug scans beacon node logs to verify that the dependent root
|
||||
// bug did NOT occur. The bug would manifest as returning a dependent root from
|
||||
// the wrong epoch (current epoch instead of previous epoch).
|
||||
//
|
||||
// The beacon node logs "E2E_DEPENDENT_ROOT_BUG" when it detects this condition.
|
||||
//
|
||||
// WITH the fix: No bug log appears → test PASSES
|
||||
// WITHOUT the fix: Bug log appears → test FAILS
|
||||
func checkNoDependentRootBug(_ *types.EvaluationContext, _ ...*grpc.ClientConn) error {
|
||||
// This log message indicates the bug was triggered
|
||||
bugLogMessage := "E2E_DEPENDENT_ROOT_BUG"
|
||||
|
||||
for i := 0; i < e2e.TestParams.BeaconNodeCount; i++ {
|
||||
logFile := path.Join(e2e.TestParams.LogPath, fmt.Sprintf(e2e.BeaconNodeLogFileName, i))
|
||||
found, err := searchLogForMessages(logFile, []string{bugLogMessage})
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "failed to search beacon node %d log file", i)
|
||||
}
|
||||
if found {
|
||||
// Bug was detected - test FAILS
|
||||
return errors.New("E2E_DEPENDENT_ROOT_BUG detected: dependent root returned from wrong epoch - the fix is not working or not present")
|
||||
}
|
||||
}
|
||||
|
||||
// No bug detected - test PASSES
|
||||
log.Info("No dependent root bug detected - fix is working correctly")
|
||||
return nil
|
||||
}
|
||||
|
||||
// searchLogForMessages searches a log file for any of the given messages.
|
||||
func searchLogForMessages(logPath string, messages []string) (bool, error) {
|
||||
file, err := os.Open(logPath) // #nosec G304 -- test code only
|
||||
if err != nil {
|
||||
return false, errors.Wrap(err, "failed to open log file")
|
||||
}
|
||||
defer func() {
|
||||
if err := file.Close(); err != nil {
|
||||
log.WithError(err).Error("Failed to close log file")
|
||||
}
|
||||
}()
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
for _, msg := range messages {
|
||||
if strings.Contains(line, msg) {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
return false, errors.Wrap(err, "error scanning log file")
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
37
testing/endtoend/minimal_dependent_root_e2e_test.go
Normal file
37
testing/endtoend/minimal_dependent_root_e2e_test.go
Normal file
@@ -0,0 +1,37 @@
|
||||
package endtoend
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/config/params"
|
||||
"github.com/OffchainLabs/prysm/v7/runtime/version"
|
||||
ev "github.com/OffchainLabs/prysm/v7/testing/endtoend/evaluators"
|
||||
"github.com/OffchainLabs/prysm/v7/testing/endtoend/types"
|
||||
)
|
||||
|
||||
// TestEndToEnd_MinimalConfig_DependentRoot tests that the beacon node correctly
|
||||
// handles the dependent root bug scenario where:
|
||||
// 1. A block at the first slot of an epoch becomes finalized
|
||||
// 2. The parent of that block is pruned during finalization
|
||||
// 3. Dependent root queries for that epoch would return wrong data without the fix
|
||||
//
|
||||
// The test adds verification logging that detects when a dependent root is
|
||||
// returned from the wrong epoch (E2E_DEPENDENT_ROOT_BUG).
|
||||
//
|
||||
// WITH the fix: No bug is detected → test PASSES
|
||||
// WITHOUT the fix: Bug is detected → test FAILS
|
||||
func TestEndToEnd_MinimalConfig_DependentRoot(t *testing.T) {
|
||||
r := e2eMinimalDependentRoot(t, types.InitForkCfg(version.Electra, version.Electra, params.E2ETestConfig()))
|
||||
r.run()
|
||||
}
|
||||
|
||||
func e2eMinimalDependentRoot(t *testing.T, cfg *params.BeaconChainConfig) *testRunner {
|
||||
// Use the standard e2eMinimal setup with the dependent root evaluators
|
||||
r := e2eMinimal(t, cfg,
|
||||
func(c *types.E2EConfig) {
|
||||
// Add the dependent root evaluators to check for the bug scenario
|
||||
c.Evaluators = append(c.Evaluators, ev.DependentRootEvaluators(2)...)
|
||||
},
|
||||
)
|
||||
return r
|
||||
}
|
||||
Reference in New Issue
Block a user