Compare commits

..

1 Commits

Author SHA1 Message Date
terence tsao
0afbe5a4a5 gloas: add process execution payload 2026-01-12 06:02:49 +08:00
114 changed files with 3008 additions and 3203 deletions

View File

@@ -72,7 +72,7 @@ Do NOT add new `go_repository` to the WORKSPACE file. All dependencies should li
To enable conditional compilation and custom configuration for tests (where compiled code has more
debug info, while not being completely optimized), we rely on Go's build tags/constraints mechanism
(see official docs on [build constraints](https://pkg.go.dev/go/build#hdr-Build_Constraints)).
(see official docs on [build constraints](https://golang.org/pkg/go/build/#hdr-Build_Constraints)).
Therefore, whenever using `go test`, do not forget to pass in extra build tag, eg:
```bash

View File

@@ -9,7 +9,7 @@ This README details how to setup Prysm for interop testing for usage with other
## Installation & Setup
1. Install [Bazel](https://bazel.build/install) **(Recommended)**
1. Install [Bazel](https://docs.bazel.build/versions/master/install.html) **(Recommended)**
2. `git clone https://github.com/OffchainLabs/prysm && cd prysm`
3. `bazel build //cmd/...`

View File

@@ -15,7 +15,7 @@
## 📖 Overview
This is the core repository for Prysm, a [Golang](https://go.dev/) implementation of the [Ethereum Consensus](https://ethereum.org/en/developers/docs/consensus-mechanisms/#proof-of-stake) [specification](https://github.com/ethereum/consensus-specs), developed by [Offchain Labs](https://www.offchainlabs.com).
This is the core repository for Prysm, a [Golang](https://golang.org/) implementation of the [Ethereum Consensus](https://ethereum.org/en/developers/docs/consensus-mechanisms/#proof-of-stake) [specification](https://github.com/ethereum/consensus-specs), developed by [Offchain Labs](https://www.offchainlabs.com).
See the [Changelog](https://github.com/OffchainLabs/prysm/releases) for details of the latest releases and upcoming breaking changes.
@@ -23,7 +23,7 @@ See the [Changelog](https://github.com/OffchainLabs/prysm/releases) for details
## 🚀 Getting Started
A detailed set of installation and usage instructions as well as breakdowns of each individual component are available in the **[official documentation portal](https://prysm.offchainlabs.com/docs/)**.
A detailed set of installation and usage instructions as well as breakdowns of each individual component are available in the **[official documentation portal](https://docs.prylabs.network)**.
💬 **Need help?** Join our **[Discord Community](https://discord.gg/prysm)** for support.
@@ -51,7 +51,7 @@ Prysm maintains two permanent branches:
### 🛠 Contribution Guide
Want to get involved? Check out our **[Contribution Guide](https://prysm.offchainlabs.com/docs/contribute/contribution-guidelines/)** to learn more!
Want to get involved? Check out our **[Contribution Guide](https://docs.prylabs.network/docs/contribute/contribution-guidelines/)** to learn more!
---

View File

@@ -273,16 +273,16 @@ filegroup(
url = "https://github.com/ethereum/EIPs/archive/5480440fe51742ed23342b68cf106cefd427e39d.tar.gz",
)
consensus_spec_version = "v1.7.0-alpha.1"
consensus_spec_version = "v1.7.0-alpha.0"
load("@prysm//tools:download_spectests.bzl", "consensus_spec_tests")
consensus_spec_tests(
name = "consensus_spec_tests",
flavors = {
"general": "sha256-j5R3jA7Oo4OSDMTvpMuD+8RomaCByeFSwtfkq6fL0Zg=",
"minimal": "sha256-tdTqByoyswOS4r6OxFmo70y2BP7w1TgEok+gf4cbxB0=",
"mainnet": "sha256-5gB4dt6SnSDKzdBc06VedId3NkgvSYyv9n9FRxWKwYI=",
"general": "sha256-b+rJOuVqq+Dy53quPcNYcQwPFoMU7Wp7tdUVe7n0g8w=",
"minimal": "sha256-qxRIxtjPxVsVCY90WsBJKhk0027XDSmhjnRvRN14V1c=",
"mainnet": "sha256-NsuOQG3LzeiEE1TrWuvQ6vu6BboHv7h7f/RTS0pWkCs=",
},
version = consensus_spec_version,
)
@@ -298,7 +298,7 @@ filegroup(
visibility = ["//visibility:public"],
)
""",
integrity = "sha256-J+43DrK1pF658kTXTwMS6zGf4KDjvas++m8w2a8swpg=",
integrity = "sha256-hwNdUBgdBrkk6pWIpNYbzbwswUuOu6AMD2exN8uv+QQ=",
strip_prefix = "consensus-specs-" + consensus_spec_version[1:],
url = "https://github.com/ethereum/consensus-specs/archive/refs/tags/%s.tar.gz" % consensus_spec_version,
)

View File

@@ -74,6 +74,7 @@ go_library(
"//beacon-chain/state:go_default_library",
"//beacon-chain/state/stategen:go_default_library",
"//beacon-chain/verification:go_default_library",
"//cmd/beacon-chain/flags:go_default_library",
"//config/features:go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
@@ -173,6 +174,7 @@ go_test(
"//beacon-chain/state/state-native:go_default_library",
"//beacon-chain/state/stategen:go_default_library",
"//beacon-chain/verification:go_default_library",
"//cmd/beacon-chain/flags:go_default_library",
"//config/features:go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",

View File

@@ -221,19 +221,6 @@ var (
Buckets: []float64{1, 2, 4, 8, 16, 32},
},
)
commitmentCount = promauto.NewHistogram(
prometheus.HistogramOpts{
Name: "commitment_count_max_21",
Help: "The number of blob KZG commitments per block.",
Buckets: []float64{1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21},
},
)
maxBlobsPerBlock = promauto.NewGauge(
prometheus.GaugeOpts{
Name: "max_blobs_per_block",
Help: "The maximum number of blobs allowed in a block.",
},
)
)
// reportSlotMetrics reports slot related metrics.

View File

@@ -16,7 +16,6 @@ import (
"github.com/OffchainLabs/prysm/v7/beacon-chain/slasher/types"
"github.com/OffchainLabs/prysm/v7/beacon-chain/state"
"github.com/OffchainLabs/prysm/v7/config/features"
"github.com/OffchainLabs/prysm/v7/config/params"
"github.com/OffchainLabs/prysm/v7/consensus-types/blocks"
"github.com/OffchainLabs/prysm/v7/consensus-types/interfaces"
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
@@ -259,55 +258,32 @@ func (s *Service) handleDA(ctx context.Context, avs das.AvailabilityChecker, blo
}
func (s *Service) reportPostBlockProcessing(
signedBlock interfaces.SignedBeaconBlock,
block interfaces.SignedBeaconBlock,
blockRoot [32]byte,
receivedTime time.Time,
daWaitedTime time.Duration,
) {
block := signedBlock.Block()
if block == nil {
log.WithField("blockRoot", blockRoot).Error("Nil block")
return
}
// Reports on block and fork choice metrics.
cp := s.cfg.ForkChoiceStore.FinalizedCheckpoint()
finalized := &ethpb.Checkpoint{Epoch: cp.Epoch, Root: bytesutil.SafeCopyBytes(cp.Root[:])}
reportSlotMetrics(block.Slot(), s.HeadSlot(), s.CurrentSlot(), finalized)
reportSlotMetrics(block.Block().Slot(), s.HeadSlot(), s.CurrentSlot(), finalized)
// Log block sync status.
cp = s.cfg.ForkChoiceStore.JustifiedCheckpoint()
justified := &ethpb.Checkpoint{Epoch: cp.Epoch, Root: bytesutil.SafeCopyBytes(cp.Root[:])}
if err := logBlockSyncStatus(block, blockRoot, justified, finalized, receivedTime, s.genesisTime, daWaitedTime); err != nil {
if err := logBlockSyncStatus(block.Block(), blockRoot, justified, finalized, receivedTime, s.genesisTime, daWaitedTime); err != nil {
log.WithError(err).Error("Unable to log block sync status")
}
// Log payload data
if err := logPayload(block); err != nil {
if err := logPayload(block.Block()); err != nil {
log.WithError(err).Error("Unable to log debug block payload data")
}
// Log state transition data.
if err := logStateTransitionData(block); err != nil {
if err := logStateTransitionData(block.Block()); err != nil {
log.WithError(err).Error("Unable to log state transition data")
}
timeWithoutDaWait := time.Since(receivedTime) - daWaitedTime
chainServiceProcessingTime.Observe(float64(timeWithoutDaWait.Milliseconds()))
body := block.Body()
if body == nil {
log.WithField("blockRoot", blockRoot).Error("Nil block body")
return
}
commitments, err := body.BlobKzgCommitments()
if err != nil {
log.WithError(err).Error("Unable to get blob KZG commitments")
}
commitmentCount.Observe(float64(len(commitments)))
maxBlobsPerBlock.Set(float64(params.BeaconConfig().MaxBlobsPerBlock(block.Slot())))
}
func (s *Service) executePostFinalizationTasks(ctx context.Context, finalizedState state.BeaconState) {

View File

@@ -14,6 +14,7 @@ import (
"github.com/OffchainLabs/prysm/v7/beacon-chain/cache"
statefeed "github.com/OffchainLabs/prysm/v7/beacon-chain/core/feed/state"
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/helpers"
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/peerdas"
coreTime "github.com/OffchainLabs/prysm/v7/beacon-chain/core/time"
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/transition"
"github.com/OffchainLabs/prysm/v7/beacon-chain/db"
@@ -29,6 +30,7 @@ import (
"github.com/OffchainLabs/prysm/v7/beacon-chain/startup"
"github.com/OffchainLabs/prysm/v7/beacon-chain/state"
"github.com/OffchainLabs/prysm/v7/beacon-chain/state/stategen"
"github.com/OffchainLabs/prysm/v7/cmd/beacon-chain/flags"
"github.com/OffchainLabs/prysm/v7/config/params"
"github.com/OffchainLabs/prysm/v7/consensus-types/blocks"
"github.com/OffchainLabs/prysm/v7/consensus-types/interfaces"
@@ -289,6 +291,19 @@ func (s *Service) StartFromSavedState(saved state.BeaconState) error {
return errors.Wrap(err, "failed to initialize blockchain service")
}
if !params.FuluEnabled() {
return nil
}
earliestAvailableSlot, custodySubnetCount, err := s.updateCustodyInfoInDB(saved.Slot())
if err != nil {
return errors.Wrap(err, "could not get and save custody group count")
}
if _, _, err := s.cfg.P2P.UpdateCustodyInfo(earliestAvailableSlot, custodySubnetCount); err != nil {
return errors.Wrap(err, "update custody info")
}
return nil
}
@@ -453,6 +468,73 @@ func (s *Service) removeStartupState() {
s.cfg.FinalizedStateAtStartUp = nil
}
// UpdateCustodyInfoInDB updates the custody information in the database.
// It returns the (potentially updated) custody group count and the earliest available slot.
func (s *Service) updateCustodyInfoInDB(slot primitives.Slot) (primitives.Slot, uint64, error) {
isSupernode := flags.Get().Supernode
isSemiSupernode := flags.Get().SemiSupernode
cfg := params.BeaconConfig()
custodyRequirement := cfg.CustodyRequirement
// Check if the node was previously subscribed to all data subnets, and if so,
// store the new status accordingly.
wasSupernode, err := s.cfg.BeaconDB.UpdateSubscribedToAllDataSubnets(s.ctx, isSupernode)
if err != nil {
return 0, 0, errors.Wrap(err, "update subscribed to all data subnets")
}
// Compute the target custody group count based on current flag configuration.
targetCustodyGroupCount := custodyRequirement
// Supernode: custody all groups (either currently set or previously enabled)
if isSupernode {
targetCustodyGroupCount = cfg.NumberOfCustodyGroups
}
// Semi-supernode: custody minimum needed for reconstruction, or custody requirement if higher
if isSemiSupernode {
semiSupernodeCustody, err := peerdas.MinimumCustodyGroupCountToReconstruct()
if err != nil {
return 0, 0, errors.Wrap(err, "minimum custody group count")
}
targetCustodyGroupCount = max(custodyRequirement, semiSupernodeCustody)
}
// Safely compute the fulu fork slot.
fuluForkSlot, err := fuluForkSlot()
if err != nil {
return 0, 0, errors.Wrap(err, "fulu fork slot")
}
// If slot is before the fulu fork slot, then use the earliest stored slot as the reference slot.
if slot < fuluForkSlot {
slot, err = s.cfg.BeaconDB.EarliestSlot(s.ctx)
if err != nil {
return 0, 0, errors.Wrap(err, "earliest slot")
}
}
earliestAvailableSlot, actualCustodyGroupCount, err := s.cfg.BeaconDB.UpdateCustodyInfo(s.ctx, slot, targetCustodyGroupCount)
if err != nil {
return 0, 0, errors.Wrap(err, "update custody info")
}
if isSupernode {
log.WithFields(logrus.Fields{
"current": actualCustodyGroupCount,
"target": cfg.NumberOfCustodyGroups,
}).Info("Supernode mode enabled. Will custody all data columns going forward.")
}
if wasSupernode && !isSupernode {
log.Warningf("Because the `--%s` flag was previously used, the node will continue to act as a super node.", flags.Supernode.Name)
}
return earliestAvailableSlot, actualCustodyGroupCount, nil
}
func spawnCountdownIfPreGenesis(ctx context.Context, genesisTime time.Time, db db.HeadAccessDatabase) {
currentTime := prysmTime.Now()
if currentTime.After(genesisTime) {
@@ -469,3 +551,19 @@ func spawnCountdownIfPreGenesis(ctx context.Context, genesisTime time.Time, db d
}
go slots.CountdownToGenesis(ctx, genesisTime, uint64(gState.NumValidators()), gRoot)
}
func fuluForkSlot() (primitives.Slot, error) {
cfg := params.BeaconConfig()
fuluForkEpoch := cfg.FuluForkEpoch
if fuluForkEpoch == cfg.FarFutureEpoch {
return cfg.FarFutureSlot, nil
}
forkFuluSlot, err := slots.EpochStart(fuluForkEpoch)
if err != nil {
return 0, errors.Wrap(err, "epoch start")
}
return forkFuluSlot, nil
}

View File

@@ -23,9 +23,11 @@ import (
"github.com/OffchainLabs/prysm/v7/beacon-chain/startup"
state_native "github.com/OffchainLabs/prysm/v7/beacon-chain/state/state-native"
"github.com/OffchainLabs/prysm/v7/beacon-chain/state/stategen"
"github.com/OffchainLabs/prysm/v7/cmd/beacon-chain/flags"
"github.com/OffchainLabs/prysm/v7/config/features"
fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams"
"github.com/OffchainLabs/prysm/v7/config/params"
"github.com/OffchainLabs/prysm/v7/consensus-types/blocks"
consensusblocks "github.com/OffchainLabs/prysm/v7/consensus-types/blocks"
"github.com/OffchainLabs/prysm/v7/consensus-types/interfaces"
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
@@ -594,3 +596,218 @@ func TestNotifyIndex(t *testing.T) {
t.Errorf("Notifier channel did not receive the index")
}
}
func TestUpdateCustodyInfoInDB(t *testing.T) {
const (
fuluForkEpoch = 10
custodyRequirement = uint64(4)
earliestStoredSlot = primitives.Slot(12)
numberOfCustodyGroups = uint64(64)
)
params.SetupTestConfigCleanup(t)
cfg := params.BeaconConfig()
cfg.FuluForkEpoch = fuluForkEpoch
cfg.CustodyRequirement = custodyRequirement
cfg.NumberOfCustodyGroups = numberOfCustodyGroups
params.OverrideBeaconConfig(cfg)
ctx := t.Context()
pbBlock := util.NewBeaconBlock()
pbBlock.Block.Slot = 12
signedBeaconBlock, err := blocks.NewSignedBeaconBlock(pbBlock)
require.NoError(t, err)
roBlock, err := blocks.NewROBlock(signedBeaconBlock)
require.NoError(t, err)
t.Run("CGC increases before fulu", func(t *testing.T) {
service, requirements := minimalTestService(t)
err = requirements.db.SaveBlock(ctx, roBlock)
require.NoError(t, err)
// Before Fulu
// -----------
actualEas, actualCgc, err := service.updateCustodyInfoInDB(15)
require.NoError(t, err)
require.Equal(t, earliestStoredSlot, actualEas)
require.Equal(t, custodyRequirement, actualCgc)
actualEas, actualCgc, err = service.updateCustodyInfoInDB(17)
require.NoError(t, err)
require.Equal(t, earliestStoredSlot, actualEas)
require.Equal(t, custodyRequirement, actualCgc)
resetFlags := flags.Get()
gFlags := new(flags.GlobalFlags)
gFlags.Supernode = true
flags.Init(gFlags)
defer flags.Init(resetFlags)
actualEas, actualCgc, err = service.updateCustodyInfoInDB(19)
require.NoError(t, err)
require.Equal(t, earliestStoredSlot, actualEas)
require.Equal(t, numberOfCustodyGroups, actualCgc)
// After Fulu
// ----------
actualEas, actualCgc, err = service.updateCustodyInfoInDB(fuluForkEpoch*primitives.Slot(cfg.SlotsPerEpoch) + 1)
require.NoError(t, err)
require.Equal(t, earliestStoredSlot, actualEas)
require.Equal(t, numberOfCustodyGroups, actualCgc)
})
t.Run("CGC increases after fulu", func(t *testing.T) {
service, requirements := minimalTestService(t)
err = requirements.db.SaveBlock(ctx, roBlock)
require.NoError(t, err)
// Before Fulu
// -----------
actualEas, actualCgc, err := service.updateCustodyInfoInDB(15)
require.NoError(t, err)
require.Equal(t, earliestStoredSlot, actualEas)
require.Equal(t, custodyRequirement, actualCgc)
actualEas, actualCgc, err = service.updateCustodyInfoInDB(17)
require.NoError(t, err)
require.Equal(t, earliestStoredSlot, actualEas)
require.Equal(t, custodyRequirement, actualCgc)
// After Fulu
// ----------
resetFlags := flags.Get()
gFlags := new(flags.GlobalFlags)
gFlags.Supernode = true
flags.Init(gFlags)
defer flags.Init(resetFlags)
slot := fuluForkEpoch*primitives.Slot(cfg.SlotsPerEpoch) + 1
actualEas, actualCgc, err = service.updateCustodyInfoInDB(slot)
require.NoError(t, err)
require.Equal(t, slot, actualEas)
require.Equal(t, numberOfCustodyGroups, actualCgc)
actualEas, actualCgc, err = service.updateCustodyInfoInDB(slot + 2)
require.NoError(t, err)
require.Equal(t, slot, actualEas)
require.Equal(t, numberOfCustodyGroups, actualCgc)
})
t.Run("Supernode downgrade prevented", func(t *testing.T) {
service, requirements := minimalTestService(t)
err = requirements.db.SaveBlock(ctx, roBlock)
require.NoError(t, err)
// Enable supernode
resetFlags := flags.Get()
gFlags := new(flags.GlobalFlags)
gFlags.Supernode = true
flags.Init(gFlags)
slot := fuluForkEpoch*primitives.Slot(cfg.SlotsPerEpoch) + 1
actualEas, actualCgc, err := service.updateCustodyInfoInDB(slot)
require.NoError(t, err)
require.Equal(t, slot, actualEas)
require.Equal(t, numberOfCustodyGroups, actualCgc)
// Try to downgrade by removing flag
gFlags.Supernode = false
flags.Init(gFlags)
defer flags.Init(resetFlags)
// Should still be supernode
actualEas, actualCgc, err = service.updateCustodyInfoInDB(slot + 2)
require.NoError(t, err)
require.Equal(t, slot, actualEas)
require.Equal(t, numberOfCustodyGroups, actualCgc) // Still 64, not downgraded
})
t.Run("Semi-supernode downgrade prevented", func(t *testing.T) {
service, requirements := minimalTestService(t)
err = requirements.db.SaveBlock(ctx, roBlock)
require.NoError(t, err)
// Enable semi-supernode
resetFlags := flags.Get()
gFlags := new(flags.GlobalFlags)
gFlags.SemiSupernode = true
flags.Init(gFlags)
slot := fuluForkEpoch*primitives.Slot(cfg.SlotsPerEpoch) + 1
actualEas, actualCgc, err := service.updateCustodyInfoInDB(slot)
require.NoError(t, err)
require.Equal(t, slot, actualEas)
semiSupernodeCustody := numberOfCustodyGroups / 2 // 64
require.Equal(t, semiSupernodeCustody, actualCgc) // Semi-supernode custodies 64 groups
// Try to downgrade by removing flag
gFlags.SemiSupernode = false
flags.Init(gFlags)
defer flags.Init(resetFlags)
// UpdateCustodyInfo should prevent downgrade - custody count should remain at 64
actualEas, actualCgc, err = service.updateCustodyInfoInDB(slot + 2)
require.NoError(t, err)
require.Equal(t, slot, actualEas)
require.Equal(t, semiSupernodeCustody, actualCgc) // Still 64 due to downgrade prevention by UpdateCustodyInfo
})
t.Run("Semi-supernode to supernode upgrade allowed", func(t *testing.T) {
service, requirements := minimalTestService(t)
err = requirements.db.SaveBlock(ctx, roBlock)
require.NoError(t, err)
// Start with semi-supernode
resetFlags := flags.Get()
gFlags := new(flags.GlobalFlags)
gFlags.SemiSupernode = true
flags.Init(gFlags)
slot := fuluForkEpoch*primitives.Slot(cfg.SlotsPerEpoch) + 1
actualEas, actualCgc, err := service.updateCustodyInfoInDB(slot)
require.NoError(t, err)
require.Equal(t, slot, actualEas)
semiSupernodeCustody := numberOfCustodyGroups / 2 // 64
require.Equal(t, semiSupernodeCustody, actualCgc) // Semi-supernode custodies 64 groups
// Upgrade to full supernode
gFlags.SemiSupernode = false
gFlags.Supernode = true
flags.Init(gFlags)
defer flags.Init(resetFlags)
// Should upgrade to full supernode
upgradeSlot := slot + 2
actualEas, actualCgc, err = service.updateCustodyInfoInDB(upgradeSlot)
require.NoError(t, err)
require.Equal(t, upgradeSlot, actualEas) // Earliest slot updates when upgrading
require.Equal(t, numberOfCustodyGroups, actualCgc) // Upgraded to 128
})
t.Run("Semi-supernode with high validator requirements uses higher custody", func(t *testing.T) {
service, requirements := minimalTestService(t)
err = requirements.db.SaveBlock(ctx, roBlock)
require.NoError(t, err)
// Enable semi-supernode
resetFlags := flags.Get()
gFlags := new(flags.GlobalFlags)
gFlags.SemiSupernode = true
flags.Init(gFlags)
defer flags.Init(resetFlags)
// Mock a high custody requirement (simulating many validators)
// We need to override the custody requirement calculation
// For this test, we'll verify the logic by checking if custodyRequirement > 64
// Since custodyRequirement in minimalTestService is 4, we can't test the high case here
// This would require a different test setup with actual validators
slot := fuluForkEpoch*primitives.Slot(cfg.SlotsPerEpoch) + 1
actualEas, actualCgc, err := service.updateCustodyInfoInDB(slot)
require.NoError(t, err)
require.Equal(t, slot, actualEas)
semiSupernodeCustody := numberOfCustodyGroups / 2 // 64
// With low validator requirements (4), should use semi-supernode minimum (64)
require.Equal(t, semiSupernodeCustody, actualCgc)
})
}

View File

@@ -26,6 +26,7 @@ go_library(
"//beacon-chain/core/time:go_default_library",
"//beacon-chain/core/validators:go_default_library",
"//beacon-chain/state:go_default_library",
"//config/features:go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//consensus-types:go_default_library",

View File

@@ -7,6 +7,7 @@ import (
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/helpers"
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/signing"
"github.com/OffchainLabs/prysm/v7/beacon-chain/state"
"github.com/OffchainLabs/prysm/v7/config/features"
fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams"
"github.com/OffchainLabs/prysm/v7/config/params"
"github.com/OffchainLabs/prysm/v7/consensus-types/interfaces"
@@ -212,7 +213,12 @@ func ProcessWithdrawals(st state.BeaconState, executionData interfaces.Execution
if err != nil {
return nil, errors.Wrap(err, "could not get next withdrawal validator index")
}
nextValidatorIndex += primitives.ValidatorIndex(params.BeaconConfig().MaxValidatorsPerWithdrawalsSweep)
if features.Get().LowValcountSweep {
bound := min(uint64(st.NumValidators()), params.BeaconConfig().MaxValidatorsPerWithdrawalsSweep)
nextValidatorIndex += primitives.ValidatorIndex(bound)
} else {
nextValidatorIndex += primitives.ValidatorIndex(params.BeaconConfig().MaxValidatorsPerWithdrawalsSweep)
}
nextValidatorIndex = nextValidatorIndex % primitives.ValidatorIndex(st.NumValidators())
} else {
nextValidatorIndex = expectedWithdrawals[len(expectedWithdrawals)-1].ValidatorIndex + 1

View File

@@ -0,0 +1,43 @@
load("@prysm//tools/go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = ["payload.go"],
importpath = "github.com/OffchainLabs/prysm/v7/beacon-chain/core/gloas",
visibility = ["//visibility:public"],
deps = [
"//beacon-chain/core/electra:go_default_library",
"//beacon-chain/core/signing:go_default_library",
"//beacon-chain/state:go_default_library",
"//config/params:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//consensus-types/primitives:go_default_library",
"//crypto/bls:go_default_library",
"//encoding/ssz:go_default_library",
"//proto/engine/v1:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//time/slots:go_default_library",
"@com_github_pkg_errors//:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = ["payload_test.go"],
embed = [":go_default_library"],
deps = [
"//beacon-chain/core/signing:go_default_library",
"//beacon-chain/state:go_default_library",
"//beacon-chain/state/state-native:go_default_library",
"//config/params:go_default_library",
"//consensus-types/blocks:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//consensus-types/primitives:go_default_library",
"//crypto/bls:go_default_library",
"//encoding/ssz:go_default_library",
"//proto/engine/v1:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//testing/require:go_default_library",
"//time/slots:go_default_library",
],
)

View File

@@ -0,0 +1,380 @@
package gloas
import (
"bytes"
"context"
"fmt"
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/electra"
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/signing"
"github.com/OffchainLabs/prysm/v7/beacon-chain/state"
"github.com/OffchainLabs/prysm/v7/config/params"
"github.com/OffchainLabs/prysm/v7/consensus-types/interfaces"
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
"github.com/OffchainLabs/prysm/v7/crypto/bls"
"github.com/OffchainLabs/prysm/v7/encoding/ssz"
enginev1 "github.com/OffchainLabs/prysm/v7/proto/engine/v1"
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
"github.com/OffchainLabs/prysm/v7/time/slots"
"github.com/pkg/errors"
)
const builderIndexSelfBuild = ^primitives.BuilderIndex(0)
// ProcessExecutionPayload processes the signed execution payload envelope for the Gloas fork.
// Spec v1.7.0-alpha.0 (pseudocode):
// def process_execution_payload(
//
// state: BeaconState,
// signed_envelope: SignedExecutionPayloadEnvelope,
// execution_engine: ExecutionEngine,
// verify: bool = True,
//
// ) -> None:
//
// envelope = signed_envelope.message
// payload = envelope.payload
//
// if verify:
// assert verify_execution_payload_envelope_signature(state, signed_envelope)
//
// previous_state_root = hash_tree_root(state)
// if state.latest_block_header.state_root == Root():
// state.latest_block_header.state_root = previous_state_root
//
// assert envelope.beacon_block_root == hash_tree_root(state.latest_block_header)
// assert envelope.slot == state.slot
//
// committed_bid = state.latest_execution_payload_bid
// assert envelope.builder_index == committed_bid.builder_index
// assert committed_bid.blob_kzg_commitments_root == hash_tree_root(envelope.blob_kzg_commitments)
// assert committed_bid.prev_randao == payload.prev_randao
//
// assert hash_tree_root(payload.withdrawals) == hash_tree_root(state.payload_expected_withdrawals)
//
// assert committed_bid.gas_limit == payload.gas_limit
// assert committed_bid.block_hash == payload.block_hash
// assert payload.parent_hash == state.latest_block_hash
// assert payload.timestamp == compute_time_at_slot(state, state.slot)
// assert (
// len(envelope.blob_kzg_commitments)
// <= get_blob_parameters(get_current_epoch(state)).max_blobs_per_block
// )
// versioned_hashes = [
// kzg_commitment_to_versioned_hash(commitment) for commitment in envelope.blob_kzg_commitments
// ]
// requests = envelope.execution_requests
// assert execution_engine.verify_and_notify_new_payload(
// NewPayloadRequest(
// execution_payload=payload,
// versioned_hashes=versioned_hashes,
// parent_beacon_block_root=state.latest_block_header.parent_root,
// execution_requests=requests,
// )
// )
//
// for op in requests.deposits: process_deposit_request(state, op)
// for op in requests.withdrawals: process_withdrawal_request(state, op)
// for op in requests.consolidations: process_consolidation_request(state, op)
//
// payment = state.builder_pending_payments[SLOTS_PER_EPOCH + state.slot % SLOTS_PER_EPOCH]
// amount = payment.withdrawal.amount
// if amount > 0:
// state.builder_pending_withdrawals.append(payment.withdrawal)
// state.builder_pending_payments[SLOTS_PER_EPOCH + state.slot % SLOTS_PER_EPOCH] = (
// BuilderPendingPayment()
// )
//
// state.execution_payload_availability[state.slot % SLOTS_PER_HISTORICAL_ROOT] = 0b1
// state.latest_block_hash = payload.block_hash
//
// if verify:
// assert envelope.state_root == hash_tree_root(state)
func ProcessExecutionPayload(
ctx context.Context,
st state.BeaconState,
signedEnvelope interfaces.ROSignedExecutionPayloadEnvelope,
) error {
// Verify the signature on the signed execution payload envelope
if err := verifyExecutionPayloadEnvelopeSignature(st, signedEnvelope); err != nil {
return errors.Wrap(err, "signature verification failed")
}
envelope, err := signedEnvelope.Envelope()
if err != nil {
return errors.Wrap(err, "could not get envelope from signed envelope")
}
payload, err := envelope.Execution()
if err != nil {
return errors.Wrap(err, "could not get execution payload from envelope")
}
latestHeader := st.LatestBlockHeader()
if len(latestHeader.StateRoot) == 0 || bytes.Equal(latestHeader.StateRoot, make([]byte, 32)) {
previousStateRoot, err := st.HashTreeRoot(ctx)
if err != nil {
return errors.Wrap(err, "could not compute state root")
}
latestHeader.StateRoot = previousStateRoot[:]
if err := st.SetLatestBlockHeader(latestHeader); err != nil {
return errors.Wrap(err, "could not set latest block header")
}
}
blockHeaderRoot, err := latestHeader.HashTreeRoot()
if err != nil {
return errors.Wrap(err, "could not compute block header root")
}
beaconBlockRoot := envelope.BeaconBlockRoot()
if !bytes.Equal(beaconBlockRoot[:], blockHeaderRoot[:]) {
return errors.Errorf("envelope beacon block root does not match state latest block header root: envelope=%#x, header=%#x", beaconBlockRoot, blockHeaderRoot)
}
if envelope.Slot() != st.Slot() {
return errors.Errorf("envelope slot does not match state slot: envelope=%d, state=%d", envelope.Slot(), st.Slot())
}
latestBid, err := st.LatestExecutionPayloadBid()
if err != nil {
return errors.Wrap(err, "could not get latest execution payload bid")
}
if latestBid == nil {
return errors.New("latest execution payload bid is nil")
}
if envelope.BuilderIndex() != latestBid.BuilderIndex() {
return errors.Errorf("envelope builder index does not match committed bid builder index: envelope=%d, bid=%d", envelope.BuilderIndex(), latestBid.BuilderIndex())
}
envelopeBlobCommitments := envelope.BlobKzgCommitments()
envelopeBlobRoot, err := ssz.KzgCommitmentsRoot(envelopeBlobCommitments)
if err != nil {
return errors.Wrap(err, "could not compute envelope blob KZG commitments root")
}
committedBlobRoot := latestBid.BlobKzgCommitmentsRoot()
if !bytes.Equal(committedBlobRoot[:], envelopeBlobRoot[:]) {
return errors.Errorf("committed bid blob KZG commitments root does not match envelope: bid=%#x, envelope=%#x", committedBlobRoot, envelopeBlobRoot)
}
withdrawals, err := payload.Withdrawals()
if err != nil {
return errors.Wrap(err, "could not get withdrawals from payload")
}
cfg := params.BeaconConfig()
withdrawalsRoot, err := ssz.WithdrawalSliceRoot(withdrawals, cfg.MaxWithdrawalsPerPayload)
if err != nil {
return errors.Wrap(err, "could not compute withdrawals root")
}
expectedWithdrawals, err := st.PayloadExpectedWithdrawals()
if err != nil {
return errors.Wrap(err, "could not get expected withdrawals")
}
expectedRoot, err := ssz.WithdrawalSliceRoot(expectedWithdrawals, cfg.MaxWithdrawalsPerPayload)
if err != nil {
return errors.Wrap(err, "could not compute expected withdrawals root")
}
if !bytes.Equal(withdrawalsRoot[:], expectedRoot[:]) {
return errors.Errorf("payload withdrawals root does not match expected withdrawals: payload=%#x, expected=%#x", withdrawalsRoot, expectedRoot)
}
if latestBid.GasLimit() != payload.GasLimit() {
return errors.Errorf("committed bid gas limit does not match payload gas limit: bid=%d, payload=%d", latestBid.GasLimit(), payload.GasLimit())
}
latestBidPrevRandao := latestBid.PrevRandao()
if !bytes.Equal(payload.PrevRandao(), latestBidPrevRandao[:]) {
return errors.Errorf("payload prev randao does not match committed bid prev randao: payload=%#x, bid=%#x", payload.PrevRandao(), latestBidPrevRandao)
}
bidBlockHash := latestBid.BlockHash()
payloadBlockHash := payload.BlockHash()
if !bytes.Equal(bidBlockHash[:], payloadBlockHash) {
return errors.Errorf("committed bid block hash does not match payload block hash: bid=%#x, payload=%#x", bidBlockHash, payloadBlockHash)
}
latestBlockHash, err := st.LatestBlockHash()
if err != nil {
return errors.Wrap(err, "could not get latest block hash")
}
if !bytes.Equal(payload.ParentHash(), latestBlockHash[:]) {
return errors.Errorf("payload parent hash does not match state latest block hash: payload=%#x, state=%#x", payload.ParentHash(), latestBlockHash)
}
t, err := slots.StartTime(st.GenesisTime(), st.Slot())
if err != nil {
return errors.Wrap(err, "could not compute timestamp")
}
if payload.Timestamp() != uint64(t.Unix()) {
return errors.Errorf("payload timestamp does not match expected timestamp: payload=%d, expected=%d", payload.Timestamp(), uint64(t.Unix()))
}
maxBlobsPerBlock := cfg.MaxBlobsPerBlock(envelope.Slot())
if len(envelopeBlobCommitments) > maxBlobsPerBlock {
return errors.Errorf("too many blob KZG commitments: got=%d, max=%d", len(envelopeBlobCommitments), maxBlobsPerBlock)
}
if err := processExecutionRequests(ctx, st, envelope.ExecutionRequests()); err != nil {
return errors.Wrap(err, "could not process execution requests")
}
if err := queueBuilderPayment(ctx, st); err != nil {
return errors.Wrap(err, "could not queue builder payment")
}
if err := st.SetExecutionPayloadAvailability(st.Slot(), true); err != nil {
return errors.Wrap(err, "could not set execution payload availability")
}
if err := st.SetLatestBlockHash([32]byte(payload.BlockHash())); err != nil {
return errors.Wrap(err, "could not set latest block hash")
}
r, err := st.HashTreeRoot(ctx)
if err != nil {
return errors.Wrap(err, "could not get hash tree root")
}
if r != envelope.StateRoot() {
return fmt.Errorf("state root mismatch: expected %#x, got %#x", envelope.StateRoot(), r)
}
return nil
}
// processExecutionRequests processes deposits, withdrawals, and consolidations from execution requests.
// Spec v1.7.0-alpha.0 (pseudocode):
// for op in requests.deposits: process_deposit_request(state, op)
// for op in requests.withdrawals: process_withdrawal_request(state, op)
// for op in requests.consolidations: process_consolidation_request(state, op)
func processExecutionRequests(ctx context.Context, st state.BeaconState, requests *enginev1.ExecutionRequests) error {
var err error
st, err = electra.ProcessDepositRequests(ctx, st, requests.Deposits)
if err != nil {
return errors.Wrap(err, "could not process deposit requests")
}
st, err = electra.ProcessWithdrawalRequests(ctx, st, requests.Withdrawals)
if err != nil {
return errors.Wrap(err, "could not process withdrawal requests")
}
err = electra.ProcessConsolidationRequests(ctx, st, requests.Consolidations)
if err != nil {
return errors.Wrap(err, "could not process consolidation requests")
}
return nil
}
// queueBuilderPayment implements the builder payment queuing logic for Gloas.
// Spec v1.7.0-alpha.0 (pseudocode):
// payment = state.builder_pending_payments[SLOTS_PER_EPOCH + state.slot % SLOTS_PER_EPOCH]
// amount = payment.withdrawal.amount
// if amount > 0:
//
// state.builder_pending_withdrawals.append(payment.withdrawal)
//
// state.builder_pending_payments[SLOTS_PER_EPOCH + state.slot % SLOTS_PER_EPOCH] = BuilderPendingPayment()
func queueBuilderPayment(ctx context.Context, st state.BeaconState) error {
slot := st.Slot()
payment, err := st.BuilderPendingPayment(slot)
if err != nil {
return errors.Wrap(err, "could not get builder pending payment")
}
if payment.Withdrawal != nil && payment.Withdrawal.Amount > 0 {
if err := st.AppendBuilderPendingWithdrawal(payment.Withdrawal); err != nil {
return errors.Wrap(err, "could not append builder pending withdrawal")
}
}
reset := &ethpb.BuilderPendingPayment{
Withdrawal: &ethpb.BuilderPendingWithdrawal{
FeeRecipient: make([]byte, 20),
},
}
if err := st.SetBuilderPendingPayment(slot, reset); err != nil {
return errors.Wrap(err, "could not set builder pending payment")
}
return nil
}
// verifyExecutionPayloadEnvelopeSignature verifies the BLS signature on a signed execution payload envelope.
// Spec v1.7.0-alpha.0 (pseudocode):
// builder_index = signed_envelope.message.builder_index
// if builder_index == BUILDER_INDEX_SELF_BUILD:
//
// validator_index = state.latest_block_header.proposer_index
// pubkey = state.validators[validator_index].pubkey
//
// else:
//
// pubkey = state.builders[builder_index].pubkey
//
// signing_root = compute_signing_root(
//
// signed_envelope.message, get_domain(state, DOMAIN_BEACON_BUILDER)
//
// )
// return bls.Verify(pubkey, signing_root, signed_envelope.signature)
func verifyExecutionPayloadEnvelopeSignature(st state.BeaconState, signedEnvelope interfaces.ROSignedExecutionPayloadEnvelope) error {
envelope, err := signedEnvelope.Envelope()
if err != nil {
return fmt.Errorf("failed to get envelope: %w", err)
}
builderIdx := envelope.BuilderIndex()
var publicKey bls.PublicKey
switch builderIdx {
case builderIndexSelfBuild:
header := st.LatestBlockHeader()
if header == nil {
return fmt.Errorf("latest block header is nil")
}
proposerPubkey := st.PubkeyAtIndex(header.ProposerIndex)
key, err := bls.PublicKeyFromBytes(proposerPubkey[:])
if err != nil {
return fmt.Errorf("invalid proposer public key: %w", err)
}
publicKey = key
default:
builder, err := st.Builder(builderIdx)
if err != nil {
return fmt.Errorf("failed to get builder: %w", err)
}
if builder == nil {
return fmt.Errorf("builder at index %d not found", builderIdx)
}
key, err := bls.PublicKeyFromBytes(builder.Pubkey)
if err != nil {
return fmt.Errorf("invalid builder public key: %w", err)
}
publicKey = key
}
signatureBytes := signedEnvelope.Signature()
signature, err := bls.SignatureFromBytes(signatureBytes[:])
if err != nil {
return fmt.Errorf("invalid signature format: %w", err)
}
currentEpoch := slots.ToEpoch(envelope.Slot())
domain, err := signing.Domain(
st.Fork(),
currentEpoch,
params.BeaconConfig().DomainBeaconBuilder,
st.GenesisValidatorsRoot(),
)
if err != nil {
return fmt.Errorf("failed to compute signing domain: %w", err)
}
signingRoot, err := signedEnvelope.SigningRoot(domain)
if err != nil {
return fmt.Errorf("failed to compute signing root: %w", err)
}
if !signature.Verify(publicKey, signingRoot[:]) {
return fmt.Errorf("signature verification failed: %w", signing.ErrSigFailedToVerify)
}
return nil
}

View File

@@ -0,0 +1,280 @@
package gloas
import (
"bytes"
"context"
"testing"
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/signing"
"github.com/OffchainLabs/prysm/v7/beacon-chain/state"
state_native "github.com/OffchainLabs/prysm/v7/beacon-chain/state/state-native"
"github.com/OffchainLabs/prysm/v7/config/params"
"github.com/OffchainLabs/prysm/v7/consensus-types/blocks"
"github.com/OffchainLabs/prysm/v7/consensus-types/interfaces"
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
"github.com/OffchainLabs/prysm/v7/crypto/bls"
"github.com/OffchainLabs/prysm/v7/encoding/ssz"
enginev1 "github.com/OffchainLabs/prysm/v7/proto/engine/v1"
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
"github.com/OffchainLabs/prysm/v7/testing/require"
"github.com/OffchainLabs/prysm/v7/time/slots"
)
type payloadFixture struct {
state state.BeaconState
signed interfaces.ROSignedExecutionPayloadEnvelope
signedProto *ethpb.SignedExecutionPayloadEnvelope
envelope *ethpb.ExecutionPayloadEnvelope
payload *enginev1.ExecutionPayloadDeneb
slot primitives.Slot
}
func buildPayloadFixture(t *testing.T, mutate func(payload *enginev1.ExecutionPayloadDeneb, bid *ethpb.ExecutionPayloadBid, envelope *ethpb.ExecutionPayloadEnvelope)) payloadFixture {
t.Helper()
cfg := params.BeaconConfig()
slot := primitives.Slot(5)
builderIdx := primitives.BuilderIndex(0)
sk, err := bls.RandKey()
require.NoError(t, err)
pk := sk.PublicKey().Marshal()
randao := bytes.Repeat([]byte{0xAA}, 32)
parentHash := bytes.Repeat([]byte{0xBB}, 32)
blockHash := bytes.Repeat([]byte{0xCC}, 32)
withdrawals := []*enginev1.Withdrawal{
{Index: 0, ValidatorIndex: 1, Address: bytes.Repeat([]byte{0x01}, 20), Amount: 0},
}
blobCommitments := [][]byte{}
blobRoot, err := ssz.KzgCommitmentsRoot(blobCommitments)
require.NoError(t, err)
payload := &enginev1.ExecutionPayloadDeneb{
ParentHash: parentHash,
FeeRecipient: bytes.Repeat([]byte{0x01}, 20),
StateRoot: bytes.Repeat([]byte{0x02}, 32),
ReceiptsRoot: bytes.Repeat([]byte{0x03}, 32),
LogsBloom: bytes.Repeat([]byte{0x04}, 256),
PrevRandao: randao,
BlockNumber: 1,
GasLimit: 1,
GasUsed: 0,
Timestamp: 100,
ExtraData: []byte{},
BaseFeePerGas: bytes.Repeat([]byte{0x05}, 32),
BlockHash: blockHash,
Transactions: [][]byte{},
Withdrawals: withdrawals,
BlobGasUsed: 0,
ExcessBlobGas: 0,
}
bid := &ethpb.ExecutionPayloadBid{
ParentBlockHash: parentHash,
ParentBlockRoot: bytes.Repeat([]byte{0xDD}, 32),
BlockHash: blockHash,
PrevRandao: randao,
GasLimit: 1,
BuilderIndex: builderIdx,
Slot: slot,
Value: 0,
ExecutionPayment: 0,
BlobKzgCommitmentsRoot: blobRoot[:],
FeeRecipient: bytes.Repeat([]byte{0xEE}, 20),
}
header := &ethpb.BeaconBlockHeader{
Slot: slot,
ParentRoot: bytes.Repeat([]byte{0x11}, 32),
StateRoot: bytes.Repeat([]byte{0x22}, 32),
BodyRoot: bytes.Repeat([]byte{0x33}, 32),
}
headerRoot, err := header.HashTreeRoot()
require.NoError(t, err)
envelope := &ethpb.ExecutionPayloadEnvelope{
Slot: slot,
BuilderIndex: builderIdx,
BeaconBlockRoot: headerRoot[:],
Payload: payload,
BlobKzgCommitments: blobCommitments,
ExecutionRequests: &enginev1.ExecutionRequests{},
}
if mutate != nil {
mutate(payload, bid, envelope)
}
genesisRoot := bytes.Repeat([]byte{0xAB}, 32)
blockRoots := make([][]byte, cfg.SlotsPerHistoricalRoot)
stateRoots := make([][]byte, cfg.SlotsPerHistoricalRoot)
for i := range blockRoots {
blockRoots[i] = bytes.Repeat([]byte{0x44}, 32)
stateRoots[i] = bytes.Repeat([]byte{0x55}, 32)
}
randaoMixes := make([][]byte, cfg.EpochsPerHistoricalVector)
for i := range randaoMixes {
randaoMixes[i] = randao
}
withdrawalCreds := make([]byte, 32)
withdrawalCreds[0] = cfg.ETH1AddressWithdrawalPrefixByte
eth1Data := &ethpb.Eth1Data{
DepositRoot: bytes.Repeat([]byte{0x66}, 32),
DepositCount: 0,
BlockHash: bytes.Repeat([]byte{0x77}, 32),
}
vals := []*ethpb.Validator{
{
PublicKey: pk,
WithdrawalCredentials: withdrawalCreds,
EffectiveBalance: cfg.MinActivationBalance + 1_000,
},
}
balances := []uint64{cfg.MinActivationBalance + 1_000}
payments := make([]*ethpb.BuilderPendingPayment, cfg.SlotsPerEpoch*2)
for i := range payments {
payments[i] = &ethpb.BuilderPendingPayment{
Withdrawal: &ethpb.BuilderPendingWithdrawal{
FeeRecipient: make([]byte, 20),
},
}
}
executionPayloadAvailability := make([]byte, cfg.SlotsPerHistoricalRoot/8)
builders := make([]*ethpb.Builder, builderIdx+1)
builders[builderIdx] = &ethpb.Builder{
Pubkey: pk,
Version: []byte{0},
ExecutionAddress: bytes.Repeat([]byte{0x09}, 20),
Balance: 0,
DepositEpoch: 0,
WithdrawableEpoch: 0,
}
genesisTime := uint64(0)
slotSeconds := cfg.SecondsPerSlot * uint64(slot)
if payload.Timestamp > slotSeconds {
genesisTime = payload.Timestamp - slotSeconds
}
stProto := &ethpb.BeaconStateGloas{
Slot: slot,
GenesisTime: genesisTime,
GenesisValidatorsRoot: genesisRoot,
Fork: &ethpb.Fork{
CurrentVersion: bytes.Repeat([]byte{0x01}, 4),
PreviousVersion: bytes.Repeat([]byte{0x01}, 4),
Epoch: 0,
},
LatestBlockHeader: header,
BlockRoots: blockRoots,
StateRoots: stateRoots,
RandaoMixes: randaoMixes,
Eth1Data: eth1Data,
Validators: vals,
Balances: balances,
LatestBlockHash: payload.ParentHash,
LatestExecutionPayloadBid: bid,
BuilderPendingPayments: payments,
ExecutionPayloadAvailability: executionPayloadAvailability,
BuilderPendingWithdrawals: []*ethpb.BuilderPendingWithdrawal{},
PayloadExpectedWithdrawals: payload.Withdrawals,
Builders: builders,
}
st, err := state_native.InitializeFromProtoGloas(stProto)
require.NoError(t, err)
expected := st.Copy()
ctx := context.Background()
require.NoError(t, processExecutionRequests(ctx, expected, envelope.ExecutionRequests))
require.NoError(t, queueBuilderPayment(ctx, expected))
require.NoError(t, expected.SetExecutionPayloadAvailability(slot, true))
var blockHashArr [32]byte
copy(blockHashArr[:], payload.BlockHash)
require.NoError(t, expected.SetLatestBlockHash(blockHashArr))
expectedRoot, err := expected.HashTreeRoot(ctx)
require.NoError(t, err)
envelope.StateRoot = expectedRoot[:]
epoch := slots.ToEpoch(slot)
domain, err := signing.Domain(st.Fork(), epoch, cfg.DomainBeaconBuilder, st.GenesisValidatorsRoot())
require.NoError(t, err)
signingRoot, err := signing.ComputeSigningRoot(envelope, domain)
require.NoError(t, err)
signature := sk.Sign(signingRoot[:]).Marshal()
signedProto := &ethpb.SignedExecutionPayloadEnvelope{
Message: envelope,
Signature: signature,
}
signed, err := blocks.WrappedROSignedExecutionPayloadEnvelope(signedProto)
require.NoError(t, err)
return payloadFixture{
state: st,
signed: signed,
signedProto: signedProto,
envelope: envelope,
payload: payload,
slot: slot,
}
}
func TestProcessExecutionPayload_Success(t *testing.T) {
fixture := buildPayloadFixture(t, nil)
require.NoError(t, ProcessExecutionPayload(t.Context(), fixture.state, fixture.signed))
latestHash, err := fixture.state.LatestBlockHash()
require.NoError(t, err)
var expectedHash [32]byte
copy(expectedHash[:], fixture.payload.BlockHash)
require.Equal(t, expectedHash, latestHash)
payment, err := fixture.state.BuilderPendingPayment(fixture.slot)
require.NoError(t, err)
require.NotNil(t, payment)
require.Equal(t, primitives.Gwei(0), payment.Withdrawal.Amount)
}
func TestProcessExecutionPayload_PrevRandaoMismatch(t *testing.T) {
fixture := buildPayloadFixture(t, func(_ *enginev1.ExecutionPayloadDeneb, bid *ethpb.ExecutionPayloadBid, _ *ethpb.ExecutionPayloadEnvelope) {
bid.PrevRandao = bytes.Repeat([]byte{0xFF}, 32)
})
err := ProcessExecutionPayload(t.Context(), fixture.state, fixture.signed)
require.ErrorContains(t, "prev randao", err)
}
func TestQueueBuilderPayment_ZeroAmountClearsSlot(t *testing.T) {
fixture := buildPayloadFixture(t, nil)
require.NoError(t, queueBuilderPayment(t.Context(), fixture.state))
payment, err := fixture.state.BuilderPendingPayment(fixture.slot)
require.NoError(t, err)
require.NotNil(t, payment)
require.Equal(t, primitives.Gwei(0), payment.Withdrawal.Amount)
}
func TestVerifyExecutionPayloadEnvelopeSignature_InvalidSig(t *testing.T) {
fixture := buildPayloadFixture(t, nil)
signedProto := &ethpb.SignedExecutionPayloadEnvelope{
Message: fixture.signedProto.Message,
Signature: bytes.Repeat([]byte{0xFF}, 96),
}
badSigned, err := blocks.WrappedROSignedExecutionPayloadEnvelope(signedProto)
require.NoError(t, err)
err = verifyExecutionPayloadEnvelopeSignature(fixture.state, badSigned)
require.ErrorContains(t, "invalid signature format", err)
}

View File

@@ -89,7 +89,6 @@ type NoHeadAccessDatabase interface {
SaveBlocks(ctx context.Context, blocks []interfaces.ReadOnlySignedBeaconBlock) error
SaveROBlocks(ctx context.Context, blks []blocks.ROBlock, cache bool) error
SaveGenesisBlockRoot(ctx context.Context, blockRoot [32]byte) error
SlotByBlockRoot(context.Context, [32]byte) (primitives.Slot, error)
// State related methods.
SaveState(ctx context.Context, state state.ReadOnlyBeaconState, blockRoot [32]byte) error
SaveStates(ctx context.Context, states []state.ReadOnlyBeaconState, blockRoots [][32]byte) error
@@ -97,7 +96,6 @@ type NoHeadAccessDatabase interface {
DeleteStates(ctx context.Context, blockRoots [][32]byte) error
SaveStateSummary(ctx context.Context, summary *ethpb.StateSummary) error
SaveStateSummaries(ctx context.Context, summaries []*ethpb.StateSummary) error
SlotInDiffTree(primitives.Slot) (uint64, int, error)
// Checkpoint operations.
SaveJustifiedCheckpoint(ctx context.Context, checkpoint *ethpb.Checkpoint) error
SaveFinalizedCheckpoint(ctx context.Context, checkpoint *ethpb.Checkpoint) error

View File

@@ -32,7 +32,6 @@ go_library(
"state_diff_helpers.go",
"state_summary.go",
"state_summary_cache.go",
"testing_helpers.go",
"utils.go",
"validated_checkpoint.go",
"wss.go",

View File

@@ -23,16 +23,6 @@ const (
The data at level 0 is saved every 2**exponent[0] slots and always contains a full state snapshot that is used as a base for the delta saved at other levels.
*/
// SlotInDiffTree returns whether the given slot is a saving point in the diff tree.
// It it is, it also returns the offset and level in the tree.
func (s *Store) SlotInDiffTree(slot primitives.Slot) (uint64, int, error) {
offset := s.getOffset()
if uint64(slot) < offset {
return 0, -1, ErrSlotBeforeOffset
}
return offset, computeLevel(offset, slot), nil
}
// saveStateByDiff takes a state and decides between saving a full state snapshot or a diff.
func (s *Store) saveStateByDiff(ctx context.Context, st state.ReadOnlyBeaconState) error {
_, span := trace.StartSpan(ctx, "BeaconDB.saveStateByDiff")
@@ -43,10 +33,13 @@ func (s *Store) saveStateByDiff(ctx context.Context, st state.ReadOnlyBeaconStat
}
slot := st.Slot()
offset, lvl, err := s.SlotInDiffTree(slot)
if err != nil {
return errors.Wrap(err, "could not determine if slot is in diff tree")
offset := s.getOffset()
if uint64(slot) < offset {
return ErrSlotBeforeOffset
}
// Find the level to save the state.
lvl := computeLevel(offset, slot)
if lvl == -1 {
return nil
}

View File

@@ -1,37 +0,0 @@
package kv
import (
"encoding/binary"
"testing"
"go.etcd.io/bbolt"
)
// InitStateDiffCacheForTesting initializes the state diff cache with the given offset.
// This is intended for testing purposes when setting up state diff after database creation.
// This file is only compiled when the "testing" build tag is set.
func (s *Store) InitStateDiffCacheForTesting(t testing.TB, offset uint64) error {
// First, set the offset in the database.
err := s.db.Update(func(tx *bbolt.Tx) error {
bucket := tx.Bucket(stateDiffBucket)
if bucket == nil {
return bbolt.ErrBucketNotFound
}
offsetBytes := make([]byte, 8)
binary.LittleEndian.PutUint64(offsetBytes, offset)
return bucket.Put([]byte("offset"), offsetBytes)
})
if err != nil {
return err
}
// Then create the state diff cache.
sdCache, err := newStateDiffCache(s)
if err != nil {
return err
}
s.stateDiffCache = sdCache
return nil
}

View File

@@ -205,7 +205,7 @@ func TestGetSpec(t *testing.T) {
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), &resp))
data, ok := resp.Data.(map[string]any)
require.Equal(t, true, ok)
assert.Equal(t, 175, len(data))
assert.Equal(t, 176, len(data))
for k, v := range data {
t.Run(k, func(t *testing.T) {
switch k {
@@ -419,6 +419,8 @@ func TestGetSpec(t *testing.T) {
assert.Equal(t, "0x0a000000", v)
case "DOMAIN_APPLICATION_BUILDER":
assert.Equal(t, "0x00000001", v)
case "DOMAIN_BEACON_BUILDER":
assert.Equal(t, "0x1b000000", v)
case "DOMAIN_BLOB_SIDECAR":
assert.Equal(t, "0x00000000", v)
case "TRANSITION_TOTAL_DIFFICULTY":

View File

@@ -5,6 +5,7 @@ go_library(
srcs = [
"error.go",
"interfaces.go",
"interfaces_gloas.go",
"prometheus.go",
],
importpath = "github.com/OffchainLabs/prysm/v7/beacon-chain/state",

View File

@@ -66,6 +66,7 @@ type ReadOnlyBeaconState interface {
ReadOnlyDeposits
ReadOnlyConsolidations
ReadOnlyProposerLookahead
ReadOnlyGloasFields
ToProtoUnsafe() any
ToProto() any
GenesisTime() time.Time
@@ -102,6 +103,7 @@ type WriteOnlyBeaconState interface {
WriteOnlyDeposits
WriteOnlyProposerLookahead
SetGenesisTime(val time.Time) error
WriteOnlyGloasFields
SetGenesisValidatorsRoot(val []byte) error
SetSlot(val primitives.Slot) error
SetFork(val *ethpb.Fork) error

View File

@@ -0,0 +1,27 @@
package state
import (
"github.com/OffchainLabs/prysm/v7/consensus-types/interfaces"
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
enginev1 "github.com/OffchainLabs/prysm/v7/proto/engine/v1"
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
)
type WriteOnlyGloasFields interface {
SetExecutionPayloadBid(h interfaces.ROExecutionPayloadBid) error
SetBuilderPendingPayment(index primitives.Slot, payment *ethpb.BuilderPendingPayment) error
SetLatestBlockHash(hash [32]byte) error
SetExecutionPayloadAvailability(index primitives.Slot, available bool) error
SetBuilderPendingPayments(payments []*ethpb.BuilderPendingPayment) error
AppendBuilderPendingWithdrawal(withdrawal *ethpb.BuilderPendingWithdrawal) error
}
type ReadOnlyGloasFields interface {
LatestBlockHash() ([32]byte, error)
BuilderPendingPayment(slot primitives.Slot) (*ethpb.BuilderPendingPayment, error)
BuilderPendingPayments() ([]*ethpb.BuilderPendingPayment, error)
BuilderPendingWithdrawals() ([]*ethpb.BuilderPendingWithdrawal, error)
LatestExecutionPayloadBid() (interfaces.ROExecutionPayloadBid, error)
PayloadExpectedWithdrawals() ([]*enginev1.Withdrawal, error)
Builder(index primitives.BuilderIndex) (*ethpb.Builder, error)
}

View File

@@ -14,6 +14,7 @@ go_library(
"getters_deposits.go",
"getters_eth1.go",
"getters_exit.go",
"getters_gloas.go",
"getters_misc.go",
"getters_participation.go",
"getters_payload_header.go",
@@ -36,6 +37,7 @@ go_library(
"setters_deposit_requests.go",
"setters_deposits.go",
"setters_eth1.go",
"setters_gloas.go",
"setters_misc.go",
"setters_participation.go",
"setters_payload_header.go",
@@ -97,6 +99,7 @@ go_test(
"getters_deposit_requests_test.go",
"getters_deposits_test.go",
"getters_exit_test.go",
"getters_gloas_test.go",
"getters_participation_test.go",
"getters_setters_lookahead_test.go",
"getters_test.go",
@@ -114,6 +117,7 @@ go_test(
"setters_deposit_requests_test.go",
"setters_deposits_test.go",
"setters_eth1_test.go",
"setters_gloas_test.go",
"setters_misc_test.go",
"setters_participation_test.go",
"setters_payload_header_test.go",

View File

@@ -0,0 +1,101 @@
package state_native
import (
"fmt"
"github.com/OffchainLabs/prysm/v7/config/params"
"github.com/OffchainLabs/prysm/v7/consensus-types/blocks"
"github.com/OffchainLabs/prysm/v7/consensus-types/interfaces"
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
enginev1 "github.com/OffchainLabs/prysm/v7/proto/engine/v1"
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
)
// LatestBlockHash returns the hash of the latest execution block.
func (b *BeaconState) LatestBlockHash() ([32]byte, error) {
b.lock.RLock()
defer b.lock.RUnlock()
if b.latestBlockHash == nil {
return [32]byte{}, nil
}
return [32]byte(b.latestBlockHash), nil
}
// BuilderPendingPayment returns a specific builder pending payment for the given slot.
func (b *BeaconState) BuilderPendingPayment(slot primitives.Slot) (*ethpb.BuilderPendingPayment, error) {
b.lock.RLock()
defer b.lock.RUnlock()
slotsPerEpoch := params.BeaconConfig().SlotsPerEpoch
paymentIndex := slotsPerEpoch + (slot % slotsPerEpoch)
return ethpb.CopyBuilderPendingPayment(b.builderPendingPayments[paymentIndex]), nil
}
// BuilderPendingPayments returns the builder pending payments.
func (b *BeaconState) BuilderPendingPayments() ([]*ethpb.BuilderPendingPayment, error) {
b.lock.RLock()
defer b.lock.RUnlock()
if b.builderPendingPayments == nil {
return make([]*ethpb.BuilderPendingPayment, 0), nil
}
return ethpb.CopyBuilderPendingPaymentSlice(b.builderPendingPayments), nil
}
// BuilderPendingWithdrawals returns the builder pending withdrawals.
func (b *BeaconState) BuilderPendingWithdrawals() ([]*ethpb.BuilderPendingWithdrawal, error) {
b.lock.RLock()
defer b.lock.RUnlock()
if b.builderPendingWithdrawals == nil {
return make([]*ethpb.BuilderPendingWithdrawal, 0), nil
}
return ethpb.CopyBuilderPendingWithdrawalSlice(b.builderPendingWithdrawals), nil
}
// LatestExecutionPayloadBid returns the cached latest execution payload bid for Gloas.
func (b *BeaconState) LatestExecutionPayloadBid() (interfaces.ROExecutionPayloadBid, error) {
b.lock.RLock()
defer b.lock.RUnlock()
if b.latestExecutionPayloadBid == nil {
return nil, nil
}
return blocks.WrappedROExecutionPayloadBid(b.latestExecutionPayloadBid.Copy())
}
// PayloadExpectedWithdrawals returns the expected withdrawals for the current payload.
func (b *BeaconState) PayloadExpectedWithdrawals() ([]*enginev1.Withdrawal, error) {
b.lock.RLock()
defer b.lock.RUnlock()
if b.payloadExpectedWithdrawals == nil {
return make([]*enginev1.Withdrawal, 0), nil
}
return b.payloadExpectedWithdrawalsVal(), nil
}
// Builder returns the builder at the given index.
func (b *BeaconState) Builder(index primitives.BuilderIndex) (*ethpb.Builder, error) {
b.lock.RLock()
defer b.lock.RUnlock()
if b.builders == nil {
return nil, nil
}
if uint64(index) >= uint64(len(b.builders)) {
return nil, fmt.Errorf("builder index %d out of bounds", index)
}
if b.builders[index] == nil {
return nil, nil
}
return ethpb.CopyBuilder(b.builders[index]), nil
}

View File

@@ -0,0 +1,67 @@
package state_native_test
import (
"testing"
state_native "github.com/OffchainLabs/prysm/v7/beacon-chain/state/state-native"
"github.com/OffchainLabs/prysm/v7/config/params"
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
"github.com/OffchainLabs/prysm/v7/testing/assert"
"github.com/OffchainLabs/prysm/v7/testing/require"
)
func TestLatestBlockHash(t *testing.T) {
t.Run("nil hash returns zero", func(t *testing.T) {
s, err := state_native.InitializeFromProtoGloas(&ethpb.BeaconStateGloas{})
require.NoError(t, err)
hash, err := s.LatestBlockHash()
require.NoError(t, err)
assert.Equal(t, [32]byte{}, hash)
})
t.Run("returns value", func(t *testing.T) {
var want [32]byte
copy(want[:], []byte("latest-block-hash"))
s, err := state_native.InitializeFromProtoGloas(&ethpb.BeaconStateGloas{
LatestBlockHash: want[:],
})
require.NoError(t, err)
hash, err := s.LatestBlockHash()
require.NoError(t, err)
assert.Equal(t, want, hash)
})
}
func TestBuilderPendingPayment(t *testing.T) {
t.Run("returns payment for slot", func(t *testing.T) {
slotsPerEpoch := params.BeaconConfig().SlotsPerEpoch
slot := primitives.Slot(7)
paymentIndex := slotsPerEpoch + (slot % slotsPerEpoch)
payments := make([]*ethpb.BuilderPendingPayment, slotsPerEpoch*2)
payment := &ethpb.BuilderPendingPayment{
Weight: 11,
Withdrawal: &ethpb.BuilderPendingWithdrawal{
FeeRecipient: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20},
Amount: 99,
BuilderIndex: 3,
},
}
payments[paymentIndex] = payment
s, err := state_native.InitializeFromProtoGloas(&ethpb.BeaconStateGloas{
BuilderPendingPayments: payments,
})
require.NoError(t, err)
got, err := s.BuilderPendingPayment(slot)
require.NoError(t, err)
require.DeepEqual(t, payment, got)
got.Withdrawal.FeeRecipient[0] = 9
require.Equal(t, byte(1), payment.Withdrawal.FeeRecipient[0])
})
}

View File

@@ -0,0 +1,101 @@
package state_native
import (
"github.com/OffchainLabs/prysm/v7/beacon-chain/state/state-native/types"
"github.com/OffchainLabs/prysm/v7/config/params"
"github.com/OffchainLabs/prysm/v7/consensus-types/interfaces"
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
)
func (b *BeaconState) SetExecutionPayloadBid(h interfaces.ROExecutionPayloadBid) error {
b.lock.Lock()
defer b.lock.Unlock()
parentBlockHash := h.ParentBlockHash()
parentBlockRoot := h.ParentBlockRoot()
blockHash := h.BlockHash()
randao := h.PrevRandao()
blobKzgCommitmentsRoot := h.BlobKzgCommitmentsRoot()
feeRecipient := h.FeeRecipient()
b.latestExecutionPayloadBid = &ethpb.ExecutionPayloadBid{
ParentBlockHash: parentBlockHash[:],
ParentBlockRoot: parentBlockRoot[:],
BlockHash: blockHash[:],
PrevRandao: randao[:],
GasLimit: h.GasLimit(),
BuilderIndex: h.BuilderIndex(),
Slot: h.Slot(),
Value: h.Value(),
ExecutionPayment: h.ExecutionPayment(),
BlobKzgCommitmentsRoot: blobKzgCommitmentsRoot[:],
FeeRecipient: feeRecipient[:],
}
b.markFieldAsDirty(types.LatestExecutionPayloadBid)
return nil
}
// SetBuilderPendingPayment sets a builder pending payment for the specified slot.
func (b *BeaconState) SetBuilderPendingPayment(slot primitives.Slot, payment *ethpb.BuilderPendingPayment) error {
b.lock.Lock()
defer b.lock.Unlock()
slotsPerEpoch := params.BeaconConfig().SlotsPerEpoch
paymentIndex := slotsPerEpoch + (slot % slotsPerEpoch)
b.builderPendingPayments[paymentIndex] = ethpb.CopyBuilderPendingPayment(payment)
b.markFieldAsDirty(types.BuilderPendingPayments)
return nil
}
// SetLatestBlockHash sets the latest execution block hash.
func (b *BeaconState) SetLatestBlockHash(hash [32]byte) error {
b.lock.Lock()
defer b.lock.Unlock()
b.latestBlockHash = hash[:]
b.markFieldAsDirty(types.LatestBlockHash)
return nil
}
// SetExecutionPayloadAvailability sets the execution payload availability bit for a specific slot.
func (b *BeaconState) SetExecutionPayloadAvailability(index primitives.Slot, available bool) error {
b.lock.Lock()
defer b.lock.Unlock()
bitIndex := index % params.BeaconConfig().SlotsPerHistoricalRoot
byteIndex := bitIndex / 8
bitPosition := bitIndex % 8
// Set or clear the bit
if available {
b.executionPayloadAvailability[byteIndex] |= 1 << bitPosition
} else {
b.executionPayloadAvailability[byteIndex] &^= 1 << bitPosition
}
b.markFieldAsDirty(types.ExecutionPayloadAvailability)
return nil
}
// SetBuilderPendingPayments sets the entire builder pending payments array.
func (b *BeaconState) SetBuilderPendingPayments(payments []*ethpb.BuilderPendingPayment) error {
b.lock.Lock()
defer b.lock.Unlock()
b.builderPendingPayments = ethpb.CopyBuilderPendingPaymentSlice(payments)
b.markFieldAsDirty(types.BuilderPendingPayments)
return nil
}
// AppendBuilderPendingWithdrawal appends a builder pending withdrawal to the list.
func (b *BeaconState) AppendBuilderPendingWithdrawal(withdrawal *ethpb.BuilderPendingWithdrawal) error {
b.lock.Lock()
defer b.lock.Unlock()
b.builderPendingWithdrawals = append(b.builderPendingWithdrawals, ethpb.CopyBuilderPendingWithdrawal(withdrawal))
b.markFieldAsDirty(types.BuilderPendingWithdrawals)
return nil
}

View File

@@ -0,0 +1,121 @@
package state_native
import (
"testing"
"github.com/OffchainLabs/prysm/v7/beacon-chain/state/state-native/types"
"github.com/OffchainLabs/prysm/v7/config/params"
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
"github.com/OffchainLabs/prysm/v7/testing/require"
)
func TestSetBuilderPendingPayment(t *testing.T) {
slotsPerEpoch := params.BeaconConfig().SlotsPerEpoch
slot := primitives.Slot(7)
paymentIndex := slotsPerEpoch + (slot % slotsPerEpoch)
payment := &ethpb.BuilderPendingPayment{
Weight: 11,
Withdrawal: &ethpb.BuilderPendingWithdrawal{
FeeRecipient: []byte{1, 2, 3, 4, 5},
Amount: 99,
BuilderIndex: 3,
},
}
state := &BeaconState{
builderPendingPayments: make([]*ethpb.BuilderPendingPayment, slotsPerEpoch*2),
dirtyFields: make(map[types.FieldIndex]bool),
}
require.NoError(t, state.SetBuilderPendingPayment(slot, payment))
require.Equal(t, true, state.dirtyFields[types.BuilderPendingPayments])
require.DeepEqual(t, payment, state.builderPendingPayments[paymentIndex])
state.builderPendingPayments[paymentIndex].Withdrawal.FeeRecipient[0] = 9
require.Equal(t, byte(1), payment.Withdrawal.FeeRecipient[0])
}
func TestSetLatestBlockHash(t *testing.T) {
var hash [32]byte
copy(hash[:], []byte("latest-block-hash"))
state := &BeaconState{
dirtyFields: make(map[types.FieldIndex]bool),
}
require.NoError(t, state.SetLatestBlockHash(hash))
require.Equal(t, true, state.dirtyFields[types.LatestBlockHash])
require.DeepEqual(t, hash[:], state.latestBlockHash)
}
func TestSetExecutionPayloadAvailability(t *testing.T) {
state := &BeaconState{
executionPayloadAvailability: make([]byte, params.BeaconConfig().SlotsPerHistoricalRoot/8),
dirtyFields: make(map[types.FieldIndex]bool),
}
slot := primitives.Slot(10)
bitIndex := slot % params.BeaconConfig().SlotsPerHistoricalRoot
byteIndex := bitIndex / 8
bitPosition := bitIndex % 8
require.NoError(t, state.SetExecutionPayloadAvailability(slot, true))
require.Equal(t, true, state.dirtyFields[types.ExecutionPayloadAvailability])
require.Equal(t, byte(1<<bitPosition), state.executionPayloadAvailability[byteIndex]&(1<<bitPosition))
require.NoError(t, state.SetExecutionPayloadAvailability(slot, false))
require.Equal(t, byte(0), state.executionPayloadAvailability[byteIndex]&(1<<bitPosition))
}
func TestSetBuilderPendingPayments(t *testing.T) {
payments := []*ethpb.BuilderPendingPayment{
{
Weight: 1,
Withdrawal: &ethpb.BuilderPendingWithdrawal{
FeeRecipient: []byte{1, 1, 1},
Amount: 5,
BuilderIndex: 2,
},
},
{
Weight: 2,
Withdrawal: &ethpb.BuilderPendingWithdrawal{
FeeRecipient: []byte{2, 2, 2},
Amount: 6,
BuilderIndex: 4,
},
},
}
state := &BeaconState{
dirtyFields: make(map[types.FieldIndex]bool),
}
require.NoError(t, state.SetBuilderPendingPayments(payments))
require.Equal(t, true, state.dirtyFields[types.BuilderPendingPayments])
require.DeepEqual(t, payments, state.builderPendingPayments)
state.builderPendingPayments[0].Weight = 99
require.Equal(t, primitives.Gwei(1), payments[0].Weight)
}
func TestAppendBuilderPendingWithdrawal(t *testing.T) {
withdrawal := &ethpb.BuilderPendingWithdrawal{
FeeRecipient: []byte{9, 8, 7},
Amount: 77,
BuilderIndex: 12,
}
state := &BeaconState{
dirtyFields: make(map[types.FieldIndex]bool),
}
require.NoError(t, state.AppendBuilderPendingWithdrawal(withdrawal))
require.Equal(t, true, state.dirtyFields[types.BuilderPendingWithdrawals])
require.Equal(t, 1, len(state.builderPendingWithdrawals))
require.DeepEqual(t, withdrawal, state.builderPendingWithdrawals[0])
state.builderPendingWithdrawals[0].FeeRecipient[0] = 1
require.Equal(t, byte(9), withdrawal.FeeRecipient[0])
}

View File

@@ -29,7 +29,6 @@ go_library(
"//beacon-chain/state:go_default_library",
"//beacon-chain/sync/backfill/coverage:go_default_library",
"//cache/lru:go_default_library",
"//config/features:go_default_library",
"//config/params:go_default_library",
"//consensus-types/blocks:go_default_library",
"//consensus-types/interfaces:go_default_library",
@@ -69,14 +68,11 @@ go_test(
"//beacon-chain/core/helpers:go_default_library",
"//beacon-chain/core/transition:go_default_library",
"//beacon-chain/db:go_default_library",
"//beacon-chain/db/kv:go_default_library",
"//beacon-chain/db/testing:go_default_library",
"//beacon-chain/forkchoice/doubly-linked-tree:go_default_library",
"//beacon-chain/state:go_default_library",
"//beacon-chain/state/state-native:go_default_library",
"//beacon-chain/state/testing:go_default_library",
"//cmd/beacon-chain/flags:go_default_library",
"//config/features:go_default_library",
"//config/params:go_default_library",
"//consensus-types/blocks:go_default_library",
"//consensus-types/blocks/testing:go_default_library",

View File

@@ -5,13 +5,10 @@ import (
"encoding/hex"
"fmt"
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/transition"
"github.com/OffchainLabs/prysm/v7/beacon-chain/state"
"github.com/OffchainLabs/prysm/v7/config/features"
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
"github.com/OffchainLabs/prysm/v7/encoding/bytesutil"
"github.com/OffchainLabs/prysm/v7/monitoring/tracing/trace"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
@@ -28,10 +25,6 @@ func (s *State) MigrateToCold(ctx context.Context, fRoot [32]byte) error {
s.migrationLock.Lock()
defer s.migrationLock.Unlock()
if features.Get().EnableStateDiff {
return s.migrateToColdHdiff(ctx, fRoot)
}
s.finalizedInfo.lock.RLock()
oldFSlot := s.finalizedInfo.slot
s.finalizedInfo.lock.RUnlock()
@@ -97,8 +90,21 @@ func (s *State) MigrateToCold(ctx context.Context, fRoot [32]byte) error {
}
}
}
if s.beaconDB.HasState(ctx, aRoot) {
s.migrateHotToCold(aRoot)
// If you are migrating a state and its already part of the hot state cache saved to the db,
// you can just remove it from the hot state cache as it becomes redundant.
s.saveHotStateDB.lock.Lock()
roots := s.saveHotStateDB.blockRootsOfSavedStates
for i := range roots {
if aRoot == roots[i] {
s.saveHotStateDB.blockRootsOfSavedStates = append(roots[:i], roots[i+1:]...)
// There shouldn't be duplicated roots in `blockRootsOfSavedStates`.
// Break here is ok.
break
}
}
s.saveHotStateDB.lock.Unlock()
continue
}
@@ -123,103 +129,3 @@ func (s *State) MigrateToCold(ctx context.Context, fRoot [32]byte) error {
return nil
}
// migrateToColdHdiff saves the state-diffs for slots that are in the state diff tree after finalization
func (s *State) migrateToColdHdiff(ctx context.Context, fRoot [32]byte) error {
s.finalizedInfo.lock.RLock()
oldFSlot := s.finalizedInfo.slot
s.finalizedInfo.lock.RUnlock()
fSlot, err := s.beaconDB.SlotByBlockRoot(ctx, fRoot)
if err != nil {
return errors.Wrap(err, "could not get slot by block root")
}
for slot := oldFSlot; slot < fSlot; slot++ {
if ctx.Err() != nil {
return ctx.Err()
}
_, lvl, err := s.beaconDB.SlotInDiffTree(slot)
if err != nil {
log.WithError(err).Errorf("could not determine if slot %d is in diff tree", slot)
continue
}
if lvl == -1 {
continue
}
// The state needs to be saved.
// Try the epoch boundary cache first.
cached, exists, err := s.epochBoundaryStateCache.getBySlot(slot)
if err != nil {
log.WithError(err).Errorf("could not get epoch boundary state for slot %d", slot)
cached = nil
exists = false
}
var aRoot [32]byte
var aState state.BeaconState
if exists {
aRoot = cached.root
aState = cached.state
} else {
_, roots, err := s.beaconDB.HighestRootsBelowSlot(ctx, slot)
if err != nil {
return err
}
// Given the block has been finalized, the db should not have more than one block in a given slot.
// We should error out when this happens.
if len(roots) != 1 {
return errUnknownBlock
}
aRoot = roots[0]
// Different than the legacy MigrateToCold, we need to always get the state even if
// the state exists in DB as part of the hot state db, because we need to process slots
// to the state diff tree slots.
aState, err = s.StateByRoot(ctx, aRoot)
if err != nil {
return err
}
}
if s.beaconDB.HasState(ctx, aRoot) {
s.migrateHotToCold(aRoot)
continue
}
// advance slots to the target slot
if aState.Slot() < slot {
aState, err = transition.ProcessSlots(ctx, aState, slot)
if err != nil {
return errors.Wrapf(err, "could not process slots to slot %d", slot)
}
}
if err := s.beaconDB.SaveState(ctx, aState, aRoot); err != nil {
return err
}
log.WithFields(
logrus.Fields{
"slot": aState.Slot(),
"root": fmt.Sprintf("%#x", aRoot),
}).Info("Saved state in DB")
}
// Update finalized info in memory.
fInfo, ok, err := s.epochBoundaryStateCache.getByBlockRoot(fRoot)
if err != nil {
return err
}
if ok {
s.SaveFinalizedState(fSlot, fRoot, fInfo.state)
}
return nil
}
func (s *State) migrateHotToCold(aRoot [32]byte) {
// If you are migrating a state and its already part of the hot state cache saved to the db,
// you can just remove it from the hot state cache as it becomes redundant.
s.saveHotStateDB.lock.Lock()
roots := s.saveHotStateDB.blockRootsOfSavedStates
for i := range roots {
if aRoot == roots[i] {
s.saveHotStateDB.blockRootsOfSavedStates = append(roots[:i], roots[i+1:]...)
// There shouldn't be duplicated roots in `blockRootsOfSavedStates`.
// Break here is ok.
break
}
}
s.saveHotStateDB.lock.Unlock()
}

View File

@@ -4,11 +4,8 @@ import (
"testing"
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/blocks"
"github.com/OffchainLabs/prysm/v7/beacon-chain/db/kv"
testDB "github.com/OffchainLabs/prysm/v7/beacon-chain/db/testing"
doublylinkedtree "github.com/OffchainLabs/prysm/v7/beacon-chain/forkchoice/doubly-linked-tree"
"github.com/OffchainLabs/prysm/v7/cmd/beacon-chain/flags"
"github.com/OffchainLabs/prysm/v7/config/features"
consensusblocks "github.com/OffchainLabs/prysm/v7/consensus-types/blocks"
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
@@ -227,170 +224,3 @@ func TestMigrateToCold_ParallelCalls(t *testing.T) {
assert.DeepEqual(t, [][32]byte{r7}, service.saveHotStateDB.blockRootsOfSavedStates, "Did not remove all saved hot state roots")
require.LogsContain(t, hook, "Saved state in DB")
}
// =========================================================================
// Tests for migrateToColdHdiff (state diff migration)
// =========================================================================
// setStateDiffExponents sets state diff exponents for testing.
// Uses exponents [6, 5] which means:
// - Level 0: Every 2^6 = 64 slots (full snapshot)
// - Level 1: Every 2^5 = 32 slots (diff)
func setStateDiffExponents() {
globalFlags := flags.GlobalFlags{
StateDiffExponents: []int{6, 5},
}
flags.Init(&globalFlags)
}
// TestMigrateToColdHdiff_CanUpdateFinalizedInfo verifies that the migration
// correctly updates finalized info when migrating to slots not in the diff tree.
func TestMigrateToColdHdiff_CanUpdateFinalizedInfo(t *testing.T) {
ctx := t.Context()
// Set exponents and create DB first (without EnableStateDiff flag).
setStateDiffExponents()
beaconDB := testDB.SetupDB(t)
// Initialize the state diff cache via the method on *kv.Store (not in interface).
require.NoError(t, beaconDB.(*kv.Store).InitStateDiffCacheForTesting(t, 0))
// Now enable the feature flag.
resetCfg := features.InitWithReset(&features.Flags{EnableStateDiff: true})
defer resetCfg()
service := New(beaconDB, doublylinkedtree.New())
beaconState, _ := util.DeterministicGenesisState(t, 32)
genesisStateRoot, err := beaconState.HashTreeRoot(ctx)
require.NoError(t, err)
genesis := blocks.NewGenesisBlock(genesisStateRoot[:])
util.SaveBlock(t, ctx, beaconDB, genesis)
gRoot, err := genesis.Block.HashTreeRoot()
require.NoError(t, err)
require.NoError(t, beaconDB.SaveGenesisBlockRoot(ctx, gRoot))
// Put genesis state in epoch boundary cache so migrateToColdHdiff doesn't need to retrieve from DB.
require.NoError(t, service.epochBoundaryStateCache.put(gRoot, beaconState))
// Set initial finalized info at genesis.
service.finalizedInfo = &finalizedInfo{
slot: 0,
root: gRoot,
state: beaconState,
}
// Create finalized block at slot 10 (not in diff tree, so no intermediate states saved).
finalizedState := beaconState.Copy()
require.NoError(t, finalizedState.SetSlot(10))
b := util.NewBeaconBlock()
b.Block.Slot = 10
fRoot, err := b.Block.HashTreeRoot()
require.NoError(t, err)
util.SaveBlock(t, ctx, beaconDB, b)
require.NoError(t, service.epochBoundaryStateCache.put(fRoot, finalizedState))
require.NoError(t, service.MigrateToCold(ctx, fRoot))
// Verify finalized info is updated.
assert.Equal(t, primitives.Slot(10), service.finalizedInfo.slot)
assert.DeepEqual(t, fRoot, service.finalizedInfo.root)
expectedHTR, err := finalizedState.HashTreeRoot(ctx)
require.NoError(t, err)
actualHTR, err := service.finalizedInfo.state.HashTreeRoot(ctx)
require.NoError(t, err)
assert.DeepEqual(t, expectedHTR, actualHTR)
}
// TestMigrateToColdHdiff_SkipsSlotsNotInDiffTree verifies that the migration
// skips slots that are not in the diff tree.
func TestMigrateToColdHdiff_SkipsSlotsNotInDiffTree(t *testing.T) {
hook := logTest.NewGlobal()
ctx := t.Context()
// Set exponents and create DB first (without EnableStateDiff flag).
setStateDiffExponents()
beaconDB := testDB.SetupDB(t)
// Initialize the state diff cache via the method on *kv.Store (not in interface).
require.NoError(t, beaconDB.(*kv.Store).InitStateDiffCacheForTesting(t, 0))
// Now enable the feature flag.
resetCfg := features.InitWithReset(&features.Flags{EnableStateDiff: true})
defer resetCfg()
service := New(beaconDB, doublylinkedtree.New())
beaconState, pks := util.DeterministicGenesisState(t, 32)
genesisStateRoot, err := beaconState.HashTreeRoot(ctx)
require.NoError(t, err)
genesis := blocks.NewGenesisBlock(genesisStateRoot[:])
util.SaveBlock(t, ctx, beaconDB, genesis)
gRoot, err := genesis.Block.HashTreeRoot()
require.NoError(t, err)
require.NoError(t, beaconDB.SaveGenesisBlockRoot(ctx, gRoot))
// Start from slot 1 to avoid slot 0 which is in the diff tree.
service.finalizedInfo = &finalizedInfo{
slot: 1,
root: gRoot,
state: beaconState,
}
// Reset the log hook to ignore setup logs.
hook.Reset()
// Create a block at slot 20 (NOT in diff tree with exponents [6,5]).
b20, err := util.GenerateFullBlock(beaconState, pks, util.DefaultBlockGenConfig(), 20)
require.NoError(t, err)
r20, err := b20.Block.HashTreeRoot()
require.NoError(t, err)
util.SaveBlock(t, ctx, beaconDB, b20)
require.NoError(t, beaconDB.SaveStateSummary(ctx, &ethpb.StateSummary{Slot: 20, Root: r20[:]}))
// Put finalized state in cache.
finalizedState := beaconState.Copy()
require.NoError(t, finalizedState.SetSlot(20))
require.NoError(t, service.epochBoundaryStateCache.put(r20, finalizedState))
require.NoError(t, service.MigrateToCold(ctx, r20))
// Verify NO states were saved during migration (slots 1-19 are not in diff tree).
assert.LogsDoNotContain(t, hook, "Saved state in DB")
}
// TestMigrateToColdHdiff_NoOpWhenFinalizedSlotNotAdvanced verifies that
// migration is a no-op when the finalized slot has not advanced.
func TestMigrateToColdHdiff_NoOpWhenFinalizedSlotNotAdvanced(t *testing.T) {
ctx := t.Context()
// Set exponents and create DB first (without EnableStateDiff flag).
setStateDiffExponents()
beaconDB := testDB.SetupDB(t)
// Initialize the state diff cache via the method on *kv.Store (not in interface).
require.NoError(t, beaconDB.(*kv.Store).InitStateDiffCacheForTesting(t, 0))
// Now enable the feature flag.
resetCfg := features.InitWithReset(&features.Flags{EnableStateDiff: true})
defer resetCfg()
service := New(beaconDB, doublylinkedtree.New())
beaconState, _ := util.DeterministicGenesisState(t, 32)
genesisStateRoot, err := beaconState.HashTreeRoot(ctx)
require.NoError(t, err)
genesis := blocks.NewGenesisBlock(genesisStateRoot[:])
util.SaveBlock(t, ctx, beaconDB, genesis)
gRoot, err := genesis.Block.HashTreeRoot()
require.NoError(t, err)
require.NoError(t, beaconDB.SaveGenesisBlockRoot(ctx, gRoot))
// Set finalized info already at slot 50.
finalizedState := beaconState.Copy()
require.NoError(t, finalizedState.SetSlot(50))
service.finalizedInfo = &finalizedInfo{
slot: 50,
root: gRoot,
state: finalizedState,
}
// Create block at same slot 50.
b := util.NewBeaconBlock()
b.Block.Slot = 50
fRoot, err := b.Block.HashTreeRoot()
require.NoError(t, err)
util.SaveBlock(t, ctx, beaconDB, b)
require.NoError(t, service.epochBoundaryStateCache.put(fRoot, finalizedState))
// Migration should be a no-op (finalized slot not advancing).
require.NoError(t, service.MigrateToCold(ctx, fRoot))
}

View File

@@ -238,9 +238,14 @@ func recomputeRootFromLayerVariable(idx int, item [32]byte, layers [][]*[32]byte
// AddInMixin describes a method from which a length mixin is added to the
// provided root.
func AddInMixin(root [32]byte, length uint64) ([32]byte, error) {
var rootBufRoot [32]byte
binary.LittleEndian.PutUint64(rootBufRoot[:], length)
return ssz.MixInLength(root, rootBufRoot[:]), nil
rootBuf := new(bytes.Buffer)
if err := binary.Write(rootBuf, binary.LittleEndian, length); err != nil {
return [32]byte{}, errors.Wrap(err, "could not marshal eth1data votes length")
}
// We need to mix in the length of the slice.
rootBufRoot := make([]byte, 32)
copy(rootBufRoot, rootBuf.Bytes())
return ssz.MixInLength(root, rootBufRoot), nil
}
// Merkleize 32-byte leaves into a Merkle trie for its adequate depth, returning

View File

@@ -10,72 +10,20 @@ import (
"github.com/OffchainLabs/prysm/v7/beacon-chain/p2p"
"github.com/OffchainLabs/prysm/v7/cmd/beacon-chain/flags"
"github.com/OffchainLabs/prysm/v7/config/params"
"github.com/OffchainLabs/prysm/v7/time/slots"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
var nilFinalizedStateError = errors.New("finalized state is nil")
func (s *Service) maintainCustodyInfo() error {
// Rationale of slot choice:
// - If syncing with an empty DB from genesis, then justifiedSlot = finalizedSlot = 0,
// and the node starts to sync from slot 0 ==> Using justifiedSlot is correct.
// - If syncing with an empty DB from a checkpoint, then justifiedSlot = finalizedSlot = checkpointSlot,
// and the node starts to sync from checkpointSlot ==> Using justifiedSlot is correct.
// - If syncing with a non-empty DB, then justifiedSlot > finalizedSlot,
// and the node starts to sync from justifiedSlot + 1 ==> Using justifiedSlot + 1 is correct.
func (s *Service) maintainCustodyInfo() {
const interval = 1 * time.Minute
finalizedCheckpoint, err := s.cfg.beaconDB.FinalizedCheckpoint(s.ctx)
if err != nil {
return errors.Wrap(err, "finalized checkpoint")
}
if finalizedCheckpoint == nil {
return errors.New("finalized checkpoint is nil")
}
finalizedSlot, err := slots.EpochStart(finalizedCheckpoint.Epoch)
if err != nil {
return errors.Wrap(err, "epoch start for finalized slot")
}
justifiedCheckpoint, err := s.cfg.beaconDB.JustifiedCheckpoint(s.ctx)
if err != nil {
return errors.Wrap(err, "justified checkpoint")
}
if justifiedCheckpoint == nil {
return errors.New("justified checkpoint is nil")
}
justifiedSlot, err := slots.EpochStart(justifiedCheckpoint.Epoch)
if err != nil {
return errors.Wrap(err, "epoch start for justified slot")
}
slot := justifiedSlot
if justifiedSlot > finalizedSlot {
slot++
}
earliestAvailableSlot, custodySubnetCount, err := s.updateCustodyInfoInDB(slot)
if err != nil {
return errors.Wrap(err, "could not get and save custody group count")
}
if _, _, err := s.cfg.p2p.UpdateCustodyInfo(earliestAvailableSlot, custodySubnetCount); err != nil {
return errors.Wrap(err, "update custody info")
}
async.RunEvery(s.ctx, interval, func() {
if err := s.updateCustodyInfoIfNeeded(); err != nil {
log.WithError(err).Error("Failed to update custody info")
}
})
return nil
}
func (s *Service) updateCustodyInfoIfNeeded() error {

View File

@@ -12,7 +12,6 @@ import (
mock "github.com/OffchainLabs/prysm/v7/beacon-chain/blockchain/testing"
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/transition"
"github.com/OffchainLabs/prysm/v7/beacon-chain/db/kv"
dbTest "github.com/OffchainLabs/prysm/v7/beacon-chain/db/testing"
testingDB "github.com/OffchainLabs/prysm/v7/beacon-chain/db/testing"
"github.com/OffchainLabs/prysm/v7/beacon-chain/p2p"
"github.com/OffchainLabs/prysm/v7/beacon-chain/p2p/peers"
@@ -935,7 +934,6 @@ func TestStatusRPCRequest_BadPeerHandshake(t *testing.T) {
r := &Service{
cfg: &config{
p2p: p1,
beaconDB: dbTest.SetupDB(t),
chain: chain,
stateNotifier: chain.StateNotifier(),
initialSync: &mockSync.Sync{IsSyncing: false},

View File

@@ -17,7 +17,6 @@ import (
blockfeed "github.com/OffchainLabs/prysm/v7/beacon-chain/core/feed/block"
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/feed/operation"
statefeed "github.com/OffchainLabs/prysm/v7/beacon-chain/core/feed/state"
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/peerdas"
"github.com/OffchainLabs/prysm/v7/beacon-chain/db"
"github.com/OffchainLabs/prysm/v7/beacon-chain/db/filesystem"
"github.com/OffchainLabs/prysm/v7/beacon-chain/execution"
@@ -34,11 +33,9 @@ import (
"github.com/OffchainLabs/prysm/v7/beacon-chain/sync/backfill/coverage"
"github.com/OffchainLabs/prysm/v7/beacon-chain/verification"
lruwrpr "github.com/OffchainLabs/prysm/v7/cache/lru"
"github.com/OffchainLabs/prysm/v7/cmd/beacon-chain/flags"
"github.com/OffchainLabs/prysm/v7/config/params"
"github.com/OffchainLabs/prysm/v7/consensus-types/blocks"
"github.com/OffchainLabs/prysm/v7/consensus-types/interfaces"
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
leakybucket "github.com/OffchainLabs/prysm/v7/container/leaky-bucket"
"github.com/OffchainLabs/prysm/v7/crypto/rand"
"github.com/OffchainLabs/prysm/v7/runtime"
@@ -278,6 +275,11 @@ func (s *Service) Start() {
s.processPendingBlocksQueue()
s.maintainPeerStatuses()
if params.FuluEnabled() {
s.maintainCustodyInfo()
}
s.resyncIfBehind()
// Update sync metrics.
@@ -285,15 +287,6 @@ func (s *Service) Start() {
// Prune data column cache periodically on finalization.
async.RunEvery(s.ctx, 30*time.Second, s.pruneDataColumnCache)
if !params.FuluEnabled() {
return
}
if err := s.maintainCustodyInfo(); err != nil {
log.WithError(err).Error("Failed to maintain custody info")
}
}
// Stop the regular sync service.
@@ -459,89 +452,6 @@ func (s *Service) waitForInitialSync(ctx context.Context) error {
}
}
// UpdateCustodyInfoInDB updates the custody information in the database.
// It returns the (potentially updated) custody group count and the earliest available slot.
func (s *Service) updateCustodyInfoInDB(slot primitives.Slot) (primitives.Slot, uint64, error) {
isSupernode := flags.Get().Supernode
isSemiSupernode := flags.Get().SemiSupernode
cfg := params.BeaconConfig()
custodyRequirement := cfg.CustodyRequirement
// Check if the node was previously subscribed to all data subnets, and if so,
// store the new status accordingly.
wasSupernode, err := s.cfg.beaconDB.UpdateSubscribedToAllDataSubnets(s.ctx, isSupernode)
if err != nil {
return 0, 0, errors.Wrap(err, "update subscribed to all data subnets")
}
// Compute the target custody group count based on current flag configuration.
targetCustodyGroupCount := custodyRequirement
// Supernode: custody all groups (either currently set or previously enabled)
if isSupernode {
targetCustodyGroupCount = cfg.NumberOfCustodyGroups
}
// Semi-supernode: custody minimum needed for reconstruction, or custody requirement if higher
if isSemiSupernode {
semiSupernodeCustody, err := peerdas.MinimumCustodyGroupCountToReconstruct()
if err != nil {
return 0, 0, errors.Wrap(err, "minimum custody group count")
}
targetCustodyGroupCount = max(custodyRequirement, semiSupernodeCustody)
}
// Safely compute the fulu fork slot.
fuluForkSlot, err := fuluForkSlot()
if err != nil {
return 0, 0, errors.Wrap(err, "fulu fork slot")
}
// If slot is before the fulu fork slot, then use the earliest stored slot as the reference slot.
if slot < fuluForkSlot {
slot, err = s.cfg.beaconDB.EarliestSlot(s.ctx)
if err != nil {
return 0, 0, errors.Wrap(err, "earliest slot")
}
}
earliestAvailableSlot, actualCustodyGroupCount, err := s.cfg.beaconDB.UpdateCustodyInfo(s.ctx, slot, targetCustodyGroupCount)
if err != nil {
return 0, 0, errors.Wrap(err, "update custody info")
}
if isSupernode {
log.WithFields(logrus.Fields{
"current": actualCustodyGroupCount,
"target": cfg.NumberOfCustodyGroups,
}).Info("Supernode mode enabled. Will custody all data columns going forward.")
}
if wasSupernode && !isSupernode {
log.Warningf("Because the `--%s` flag was previously used, the node will continue to act as a super node.", flags.Supernode.Name)
}
return earliestAvailableSlot, actualCustodyGroupCount, nil
}
func fuluForkSlot() (primitives.Slot, error) {
cfg := params.BeaconConfig()
fuluForkEpoch := cfg.FuluForkEpoch
if fuluForkEpoch == cfg.FarFutureEpoch {
return cfg.FarFutureSlot, nil
}
forkFuluSlot, err := slots.EpochStart(fuluForkEpoch)
if err != nil {
return 0, errors.Wrap(err, "epoch start")
}
return forkFuluSlot, nil
}
// Checker defines a struct which can verify whether a node is currently
// synchronizing a chain with the rest of peers in the network.
type Checker interface {

View File

@@ -16,9 +16,6 @@ import (
"github.com/OffchainLabs/prysm/v7/beacon-chain/startup"
state_native "github.com/OffchainLabs/prysm/v7/beacon-chain/state/state-native"
mockSync "github.com/OffchainLabs/prysm/v7/beacon-chain/sync/initial-sync/testing"
"github.com/OffchainLabs/prysm/v7/cmd/beacon-chain/flags"
"github.com/OffchainLabs/prysm/v7/config/params"
"github.com/OffchainLabs/prysm/v7/consensus-types/blocks"
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
leakybucket "github.com/OffchainLabs/prysm/v7/container/leaky-bucket"
"github.com/OffchainLabs/prysm/v7/crypto/bls"
@@ -443,224 +440,3 @@ func TestService_Stop_ConcurrentGoodbyeMessages(t *testing.T) {
require.Equal(t, false, util.WaitTimeout(&wg, 2*time.Second))
}
func TestUpdateCustodyInfoInDB(t *testing.T) {
const (
fuluForkEpoch = 10
custodyRequirement = uint64(4)
earliestStoredSlot = primitives.Slot(12)
numberOfCustodyGroups = uint64(64)
)
params.SetupTestConfigCleanup(t)
cfg := params.BeaconConfig()
cfg.FuluForkEpoch = fuluForkEpoch
cfg.CustodyRequirement = custodyRequirement
cfg.NumberOfCustodyGroups = numberOfCustodyGroups
params.OverrideBeaconConfig(cfg)
ctx := t.Context()
pbBlock := util.NewBeaconBlock()
pbBlock.Block.Slot = 12
signedBeaconBlock, err := blocks.NewSignedBeaconBlock(pbBlock)
require.NoError(t, err)
roBlock, err := blocks.NewROBlock(signedBeaconBlock)
require.NoError(t, err)
t.Run("CGC increases before fulu", func(t *testing.T) {
beaconDB := dbTest.SetupDB(t)
service := Service{cfg: &config{beaconDB: beaconDB}}
err = beaconDB.SaveBlock(ctx, roBlock)
require.NoError(t, err)
// Before Fulu
// -----------
actualEas, actualCgc, err := service.updateCustodyInfoInDB(15)
require.NoError(t, err)
require.Equal(t, earliestStoredSlot, actualEas)
require.Equal(t, custodyRequirement, actualCgc)
actualEas, actualCgc, err = service.updateCustodyInfoInDB(17)
require.NoError(t, err)
require.Equal(t, earliestStoredSlot, actualEas)
require.Equal(t, custodyRequirement, actualCgc)
resetFlags := flags.Get()
gFlags := new(flags.GlobalFlags)
gFlags.Supernode = true
flags.Init(gFlags)
defer flags.Init(resetFlags)
actualEas, actualCgc, err = service.updateCustodyInfoInDB(19)
require.NoError(t, err)
require.Equal(t, earliestStoredSlot, actualEas)
require.Equal(t, numberOfCustodyGroups, actualCgc)
// After Fulu
// ----------
actualEas, actualCgc, err = service.updateCustodyInfoInDB(fuluForkEpoch*primitives.Slot(cfg.SlotsPerEpoch) + 1)
require.NoError(t, err)
require.Equal(t, earliestStoredSlot, actualEas)
require.Equal(t, numberOfCustodyGroups, actualCgc)
})
t.Run("CGC increases after fulu", func(t *testing.T) {
beaconDB := dbTest.SetupDB(t)
service := Service{cfg: &config{beaconDB: beaconDB}}
err = beaconDB.SaveBlock(ctx, roBlock)
require.NoError(t, err)
// Before Fulu
// -----------
actualEas, actualCgc, err := service.updateCustodyInfoInDB(15)
require.NoError(t, err)
require.Equal(t, earliestStoredSlot, actualEas)
require.Equal(t, custodyRequirement, actualCgc)
actualEas, actualCgc, err = service.updateCustodyInfoInDB(17)
require.NoError(t, err)
require.Equal(t, earliestStoredSlot, actualEas)
require.Equal(t, custodyRequirement, actualCgc)
// After Fulu
// ----------
resetFlags := flags.Get()
gFlags := new(flags.GlobalFlags)
gFlags.Supernode = true
flags.Init(gFlags)
defer flags.Init(resetFlags)
slot := fuluForkEpoch*primitives.Slot(cfg.SlotsPerEpoch) + 1
actualEas, actualCgc, err = service.updateCustodyInfoInDB(slot)
require.NoError(t, err)
require.Equal(t, slot, actualEas)
require.Equal(t, numberOfCustodyGroups, actualCgc)
actualEas, actualCgc, err = service.updateCustodyInfoInDB(slot + 2)
require.NoError(t, err)
require.Equal(t, slot, actualEas)
require.Equal(t, numberOfCustodyGroups, actualCgc)
})
t.Run("Supernode downgrade prevented", func(t *testing.T) {
beaconDB := dbTest.SetupDB(t)
service := Service{cfg: &config{beaconDB: beaconDB}}
err = beaconDB.SaveBlock(ctx, roBlock)
require.NoError(t, err)
// Enable supernode
resetFlags := flags.Get()
gFlags := new(flags.GlobalFlags)
gFlags.Supernode = true
flags.Init(gFlags)
slot := fuluForkEpoch*primitives.Slot(cfg.SlotsPerEpoch) + 1
actualEas, actualCgc, err := service.updateCustodyInfoInDB(slot)
require.NoError(t, err)
require.Equal(t, slot, actualEas)
require.Equal(t, numberOfCustodyGroups, actualCgc)
// Try to downgrade by removing flag
gFlags.Supernode = false
flags.Init(gFlags)
defer flags.Init(resetFlags)
// Should still be supernode
actualEas, actualCgc, err = service.updateCustodyInfoInDB(slot + 2)
require.NoError(t, err)
require.Equal(t, slot, actualEas)
require.Equal(t, numberOfCustodyGroups, actualCgc) // Still 64, not downgraded
})
t.Run("Semi-supernode downgrade prevented", func(t *testing.T) {
beaconDB := dbTest.SetupDB(t)
service := Service{cfg: &config{beaconDB: beaconDB}}
err = beaconDB.SaveBlock(ctx, roBlock)
require.NoError(t, err)
// Enable semi-supernode
resetFlags := flags.Get()
gFlags := new(flags.GlobalFlags)
gFlags.SemiSupernode = true
flags.Init(gFlags)
slot := fuluForkEpoch*primitives.Slot(cfg.SlotsPerEpoch) + 1
actualEas, actualCgc, err := service.updateCustodyInfoInDB(slot)
require.NoError(t, err)
require.Equal(t, slot, actualEas)
semiSupernodeCustody := numberOfCustodyGroups / 2 // 64
require.Equal(t, semiSupernodeCustody, actualCgc) // Semi-supernode custodies 64 groups
// Try to downgrade by removing flag
gFlags.SemiSupernode = false
flags.Init(gFlags)
defer flags.Init(resetFlags)
// UpdateCustodyInfo should prevent downgrade - custody count should remain at 64
actualEas, actualCgc, err = service.updateCustodyInfoInDB(slot + 2)
require.NoError(t, err)
require.Equal(t, slot, actualEas)
require.Equal(t, semiSupernodeCustody, actualCgc) // Still 64 due to downgrade prevention by UpdateCustodyInfo
})
t.Run("Semi-supernode to supernode upgrade allowed", func(t *testing.T) {
beaconDB := dbTest.SetupDB(t)
service := Service{cfg: &config{beaconDB: beaconDB}}
err = beaconDB.SaveBlock(ctx, roBlock)
require.NoError(t, err)
// Start with semi-supernode
resetFlags := flags.Get()
gFlags := new(flags.GlobalFlags)
gFlags.SemiSupernode = true
flags.Init(gFlags)
slot := fuluForkEpoch*primitives.Slot(cfg.SlotsPerEpoch) + 1
actualEas, actualCgc, err := service.updateCustodyInfoInDB(slot)
require.NoError(t, err)
require.Equal(t, slot, actualEas)
semiSupernodeCustody := numberOfCustodyGroups / 2 // 64
require.Equal(t, semiSupernodeCustody, actualCgc) // Semi-supernode custodies 64 groups
// Upgrade to full supernode
gFlags.SemiSupernode = false
gFlags.Supernode = true
flags.Init(gFlags)
defer flags.Init(resetFlags)
// Should upgrade to full supernode
upgradeSlot := slot + 2
actualEas, actualCgc, err = service.updateCustodyInfoInDB(upgradeSlot)
require.NoError(t, err)
require.Equal(t, upgradeSlot, actualEas) // Earliest slot updates when upgrading
require.Equal(t, numberOfCustodyGroups, actualCgc) // Upgraded to 128
})
t.Run("Semi-supernode with high validator requirements uses higher custody", func(t *testing.T) {
beaconDB := dbTest.SetupDB(t)
service := Service{cfg: &config{beaconDB: beaconDB}}
err = beaconDB.SaveBlock(ctx, roBlock)
require.NoError(t, err)
// Enable semi-supernode
resetFlags := flags.Get()
gFlags := new(flags.GlobalFlags)
gFlags.SemiSupernode = true
flags.Init(gFlags)
defer flags.Init(resetFlags)
// Mock a high custody requirement (simulating many validators)
// We need to override the custody requirement calculation
// For this test, we'll verify the logic by checking if custodyRequirement > 64
// Since custodyRequirement in minimalTestService is 4, we can't test the high case here
// This would require a different test setup with actual validators
slot := fuluForkEpoch*primitives.Slot(cfg.SlotsPerEpoch) + 1
actualEas, actualCgc, err := service.updateCustodyInfoInDB(slot)
require.NoError(t, err)
require.Equal(t, slot, actualEas)
semiSupernodeCustody := numberOfCustodyGroups / 2 // 64
// With low validator requirements (4), should use semi-supernode minimum (64)
require.Equal(t, semiSupernodeCustody, actualCgc)
})
}

View File

@@ -588,12 +588,6 @@ func fcReturnsTargetRoot(root [32]byte) func([32]byte, primitives.Epoch) ([32]by
}
}
func fcReturnsDependentRoot() func([32]byte, primitives.Epoch) ([32]byte, error) {
return func(root [32]byte, epoch primitives.Epoch) ([32]byte, error) {
return root, nil
}
}
type mockSignatureCache struct {
svCalledForSig map[signatureData]bool
svcb func(sig signatureData) (bool, error)

View File

@@ -1,6 +1,7 @@
package verification
import (
"bytes"
"context"
"crypto/sha256"
"fmt"
@@ -18,7 +19,6 @@ import (
"github.com/OffchainLabs/prysm/v7/runtime/logging"
"github.com/OffchainLabs/prysm/v7/time/slots"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
var (
@@ -293,57 +293,55 @@ func (dv *RODataColumnsVerifier) ValidProposerSignature(ctx context.Context) (er
// The returned state is guaranteed to be at the same epoch as the data column's epoch, and have the same randao mix and active
// validator indices as the data column's parent state advanced to the data column's slot.
func (dv *RODataColumnsVerifier) getVerifyingState(ctx context.Context, dataColumn blocks.RODataColumn) (state.ReadOnlyBeaconState, error) {
dataColumnSlot := dataColumn.Slot()
dataColumnEpoch := slots.ToEpoch(dataColumnSlot)
if dataColumnEpoch == 0 {
return dv.hsp.HeadStateReadOnly(ctx)
}
parentRoot := dataColumn.ParentRoot()
dcDependentRoot, err := dv.fc.DependentRootForEpoch(parentRoot, dataColumnEpoch-1)
if err != nil {
return nil, err
}
headRoot, err := dv.hsp.HeadRoot(ctx)
if err != nil {
return nil, err
}
headDependentRoot, err := dv.fc.DependentRootForEpoch(bytesutil.ToBytes32(headRoot), dataColumnEpoch-1)
if err != nil {
return nil, err
}
if dcDependentRoot == headDependentRoot {
headSlot := dv.hsp.HeadSlot()
headEpoch := slots.ToEpoch(headSlot)
if headEpoch == dataColumnEpoch || headEpoch == dataColumnEpoch-1 {
parentRoot := dataColumn.ParentRoot()
dataColumnSlot := dataColumn.Slot()
dataColumnEpoch := slots.ToEpoch(dataColumnSlot)
headSlot := dv.hsp.HeadSlot()
headEpoch := slots.ToEpoch(headSlot)
// Use head if it's the parent
if bytes.Equal(parentRoot[:], headRoot) {
// If they are in the same epoch, then we can return the head state directly
if dataColumnEpoch == headEpoch {
return dv.hsp.HeadStateReadOnly(ctx)
}
if headEpoch+1 < dataColumnEpoch {
headState, err := dv.hsp.HeadState(ctx)
if err != nil {
return nil, err
}
return transition.ProcessSlotsUsingNextSlotCache(ctx, headState, headRoot, dataColumnSlot)
// Otherwise, we need to process the head state to the data column's slot
headState, err := dv.hsp.HeadState(ctx)
if err != nil {
return nil, err
}
return transition.ProcessSlotsUsingNextSlotCache(ctx, headState, headRoot, dataColumnSlot)
}
// If head and data column are in the same epoch and head is compatible with the parent's depdendent root, then use head
if dataColumnEpoch == headEpoch {
headDependent, err := dv.fc.DependentRootForEpoch(bytesutil.ToBytes32(headRoot), dataColumnEpoch)
if err != nil {
return nil, err
}
parentDependent, err := dv.fc.DependentRootForEpoch(parentRoot, dataColumnEpoch)
if err != nil {
return nil, err
}
if bytes.Equal(headDependent[:], parentDependent[:]) {
return dv.hsp.HeadStateReadOnly(ctx)
}
}
logrus.WithFields(logrus.Fields{
"slot": dataColumnSlot,
"parentRoot": fmt.Sprintf("%#x", parentRoot),
"headRoot": fmt.Sprintf("%#x", headRoot),
}).Debug("Replying state for data column verification")
targetRoot, err := dv.fc.TargetRootForEpoch(parentRoot, dataColumnEpoch)
// Otherwise retrieve the parent state and advance it to the data column's slot
parentState, err := dv.sr.StateByRoot(ctx, parentRoot)
if err != nil {
return nil, err
}
targetState, err := dv.sr.StateByRoot(ctx, targetRoot)
if err != nil {
return nil, err
parentEpoch := slots.ToEpoch(parentState.Slot())
if dataColumnEpoch == parentEpoch {
return parentState, nil
}
targetEpoch := slots.ToEpoch(targetState.Slot())
if targetEpoch == dataColumnEpoch || targetEpoch == dataColumnEpoch-1 {
return targetState, nil
}
return transition.ProcessSlotsUsingNextSlotCache(ctx, targetState, parentRoot[:], dataColumnSlot)
return transition.ProcessSlotsUsingNextSlotCache(ctx, parentState, parentRoot[:], dataColumnSlot)
}
func (dv *RODataColumnsVerifier) SidecarParentSeen(parentSeen func([fieldparams.RootLength]byte) bool) (err error) {

View File

@@ -1,7 +1,6 @@
package verification
import (
"context"
"reflect"
"testing"
"time"
@@ -10,7 +9,6 @@ import (
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/peerdas"
forkchoicetypes "github.com/OffchainLabs/prysm/v7/beacon-chain/forkchoice/types"
"github.com/OffchainLabs/prysm/v7/beacon-chain/startup"
"github.com/OffchainLabs/prysm/v7/beacon-chain/state"
fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams"
"github.com/OffchainLabs/prysm/v7/config/params"
"github.com/OffchainLabs/prysm/v7/consensus-types/blocks"
@@ -283,7 +281,7 @@ func TestColumnSlotAboveFinalized(t *testing.T) {
func TestValidProposerSignature(t *testing.T) {
const (
columnSlot = 97
columnSlot = 0
blobCount = 1
)
@@ -296,83 +294,59 @@ func TestValidProposerSignature(t *testing.T) {
// The signature data does not depend on the data column itself, so we can use the first one.
expectedSignatureData := columnToSignatureData(firstColumn)
// Create a proper Fulu state for verification.
// We need enough validators to cover the proposer index.
numValidators := max(uint64(firstColumn.ProposerIndex()+1), 64)
fuluState, _ := util.DeterministicGenesisStateFulu(t, numValidators)
// Head state provider that returns the fuluState via HeadStateReadOnly path.
headStateWithState := &mockHeadStateProvider{
headRoot: parentRoot[:],
headSlot: columnSlot,
headStateReadOnly: fuluState,
}
// Head state provider that will fail (headStateReadOnly is nil).
headStateNotFound := &mockHeadStateProvider{
headRoot: parentRoot[:],
headSlot: columnSlot,
}
testCases := []struct {
isError bool
vscbShouldError bool
svcbReturn bool
stateByRooter StateByRooter
headStateProvider *mockHeadStateProvider
vscbError error
svcbError error
name string
isError bool
vscbShouldError bool
svcbReturn bool
stateByRooter StateByRooter
vscbError error
svcbError error
name string
}{
{
name: "cache hit - success",
svcbReturn: true,
svcbError: nil,
vscbShouldError: true,
vscbError: nil,
stateByRooter: &mockStateByRooter{sbr: sbrErrorIfCalled(t)},
headStateProvider: headStateWithState,
isError: false,
name: "cache hit - success",
svcbReturn: true,
svcbError: nil,
vscbShouldError: true,
vscbError: nil,
stateByRooter: &mockStateByRooter{sbr: sbrErrorIfCalled(t)},
isError: false,
},
{
name: "cache hit - error",
svcbReturn: true,
svcbError: errors.New("derp"),
vscbShouldError: true,
vscbError: nil,
stateByRooter: &mockStateByRooter{sbr: sbrErrorIfCalled(t)},
headStateProvider: headStateWithState,
isError: true,
name: "cache hit - error",
svcbReturn: true,
svcbError: errors.New("derp"),
vscbShouldError: true,
vscbError: nil,
stateByRooter: &mockStateByRooter{sbr: sbrErrorIfCalled(t)},
isError: true,
},
{
name: "cache miss - success",
svcbReturn: false,
svcbError: nil,
vscbShouldError: false,
vscbError: nil,
stateByRooter: sbrForValOverrideWithT(t, firstColumn.ProposerIndex(), validator),
headStateProvider: headStateWithState,
isError: false,
name: "cache miss - success",
svcbReturn: false,
svcbError: nil,
vscbShouldError: false,
vscbError: nil,
stateByRooter: sbrForValOverrideWithT(t, firstColumn.ProposerIndex(), validator),
isError: false,
},
{
name: "cache miss - state not found",
svcbReturn: false,
svcbError: nil,
vscbShouldError: false,
vscbError: nil,
stateByRooter: sbrNotFound(t, expectedSignatureData.Parent),
headStateProvider: headStateNotFound,
isError: true,
name: "cache miss - state not found",
svcbReturn: false,
svcbError: nil,
vscbShouldError: false,
vscbError: nil,
stateByRooter: sbrNotFound(t, expectedSignatureData.Parent),
isError: true,
},
{
name: "cache miss - signature failure",
svcbReturn: false,
svcbError: nil,
vscbShouldError: false,
vscbError: errors.New("signature, not so good!"),
stateByRooter: sbrForValOverrideWithT(t, firstColumn.ProposerIndex(), validator),
headStateProvider: headStateWithState,
isError: true,
name: "cache miss - signature failure",
svcbReturn: false,
svcbError: nil,
vscbShouldError: false,
vscbError: errors.New("signature, not so good!"),
stateByRooter: sbrForValOverrideWithT(t, firstColumn.ProposerIndex(), validator),
isError: true,
},
}
@@ -403,10 +377,9 @@ func TestValidProposerSignature(t *testing.T) {
shared: &sharedResources{
sc: signatureCache,
sr: tc.stateByRooter,
hsp: tc.headStateProvider,
hsp: &mockHeadStateProvider{},
fc: &mockForkchoicer{
DependentRootForEpochCB: fcReturnsDependentRoot(),
TargetRootForEpochCB: fcReturnsTargetRoot([fieldparams.RootLength]byte{}),
TargetRootForEpochCB: fcReturnsTargetRoot([fieldparams.RootLength]byte{}),
},
},
}
@@ -432,7 +405,7 @@ func TestValidProposerSignature(t *testing.T) {
func TestDataColumnsSidecarParentSeen(t *testing.T) {
const (
columnSlot = 97
columnSlot = 0
blobCount = 1
)
@@ -536,7 +509,7 @@ func TestDataColumnsSidecarParentValid(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
const (
columnSlot = 97
columnSlot = 0
blobCount = 1
)
@@ -657,7 +630,7 @@ func TestDataColumnsSidecarDescendsFromFinalized(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
const (
columnSlot = 97
columnSlot = 0
blobCount = 1
)
@@ -720,7 +693,7 @@ func TestDataColumnsSidecarInclusionProven(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
const (
columnSlot = 97
columnSlot = 0
blobCount = 1
)
@@ -775,7 +748,7 @@ func TestDataColumnsSidecarKzgProofVerified(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
const (
columnSlot = 97
columnSlot = 0
blobCount = 1
)
@@ -951,135 +924,3 @@ func TestColumnRequirementSatisfaction(t *testing.T) {
require.NoError(t, err)
}
func TestGetVerifyingStateEdgeCases(t *testing.T) {
const (
columnSlot = 97 // epoch 3
blobCount = 1
)
parentRoot := [fieldparams.RootLength]byte{}
columns := GenerateTestDataColumns(t, parentRoot, columnSlot, blobCount)
// Create a proper Fulu state for verification.
numValidators := max(uint64(columns[0].ProposerIndex()+1), 64)
fuluState, _ := util.DeterministicGenesisStateFulu(t, numValidators)
t.Run("different dependent roots - uses StateByRoot path", func(t *testing.T) {
// Parent and head are on different forks with different dependent roots.
// This forces the code to use TargetRootForEpoch -> StateByRoot path.
signatureCache := &mockSignatureCache{
svcb: func(signatureData signatureData) (bool, error) {
return false, nil // Cache miss
},
vscb: func(signatureData signatureData, _ validatorAtIndexer) (err error) {
return nil // Signature valid
},
}
// StateByRoot will be called because dependent roots differ
stateByRootCalled := false
stateByRooter := &mockStateByRooter{
sbr: func(_ context.Context, root [32]byte) (state.BeaconState, error) {
stateByRootCalled = true
return fuluState, nil
},
}
initializer := Initializer{
shared: &sharedResources{
sc: signatureCache,
sr: stateByRooter,
hsp: &mockHeadStateProvider{
headRoot: []byte{0xff}, // Different from parentRoot
headSlot: columnSlot,
},
fc: &mockForkchoicer{
// Return different roots for parent vs head to simulate different forks
DependentRootForEpochCB: func(root [32]byte, epoch primitives.Epoch) ([32]byte, error) {
return root, nil // Returns input, so parent [0...] != head [0xff...]
},
TargetRootForEpochCB: fcReturnsTargetRoot([fieldparams.RootLength]byte{}),
},
},
}
verifier := initializer.NewDataColumnsVerifier(columns, GossipDataColumnSidecarRequirements)
err := verifier.ValidProposerSignature(t.Context())
require.NoError(t, err)
require.Equal(t, true, stateByRootCalled, "StateByRoot should be called when dependent roots differ")
})
t.Run("same dependent root head far ahead - uses head state with ProcessSlots", func(t *testing.T) {
// Parent is ancestor of head on same chain, but head is in epoch 1 while column is in epoch 3.
// headEpoch (1) + 1 < dataColumnEpoch (3), so ProcessSlots is called on head state.
signatureCache := &mockSignatureCache{
svcb: func(signatureData signatureData) (bool, error) {
return false, nil // Cache miss
},
vscb: func(signatureData signatureData, _ validatorAtIndexer) (err error) {
return nil // Signature valid
},
}
headStateCalled := false
initializer := Initializer{
shared: &sharedResources{
sc: signatureCache,
sr: &mockStateByRooter{sbr: sbrErrorIfCalled(t)}, // Should not be called
hsp: &mockHeadStateProvider{
headRoot: parentRoot[:], // Same as parent
headSlot: 32, // Epoch 1
headState: fuluState.Copy(), // HeadState (not ReadOnly) for ProcessSlots
headStateReadOnly: nil, // Should not use ReadOnly path
},
fc: &mockForkchoicer{
// Return same root for both to simulate same chain
DependentRootForEpochCB: func(root [32]byte, epoch primitives.Epoch) ([32]byte, error) {
return [32]byte{0xaa}, nil // Same for all inputs
},
TargetRootForEpochCB: fcReturnsTargetRoot([fieldparams.RootLength]byte{}),
},
},
}
// Wrap to detect HeadState call
originalHsp := initializer.shared.hsp.(*mockHeadStateProvider)
wrappedHsp := &mockHeadStateProvider{
headRoot: originalHsp.headRoot,
headSlot: originalHsp.headSlot,
headState: originalHsp.headState,
}
initializer.shared.hsp = &headStateCallTracker{
mockHeadStateProvider: wrappedHsp,
headStateCalled: &headStateCalled,
}
verifier := initializer.NewDataColumnsVerifier(columns, GossipDataColumnSidecarRequirements)
err := verifier.ValidProposerSignature(t.Context())
require.NoError(t, err)
require.Equal(t, true, headStateCalled, "HeadState should be called when head is far ahead")
})
}
// headStateCallTracker wraps mockHeadStateProvider to track HeadState calls.
type headStateCallTracker struct {
*mockHeadStateProvider
headStateCalled *bool
}
func (h *headStateCallTracker) HeadState(ctx context.Context) (state.BeaconState, error) {
*h.headStateCalled = true
return h.mockHeadStateProvider.HeadState(ctx)
}
func (h *headStateCallTracker) HeadRoot(ctx context.Context) ([]byte, error) {
return h.mockHeadStateProvider.HeadRoot(ctx)
}
func (h *headStateCallTracker) HeadSlot() primitives.Slot {
return h.mockHeadStateProvider.HeadSlot()
}
func (h *headStateCallTracker) HeadStateReadOnly(ctx context.Context) (state.ReadOnlyBeaconState, error) {
return h.mockHeadStateProvider.HeadStateReadOnly(ctx)
}

View File

@@ -1,3 +0,0 @@
### Changed
- post electra we now call attestation data once per slot and use a cache for subsequent requests

View File

@@ -1,11 +0,0 @@
### Added
- Adding basic fulu fork transition support for mainnet and minimal e2e tests (multi scenario is not included)
### Changed
- updated go ethereum to 1.16.7
### Removed
- removed github.com/MariusVanDerWijden/FuzzyVM and github.com/MariusVanDerWijden/tx-fuzz due to lack of support post 1.16.7, only used in e2e for transaction fuzzing

View File

@@ -1,3 +0,0 @@
### Changed
- updating geth dependency from 1.16.7 to 1.16.8.

View File

@@ -1,3 +0,0 @@
### Fixed
- When adding the `--[semi-]supernode` flag, update the ealiest available slot accordingly.

View File

@@ -1,2 +0,0 @@
### Added
- `commitment_count_in_gossip_processed_blocks` gauge metric to track the number of blob KZG commitments in processed beacon blocks.

View File

@@ -1,3 +0,0 @@
### Fixed
- fixed broken and old links to actual

View File

@@ -1,2 +0,0 @@
### Changed
- Use dependent root and target root to verify data column proposer index.

View File

@@ -1,3 +0,0 @@
### Added
- Migrate to cold with the hdiff feature.

View File

@@ -1,2 +0,0 @@
### Added
- Update spectests to v1.7.0-alpha-1.

View File

@@ -1,2 +0,0 @@
### Added
- Add feature flag to use hashtree instead of gohashtre.

View File

@@ -1,3 +0,0 @@
### Changed
- Avoid unnessary heap allocation while calling MixInLength

View File

@@ -0,0 +1,3 @@
### Added
- Add process execution payload for gloas

View File

@@ -70,7 +70,6 @@ type Flags struct {
DisableStakinContractCheck bool // Disables check for deposit contract when proposing blocks
IgnoreUnviableAttestations bool // Ignore attestations whose target state is not viable (avoids lagging-node DoS).
EnableHashtree bool // Enables usage of the hashtree library for hashing
EnableVerboseSigVerification bool // EnableVerboseSigVerification specifies whether to verify individual signature if batch verification fails
EnableProposerPreprocessing bool // EnableProposerPreprocessing enables proposer pre-processing of blocks before proposing.
@@ -82,6 +81,7 @@ type Flags struct {
SaveInvalidBlob bool // SaveInvalidBlob saves invalid blob to temp.
EnableDiscoveryReboot bool // EnableDiscoveryReboot allows the node to have its local listener to be rebooted in the event of discovery issues.
LowValcountSweep bool // LowValcountSweep bounds withdrawal sweep by validator count.
// KeystoreImportDebounceInterval specifies the time duration the validator waits to reload new keys if they have
// changed on disk. This feature is for advanced use cases only.
@@ -238,10 +238,6 @@ func ConfigureBeaconChain(ctx *cli.Context) error {
logEnabled(enableFullSSZDataLogging)
cfg.EnableFullSSZDataLogging = true
}
if ctx.IsSet(enableHashtree.Name) {
logEnabled(enableHashtree)
cfg.EnableHashtree = true
}
cfg.EnableVerboseSigVerification = true
if ctx.IsSet(disableVerboseSigVerification.Name) {
logEnabled(disableVerboseSigVerification)
@@ -294,6 +290,11 @@ func ConfigureBeaconChain(ctx *cli.Context) error {
logEnabled(ignoreUnviableAttestations)
cfg.IgnoreUnviableAttestations = true
}
if ctx.Bool(lowValcountSweep.Name) {
logEnabled(lowValcountSweep)
cfg.LowValcountSweep = true
}
if ctx.IsSet(EnableStateDiff.Name) {
logEnabled(EnableStateDiff)
cfg.EnableStateDiff = true

View File

@@ -133,10 +133,6 @@ var (
Name: "enable-beacon-rest-api",
Usage: "(Experimental): Enables of the beacon REST API when querying a beacon node.",
}
enableHashtree = &cli.BoolFlag{
Name: "enable-hashtree",
Usage: "(Experimental): Enables the hashtree hashing library.",
}
disableVerboseSigVerification = &cli.BoolFlag{
Name: "disable-verbose-sig-verification",
Usage: "Disables identifying invalid signatures if batch verification fails when processing block.",
@@ -220,6 +216,13 @@ var (
Name: "ignore-unviable-attestations",
Usage: "Ignores attestations whose target state is not viable with respect to the current head (avoid expensive state replay from lagging attesters).",
}
// lowValcountSweep bounds withdrawal sweep by validator count.
lowValcountSweep = &cli.BoolFlag{
Name: "low-valcount-sweep",
Usage: "Uses validator count bound for withdrawal sweep when validator count is less than MaxValidatorsPerWithdrawalsSweep.",
Value: false,
Hidden: true,
}
)
// devModeFlags holds list of flags that are set when development mode is on.
@@ -282,7 +285,7 @@ var BeaconChainFlags = combinedFlags([]cli.Flag{
enableExperimentalAttestationPool,
forceHeadFlag,
blacklistRoots,
enableHashtree,
lowValcountSweep,
}, deprecatedBeaconFlags, deprecatedFlags, upcomingDeprecation)
func combinedFlags(flags ...[]cli.Flag) []cli.Flag {

View File

@@ -28,7 +28,6 @@ go_library(
"testutils_develop.go", # keep
"values.go",
],
embedsrcs = ["testdata/e2e_config.yaml"],
importpath = "github.com/OffchainLabs/prysm/v7/config/params",
visibility = ["//visibility:public"],
deps = [

View File

@@ -139,6 +139,7 @@ type BeaconChainConfig struct {
DomainApplicationMask [4]byte `yaml:"DOMAIN_APPLICATION_MASK" spec:"true"` // DomainApplicationMask defines the BLS signature domain for application mask.
DomainApplicationBuilder [4]byte `yaml:"DOMAIN_APPLICATION_BUILDER" spec:"true"` // DomainApplicationBuilder defines the BLS signature domain for application builder.
DomainBLSToExecutionChange [4]byte `yaml:"DOMAIN_BLS_TO_EXECUTION_CHANGE" spec:"true"` // DomainBLSToExecutionChange defines the BLS signature domain to change withdrawal addresses to ETH1 prefix
DomainBeaconBuilder [4]byte `yaml:"DOMAIN_BEACON_BUILDER" spec:"true"` // DomainBeaconBuilder defines the BLS signature domain for beacon block builder.
// Prysm constants.
GenesisValidatorsRoot [32]byte // GenesisValidatorsRoot is the root hash of the genesis validators.

View File

@@ -182,6 +182,7 @@ var mainnetBeaconConfig = &BeaconChainConfig{
DomainApplicationMask: bytesutil.Uint32ToBytes4(0x00000001),
DomainApplicationBuilder: bytesutil.Uint32ToBytes4(0x00000001),
DomainBLSToExecutionChange: bytesutil.Uint32ToBytes4(0x0A000000),
DomainBeaconBuilder: bytesutil.Uint32ToBytes4(0x0B000000),
// Prysm constants.
GenesisValidatorsRoot: [32]byte{75, 54, 61, 185, 78, 40, 97, 32, 215, 110, 185, 5, 52, 15, 221, 78, 84, 191, 233, 240, 107, 243, 63, 246, 207, 90, 210, 127, 81, 27, 254, 149},

View File

@@ -49,7 +49,7 @@ ELECTRA_FORK_VERSION: 0x050000fd
ELECTRA_FORK_EPOCH: 14
# Fulu
FULU_FORK_VERSION: 0x060000fd
FULU_FORK_EPOCH: 16
FULU_FORK_EPOCH: 18446744073709551615
# Time parameters
@@ -62,8 +62,6 @@ SLOT_DURATION_MS: 10000
SECONDS_PER_ETH1_BLOCK: 2 # Override for e2e tests
# [customized] faster time for withdrawals
MIN_VALIDATOR_WITHDRAWABILITY_DELAY: 1
# [customized] increased for e2e testing (minimal default is 16)
MAX_VALIDATORS_PER_WITHDRAWALS_SWEEP: 128
# [customized] higher frequency of committee turnover and faster time to acceptable voluntary exit
SHARD_COMMITTEE_PERIOD: 4 # Override for e2e tests
# [customized] process deposits more quickly, but insecure
@@ -124,7 +122,7 @@ MIN_SYNC_COMMITTEE_PARTICIPANTS: 1
# Other e2e overrides
# ---------------------------------------------------------------
CONFIG_NAME: "end-to-end"
SLOTS_PER_EPOCH: 8
SLOTS_PER_EPOCH: 6
EPOCHS_PER_ETH1_VOTING_PERIOD: 2
MAX_SEED_LOOKAHEAD: 1
@@ -138,8 +136,3 @@ BLOB_SCHEDULE:
# Electra
- EPOCH: 14
MAX_BLOBS_PER_BLOCK: 9
# BPO (Blob Parameter Optimization) schedule for Fulu
- EPOCH: 17
MAX_BLOBS_PER_BLOCK: 15
- EPOCH: 18
MAX_BLOBS_PER_BLOCK: 21

View File

@@ -1,30 +1,72 @@
package params
import (
_ "embed"
"sync"
)
import "math"
//go:embed testdata/e2e_config.yaml
var e2eConfigYAML []byte
var (
e2eConfigOnce sync.Once
e2eConfig *BeaconChainConfig
const (
AltairE2EForkEpoch = 6
BellatrixE2EForkEpoch = 8
CapellaE2EForkEpoch = 10
DenebE2EForkEpoch = 12
ElectraE2EForkEpoch = 14
FuluE2EForkEpoch = math.MaxUint64
)
// E2ETestConfig retrieves the configurations made specifically for E2E testing.
// The config is loaded from testdata/e2e_config.yaml and cached.
//
// WARNING: This config is only for testing, it is not meant for use outside of E2E.
func E2ETestConfig() *BeaconChainConfig {
e2eConfigOnce.Do(func() {
cfg, err := UnmarshalConfig(e2eConfigYAML, nil)
if err != nil {
log.WithError(err).Fatal("Failed to load embedded e2e config")
}
e2eConfig = cfg
})
e2eConfig := MinimalSpecConfig()
e2eConfig.DepositContractAddress = "0x4242424242424242424242424242424242424242"
e2eConfig.Eth1FollowDistance = 8
// Misc.
e2eConfig.MinGenesisActiveValidatorCount = 256
e2eConfig.GenesisDelay = 10 // 10 seconds so E2E has enough time to process deposits and get started.
e2eConfig.ChurnLimitQuotient = 65536
e2eConfig.MaxValidatorsPerWithdrawalsSweep = 128
// Time parameters.
e2eConfig.SecondsPerSlot = 10
e2eConfig.SlotDurationMilliseconds = 10000
e2eConfig.SlotsPerEpoch = 6
e2eConfig.SqrRootSlotsPerEpoch = 2
e2eConfig.SecondsPerETH1Block = 2
e2eConfig.EpochsPerEth1VotingPeriod = 2
e2eConfig.ShardCommitteePeriod = 4
e2eConfig.MaxSeedLookahead = 1
e2eConfig.MinValidatorWithdrawabilityDelay = 1
// PoW parameters.
e2eConfig.DepositChainID = 1337 // Chain ID of eth1 dev net.
e2eConfig.DepositNetworkID = 1337 // Network ID of eth1 dev net.
// Fork Parameters.
e2eConfig.AltairForkEpoch = AltairE2EForkEpoch
e2eConfig.BellatrixForkEpoch = BellatrixE2EForkEpoch
e2eConfig.CapellaForkEpoch = CapellaE2EForkEpoch
e2eConfig.DenebForkEpoch = DenebE2EForkEpoch
e2eConfig.ElectraForkEpoch = ElectraE2EForkEpoch
e2eConfig.FuluForkEpoch = FuluE2EForkEpoch
// Terminal Total Difficulty.
e2eConfig.TerminalTotalDifficulty = "480"
// Prysm constants.
e2eConfig.ConfigName = EndToEndName
e2eConfig.GenesisForkVersion = []byte{0, 0, 0, 253}
e2eConfig.AltairForkVersion = []byte{1, 0, 0, 253}
e2eConfig.BellatrixForkVersion = []byte{2, 0, 0, 253}
e2eConfig.CapellaForkVersion = []byte{3, 0, 0, 253}
e2eConfig.DenebForkVersion = []byte{4, 0, 0, 253}
e2eConfig.ElectraForkVersion = []byte{5, 0, 0, 253}
e2eConfig.FuluForkVersion = []byte{6, 0, 0, 253}
e2eConfig.BlobSchedule = []BlobScheduleEntry{
{Epoch: e2eConfig.DenebForkEpoch, MaxBlobsPerBlock: uint64(e2eConfig.DeprecatedMaxBlobsPerBlock)},
{Epoch: e2eConfig.ElectraForkEpoch, MaxBlobsPerBlock: uint64(e2eConfig.DeprecatedMaxBlobsPerBlockElectra)},
}
e2eConfig.InitializeForkSchedule()
return e2eConfig
}
@@ -51,12 +93,12 @@ func E2EMainnetTestConfig() *BeaconChainConfig {
e2eConfig.DepositNetworkID = 1337 // Network ID of eth1 dev net.
// Altair Fork Parameters.
e2eConfig.AltairForkEpoch = E2ETestConfig().AltairForkEpoch
e2eConfig.BellatrixForkEpoch = E2ETestConfig().BellatrixForkEpoch
e2eConfig.CapellaForkEpoch = E2ETestConfig().CapellaForkEpoch
e2eConfig.DenebForkEpoch = E2ETestConfig().DenebForkEpoch
e2eConfig.ElectraForkEpoch = E2ETestConfig().ElectraForkEpoch
e2eConfig.FuluForkEpoch = E2ETestConfig().FuluForkEpoch
e2eConfig.AltairForkEpoch = AltairE2EForkEpoch
e2eConfig.BellatrixForkEpoch = BellatrixE2EForkEpoch
e2eConfig.CapellaForkEpoch = CapellaE2EForkEpoch
e2eConfig.DenebForkEpoch = DenebE2EForkEpoch
e2eConfig.ElectraForkEpoch = ElectraE2EForkEpoch
e2eConfig.FuluForkEpoch = FuluE2EForkEpoch
// Terminal Total Difficulty.
e2eConfig.TerminalTotalDifficulty = "480"
@@ -77,9 +119,6 @@ func E2EMainnetTestConfig() *BeaconChainConfig {
e2eConfig.BlobSchedule = []BlobScheduleEntry{
{Epoch: e2eConfig.DenebForkEpoch, MaxBlobsPerBlock: uint64(e2eConfig.DeprecatedMaxBlobsPerBlock)},
{Epoch: e2eConfig.ElectraForkEpoch, MaxBlobsPerBlock: uint64(e2eConfig.DeprecatedMaxBlobsPerBlockElectra)},
// BPO (Blob Parameter Optimization) schedule for Fulu
{Epoch: e2eConfig.FuluForkEpoch + 1, MaxBlobsPerBlock: 15},
{Epoch: e2eConfig.FuluForkEpoch + 2, MaxBlobsPerBlock: 21},
}
e2eConfig.InitializeForkSchedule()

View File

@@ -4,6 +4,7 @@ go_library(
name = "go_default_library",
srcs = [
"execution.go",
"execution_payload_envelope.go",
"factory.go",
"get_payload.go",
"getters.go",
@@ -14,11 +15,13 @@ go_library(
"roblock.go",
"rodatacolumn.go",
"setters.go",
"signed_execution_bid.go",
"types.go",
],
importpath = "github.com/OffchainLabs/prysm/v7/consensus-types/blocks",
visibility = ["//visibility:public"],
deps = [
"//beacon-chain/core/signing:go_default_library",
"//beacon-chain/state/stateutil:go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
@@ -34,8 +37,10 @@ go_library(
"//proto/prysm/v1alpha1:go_default_library",
"//proto/prysm/v1alpha1/validator-client:go_default_library",
"//runtime/version:go_default_library",
"@com_github_ethereum_go_ethereum//common:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_prysmaticlabs_fastssz//:go_default_library",
"@com_github_prysmaticlabs_gohashtree//:go_default_library",
"@org_golang_google_protobuf//proto:go_default_library",
],
)
@@ -43,6 +48,7 @@ go_library(
go_test(
name = "go_default_test",
srcs = [
"execution_payload_envelope_test.go",
"execution_test.go",
"factory_test.go",
"getters_test.go",
@@ -56,13 +62,13 @@ go_test(
],
embed = [":go_default_library"],
deps = [
"//beacon-chain/core/signing:go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//consensus-types:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//consensus-types/primitives:go_default_library",
"//container/trie:go_default_library",
"//crypto/hash/htr:go_default_library",
"//encoding/bytesutil:go_default_library",
"//proto/engine/v1:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
@@ -72,5 +78,6 @@ go_test(
"//testing/require:go_default_library",
"@com_github_prysmaticlabs_fastssz//:go_default_library",
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
"@com_github_prysmaticlabs_gohashtree//:go_default_library",
],
)

View File

@@ -0,0 +1,156 @@
package blocks
import (
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/signing"
field_params "github.com/OffchainLabs/prysm/v7/config/fieldparams"
consensus_types "github.com/OffchainLabs/prysm/v7/consensus-types"
"github.com/OffchainLabs/prysm/v7/consensus-types/interfaces"
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
"github.com/OffchainLabs/prysm/v7/encoding/ssz"
enginev1 "github.com/OffchainLabs/prysm/v7/proto/engine/v1"
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
"github.com/ethereum/go-ethereum/common"
"google.golang.org/protobuf/proto"
)
type signedExecutionPayloadEnvelope struct {
s *ethpb.SignedExecutionPayloadEnvelope
}
type executionPayloadEnvelope struct {
p *ethpb.ExecutionPayloadEnvelope
}
// WrappedROSignedExecutionPayloadEnvelope wraps a signed execution payload envelope proto in a read-only interface.
func WrappedROSignedExecutionPayloadEnvelope(s *ethpb.SignedExecutionPayloadEnvelope) (interfaces.ROSignedExecutionPayloadEnvelope, error) {
w := signedExecutionPayloadEnvelope{s: s}
if w.IsNil() {
return nil, consensus_types.ErrNilObjectWrapped
}
return w, nil
}
// WrappedROExecutionPayloadEnvelope wraps an execution payload envelope proto in a read-only interface.
func WrappedROExecutionPayloadEnvelope(p *ethpb.ExecutionPayloadEnvelope) (interfaces.ROExecutionPayloadEnvelope, error) {
w := &executionPayloadEnvelope{p: p}
if w.IsNil() {
return nil, consensus_types.ErrNilObjectWrapped
}
return w, nil
}
// Envelope returns the execution payload envelope as a read-only interface.
func (s signedExecutionPayloadEnvelope) Envelope() (interfaces.ROExecutionPayloadEnvelope, error) {
return WrappedROExecutionPayloadEnvelope(s.s.Message)
}
// Signature returns the BLS signature as a 96-byte array.
func (s signedExecutionPayloadEnvelope) Signature() [field_params.BLSSignatureLength]byte {
return [field_params.BLSSignatureLength]byte(s.s.Signature)
}
// IsNil reports whether the signed envelope or its contents are invalid.
func (s signedExecutionPayloadEnvelope) IsNil() bool {
if s.s == nil {
return true
}
if len(s.s.Signature) != field_params.BLSSignatureLength {
return true
}
w := executionPayloadEnvelope{p: s.s.Message}
return w.IsNil()
}
// SigningRoot computes the signing root for the signed envelope with the provided domain.
func (s signedExecutionPayloadEnvelope) SigningRoot(domain []byte) (root [32]byte, err error) {
return signing.ComputeSigningRoot(s.s.Message, domain)
}
// Proto returns the underlying protobuf message.
func (s signedExecutionPayloadEnvelope) Proto() proto.Message {
return s.s
}
// IsNil reports whether the envelope or its required fields are invalid.
func (p *executionPayloadEnvelope) IsNil() bool {
if p.p == nil {
return true
}
if p.p.Payload == nil {
return true
}
if len(p.p.BeaconBlockRoot) != field_params.RootLength {
return true
}
if p.p.BlobKzgCommitments == nil {
return true
}
return false
}
// IsBlinded reports whether the envelope contains a blinded payload.
func (p *executionPayloadEnvelope) IsBlinded() bool {
return !p.IsNil() && p.p.Payload == nil
}
// Execution returns the execution payload as a read-only interface.
func (p *executionPayloadEnvelope) Execution() (interfaces.ExecutionData, error) {
if p.IsBlinded() {
return nil, consensus_types.ErrNilObjectWrapped
}
return WrappedExecutionPayloadDeneb(p.p.Payload)
}
// ExecutionRequests returns the execution requests attached to the envelope.
func (p *executionPayloadEnvelope) ExecutionRequests() *enginev1.ExecutionRequests {
return p.p.ExecutionRequests
}
// BuilderIndex returns the proposer/builder index for the envelope.
func (p *executionPayloadEnvelope) BuilderIndex() primitives.BuilderIndex {
return p.p.BuilderIndex
}
// BeaconBlockRoot returns the beacon block root referenced by the envelope.
func (p *executionPayloadEnvelope) BeaconBlockRoot() [field_params.RootLength]byte {
return [field_params.RootLength]byte(p.p.BeaconBlockRoot)
}
// BlobKzgCommitments returns a copy of the envelope's KZG commitments.
func (p *executionPayloadEnvelope) BlobKzgCommitments() [][]byte {
commitments := make([][]byte, len(p.p.BlobKzgCommitments))
for i, commit := range p.p.BlobKzgCommitments {
commitments[i] = make([]byte, len(commit))
copy(commitments[i], commit)
}
return commitments
}
// VersionedHashes returns versioned hashes derived from the KZG commitments.
func (p *executionPayloadEnvelope) VersionedHashes() []common.Hash {
commitments := p.p.BlobKzgCommitments
versionedHashes := make([]common.Hash, len(commitments))
for i, commitment := range commitments {
versionedHashes[i] = primitives.ConvertKzgCommitmentToVersionedHash(commitment)
}
return versionedHashes
}
// BlobKzgCommitmentsRoot returns the SSZ root of the envelope's KZG commitments.
func (p *executionPayloadEnvelope) BlobKzgCommitmentsRoot() ([field_params.RootLength]byte, error) {
if p.IsNil() || p.p.BlobKzgCommitments == nil {
return [field_params.RootLength]byte{}, consensus_types.ErrNilObjectWrapped
}
return ssz.KzgCommitmentsRoot(p.p.BlobKzgCommitments)
}
// Slot returns the slot of the envelope.
func (p *executionPayloadEnvelope) Slot() primitives.Slot {
return p.p.Slot
}
// StateRoot returns the state root carried by the envelope.
func (p *executionPayloadEnvelope) StateRoot() [field_params.RootLength]byte {
return [field_params.RootLength]byte(p.p.StateRoot)
}

View File

@@ -0,0 +1,115 @@
package blocks_test
import (
"bytes"
"testing"
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/signing"
consensus_types "github.com/OffchainLabs/prysm/v7/consensus-types"
"github.com/OffchainLabs/prysm/v7/consensus-types/blocks"
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
enginev1 "github.com/OffchainLabs/prysm/v7/proto/engine/v1"
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
"github.com/OffchainLabs/prysm/v7/testing/assert"
"github.com/OffchainLabs/prysm/v7/testing/require"
)
func validExecutionPayloadEnvelope() *ethpb.ExecutionPayloadEnvelope {
payload := &enginev1.ExecutionPayloadDeneb{
ParentHash: bytes.Repeat([]byte{0x01}, 32),
FeeRecipient: bytes.Repeat([]byte{0x02}, 20),
StateRoot: bytes.Repeat([]byte{0x03}, 32),
ReceiptsRoot: bytes.Repeat([]byte{0x04}, 32),
LogsBloom: bytes.Repeat([]byte{0x05}, 256),
PrevRandao: bytes.Repeat([]byte{0x06}, 32),
BlockNumber: 1,
GasLimit: 2,
GasUsed: 3,
Timestamp: 4,
BaseFeePerGas: bytes.Repeat([]byte{0x07}, 32),
BlockHash: bytes.Repeat([]byte{0x08}, 32),
Transactions: [][]byte{},
Withdrawals: []*enginev1.Withdrawal{},
BlobGasUsed: 0,
ExcessBlobGas: 0,
}
return &ethpb.ExecutionPayloadEnvelope{
Payload: payload,
ExecutionRequests: &enginev1.ExecutionRequests{},
BuilderIndex: 10,
BeaconBlockRoot: bytes.Repeat([]byte{0xAA}, 32),
Slot: 9,
BlobKzgCommitments: [][]byte{bytes.Repeat([]byte{0x0C}, 48)},
StateRoot: bytes.Repeat([]byte{0xBB}, 32),
}
}
func TestWrappedROExecutionPayloadEnvelope(t *testing.T) {
t.Run("returns error on invalid beacon root length", func(t *testing.T) {
invalid := validExecutionPayloadEnvelope()
invalid.BeaconBlockRoot = []byte{0x01}
_, err := blocks.WrappedROExecutionPayloadEnvelope(invalid)
require.Equal(t, consensus_types.ErrNilObjectWrapped, err)
})
t.Run("wraps and exposes fields", func(t *testing.T) {
env := validExecutionPayloadEnvelope()
wrapped, err := blocks.WrappedROExecutionPayloadEnvelope(env)
require.NoError(t, err)
require.Equal(t, primitives.BuilderIndex(10), wrapped.BuilderIndex())
require.Equal(t, primitives.Slot(9), wrapped.Slot())
assert.DeepEqual(t, [32]byte(bytes.Repeat([]byte{0xAA}, 32)), wrapped.BeaconBlockRoot())
assert.DeepEqual(t, [32]byte(bytes.Repeat([]byte{0xBB}, 32)), wrapped.StateRoot())
commitments := wrapped.BlobKzgCommitments()
assert.DeepEqual(t, env.BlobKzgCommitments, commitments)
versioned := wrapped.VersionedHashes()
require.Equal(t, 1, len(versioned))
reqs := wrapped.ExecutionRequests()
require.NotNil(t, reqs)
exec, err := wrapped.Execution()
require.NoError(t, err)
assert.DeepEqual(t, env.Payload.ParentHash, exec.ParentHash())
})
}
func TestWrappedROSignedExecutionPayloadEnvelope(t *testing.T) {
t.Run("returns error for invalid signature length", func(t *testing.T) {
signed := &ethpb.SignedExecutionPayloadEnvelope{
Message: validExecutionPayloadEnvelope(),
Signature: bytes.Repeat([]byte{0xAA}, 95),
}
_, err := blocks.WrappedROSignedExecutionPayloadEnvelope(signed)
require.Equal(t, consensus_types.ErrNilObjectWrapped, err)
})
t.Run("wraps and provides envelope/signing data", func(t *testing.T) {
sig := bytes.Repeat([]byte{0xAB}, 96)
signed := &ethpb.SignedExecutionPayloadEnvelope{
Message: validExecutionPayloadEnvelope(),
Signature: sig,
}
wrapped, err := blocks.WrappedROSignedExecutionPayloadEnvelope(signed)
require.NoError(t, err)
gotSig := wrapped.Signature()
assert.DeepEqual(t, [96]byte(sig), gotSig)
env, err := wrapped.Envelope()
require.NoError(t, err)
assert.DeepEqual(t, [32]byte(bytes.Repeat([]byte{0xAA}, 32)), env.BeaconBlockRoot())
domain := bytes.Repeat([]byte{0xCC}, 32)
wantRoot, err := signing.ComputeSigningRoot(signed.Message, domain)
require.NoError(t, err)
gotRoot, err := wrapped.SigningRoot(domain)
require.NoError(t, err)
require.Equal(t, wantRoot, gotRoot)
})
}

View File

@@ -5,10 +5,10 @@ import (
"github.com/OffchainLabs/prysm/v7/config/params"
"github.com/OffchainLabs/prysm/v7/consensus-types/interfaces"
"github.com/OffchainLabs/prysm/v7/container/trie"
"github.com/OffchainLabs/prysm/v7/crypto/hash/htr"
"github.com/OffchainLabs/prysm/v7/encoding/ssz"
"github.com/OffchainLabs/prysm/v7/runtime/version"
"github.com/pkg/errors"
"github.com/prysmaticlabs/gohashtree"
)
const (
@@ -45,7 +45,7 @@ func VerifyKZGInclusionProof(blob ROBlob) error {
return errInvalidBodyRoot
}
chunks := makeChunk(blob.KzgCommitment)
htr.HashChunks(chunks, chunks)
gohashtree.HashChunks(chunks, chunks)
verified := trie.VerifyMerkleProof(root, chunks[0][:], blob.Index+KZGOffset, blob.CommitmentInclusionProof)
if !verified {
return errInvalidInclusionProof
@@ -182,7 +182,7 @@ func LeavesFromCommitments(commitments [][]byte) [][]byte {
leaves := make([][]byte, len(commitments))
for i, kzg := range commitments {
chunk := makeChunk(kzg)
htr.HashChunks(chunk, chunk)
gohashtree.HashChunks(chunk, chunk)
leaves[i] = chunk[0][:]
}
return leaves

View File

@@ -8,10 +8,10 @@ import (
fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams"
"github.com/OffchainLabs/prysm/v7/consensus-types/interfaces"
"github.com/OffchainLabs/prysm/v7/container/trie"
"github.com/OffchainLabs/prysm/v7/crypto/hash/htr"
enginev1 "github.com/OffchainLabs/prysm/v7/proto/engine/v1"
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
"github.com/OffchainLabs/prysm/v7/testing/require"
"github.com/prysmaticlabs/gohashtree"
)
func Test_MerkleProofKZGCommitment_Altair(t *testing.T) {
@@ -108,7 +108,7 @@ func Test_MerkleProofKZGCommitment(t *testing.T) {
require.Equal(t, true, trie.VerifyMerkleProof(root[:], commitmentsRoot[:], kzgPosition, topProof[:len(topProof)-1]))
chunk := makeChunk(kzgs[index])
htr.HashChunks(chunk, chunk)
gohashtree.HashChunks(chunk, chunk)
require.Equal(t, true, trie.VerifyMerkleProof(root[:], chunk[0][:], uint64(index+KZGOffset), proof))
}

View File

@@ -0,0 +1,140 @@
package blocks
import (
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/signing"
consensus_types "github.com/OffchainLabs/prysm/v7/consensus-types"
"github.com/OffchainLabs/prysm/v7/consensus-types/interfaces"
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
)
// signedExecutionPayloadBid wraps the protobuf signed execution payload bid
// and implements the ROSignedExecutionPayloadBid interface.
type signedExecutionPayloadBid struct {
bid *ethpb.SignedExecutionPayloadBid
}
// executionPayloadBidGloas wraps the protobuf execution payload bid for Gloas fork
// and implements the ROExecutionPayloadBidGloas interface.
type executionPayloadBidGloas struct {
payload *ethpb.ExecutionPayloadBid
}
// IsNil checks if the signed execution payload bid is nil or invalid.
func (s signedExecutionPayloadBid) IsNil() bool {
if s.bid == nil {
return true
}
if _, err := WrappedROExecutionPayloadBid(s.bid.Message); err != nil {
return true
}
return len(s.bid.Signature) != 96
}
// IsNil checks if the execution payload bid is nil or has invalid fields.
func (h executionPayloadBidGloas) IsNil() bool {
if h.payload == nil {
return true
}
if len(h.payload.ParentBlockHash) != 32 ||
len(h.payload.ParentBlockRoot) != 32 ||
len(h.payload.BlockHash) != 32 ||
len(h.payload.BlobKzgCommitmentsRoot) != 32 {
return true
}
return false
}
// WrappedROSignedExecutionPayloadBid creates a new read-only signed execution payload bid
// wrapper from the given protobuf message.
func WrappedROSignedExecutionPayloadBid(pb *ethpb.SignedExecutionPayloadBid) (interfaces.ROSignedExecutionPayloadBid, error) {
wrapper := signedExecutionPayloadBid{bid: pb}
if wrapper.IsNil() {
return nil, consensus_types.ErrNilObjectWrapped
}
return wrapper, nil
}
// WrappedROExecutionPayloadBid creates a new read-only execution payload bid
// wrapper for the Gloas fork from the given protobuf message.
func WrappedROExecutionPayloadBid(pb *ethpb.ExecutionPayloadBid) (interfaces.ROExecutionPayloadBid, error) {
wrapper := executionPayloadBidGloas{payload: pb}
if wrapper.IsNil() {
return nil, consensus_types.ErrNilObjectWrapped
}
return wrapper, nil
}
// Bid returns the execution payload bid as a read-only interface.
func (s signedExecutionPayloadBid) Bid() (interfaces.ROExecutionPayloadBid, error) {
return WrappedROExecutionPayloadBid(s.bid.Message)
}
// SigningRoot computes the signing root for the execution payload bid with the given domain.
func (s signedExecutionPayloadBid) SigningRoot(domain []byte) ([32]byte, error) {
return signing.ComputeSigningRoot(s.bid.Message, domain)
}
// Signature returns the BLS signature as a 96-byte array.
func (s signedExecutionPayloadBid) Signature() [96]byte {
return [96]byte(s.bid.Signature)
}
// ParentBlockHash returns the hash of the parent execution block.
func (h executionPayloadBidGloas) ParentBlockHash() [32]byte {
return [32]byte(h.payload.ParentBlockHash)
}
// ParentBlockRoot returns the beacon block root of the parent block.
func (h executionPayloadBidGloas) ParentBlockRoot() [32]byte {
return [32]byte(h.payload.ParentBlockRoot)
}
// PrevRandao returns the previous randao value for the execution block.
func (h executionPayloadBidGloas) PrevRandao() [32]byte {
return [32]byte(h.payload.PrevRandao)
}
// BlockHash returns the hash of the execution block.
func (h executionPayloadBidGloas) BlockHash() [32]byte {
return [32]byte(h.payload.BlockHash)
}
// GasLimit returns the gas limit for the execution block.
func (h executionPayloadBidGloas) GasLimit() uint64 {
return h.payload.GasLimit
}
// BuilderIndex returns the builder index of the builder who created this bid.
func (h executionPayloadBidGloas) BuilderIndex() primitives.BuilderIndex {
return h.payload.BuilderIndex
}
// Slot returns the beacon chain slot for which this bid was created.
func (h executionPayloadBidGloas) Slot() primitives.Slot {
return h.payload.Slot
}
// Value returns the payment value offered by the builder in Gwei.
func (h executionPayloadBidGloas) Value() primitives.Gwei {
return primitives.Gwei(h.payload.Value)
}
// ExecutionPayment returns the execution payment offered by the builder.
func (h executionPayloadBidGloas) ExecutionPayment() primitives.Gwei {
return primitives.Gwei(h.payload.ExecutionPayment)
}
// BlobKzgCommitmentsRoot returns the root of the KZG commitments for blobs.
func (h executionPayloadBidGloas) BlobKzgCommitmentsRoot() [32]byte {
return [32]byte(h.payload.BlobKzgCommitmentsRoot)
}
// FeeRecipient returns the execution address that will receive the builder payment.
func (h executionPayloadBidGloas) FeeRecipient() [20]byte {
return [20]byte(h.payload.FeeRecipient)
}

View File

@@ -5,7 +5,9 @@ go_library(
srcs = [
"beacon_block.go",
"error.go",
"execution_payload_envelope.go",
"light_client.go",
"signed_execution_payload_bid.go",
"utils.go",
"validator.go",
],
@@ -18,6 +20,7 @@ go_library(
"//proto/prysm/v1alpha1:go_default_library",
"//proto/prysm/v1alpha1/validator-client:go_default_library",
"//runtime/version:go_default_library",
"@com_github_ethereum_go_ethereum//common:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_prysmaticlabs_fastssz//:go_default_library",
"@org_golang_google_protobuf//proto:go_default_library",

View File

@@ -0,0 +1,31 @@
package interfaces
import (
field_params "github.com/OffchainLabs/prysm/v7/config/fieldparams"
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
enginev1 "github.com/OffchainLabs/prysm/v7/proto/engine/v1"
"github.com/ethereum/go-ethereum/common"
"google.golang.org/protobuf/proto"
)
type ROSignedExecutionPayloadEnvelope interface {
Envelope() (ROExecutionPayloadEnvelope, error)
Signature() [field_params.BLSSignatureLength]byte
SigningRoot([]byte) ([32]byte, error)
IsNil() bool
Proto() proto.Message
}
type ROExecutionPayloadEnvelope interface {
Execution() (ExecutionData, error)
ExecutionRequests() *enginev1.ExecutionRequests
BuilderIndex() primitives.BuilderIndex
BeaconBlockRoot() [field_params.RootLength]byte
BlobKzgCommitments() [][]byte
BlobKzgCommitmentsRoot() ([field_params.RootLength]byte, error)
VersionedHashes() []common.Hash
Slot() primitives.Slot
StateRoot() [field_params.RootLength]byte
IsBlinded() bool
IsNil() bool
}

View File

@@ -0,0 +1,28 @@
package interfaces
import (
field_params "github.com/OffchainLabs/prysm/v7/config/fieldparams"
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
)
type ROSignedExecutionPayloadBid interface {
Bid() (ROExecutionPayloadBid, error)
Signature() [field_params.BLSSignatureLength]byte
SigningRoot([]byte) ([32]byte, error)
IsNil() bool
}
type ROExecutionPayloadBid interface {
ParentBlockHash() [32]byte
ParentBlockRoot() [32]byte
PrevRandao() [32]byte
BlockHash() [32]byte
GasLimit() uint64
BuilderIndex() primitives.BuilderIndex
Slot() primitives.Slot
Value() primitives.Gwei
ExecutionPayment() primitives.Gwei
BlobKzgCommitmentsRoot() [32]byte
FeeRecipient() [20]byte
IsNil() bool
}

View File

@@ -5,12 +5,7 @@ go_library(
srcs = ["hashtree.go"],
importpath = "github.com/OffchainLabs/prysm/v7/crypto/hash/htr",
visibility = ["//visibility:public"],
deps = [
"//config/features:go_default_library",
"@com_github_offchainlabs_hashtree//:go_default_library",
"@com_github_prysmaticlabs_gohashtree//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
],
deps = ["@com_github_prysmaticlabs_gohashtree//:go_default_library"],
)
go_test(
@@ -18,8 +13,5 @@ go_test(
size = "small",
srcs = ["hashtree_test.go"],
embed = [":go_default_library"],
deps = [
"//config/features:go_default_library",
"//testing/require:go_default_library",
],
deps = ["//testing/require:go_default_library"],
)

View File

@@ -4,38 +4,14 @@ import (
"runtime"
"sync"
"github.com/OffchainLabs/hashtree"
"github.com/OffchainLabs/prysm/v7/config/features"
"github.com/prysmaticlabs/gohashtree"
log "github.com/sirupsen/logrus"
)
const minSliceSizeToParallelize = 5000
// Hash hashes chunks pairwise into digests using the configured hashing library.
// It performs input validation (odd chunks, digest length).
func Hash(digests, chunks [][32]byte) error {
if features.Get().EnableHashtree {
return hashtree.Hash(digests, chunks)
}
return gohashtree.Hash(digests, chunks)
}
// HashChunks hashes chunks pairwise into digests without error checking.
// The caller must ensure inputs are valid (even chunks, sufficient digest space).
func HashChunks(digests, chunks [][32]byte) {
if features.Get().EnableHashtree {
if err := hashtree.Hash(digests, chunks); err != nil {
log.WithError(err).Error("Could not hash chunks")
}
} else {
gohashtree.HashChunks(digests, chunks)
}
}
func hashParallel(inputList [][32]byte, outputList [][32]byte, wg *sync.WaitGroup) {
defer wg.Done()
err := Hash(outputList, inputList)
err := gohashtree.Hash(outputList, inputList)
if err != nil {
panic(err) // lint:nopanic -- This should never panic.
}
@@ -49,7 +25,7 @@ func hashParallel(inputList [][32]byte, outputList [][32]byte, wg *sync.WaitGrou
func VectorizedSha256(inputList [][32]byte) [][32]byte {
outputList := make([][32]byte, len(inputList)/2)
if len(inputList) < minSliceSizeToParallelize {
err := Hash(outputList, inputList)
err := gohashtree.Hash(outputList, inputList)
if err != nil {
panic(err) // lint:nopanic -- This should never panic.
}
@@ -62,7 +38,7 @@ func VectorizedSha256(inputList [][32]byte) [][32]byte {
for j := range n {
go hashParallel(inputList[j*2*groupSize:(j+1)*2*groupSize], outputList[j*groupSize:], &wg)
}
err := Hash(outputList[n*groupSize:], inputList[n*2*groupSize:])
err := gohashtree.Hash(outputList[n*groupSize:], inputList[n*2*groupSize:])
if err != nil {
panic(err) // lint:nopanic -- This should never panic.
}

View File

@@ -4,7 +4,6 @@ import (
"sync"
"testing"
"github.com/OffchainLabs/prysm/v7/config/features"
"github.com/OffchainLabs/prysm/v7/testing/require"
)
@@ -24,25 +23,3 @@ func Test_VectorizedSha256(t *testing.T) {
require.Equal(t, r, hash2[i])
}
}
func Test_VectorizedSha256_hashtree_enabled(t *testing.T) {
resetCfg := features.InitWithReset(&features.Flags{
EnableHashtree: true,
})
defer resetCfg()
largeSlice := make([][32]byte, 32*minSliceSizeToParallelize)
secondLargeSlice := make([][32]byte, 32*minSliceSizeToParallelize)
hash1 := make([][32]byte, 16*minSliceSizeToParallelize)
wg := sync.WaitGroup{}
wg.Go(func() {
tempHash := VectorizedSha256(largeSlice)
copy(hash1, tempHash)
})
wg.Wait()
hash2 := VectorizedSha256(secondLargeSlice)
require.Equal(t, len(hash1), len(hash2))
for i, r := range hash1 {
require.Equal(t, r, hash2[i])
}
}

217
deps.bzl
View File

@@ -73,8 +73,8 @@ def prysm_deps():
go_repository(
name = "com_github_allegro_bigcache",
importpath = "github.com/allegro/bigcache",
sum = "h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8=",
version = "v1.2.1-0.20190218064605-e24eb225f156",
sum = "h1:hg1sY1raCwic3Vnsvje6TT7/pnZba83LeFck5NrFKSc=",
version = "v1.2.1",
)
go_repository(
name = "com_github_andreasbriese_bbloom",
@@ -301,8 +301,8 @@ def prysm_deps():
go_repository(
name = "com_github_bits_and_blooms_bitset",
importpath = "github.com/bits-and-blooms/bitset",
sum = "h1:Tquv9S8+SGaS3EhyA+up3FXzmkhxPGjQQCkcs2uw7w4=",
version = "v1.22.0",
sum = "h1:1X2TS7aHz1ELcC0yU1y2stUs/0ig5oMU6STFZGrhvHI=",
version = "v1.17.0",
)
go_repository(
name = "com_github_bradfitz_go_smtpd",
@@ -482,8 +482,8 @@ def prysm_deps():
go_repository(
name = "com_github_cockroachdb_pebble",
importpath = "github.com/cockroachdb/pebble",
sum = "h1:5AAWCBWbat0uE0blr8qzufZP5tBjkRyy/jWe1QWLnvw=",
version = "v1.1.5",
sum = "h1:CUh2IPtR4swHlEj48Rhfzw6l/d0qA31fItcIszQVIsA=",
version = "v1.1.2",
)
go_repository(
name = "com_github_cockroachdb_redact",
@@ -512,16 +512,14 @@ def prysm_deps():
go_repository(
name = "com_github_consensys_bavard",
importpath = "github.com/consensys/bavard",
sum = "h1:dTlIwEdFQmldzFf5F6bbTcYWhvnAgZai2g8eq3Wwxqg=",
version = "v0.1.31-0.20250406004941-2db259e4b582",
sum = "h1:Uw2CGvbXSZWhqK59X0VG/zOjpTFuOMcPLStrp1ihI0A=",
version = "v0.1.22",
)
go_repository(
name = "com_github_consensys_gnark_crypto",
importpath = "github.com/consensys/gnark-crypto",
patch_args = ["-p1"],
patches = ["//third_party:com_github_consensys_gnark_crypto.patch"],
sum = "h1:vIye/FqI50VeAr0B3dx+YjeIvmc3LWz4yEfbWBpTUf0=",
version = "v0.18.0",
sum = "h1:DDBdl4HaBtdQsq/wfMwJvZNE80sHidrK3Nfrefatm0E=",
version = "v0.14.0",
)
go_repository(
name = "com_github_containerd_cgroups",
@@ -557,14 +555,8 @@ def prysm_deps():
go_repository(
name = "com_github_cpuguy83_go_md2man_v2",
importpath = "github.com/cpuguy83/go-md2man/v2",
sum = "h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo=",
version = "v2.0.7",
)
go_repository(
name = "com_github_crate_crypto_go_eth_kzg",
importpath = "github.com/crate-crypto/go-eth-kzg",
sum = "h1:WzDGjHk4gFg6YzV0rJOAsTK4z3Qkz5jd4RE3DAvPFkg=",
version = "v1.4.0",
sum = "h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=",
version = "v2.0.5",
)
go_repository(
name = "com_github_crate_crypto_go_ipa",
@@ -599,8 +591,8 @@ def prysm_deps():
go_repository(
name = "com_github_datadog_zstd",
importpath = "github.com/DataDog/zstd",
sum = "h1:ybO8RBeh29qrxIhCA9E8gKY6xfONU9T6G6aP9DTKfLE=",
version = "v1.5.7",
sum = "h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ=",
version = "v1.5.5",
)
go_repository(
name = "com_github_davecgh_go_spew",
@@ -614,29 +606,23 @@ def prysm_deps():
sum = "h1:pFUpOrbxDR6AkioZ1ySsx5yxlDQZ8stG2b88gTPxgJU=",
version = "v0.0.0-20200604182044-b73af7476f6c",
)
go_repository(
name = "com_github_dchest_siphash",
importpath = "github.com/dchest/siphash",
sum = "h1:QXwFc8cFOR2dSa/gE6o/HokBMWtLUaNDVd+22aKHeEA=",
version = "v1.2.3",
)
go_repository(
name = "com_github_deckarep_golang_set_v2",
importpath = "github.com/deckarep/golang-set/v2",
sum = "h1:swm0rlPCmdWn9mESxKOjWk8hXSqoxOp+ZlfuyaAdFlQ=",
version = "v2.8.0",
sum = "h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM=",
version = "v2.6.0",
)
go_repository(
name = "com_github_decred_dcrd_crypto_blake256",
importpath = "github.com/decred/dcrd/crypto/blake256",
sum = "h1:zPMNGQCm0g4QTY27fOCorQW7EryeQ/U0x++OzVrdms8=",
version = "v1.1.0",
sum = "h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y=",
version = "v1.0.1",
)
go_repository(
name = "com_github_decred_dcrd_dcrec_secp256k1_v4",
importpath = "github.com/decred/dcrd/dcrec/secp256k1/v4",
sum = "h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc=",
version = "v4.4.0",
sum = "h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg=",
version = "v4.3.0",
)
go_repository(
name = "com_github_deepmap_oapi_codegen",
@@ -749,8 +735,8 @@ def prysm_deps():
go_repository(
name = "com_github_emicklei_dot",
importpath = "github.com/emicklei/dot",
sum = "h1:08GN+DD79cy/tzN6uLCT84+2Wk9u+wvqP+Hkx/dIR8A=",
version = "v1.6.2",
sum = "h1:Ase39UD9T9fRBOb5ptgpixrxfx8abVzNWZi2+lr53PI=",
version = "v0.11.0",
)
go_repository(
name = "com_github_emicklei_go_restful_v3",
@@ -793,12 +779,6 @@ def prysm_deps():
sum = "h1:aVtoLK5xwJ6c5RiqO8g8ptJ5KU+2Hdquf6G3aXiHh5s=",
version = "v2.1.5",
)
go_repository(
name = "com_github_ethereum_go_bigmodexpfix",
importpath = "github.com/ethereum/go-bigmodexpfix",
sum = "h1:rvv6MJhy07IMfEKuARQ9TKojGqLVNxQajaXEp/BoqSk=",
version = "v0.0.0-20250911101455-f9e208c548ab",
)
go_repository(
name = "com_github_ethereum_go_ethereum",
build_directives = [
@@ -810,8 +790,8 @@ def prysm_deps():
patches = [
"//third_party:com_github_ethereum_go_ethereum_secp256k1.patch",
],
sum = "h1:LLLfkZWijhR5m6yrAXbdlTeXoqontH+Ga2f9igY7law=",
version = "v1.16.8",
sum = "h1:bRra1zi+/q+qyXZ6fylZOrlaF8kDdnlTtzNTmNHfX+g=",
version = "v1.15.9",
)
go_repository(
name = "com_github_ethereum_go_verkle",
@@ -852,8 +832,8 @@ def prysm_deps():
go_repository(
name = "com_github_ferranbt_fastssz",
importpath = "github.com/ferranbt/fastssz",
sum = "h1:OCDB+dYDEQDvAgtAGnTSidK1Pe2tW3nFV40XyMkTeDY=",
version = "v0.1.4",
sum = "h1:ZI+z3JH05h4kgmFXdHuR1aWYsgrg7o+Fw7/NCzM16Mo=",
version = "v0.1.3",
)
go_repository(
name = "com_github_fjl_gencodec",
@@ -939,6 +919,24 @@ def prysm_deps():
sum = "h1:f6D9Hr8xV8uYKlyuj8XIruxlh9WjVjdh1gIicAS7ays=",
version = "v0.0.0-20191108122812-4678299bea08",
)
go_repository(
name = "com_github_gballet_go_verkle",
importpath = "github.com/gballet/go-verkle",
sum = "h1:BAIP2GihuqhwdILrV+7GJel5lyPV3u1+PgzrWLc0TkE=",
version = "v0.1.1-0.20231031103413-a67434b50f46",
)
go_repository(
name = "com_github_gdamore_encoding",
importpath = "github.com/gdamore/encoding",
sum = "h1:YzKZckdBL6jVt2Gc+5p82qhrGiqMdG/eNs6Wy0u3Uhw=",
version = "v1.0.1",
)
go_repository(
name = "com_github_gdamore_tcell_v2",
importpath = "github.com/gdamore/tcell/v2",
sum = "h1:sg6/UnTM9jGpZU+oFYAsDahfchWAFW8Xx2yFinNSAYU=",
version = "v2.7.4",
)
go_repository(
name = "com_github_getkin_kin_openapi",
importpath = "github.com/getkin/kin-openapi",
@@ -1134,8 +1132,8 @@ def prysm_deps():
go_repository(
name = "com_github_gofrs_flock",
importpath = "github.com/gofrs/flock",
sum = "h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=",
version = "v0.12.1",
sum = "h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=",
version = "v0.8.1",
)
go_repository(
name = "com_github_gogo_googleapis",
@@ -1202,8 +1200,8 @@ def prysm_deps():
go_repository(
name = "com_github_golang_snappy",
importpath = "github.com/golang/snappy",
sum = "h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=",
version = "v1.0.0",
sum = "h1:4bw4WeyTYPp0smaXiJZCNnLrvVBqirQVreixayXezGc=",
version = "v0.0.5-0.20231225225746-43d5d4cd4e0e",
)
go_repository(
name = "com_github_golangci_lint_1",
@@ -1277,6 +1275,12 @@ def prysm_deps():
sum = "h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA=",
version = "v0.1.0",
)
go_repository(
name = "com_github_google_subcommands",
importpath = "github.com/google/subcommands",
sum = "h1:vWQspBTo2nEqTUFita5/KeEWlUL8kQObDFbub/EN9oE=",
version = "v1.2.0",
)
go_repository(
name = "com_github_google_uuid",
importpath = "github.com/google/uuid",
@@ -1538,8 +1542,8 @@ def prysm_deps():
go_repository(
name = "com_github_holiman_billy",
importpath = "github.com/holiman/billy",
sum = "h1:IZUYC/xb3giYwBLMnr8d0TGTzPKFGNTCGgGLoyeX330=",
version = "v0.0.0-20250707135307-f2f9b9aae7db",
sum = "h1:X4egAf/gcS1zATw6wn4Ej8vjuVGxeHdan+bRb2ebyv4=",
version = "v0.0.0-20240216141850-2abb0c79d3c4",
)
go_repository(
name = "com_github_holiman_bloomfilter_v2",
@@ -1547,6 +1551,12 @@ def prysm_deps():
sum = "h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao=",
version = "v2.0.3",
)
go_repository(
name = "com_github_holiman_goevmlab",
importpath = "github.com/holiman/goevmlab",
sum = "h1:JHZ8k9n9G9KXIo1qrvK5Cxah6ax5BR0qVTA9bFYl1oM=",
version = "v0.0.0-20241121133100-cfa6b078c8c4",
)
go_repository(
name = "com_github_holiman_uint256",
importpath = "github.com/holiman/uint256",
@@ -1840,8 +1850,8 @@ def prysm_deps():
go_repository(
name = "com_github_klauspost_compress",
importpath = "github.com/klauspost/compress",
sum = "h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=",
version = "v1.18.0",
sum = "h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=",
version = "v1.17.11",
)
go_repository(
name = "com_github_klauspost_cpuid",
@@ -1879,6 +1889,12 @@ def prysm_deps():
sum = "h1:E1iSMxIs4WqxTbIBLtmNBeOOC+1sCIXQeqTWVnpmwhk=",
version = "v0.0.5",
)
go_repository(
name = "com_github_korovkin_limiter",
importpath = "github.com/korovkin/limiter",
sum = "h1:7CfsXfFpCG1wrUpuyOzG8+vpL1ZqH2goz23wZ9pboGE=",
version = "v0.0.0-20230307205149-3d4b2b34c99d",
)
go_repository(
name = "com_github_kr_fs",
importpath = "github.com/kr/fs",
@@ -2046,6 +2062,12 @@ def prysm_deps():
sum = "h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=",
version = "v2.0.3+incompatible",
)
go_repository(
name = "com_github_lucasb_eyer_go_colorful",
importpath = "github.com/lucasb-eyer/go-colorful",
sum = "h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=",
version = "v1.2.0",
)
go_repository(
name = "com_github_lunixbochs_vtclean",
importpath = "github.com/lunixbochs/vtclean",
@@ -2082,6 +2104,18 @@ def prysm_deps():
sum = "h1:3l11YT8tm9MnwGFQ4kETwkzpAwY2Jt9lCrumCUW4+z4=",
version = "v0.7.0",
)
go_repository(
name = "com_github_mariusvanderwijden_fuzzyvm",
importpath = "github.com/MariusVanDerWijden/FuzzyVM",
sum = "h1:RQtzNvriR3Yu5CvVBTJPwDmfItBT90TWZ3fFondhc08=",
version = "v0.0.0-20240516070431-7828990cad7d",
)
go_repository(
name = "com_github_mariusvanderwijden_tx_fuzz",
importpath = "github.com/MariusVanDerWijden/tx-fuzz",
sum = "h1:Tq4lXivsR8mtoP4RpasUDIUpDLHfN1YhFge/kzrzK78=",
version = "v1.4.0",
)
go_repository(
name = "com_github_marten_seemann_tcp",
importpath = "github.com/marten-seemann/tcp",
@@ -2109,8 +2143,8 @@ def prysm_deps():
go_repository(
name = "com_github_mattn_go_runewidth",
importpath = "github.com/mattn/go-runewidth",
sum = "h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=",
version = "v0.0.16",
sum = "h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=",
version = "v0.0.15",
)
go_repository(
name = "com_github_matttproud_golang_protobuf_extensions",
@@ -2118,6 +2152,12 @@ def prysm_deps():
sum = "h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=",
version = "v1.0.4",
)
go_repository(
name = "com_github_matttproud_golang_protobuf_extensions_v2",
importpath = "github.com/matttproud/golang_protobuf_extensions/v2",
sum = "h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg=",
version = "v2.0.0",
)
go_repository(
name = "com_github_mgutz_ansi",
importpath = "github.com/mgutz/ansi",
@@ -2217,8 +2257,8 @@ def prysm_deps():
go_repository(
name = "com_github_mitchellh_mapstructure",
importpath = "github.com/mitchellh/mapstructure",
sum = "h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=",
version = "v1.5.0",
sum = "h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=",
version = "v1.4.1",
)
go_repository(
name = "com_github_mitchellh_pointerstructure",
@@ -2232,6 +2272,12 @@ def prysm_deps():
sum = "h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY=",
version = "v0.4.0",
)
go_repository(
name = "com_github_mmcloughlin_profile",
importpath = "github.com/mmcloughlin/profile",
sum = "h1:jhDmAqPyebOsVDOCICJoINoLb/AnLBaUw58nFzxWS2w=",
version = "v0.1.1",
)
go_repository(
name = "com_github_moby_spdystream",
importpath = "github.com/moby/spdystream",
@@ -2403,15 +2449,6 @@ def prysm_deps():
sum = "h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY=",
version = "v1.4.11",
)
go_repository(
name = "com_github_offchainlabs_hashtree",
build_file_generation = "off",
importpath = "github.com/OffchainLabs/hashtree",
patch_args = ["-p1"],
patches = ["//third_party:com_github_offchainlabs_hashtree.patch"],
sum = "h1:nM8dBAQZzHLzzM14FaAHXnHTAXZIst69v5xWuS48y/c=",
version = "v0.2.3",
)
go_repository(
name = "com_github_oklog_oklog",
importpath = "github.com/oklog/oklog",
@@ -2784,12 +2821,6 @@ def prysm_deps():
sum = "h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=",
version = "v1.1.0",
)
go_repository(
name = "com_github_projectzkm_ziren_crates_go_runtime_zkvm_runtime",
importpath = "github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime",
sum = "h1:1zYrtlhrZ6/b6SAjLSfKzWtdgqK0U+HtH/VcBWh1BaU=",
version = "v0.0.0-20251001021608-1fe7b43fc4d6",
)
go_repository(
name = "com_github_prometheus_client_golang",
importpath = "github.com/prometheus/client_golang",
@@ -2911,6 +2942,12 @@ def prysm_deps():
sum = "h1:dY6ETXrvDG7Sa4vE8ZQG4yqWg6UnOcbqTAahkV813vQ=",
version = "v0.0.0-20190826022208-cac0b30c2563",
)
go_repository(
name = "com_github_rivo_tview",
importpath = "github.com/rivo/tview",
sum = "h1:HxvWMyQ3vKQBlYZq9wfFtjbUeA6UUYZoLJmmwWee43s=",
version = "v0.0.0-20240519200218-0ac5f73025a8",
)
go_repository(
name = "com_github_rivo_uniseg",
build_directives = [
@@ -3230,8 +3267,8 @@ def prysm_deps():
go_repository(
name = "com_github_spf13_pflag",
importpath = "github.com/spf13/pflag",
sum = "h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=",
version = "v1.0.6",
sum = "h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=",
version = "v1.0.5",
)
go_repository(
name = "com_github_spf13_viper",
@@ -3347,14 +3384,14 @@ def prysm_deps():
go_repository(
name = "com_github_tklauser_go_sysconf",
importpath = "github.com/tklauser/go-sysconf",
sum = "h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4=",
version = "v0.3.15",
sum = "h1:GBUpcahXSpR2xN01jhkNAbTLRk2Yzgggk8IM08lq3r4=",
version = "v0.3.13",
)
go_repository(
name = "com_github_tklauser_numcpus",
importpath = "github.com/tklauser/numcpus",
sum = "h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso=",
version = "v0.10.0",
sum = "h1:yjuerZP127QG9m5Zh/mSO4wqurYil27tHrqwRoRjpr4=",
version = "v0.7.0",
)
go_repository(
name = "com_github_tmc_grpc_websocket_proxy",
@@ -3374,6 +3411,12 @@ def prysm_deps():
sum = "h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0=",
version = "v1.2.7",
)
go_repository(
name = "com_github_umbracle_gohashtree",
importpath = "github.com/umbracle/gohashtree",
sum = "h1:CQh33pStIp/E30b7TxDlXfM0145bn2e8boI30IxAhTg=",
version = "v0.0.2-alpha.0.20230207094856-5b775a815c10",
)
go_repository(
name = "com_github_urfave_cli",
importpath = "github.com/urfave/cli",
@@ -3383,8 +3426,8 @@ def prysm_deps():
go_repository(
name = "com_github_urfave_cli_v2",
importpath = "github.com/urfave/cli/v2",
sum = "h1:VdRdS98FNhKZ8/Az8B7MTyGQmpIr36O1EHybx/LaZ4g=",
version = "v2.27.6",
sum = "h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w=",
version = "v2.27.5",
)
go_repository(
name = "com_github_urfave_negroni",
@@ -3431,8 +3474,8 @@ def prysm_deps():
go_repository(
name = "com_github_victoriametrics_fastcache",
importpath = "github.com/VictoriaMetrics/fastcache",
sum = "h1:AW4mheMR5Vd9FkAPUv+NH6Nhw+fmbTMGMsNAoA/+4G0=",
version = "v1.13.0",
sum = "h1:N0y9ASrJ0F6h0QaC3o6uJb3NIZ9VKLjCM7NQbSmF7WI=",
version = "v1.12.2",
)
go_repository(
name = "com_github_vividcortex_gohistogram",
@@ -3554,8 +3597,8 @@ def prysm_deps():
go_repository(
name = "com_github_yusufpapurcu_wmi",
importpath = "github.com/yusufpapurcu/wmi",
sum = "h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=",
version = "v1.2.4",
sum = "h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=",
version = "v1.2.3",
)
go_repository(
name = "com_google_cloud_go",
@@ -4741,8 +4784,8 @@ def prysm_deps():
go_repository(
name = "org_golang_x_exp",
importpath = "golang.org/x/exp",
sum = "h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI=",
version = "v0.0.0-20250506013437-ce4c2cf36ca6",
sum = "h1:KL/ZBHXgKGVmuZBZ01Lt57yE5ws8ZPSkkihmEyq7FXc=",
version = "v0.0.0-20250128182459-e0ece0dbea4c",
)
go_repository(
name = "org_golang_x_exp_typeparams",

View File

@@ -21,6 +21,7 @@ go_library(
"@com_github_minio_sha256_simd//:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
"@com_github_prysmaticlabs_gohashtree//:go_default_library",
],
)

View File

@@ -5,6 +5,7 @@ import (
"encoding/binary"
fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams"
"github.com/OffchainLabs/prysm/v7/crypto/hash/htr"
"github.com/OffchainLabs/prysm/v7/encoding/bytesutil"
enginev1 "github.com/OffchainLabs/prysm/v7/proto/engine/v1"
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
@@ -141,3 +142,24 @@ func withdrawalRoot(w *enginev1.Withdrawal) ([32]byte, error) {
}
return w.HashTreeRoot()
}
// KzgCommitmentsRoot computes the HTR for a list of KZG commitments
func KzgCommitmentsRoot(commitments [][]byte) ([32]byte, error) {
roots := make([][32]byte, len(commitments))
for i, commitment := range commitments {
chunks, err := PackByChunk([][]byte{commitment})
if err != nil {
return [32]byte{}, err
}
roots[i] = htr.VectorizedSha256(chunks)[0]
}
commitmentsRoot, err := BitwiseMerkleize(roots, uint64(len(roots)), fieldparams.MaxBlobCommitmentsPerBlock)
if err != nil {
return [32]byte{}, errors.Wrap(err, "could not compute merkleization")
}
length := make([]byte, 32)
binary.LittleEndian.PutUint64(length[:8], uint64(len(roots)))
return MixInLength(commitmentsRoot, length), nil
}

View File

@@ -6,6 +6,7 @@ import (
"github.com/OffchainLabs/prysm/v7/container/trie"
"github.com/OffchainLabs/prysm/v7/crypto/hash/htr"
"github.com/pkg/errors"
"github.com/prysmaticlabs/gohashtree"
)
var errInvalidNilSlice = errors.New("invalid empty slice")
@@ -181,10 +182,10 @@ func MerkleizeListSSZ[T Hashable](elements []T, limit uint64) ([32]byte, error)
chunks := make([][32]byte, 2)
chunks[0] = body
binary.LittleEndian.PutUint64(chunks[1][:], uint64(len(elements)))
if err = htr.Hash(chunks, chunks); err != nil {
if err := gohashtree.Hash(chunks, chunks); err != nil {
return [32]byte{}, err
}
return chunks[0], nil
return chunks[0], err
}
// MerkleizeByteSliceSSZ hashes a byteslice by chunkifying it and returning the

56
go.mod
View File

@@ -3,19 +3,20 @@ module github.com/OffchainLabs/prysm/v7
go 1.25.1
require (
github.com/MariusVanDerWijden/FuzzyVM v0.0.0-20240516070431-7828990cad7d
github.com/MariusVanDerWijden/tx-fuzz v1.4.0
github.com/OffchainLabs/go-bitfield v0.0.0-20251031151322-f427d04d8506
github.com/OffchainLabs/hashtree v0.2.3
github.com/aristanetworks/goarista v0.0.0-20200805130819-fd197cf57d96
github.com/bazelbuild/rules_go v0.23.2
github.com/btcsuite/btcd/btcec/v2 v2.3.4
github.com/consensys/gnark-crypto v0.18.0
github.com/consensys/gnark-crypto v0.14.0
github.com/crate-crypto/go-kzg-4844 v1.1.0
github.com/d4l3k/messagediff v1.2.1
github.com/dgraph-io/ristretto/v2 v2.2.0
github.com/dustin/go-humanize v1.0.1
github.com/emicklei/dot v1.6.2
github.com/emicklei/dot v0.11.0
github.com/ethereum/c-kzg-4844/v2 v2.1.5
github.com/ethereum/go-ethereum v1.16.8
github.com/ethereum/go-ethereum v1.15.9
github.com/fsnotify/fsnotify v1.6.0
github.com/ghodss/yaml v1.0.0
github.com/go-yaml/yaml v2.1.0+incompatible
@@ -23,7 +24,7 @@ require (
github.com/golang-jwt/jwt/v4 v4.5.2
github.com/golang/gddo v0.0.0-20200528160355-8d077c1d8f4c
github.com/golang/protobuf v1.5.4
github.com/golang/snappy v1.0.0
github.com/golang/snappy v0.0.5-0.20231225225746-43d5d4cd4e0e
github.com/google/go-cmp v0.7.0
github.com/google/gofuzz v1.2.0
github.com/google/uuid v1.6.0
@@ -73,7 +74,7 @@ require (
github.com/thomaso-mirodin/intmath v0.0.0-20160323211736-5dc6d854e46e
github.com/trailofbits/go-mutexasserts v0.0.0-20250212181730-4c2b8e9e784b
github.com/tyler-smith/go-bip39 v1.1.0
github.com/urfave/cli/v2 v2.27.6
github.com/urfave/cli/v2 v2.27.5
github.com/uudashr/gocognit v1.0.5
github.com/wealdtech/go-bytesutil v1.1.1
github.com/wealdtech/go-eth2-util v1.6.3
@@ -88,7 +89,7 @@ require (
go.uber.org/automaxprocs v1.5.2
go.uber.org/mock v0.5.2
golang.org/x/crypto v0.44.0
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6
golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c
golang.org/x/sync v0.18.0
golang.org/x/tools v0.39.0
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1
@@ -105,13 +106,12 @@ require (
require (
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c // indirect
github.com/DataDog/zstd v1.5.7 // indirect
github.com/DataDog/zstd v1.5.5 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 // indirect
github.com/VictoriaMetrics/fastcache v1.13.0 // indirect
github.com/VictoriaMetrics/fastcache v1.12.2 // indirect
github.com/benbjohnson/clock v1.3.5 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bits-and-blooms/bitset v1.22.0 // indirect
github.com/bits-and-blooms/bitset v1.17.0 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cespare/cp v1.1.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
@@ -119,28 +119,27 @@ require (
github.com/cockroachdb/errors v1.11.3 // indirect
github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce // indirect
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect
github.com/cockroachdb/pebble v1.1.5 // indirect
github.com/cockroachdb/pebble v1.1.2 // indirect
github.com/cockroachdb/redact v1.1.5 // indirect
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect
github.com/consensys/bavard v0.1.22 // indirect
github.com/containerd/cgroups v1.1.0 // indirect
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
github.com/crate-crypto/go-eth-kzg v1.4.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect
github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect
github.com/dchest/siphash v1.2.3 // indirect
github.com/deckarep/golang-set/v2 v2.8.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect
github.com/deckarep/golang-set/v2 v2.6.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect
github.com/deepmap/oapi-codegen v1.8.2 // indirect
github.com/dlclark/regexp2 v1.7.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/dop251/goja v0.0.0-20230806174421-c933cf95e127 // indirect
github.com/elastic/gosigar v0.14.3 // indirect
github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab // indirect
github.com/ethereum/c-kzg-4844 v1.0.0 // indirect
github.com/ethereum/go-verkle v0.2.2 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/ferranbt/fastssz v0.1.4 // indirect
github.com/ferranbt/fastssz v0.1.3 // indirect
github.com/flynn/noise v1.1.0 // indirect
github.com/francoispqt/gojay v1.2.13 // indirect
github.com/getsentry/sentry-go v0.27.0 // indirect
@@ -150,7 +149,7 @@ require (
github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/gofrs/flock v0.12.1 // indirect
github.com/gofrs/flock v0.8.1 // indirect
github.com/google/gopacket v1.1.19 // indirect
github.com/google/pprof v0.0.0-20250202011525-fc3143867406 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
@@ -158,8 +157,9 @@ require (
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 // indirect
github.com/hashicorp/go-bexpr v0.1.10 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/holiman/billy v0.0.0-20250707135307-f2f9b9aae7db // indirect
github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4 // indirect
github.com/holiman/bloomfilter/v2 v2.0.3 // indirect
github.com/holiman/goevmlab v0.0.0-20241121133100-cfa6b078c8c4 // indirect
github.com/huin/goupnp v1.3.0 // indirect
github.com/influxdata/influxdb-client-go/v2 v2.4.0 // indirect
github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c // indirect
@@ -168,7 +168,7 @@ require (
github.com/jackpal/go-nat-pmp v1.0.2 // indirect
github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect
github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a // indirect
github.com/klauspost/compress v1.18.0 // indirect
github.com/klauspost/compress v1.17.11 // indirect
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
github.com/koron/go-ssdp v0.0.5 // indirect
github.com/kr/text v0.2.0 // indirect
@@ -185,14 +185,15 @@ require (
github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/miekg/dns v1.1.63 // indirect
github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect
github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mitchellh/mapstructure v1.4.1 // indirect
github.com/mitchellh/pointerstructure v1.2.0 // indirect
github.com/mmcloughlin/addchain v0.4.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/mr-tron/base58 v1.2.0 // indirect
@@ -248,12 +249,12 @@ require (
github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect
github.com/tklauser/go-sysconf v0.3.15 // indirect
github.com/tklauser/numcpus v0.10.0 // indirect
github.com/tklauser/go-sysconf v0.3.13 // indirect
github.com/tklauser/numcpus v0.7.0 // indirect
github.com/wealdtech/go-eth2-types/v2 v2.8.2 // indirect
github.com/wlynxg/anet v0.0.5 // indirect
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
github.com/yusufpapurcu/wmi v1.2.3 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 // indirect
go.opentelemetry.io/otel/metric v1.35.0 // indirect
@@ -274,6 +275,7 @@ require (
gopkg.in/cenkalti/backoff.v1 v1.1.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
lukechampine.com/blake3 v1.3.0 // indirect
rsc.io/tmplfunc v0.0.3 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect

128
go.sum
View File

@@ -48,22 +48,22 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs=
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/DataDog/zstd v1.5.7 h1:ybO8RBeh29qrxIhCA9E8gKY6xfONU9T6G6aP9DTKfLE=
github.com/DataDog/zstd v1.5.7/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ=
github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/MariusVanDerWijden/FuzzyVM v0.0.0-20240516070431-7828990cad7d h1:RQtzNvriR3Yu5CvVBTJPwDmfItBT90TWZ3fFondhc08=
github.com/MariusVanDerWijden/FuzzyVM v0.0.0-20240516070431-7828990cad7d/go.mod h1:gWTykV/ZinShgltWofTEJY4TsletuvGhB6l4+Ai2F+E=
github.com/MariusVanDerWijden/tx-fuzz v1.4.0 h1:Tq4lXivsR8mtoP4RpasUDIUpDLHfN1YhFge/kzrzK78=
github.com/MariusVanDerWijden/tx-fuzz v1.4.0/go.mod h1:gmOVECg7o5FY5VU3DQ/fY0zTk/ExBdMkUGz0vA8qqms=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/OffchainLabs/go-bitfield v0.0.0-20251031151322-f427d04d8506 h1:d/SJkN8/9Ca+1YmuDiUJxAiV4w/a9S8NcsG7GMQSrVI=
github.com/OffchainLabs/go-bitfield v0.0.0-20251031151322-f427d04d8506/go.mod h1:6TZI4FU6zT8x6ZfWa1J8YQ2NgW0wLV/W3fHRca8ISBo=
github.com/OffchainLabs/hashtree v0.2.3 h1:nM8dBAQZzHLzzM14FaAHXnHTAXZIst69v5xWuS48y/c=
github.com/OffchainLabs/hashtree v0.2.3/go.mod h1:b07+cRZs+eAR8TR57CB9TQlt5Gnl/06Xs76xt/1wq0M=
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6 h1:1zYrtlhrZ6/b6SAjLSfKzWtdgqK0U+HtH/VcBWh1BaU=
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251001021608-1fe7b43fc4d6/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/sarama v1.26.1/go.mod h1:NbSGBSSndYaIhRcBtY9V0U7AyH+x71bG668AuWys/yU=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/VictoriaMetrics/fastcache v1.13.0 h1:AW4mheMR5Vd9FkAPUv+NH6Nhw+fmbTMGMsNAoA/+4G0=
github.com/VictoriaMetrics/fastcache v1.13.0/go.mod h1:hHXhl4DA2fTL2HTZDJFXWgW0LNjo6B+4aj2Wmng3TjU=
github.com/VictoriaMetrics/fastcache v1.12.2 h1:N0y9ASrJ0F6h0QaC3o6uJb3NIZ9VKLjCM7NQbSmF7WI=
github.com/VictoriaMetrics/fastcache v1.12.2/go.mod h1:AmC+Nzz1+3G2eCPapF6UcsnkThDcMsQicp4xDukwJYI=
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
@@ -71,8 +71,9 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8=
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
github.com/allegro/bigcache v1.2.1 h1:hg1sY1raCwic3Vnsvje6TT7/pnZba83LeFck5NrFKSc=
github.com/allegro/bigcache v1.2.1/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
@@ -99,8 +100,8 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bits-and-blooms/bitset v1.22.0 h1:Tquv9S8+SGaS3EhyA+up3FXzmkhxPGjQQCkcs2uw7w4=
github.com/bits-and-blooms/bitset v1.22.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/bits-and-blooms/bitset v1.17.0 h1:1X2TS7aHz1ELcC0yU1y2stUs/0ig5oMU6STFZGrhvHI=
github.com/bits-and-blooms/bitset v1.17.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
github.com/bradfitz/gomemcache v0.0.0-20170208213004-1952afaa557d/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60=
github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ=
@@ -114,6 +115,7 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA
github.com/cespare/cp v1.1.1 h1:nCb6ZLdB7NRaqsm91JtQTAme2SKJzXVsdPIPkyJr1MU=
github.com/cespare/cp v1.1.1/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
@@ -143,15 +145,17 @@ github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce h1:giXvy4KSc/6g/e
github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce/go.mod h1:9/y3cnZ5GKakj/H4y9r9GTjCvAFta7KLgSHPJJYc52M=
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE=
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs=
github.com/cockroachdb/pebble v1.1.5 h1:5AAWCBWbat0uE0blr8qzufZP5tBjkRyy/jWe1QWLnvw=
github.com/cockroachdb/pebble v1.1.5/go.mod h1:17wO9el1YEigxkP/YtV8NtCivQDgoCyBg5c4VR/eOWo=
github.com/cockroachdb/pebble v1.1.2 h1:CUh2IPtR4swHlEj48Rhfzw6l/d0qA31fItcIszQVIsA=
github.com/cockroachdb/pebble v1.1.2/go.mod h1:4exszw1r40423ZsmkG/09AFEG83I0uDgfujJdbL6kYU=
github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30=
github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo=
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ=
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
github.com/consensys/gnark-crypto v0.18.0 h1:vIye/FqI50VeAr0B3dx+YjeIvmc3LWz4yEfbWBpTUf0=
github.com/consensys/gnark-crypto v0.18.0/go.mod h1:L3mXGFTe1ZN+RSJ+CLjUt9x7PNdx8ubaYfDROyp2Z8c=
github.com/consensys/bavard v0.1.22 h1:Uw2CGvbXSZWhqK59X0VG/zOjpTFuOMcPLStrp1ihI0A=
github.com/consensys/bavard v0.1.22/go.mod h1:k/zVjHHC4B+PQy1Pg7fgvG3ALicQw540Crag8qx+dZs=
github.com/consensys/gnark-crypto v0.14.0 h1:DDBdl4HaBtdQsq/wfMwJvZNE80sHidrK3Nfrefatm0E=
github.com/consensys/gnark-crypto v0.14.0/go.mod h1:CU4UijNPsHawiVGNxe9co07FkzCeWHHrb1li/n1XoU0=
github.com/containerd/cgroups v0.0.0-20201119153540-4cbc285b3327/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE=
github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM=
github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw=
@@ -166,10 +170,8 @@ github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo=
github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/crate-crypto/go-eth-kzg v1.4.0 h1:WzDGjHk4gFg6YzV0rJOAsTK4z3Qkz5jd4RE3DAvPFkg=
github.com/crate-crypto/go-eth-kzg v1.4.0/go.mod h1:J9/u5sWfznSObptgfa92Jq8rTswn6ahQWEuiLHOjCUI=
github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc=
github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a h1:W8mUrRp6NOVl3J+MYp5kPMoUZPp7aOYHtaua31lwRHg=
github.com/crate-crypto/go-ipa v0.0.0-20240724233137-53bbb0ceb27a/go.mod h1:sTwzHBvIzm2RfVCGNEBZgRyjwK40bVoun3ZnGOCafNM=
github.com/crate-crypto/go-kzg-4844 v1.1.0 h1:EN/u9k2TF6OWSHrCCDBBU6GLNMq88OspHHlMnHfoyU4=
@@ -184,14 +186,12 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c h1:pFUpOrbxDR6AkioZ1ySsx5yxlDQZ8stG2b88gTPxgJU=
github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c/go.mod h1:6UhI8N9EjYm1c2odKpFpAYeR8dsBeM7PtzQhRgxRr9U=
github.com/dchest/siphash v1.2.3 h1:QXwFc8cFOR2dSa/gE6o/HokBMWtLUaNDVd+22aKHeEA=
github.com/dchest/siphash v1.2.3/go.mod h1:0NvQU092bT0ipiFN++/rXm69QG9tVxLAlQHIXMPAkHc=
github.com/deckarep/golang-set/v2 v2.8.0 h1:swm0rlPCmdWn9mESxKOjWk8hXSqoxOp+ZlfuyaAdFlQ=
github.com/deckarep/golang-set/v2 v2.8.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
github.com/decred/dcrd/crypto/blake256 v1.1.0 h1:zPMNGQCm0g4QTY27fOCorQW7EryeQ/U0x++OzVrdms8=
github.com/decred/dcrd/crypto/blake256 v1.1.0/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM=
github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y=
github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
github.com/deepmap/oapi-codegen v1.6.0/go.mod h1:ryDa9AgbELGeB+YEXE1dR53yAjHwFvE9iAUlWl9Al3M=
github.com/deepmap/oapi-codegen v1.8.2 h1:SegyeYGcdi0jLLrpbCMoJxnUUn8GBXHsvr4rbzjuhfU=
github.com/deepmap/oapi-codegen v1.8.2/go.mod h1:YLgSKSDv/bZQB7N4ws6luhozi3cEdRktEqrX88CvjIw=
@@ -223,8 +223,8 @@ github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaB
github.com/elastic/gosigar v0.12.0/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs=
github.com/elastic/gosigar v0.14.3 h1:xwkKwPia+hSfg9GqrCUKYdId102m9qTJIIr7egmK/uo=
github.com/elastic/gosigar v0.14.3/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs=
github.com/emicklei/dot v1.6.2 h1:08GN+DD79cy/tzN6uLCT84+2Wk9u+wvqP+Hkx/dIR8A=
github.com/emicklei/dot v1.6.2/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s=
github.com/emicklei/dot v0.11.0 h1:Ase39UD9T9fRBOb5ptgpixrxfx8abVzNWZi2+lr53PI=
github.com/emicklei/dot v0.11.0/go.mod h1:DeV7GvQtIw4h2u73RKBkkFdvVAz0D9fzeJrgPW6gy/s=
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
@@ -234,12 +234,12 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/ethereum/c-kzg-4844 v1.0.0 h1:0X1LBXxaEtYD9xsyj9B9ctQEZIpnvVDeoBx8aHEwTNA=
github.com/ethereum/c-kzg-4844 v1.0.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0=
github.com/ethereum/c-kzg-4844/v2 v2.1.5 h1:aVtoLK5xwJ6c5RiqO8g8ptJ5KU+2Hdquf6G3aXiHh5s=
github.com/ethereum/c-kzg-4844/v2 v2.1.5/go.mod h1:u59hRTTah4Co6i9fDWtiCjTrblJv0UwsqZKCc0GfgUs=
github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab h1:rvv6MJhy07IMfEKuARQ9TKojGqLVNxQajaXEp/BoqSk=
github.com/ethereum/go-bigmodexpfix v0.0.0-20250911101455-f9e208c548ab/go.mod h1:IuLm4IsPipXKF7CW5Lzf68PIbZ5yl7FFd74l/E0o9A8=
github.com/ethereum/go-ethereum v1.16.8 h1:LLLfkZWijhR5m6yrAXbdlTeXoqontH+Ga2f9igY7law=
github.com/ethereum/go-ethereum v1.16.8/go.mod h1:Fs6QebQbavneQTYcA39PEKv2+zIjX7rPUZ14DER46wk=
github.com/ethereum/go-ethereum v1.15.9 h1:bRra1zi+/q+qyXZ6fylZOrlaF8kDdnlTtzNTmNHfX+g=
github.com/ethereum/go-ethereum v1.15.9/go.mod h1:+S9k+jFzlyVTNcYGvqFhzN/SFhI6vA+aOY4T5tLSPL0=
github.com/ethereum/go-verkle v0.2.2 h1:I2W0WjnrFUIzzVPwm8ykY+7pL2d4VhlsePn4j7cnFk8=
github.com/ethereum/go-verkle v0.2.2/go.mod h1:M3b90YRnzqKyyzBEWJGqj8Qff4IDeXnzFw0P9bFw3uk=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
@@ -248,8 +248,8 @@ github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4Nij
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/ferranbt/fastssz v0.0.0-20210120143747-11b9eff30ea9/go.mod h1:DyEu2iuLBnb/T51BlsiO3yLYdJC6UbGMrIkqK1KmQxM=
github.com/ferranbt/fastssz v0.1.4 h1:OCDB+dYDEQDvAgtAGnTSidK1Pe2tW3nFV40XyMkTeDY=
github.com/ferranbt/fastssz v0.1.4/go.mod h1:Ea3+oeoRGGLGm5shYAeDgu6PGUlcvQhE2fILyD9+tGg=
github.com/ferranbt/fastssz v0.1.3 h1:ZI+z3JH05h4kgmFXdHuR1aWYsgrg7o+Fw7/NCzM16Mo=
github.com/ferranbt/fastssz v0.1.3/go.mod h1:0Y9TEd/9XuFlh7mskMPfXiI2Dkw4Ddg9EyXt1W7MRvE=
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
github.com/flynn/noise v1.1.0 h1:KjPQoQCEFdZDiP03phOvGi11+SVVhBG2wOWAorLsstg=
github.com/flynn/noise v1.1.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag=
@@ -325,8 +325,8 @@ github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5x
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
@@ -373,8 +373,8 @@ github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8l
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.5-0.20231225225746-43d5d4cd4e0e h1:4bw4WeyTYPp0smaXiJZCNnLrvVBqirQVreixayXezGc=
github.com/golang/snappy v0.0.5-0.20231225225746-43d5d4cd4e0e/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
@@ -418,6 +418,7 @@ github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8q
github.com/google/pprof v0.0.0-20250202011525-fc3143867406 h1:wlQI2cYY0BsWmmPPAnxfQ8SDW0S3Jasn+4B8kXFxprg=
github.com/google/pprof v0.0.0-20250202011525-fc3143867406/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@@ -485,10 +486,12 @@ github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/J
github.com/herumi/bls-eth-go-binary v0.0.0-20210130185500-57372fb27371/go.mod h1:luAnRm3OsMQeokhGzpYmc0ZKwawY7o87PUEP11Z7r7U=
github.com/herumi/bls-eth-go-binary v1.31.0 h1:9eeW3EA4epCb7FIHt2luENpAW69MvKGL5jieHlBiP+w=
github.com/herumi/bls-eth-go-binary v1.31.0/go.mod h1:luAnRm3OsMQeokhGzpYmc0ZKwawY7o87PUEP11Z7r7U=
github.com/holiman/billy v0.0.0-20250707135307-f2f9b9aae7db h1:IZUYC/xb3giYwBLMnr8d0TGTzPKFGNTCGgGLoyeX330=
github.com/holiman/billy v0.0.0-20250707135307-f2f9b9aae7db/go.mod h1:xTEYN9KCHxuYHs+NmrmzFcnvHMzLLNiGFafCb1n3Mfg=
github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4 h1:X4egAf/gcS1zATw6wn4Ej8vjuVGxeHdan+bRb2ebyv4=
github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc=
github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao=
github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA=
github.com/holiman/goevmlab v0.0.0-20241121133100-cfa6b078c8c4 h1:JHZ8k9n9G9KXIo1qrvK5Cxah6ax5BR0qVTA9bFYl1oM=
github.com/holiman/goevmlab v0.0.0-20241121133100-cfa6b078c8c4/go.mod h1:+DBd7lup47uusCYWbkJPfHRG4LYjBHvyXU0c+z26/U4=
github.com/holiman/uint256 v1.3.2 h1:a9EgMPSC1AAaj1SZL5zIQD3WbwTuHrMGOerLjGmM/TA=
github.com/holiman/uint256 v1.3.2/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
@@ -546,8 +549,8 @@ github.com/kisielk/errcheck v1.8.0/go.mod h1:1kLL+jV4e+CFfueBmI1dSK2ADDyQnlrnrY/
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.9.8/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.10.1/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
@@ -643,8 +646,8 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
@@ -678,11 +681,13 @@ github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:F
github.com/mitchellh/mapstructure v0.0.0-20170523030023-d0303fe80992/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A=
github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4=
github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY=
github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU=
github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -990,8 +995,8 @@ github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3
github.com/spf13/jwalterweatherman v0.0.0-20170901151539-12bd96e66386/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.1-0.20170901120850-7aff26db30c1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.0.0/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM=
github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA=
github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg=
@@ -1028,18 +1033,20 @@ github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3/go.mod h1:
github.com/thomaso-mirodin/intmath v0.0.0-20160323211736-5dc6d854e46e h1:cR8/SYRgyQCt5cNCMniB/ZScMkhI9nk8U5C7SbISXjo=
github.com/thomaso-mirodin/intmath v0.0.0-20160323211736-5dc6d854e46e/go.mod h1:Tu4lItkATkonrYuvtVjG0/rhy15qrNGNTjPdaphtZ/8=
github.com/tjfoc/gmsm v1.3.0/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4=
github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4=
github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso=
github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ=
github.com/tklauser/go-sysconf v0.3.13 h1:GBUpcahXSpR2xN01jhkNAbTLRk2Yzgggk8IM08lq3r4=
github.com/tklauser/go-sysconf v0.3.13/go.mod h1:zwleP4Q4OehZHGn4CYZDipCgg9usW5IJePewFCGVEa0=
github.com/tklauser/numcpus v0.7.0 h1:yjuerZP127QG9m5Zh/mSO4wqurYil27tHrqwRoRjpr4=
github.com/tklauser/numcpus v0.7.0/go.mod h1:bb6dMVcj8A42tSE7i32fsIUCbQNllK5iDguyOZRUzAY=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/trailofbits/go-mutexasserts v0.0.0-20250212181730-4c2b8e9e784b h1:EBoYk5zHOfuHDBqLFx4eSPRVcbnW+L3aFJzoCi8zRnk=
github.com/trailofbits/go-mutexasserts v0.0.0-20250212181730-4c2b8e9e784b/go.mod h1:4R6Qam+w871wOlyRq59zRLjhb5x9/De/wgPeaCTaCwI=
github.com/umbracle/gohashtree v0.0.2-alpha.0.20230207094856-5b775a815c10 h1:CQh33pStIp/E30b7TxDlXfM0145bn2e8boI30IxAhTg=
github.com/umbracle/gohashtree v0.0.2-alpha.0.20230207094856-5b775a815c10/go.mod h1:x/Pa0FF5Te9kdrlZKJK82YmAkvL8+f989USgz6Jiw7M=
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli/v2 v2.27.6 h1:VdRdS98FNhKZ8/Az8B7MTyGQmpIr36O1EHybx/LaZ4g=
github.com/urfave/cli/v2 v2.27.6/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w=
github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
github.com/uudashr/gocognit v1.0.5 h1:rrSex7oHr3/pPLQ0xoWq108XMU8s678FJcQ+aSfOHa4=
github.com/uudashr/gocognit v1.0.5/go.mod h1:wgYz0mitoKOTysqxTDMOUXg+Jb5SvtihkfmugIZYpEA=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
@@ -1074,8 +1081,8 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=
github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
@@ -1177,8 +1184,8 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6 h1:y5zboxd6LQAqYIhHnB48p0ByQ/GnQx2BE33L8BOHQkI=
golang.org/x/exp v0.0.0-20250506013437-ce4c2cf36ca6/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ=
golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c h1:KL/ZBHXgKGVmuZBZ01Lt57yE5ws8ZPSkkihmEyq7FXc=
golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU=
golang.org/x/exp/typeparams v0.0.0-20231108232855-2478ac86f678 h1:1P7xPZEwZMoBoz0Yze5Nx2/4pxj6nw9ZqHWXqP0iRgQ=
golang.org/x/exp/typeparams v0.0.0-20231108232855-2478ac86f678/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
@@ -1384,6 +1391,7 @@ golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
@@ -1685,6 +1693,8 @@ lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU=
rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=

View File

@@ -189,3 +189,54 @@ func copyBeaconBlockBodyGloas(body *BeaconBlockBodyGloas) *BeaconBlockBodyGloas
return copied
}
// CopyBuilderPendingPaymentSlice creates a deep copy of a builder pending payment slice.
func CopyBuilderPendingPaymentSlice(original []*BuilderPendingPayment) []*BuilderPendingPayment {
if original == nil {
return nil
}
copied := make([]*BuilderPendingPayment, len(original))
for i, payment := range original {
copied[i] = CopyBuilderPendingPayment(payment)
}
return copied
}
// CopyBuilderPendingPayment creates a deep copy of a builder pending payment.
func CopyBuilderPendingPayment(original *BuilderPendingPayment) *BuilderPendingPayment {
if original == nil {
return nil
}
return &BuilderPendingPayment{
Weight: original.Weight,
Withdrawal: CopyBuilderPendingWithdrawal(original.Withdrawal),
}
}
// CopyBuilderPendingWithdrawalSlice creates a deep copy of a builder pending withdrawal slice.
func CopyBuilderPendingWithdrawalSlice(original []*BuilderPendingWithdrawal) []*BuilderPendingWithdrawal {
if original == nil {
return nil
}
copied := make([]*BuilderPendingWithdrawal, len(original))
for i, withdrawal := range original {
copied[i] = CopyBuilderPendingWithdrawal(withdrawal)
}
return copied
}
// CopyBuilderPendingWithdrawal creates a deep copy of a builder pending withdrawal.
func CopyBuilderPendingWithdrawal(original *BuilderPendingWithdrawal) *BuilderPendingWithdrawal {
if original == nil {
return nil
}
return &BuilderPendingWithdrawal{
FeeRecipient: bytesutil.SafeCopyBytes(original.FeeRecipient),
Amount: original.Amount,
BuilderIndex: original.BuilderIndex,
}
}

View File

@@ -1246,3 +1246,89 @@ func genPayloadAttestations(num int) []*v1alpha1.PayloadAttestation {
}
return pas
}
func TestCopyBuilderPendingWithdrawal(t *testing.T) {
t.Run("nil returns nil", func(t *testing.T) {
if got := v1alpha1.CopyBuilderPendingWithdrawal(nil); got != nil {
t.Fatalf("CopyBuilderPendingWithdrawal(nil) = %v, want nil", got)
}
})
t.Run("deep copy", func(t *testing.T) {
original := &v1alpha1.BuilderPendingWithdrawal{
FeeRecipient: []byte{0x01, 0x02},
Amount: primitives.Gwei(10),
BuilderIndex: primitives.BuilderIndex(3),
}
copied := v1alpha1.CopyBuilderPendingWithdrawal(original)
if copied == original {
t.Fatalf("expected new pointer for withdrawal copy")
}
if !reflect.DeepEqual(copied, original) {
t.Fatalf("CopyBuilderPendingWithdrawal() = %v, want %v", copied, original)
}
original.FeeRecipient[0] = 0xFF
original.Amount = primitives.Gwei(20)
original.BuilderIndex = primitives.BuilderIndex(9)
if copied.FeeRecipient[0] == original.FeeRecipient[0] {
t.Fatalf("fee recipient was not deep copied")
}
if copied.Amount != primitives.Gwei(10) {
t.Fatalf("amount mutated on copy: %d", copied.Amount)
}
if copied.BuilderIndex != primitives.BuilderIndex(3) {
t.Fatalf("builder index mutated on copy: %d", copied.BuilderIndex)
}
})
}
func TestCopyBuilderPendingPayment(t *testing.T) {
t.Run("nil returns nil", func(t *testing.T) {
if got := v1alpha1.CopyBuilderPendingPayment(nil); got != nil {
t.Fatalf("CopyBuilderPendingPayment(nil) = %v, want nil", got)
}
})
t.Run("deep copy", func(t *testing.T) {
original := &v1alpha1.BuilderPendingPayment{
Weight: primitives.Gwei(5),
Withdrawal: &v1alpha1.BuilderPendingWithdrawal{
FeeRecipient: []byte{0x0A, 0x0B},
Amount: primitives.Gwei(50),
BuilderIndex: primitives.BuilderIndex(7),
},
}
copied := v1alpha1.CopyBuilderPendingPayment(original)
if copied == original {
t.Fatalf("expected new pointer for payment copy")
}
if copied.Withdrawal == original.Withdrawal {
t.Fatalf("expected nested withdrawal to be deep copied")
}
if !reflect.DeepEqual(copied, original) {
t.Fatalf("CopyBuilderPendingPayment() = %v, want %v", copied, original)
}
original.Weight = primitives.Gwei(10)
original.Withdrawal.FeeRecipient[0] = 0xFF
original.Withdrawal.Amount = primitives.Gwei(75)
original.Withdrawal.BuilderIndex = primitives.BuilderIndex(9)
if copied.Weight != primitives.Gwei(5) {
t.Fatalf("weight mutated on copy: %d", copied.Weight)
}
if copied.Withdrawal.FeeRecipient[0] == original.Withdrawal.FeeRecipient[0] {
t.Fatalf("withdrawal fee recipient was not deep copied")
}
if copied.Withdrawal.Amount != primitives.Gwei(50) {
t.Fatalf("withdrawal amount mutated on copy: %d", copied.Withdrawal.Amount)
}
if copied.Withdrawal.BuilderIndex != primitives.BuilderIndex(7) {
t.Fatalf("withdrawal builder index mutated on copy: %d", copied.Withdrawal.BuilderIndex)
}
})
}

View File

@@ -99,7 +99,7 @@ func GethCancunTime(genesisTime time.Time, cfg *clparams.BeaconChainConfig) *uin
}
// GethPragueTime calculates the absolute time of the prague (aka electra) fork block
// by adding the relative time of the electra fork epoch to the given genesis timestamp.
// by adding the relative time of the capella the fork epoch to the given genesis timestamp.
func GethPragueTime(genesisTime time.Time, cfg *clparams.BeaconChainConfig) *uint64 {
var pragueTime *uint64
if cfg.ElectraForkEpoch != math.MaxUint64 {
@@ -128,45 +128,6 @@ func GethOsakaTime(genesisTime time.Time, cfg *clparams.BeaconChainConfig) *uint
return osakaTime
}
// GethBPO1Time calculates the absolute time of the BPO1 activation
// by finding the first BlobSchedule entry with MaxBlobsPerBlock > 9 (Electra's limit)
// which corresponds to the first BPO increase.
func GethBPO1Time(genesisTime time.Time, cfg *clparams.BeaconChainConfig) *uint64 {
for _, entry := range cfg.BlobSchedule {
// BPO1 is the first entry that increases beyond Electra's 9 blobs
if entry.MaxBlobsPerBlock > uint64(cfg.DeprecatedMaxBlobsPerBlockElectra) {
startSlot, err := slots.EpochStart(entry.Epoch)
if err == nil {
startTime := slots.UnsafeStartTime(genesisTime, startSlot)
newTime := uint64(startTime.Unix())
return &newTime
}
}
}
return nil
}
// GethBPO2Time calculates the absolute time of the BPO2 activation
// by finding the second BlobSchedule entry with MaxBlobsPerBlock > 9.
func GethBPO2Time(genesisTime time.Time, cfg *clparams.BeaconChainConfig) *uint64 {
count := 0
for _, entry := range cfg.BlobSchedule {
// Count entries that are beyond Electra's limit
if entry.MaxBlobsPerBlock > uint64(cfg.DeprecatedMaxBlobsPerBlockElectra) {
count++
if count == 2 { // BPO2 is the second such entry
startSlot, err := slots.EpochStart(entry.Epoch)
if err == nil {
startTime := slots.UnsafeStartTime(genesisTime, startSlot)
newTime := uint64(startTime.Unix())
return &newTime
}
}
}
}
return nil
}
// GethTestnetGenesis creates a genesis.json for eth1 clients with a set of defaults suitable for ephemeral testnets,
// like in an e2e test. The parameters are minimal but the full value is returned unmarshaled so that it can be
// customized as desired.
@@ -188,8 +149,6 @@ func GethTestnetGenesis(genesis time.Time, cfg *clparams.BeaconChainConfig) *cor
if cfg.FuluForkEpoch == 0 {
osakaTime = &genesisTime
}
bpo1Time := GethBPO1Time(genesis, cfg)
bpo2Time := GethBPO2Time(genesis, cfg)
cc := &params.ChainConfig{
ChainID: big.NewInt(defaultTestChainId),
HomesteadBlock: bigz,
@@ -212,16 +171,18 @@ func GethTestnetGenesis(genesis time.Time, cfg *clparams.BeaconChainConfig) *cor
CancunTime: cancunTime,
PragueTime: pragueTime,
OsakaTime: osakaTime,
BPO1Time: bpo1Time,
BPO2Time: bpo2Time,
DepositContractAddress: common.HexToAddress(cfg.DepositContractAddress),
BlobScheduleConfig: &params.BlobScheduleConfig{
Cancun: params.DefaultCancunBlobConfig,
Prague: params.DefaultPragueBlobConfig,
Osaka: params.DefaultOsakaBlobConfig,
BPO1: params.DefaultBPO1BlobConfig,
BPO2: params.DefaultBPO2BlobConfig,
BPO3: params.DefaultBPO3BlobConfig,
Cancun: &params.BlobConfig{
Target: 3,
Max: 6,
UpdateFraction: 3338477,
},
Prague: &params.BlobConfig{
Target: 6,
Max: 9,
UpdateFraction: 5007716,
},
},
}
da := defaultDepositContractAllocation(cfg.DepositContractAddress)

View File

@@ -1,4 +1,4 @@
version: v1.7.0-alpha.1
version: v1.7.0-alpha.0
style: full
specrefs:
@@ -117,8 +117,6 @@ exceptions:
dataclasses:
# Not implemented
- BlobParameters#fulu
- ExpectedWithdrawals#capella
- ExpectedWithdrawals#electra
- LatestMessage#phase0
- LightClientStore#altair
- OptimisticStore#bellatrix
@@ -128,7 +126,6 @@ exceptions:
- LightClientStore#capella
# Not implemented: gloas (future fork)
- ExpectedWithdrawals#gloas
- LatestMessage#gloas
- Store#gloas

View File

@@ -34,26 +34,6 @@
blobs: List[Blob, MAX_BLOB_COMMITMENTS_PER_BLOCK]
</spec>
- name: ExpectedWithdrawals#capella
sources: []
spec: |
<spec dataclass="ExpectedWithdrawals" fork="capella" hash="73becb4c">
class ExpectedWithdrawals(object):
withdrawals: Sequence[Withdrawal]
processed_sweep_withdrawals_count: uint64
</spec>
- name: ExpectedWithdrawals#electra
sources: []
spec: |
<spec dataclass="ExpectedWithdrawals" fork="electra" hash="a3827f01">
class ExpectedWithdrawals(object):
withdrawals: Sequence[Withdrawal]
# [New in Electra:EIP7251]
processed_partial_withdrawals_count: uint64
processed_sweep_withdrawals_count: uint64
</spec>
- name: GetPayloadResponse#bellatrix
sources:
- file: consensus-types/blocks/get_payload.go

View File

@@ -2366,8 +2366,8 @@
- file: beacon-chain/state/state-native/getters_withdrawal.go
search: func (b *BeaconState) ExpectedWithdrawals(
spec: |
<spec fn="get_expected_withdrawals" fork="capella" hash="12088347">
def get_expected_withdrawals(state: BeaconState) -> ExpectedWithdrawals:
<spec fn="get_expected_withdrawals" fork="capella" hash="d6a98c14">
def get_expected_withdrawals(state: BeaconState) -> Tuple[Sequence[Withdrawal], uint64]:
withdrawal_index = state.next_withdrawal_index
withdrawals: List[Withdrawal] = []
@@ -2377,10 +2377,7 @@
)
withdrawals.extend(validators_sweep_withdrawals)
return ExpectedWithdrawals(
withdrawals,
processed_validators_sweep_count,
)
return withdrawals, processed_validators_sweep_count
</spec>
- name: get_expected_withdrawals#electra
@@ -2388,8 +2385,8 @@
- file: beacon-chain/state/state-native/getters_withdrawal.go
search: func (b *BeaconState) ExpectedWithdrawals(
spec: |
<spec fn="get_expected_withdrawals" fork="electra" hash="254541e3">
def get_expected_withdrawals(state: BeaconState) -> ExpectedWithdrawals:
<spec fn="get_expected_withdrawals" fork="electra" hash="cfce862b">
def get_expected_withdrawals(state: BeaconState) -> Tuple[Sequence[Withdrawal], uint64, uint64]:
withdrawal_index = state.next_withdrawal_index
withdrawals: List[Withdrawal] = []
@@ -2406,12 +2403,8 @@
)
withdrawals.extend(validators_sweep_withdrawals)
return ExpectedWithdrawals(
withdrawals,
# [New in Electra:EIP7251]
processed_partial_withdrawals_count,
processed_validators_sweep_count,
)
# [Modified in Electra:EIP7251]
return withdrawals, processed_partial_withdrawals_count, processed_validators_sweep_count
</spec>
- name: get_filtered_block_tree
@@ -4939,7 +4932,7 @@
- name: prepare_execution_payload#capella
sources: []
spec: |
<spec fn="prepare_execution_payload" fork="capella" hash="998e8b92">
<spec fn="prepare_execution_payload" fork="capella" hash="c258893e">
def prepare_execution_payload(
state: BeaconState,
safe_block_hash: Hash32,
@@ -4952,12 +4945,15 @@
parent_hash = state.latest_execution_payload_header.block_hash
# Set the forkchoice head and initiate the payload build process
# [New in Capella]
withdrawals, _ = get_expected_withdrawals(state)
payload_attributes = PayloadAttributes(
timestamp=compute_time_at_slot(state, state.slot),
prev_randao=get_randao_mix(state, get_current_epoch(state)),
suggested_fee_recipient=suggested_fee_recipient,
# [New in Capella]
withdrawals=get_expected_withdrawals(state).withdrawals,
withdrawals=withdrawals,
)
return execution_engine.notify_forkchoice_updated(
head_block_hash=parent_hash,
@@ -4970,7 +4966,7 @@
- name: prepare_execution_payload#deneb
sources: []
spec: |
<spec fn="prepare_execution_payload" fork="deneb" hash="c617aa1b">
<spec fn="prepare_execution_payload" fork="deneb" hash="59f61f3a">
def prepare_execution_payload(
state: BeaconState,
safe_block_hash: Hash32,
@@ -4982,11 +4978,13 @@
parent_hash = state.latest_execution_payload_header.block_hash
# Set the forkchoice head and initiate the payload build process
withdrawals, _ = get_expected_withdrawals(state)
payload_attributes = PayloadAttributes(
timestamp=compute_time_at_slot(state, state.slot),
prev_randao=get_randao_mix(state, get_current_epoch(state)),
suggested_fee_recipient=suggested_fee_recipient,
withdrawals=get_expected_withdrawals(state).withdrawals,
withdrawals=withdrawals,
# [New in Deneb:EIP4788]
parent_beacon_block_root=hash_tree_root(state.latest_block_header),
)
@@ -5001,7 +4999,7 @@
- name: prepare_execution_payload#electra
sources: []
spec: |
<spec fn="prepare_execution_payload" fork="electra" hash="c617aa1b">
<spec fn="prepare_execution_payload" fork="electra" hash="5414b883">
def prepare_execution_payload(
state: BeaconState,
safe_block_hash: Hash32,
@@ -5012,13 +5010,15 @@
# Verify consistency of the parent hash with respect to the previous execution payload header
parent_hash = state.latest_execution_payload_header.block_hash
# [Modified in EIP7251]
# Set the forkchoice head and initiate the payload build process
withdrawals, _, _ = get_expected_withdrawals(state)
payload_attributes = PayloadAttributes(
timestamp=compute_time_at_slot(state, state.slot),
prev_randao=get_randao_mix(state, get_current_epoch(state)),
suggested_fee_recipient=suggested_fee_recipient,
withdrawals=get_expected_withdrawals(state).withdrawals,
# [New in Deneb:EIP4788]
withdrawals=withdrawals,
parent_beacon_block_root=hash_tree_root(state.latest_block_header),
)
return execution_engine.notify_forkchoice_updated(
@@ -7071,18 +7071,18 @@
- file: beacon-chain/core/blocks/withdrawals.go
search: func ProcessWithdrawals(
spec: |
<spec fn="process_withdrawals" fork="capella" hash="47327ef6">
<spec fn="process_withdrawals" fork="capella" hash="901f9fc4">
def process_withdrawals(state: BeaconState, payload: ExecutionPayload) -> None:
# Get expected withdrawals
expected = get_expected_withdrawals(state)
assert payload.withdrawals == expected.withdrawals
withdrawals, processed_validators_sweep_count = get_expected_withdrawals(state)
assert payload.withdrawals == withdrawals
# Apply expected withdrawals
apply_withdrawals(state, expected.withdrawals)
apply_withdrawals(state, withdrawals)
# Update withdrawals fields in the state
update_next_withdrawal_index(state, expected.withdrawals)
update_next_withdrawal_validator_index(state, expected.withdrawals)
update_next_withdrawal_index(state, withdrawals)
update_next_withdrawal_validator_index(state, processed_validators_sweep_count)
</spec>
- name: process_withdrawals#electra
@@ -7090,20 +7090,23 @@
- file: beacon-chain/core/blocks/withdrawals.go
search: func ProcessWithdrawals(
spec: |
<spec fn="process_withdrawals" fork="electra" hash="24a50daa">
<spec fn="process_withdrawals" fork="electra" hash="67870972">
def process_withdrawals(state: BeaconState, payload: ExecutionPayload) -> None:
# [Modified in Electra:EIP7251]
# Get expected withdrawals
expected = get_expected_withdrawals(state)
assert payload.withdrawals == expected.withdrawals
withdrawals, processed_partial_withdrawals_count, processed_validators_sweep_count = (
get_expected_withdrawals(state)
)
assert payload.withdrawals == withdrawals
# Apply expected withdrawals
apply_withdrawals(state, expected.withdrawals)
apply_withdrawals(state, withdrawals)
# Update withdrawals fields in the state
update_next_withdrawal_index(state, expected.withdrawals)
update_next_withdrawal_index(state, withdrawals)
# [New in Electra:EIP7251]
update_pending_partial_withdrawals(state, expected.processed_partial_withdrawals_count)
update_next_withdrawal_validator_index(state, expected.withdrawals)
update_pending_partial_withdrawals(state, processed_partial_withdrawals_count)
update_next_withdrawal_validator_index(state, processed_validators_sweep_count)
</spec>
- name: queue_excess_active_balance

View File

@@ -5,7 +5,6 @@ load("@prysm//tools/go:def.bzl", "go_test")
# gazelle:exclude mainnet_scenario_e2e_test.go
# gazelle:exclude minimal_scenario_e2e_test.go
# gazelle:exclude minimal_builder_e2e_test.go
# gazelle:exclude minimal_postmerge_e2e_test.go
# Presubmit tests represent the group of endtoend tests that are run on pull
# requests and must be passing before a pull request can merge.
@@ -28,7 +27,6 @@ test_suite(
],
tests = [
":go_builder_test",
":go_minimal_postmerge_test",
":go_mainnet_test",
],
)
@@ -90,7 +88,7 @@ common_deps = [
# gazelle:ignore
go_test(
name = "go_default_test",
size = "enormous",
size = "large",
testonly = True,
srcs = [
"component_handler_test.go",
@@ -155,39 +153,6 @@ go_test(
deps = common_deps,
)
# Full e2e test from post-merge (Bellatrix) through all forks (post-submit only, takes longer)
go_test(
name = "go_minimal_postmerge_test",
size = "enormous",
testonly = True,
srcs = [
"component_handler_test.go",
"endtoend_setup_test.go",
"endtoend_test.go",
"minimal_postmerge_e2e_test.go",
],
args = ["-test.v"],
data = [
"//:prysm_sh",
"//cmd/beacon-chain",
"//cmd/validator",
"//config/params:custom_configs",
"//tools/bootnode",
"@com_github_ethereum_go_ethereum//cmd/geth",
"@web3signer",
],
eth_network = "minimal",
flaky = True,
shard_count = 2,
tags = [
"e2e",
"manual",
"minimal",
"requires-network",
],
deps = common_deps,
)
go_test(
name = "go_mainnet_test",
size = "large",

View File

@@ -15,7 +15,6 @@ go_library(
importpath = "github.com/OffchainLabs/prysm/v7/testing/endtoend/components/eth1",
visibility = ["//testing/endtoend:__subpackages__"],
deps = [
"//beacon-chain/startup:go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//contracts/deposit:go_default_library",
@@ -39,6 +38,8 @@ go_library(
"@com_github_ethereum_go_ethereum//ethclient/gethclient:go_default_library",
"@com_github_ethereum_go_ethereum//rpc:go_default_library",
"@com_github_holiman_uint256//:go_default_library",
"@com_github_mariusvanderwijden_fuzzyvm//filler:go_default_library",
"@com_github_mariusvanderwijden_tx_fuzz//:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@io_bazel_rules_go//go/tools/bazel:go_default_library",

View File

@@ -267,10 +267,6 @@ func (d *Depositor) txops(ctx context.Context) (*bind.TransactOpts, error) {
return nil, err
}
txo.Nonce = big.NewInt(0).SetUint64(nonce)
// Set a high gas price to ensure deposit transactions can replace any pending
// transactions from the transaction generator that may be using the same nonce.
// The transaction generator uses 1e11 (100 Gwei), so we use 2e11 (200 Gwei).
txo.GasPrice = big.NewInt(2e11)
return txo, nil
}

View File

@@ -3,7 +3,6 @@ package eth1
import (
"context"
"math/big"
"strings"
"time"
"github.com/OffchainLabs/prysm/v7/testing/endtoend/params"
@@ -32,6 +31,10 @@ var _ e2etypes.EngineProxy = (*Proxy)(nil)
// WaitForBlocks waits for a certain amount of blocks to be mined by the ETH1 chain before returning.
func WaitForBlocks(ctx context.Context, web3 *ethclient.Client, key *keystore.Key, blocksToWait uint64) error {
nonce, err := web3.PendingNonceAt(ctx, key.Address)
if err != nil {
return err
}
chainID, err := web3.NetworkID(ctx)
if err != nil {
return err
@@ -46,36 +49,19 @@ func WaitForBlocks(ctx context.Context, web3 *ethclient.Client, key *keystore.Ke
if ctx.Err() != nil {
return ctx.Err()
}
// Get fresh nonce each iteration to handle any pending transactions
nonce, err := web3.PendingNonceAt(ctx, key.Address)
if err != nil {
return err
}
gasPrice, err := web3.SuggestGasPrice(ctx)
if err != nil {
return err
}
// Bump gas price by 20% to ensure we can replace any pending transactions
gasPrice = new(big.Int).Mul(gasPrice, big.NewInt(120))
gasPrice = new(big.Int).Div(gasPrice, big.NewInt(100))
spamTX := types.NewTransaction(nonce, key.Address, big.NewInt(0), params.SpamTxGasLimit, gasPrice, []byte{})
signed, err := types.SignTx(spamTX, types.NewEIP155Signer(chainID), key.PrivateKey)
if err != nil {
return err
}
if err = web3.SendTransaction(ctx, signed); err != nil {
// If replacement error, try again with next iteration which will get fresh nonce
if strings.Contains(err.Error(), "replacement transaction underpriced") {
time.Sleep(timeGapPerMiningTX)
block, err = web3.BlockByNumber(ctx, nil)
if err != nil {
return err
}
continue
}
return err
}
nonce++
time.Sleep(timeGapPerMiningTX)
block, err = web3.BlockByNumber(ctx, nil)
if err != nil {

View File

@@ -10,7 +10,8 @@ import (
"os"
"time"
"github.com/OffchainLabs/prysm/v7/beacon-chain/startup"
"github.com/MariusVanDerWijden/FuzzyVM/filler"
txfuzz "github.com/MariusVanDerWijden/tx-fuzz"
fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams"
"github.com/OffchainLabs/prysm/v7/config/params"
"github.com/OffchainLabs/prysm/v7/crypto/rand"
@@ -34,12 +35,11 @@ const txCount = 20
var fundedAccount *keystore.Key
type TransactionGenerator struct {
keystore string
seed int64
started chan struct{}
cancel context.CancelFunc
paused bool
useLargeBlobs bool // Use large blob transactions (6 blobs per tx) for BPO testing
keystore string
seed int64
started chan struct{}
cancel context.CancelFunc
paused bool
}
func (t *TransactionGenerator) UnderlyingProcess() *os.Process {
@@ -48,8 +48,8 @@ func (t *TransactionGenerator) UnderlyingProcess() *os.Process {
return &os.Process{}
}
func NewTransactionGenerator(keystore string, seed int64, useLargeBlobs bool) *TransactionGenerator {
return &TransactionGenerator{keystore: keystore, seed: seed, useLargeBlobs: useLargeBlobs}
func NewTransactionGenerator(keystore string, seed int64) *TransactionGenerator {
return &TransactionGenerator{keystore: keystore, seed: seed}
}
func (t *TransactionGenerator) Start(ctx context.Context) error {
@@ -67,7 +67,7 @@ func (t *TransactionGenerator) Start(ctx context.Context) error {
newGen := rand.NewDeterministicGenerator()
if seed == 0 {
seed = newGen.Int63()
logrus.WithField("Seed", seed).Info("Transaction generator")
logrus.Infof("Seed for transaction generator is: %d", seed)
}
// Set seed so that all transactions can be
// deterministically generated.
@@ -86,21 +86,12 @@ func (t *TransactionGenerator) Start(ctx context.Context) error {
return err
}
fundedAccount = newKey
// Ensure funding tx is mined before generating txs that rely on balance.
// Mine 1 block using the miner key to include the funding transfer.
backend := ethclient.NewClient(client)
defer backend.Close()
if err := WaitForBlocks(ctx, backend, mineKey, 1); err != nil {
return errors.Wrap(err, "failed to mine block for funding tx")
}
// Ensure the funded account has a comfortable minimum balance for blob and fuzzed txs.
minWei := new(big.Int).Mul(big.NewInt(1000), big.NewInt(0).SetUint64(params.BeaconConfig().GweiPerEth))
minWei.Mul(minWei, big.NewInt(1e9)) // 1000 ETH in wei
if err := ensureMinBalance(ctx, client, backend, mineKey, fundedAccount, minWei); err != nil {
rnd := make([]byte, 10000)
_, err = mathRand.Read(rnd) // #nosec G404
if err != nil {
return err
}
f := filler.NewFiller(rnd)
// Broadcast Transactions every slot
txPeriod := time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second
ticker := time.NewTicker(txPeriod)
@@ -114,7 +105,7 @@ func (t *TransactionGenerator) Start(ctx context.Context) error {
continue
}
backend := ethclient.NewClient(client)
err = SendTransaction(client, mineKey.PrivateKey, gasPrice, mineKey.Address.String(), txCount, backend, false, t.useLargeBlobs)
err = SendTransaction(client, mineKey.PrivateKey, f, gasPrice, mineKey.Address.String(), txCount, backend, false)
if err != nil {
return err
}
@@ -128,7 +119,7 @@ func (s *TransactionGenerator) Started() <-chan struct{} {
return s.started
}
func SendTransaction(client *rpc.Client, key *ecdsa.PrivateKey, gasPrice *big.Int, addr string, txCount uint64, backend *ethclient.Client, al bool, useLargeBlobs bool) error {
func SendTransaction(client *rpc.Client, key *ecdsa.PrivateKey, f *filler.Filler, gasPrice *big.Int, addr string, N uint64, backend *ethclient.Client, al bool) error {
sender := common.HexToAddress(addr)
nonce, err := backend.PendingNonceAt(context.Background(), fundedAccount.Address)
if err != nil {
@@ -145,63 +136,30 @@ func SendTransaction(client *rpc.Client, key *ecdsa.PrivateKey, gasPrice *big.In
if expectedPrice.Cmp(gasPrice) > 0 {
gasPrice = expectedPrice
}
// Check if we're post-Fulu fork
clock := startup.NewClock(e2e.TestParams.CLGenesisTime, [32]byte{})
isPostFulu := clock.CurrentEpoch() >= params.BeaconConfig().FuluForkEpoch
g, _ := errgroup.WithContext(context.Background())
txs := make([]*types.Transaction, 10)
// Send blob transactions - use different versions pre/post Fulu
if isPostFulu {
logrus.Info("Sending blob transactions with cell proofs")
// Reduced from 10 to 5 to reduce load and prevent builder/EL timeouts
for index := range uint64(5) {
g.Go(func() error {
tx, err := RandomBlobCellTx(client, fundedAccount.Address, nonce+index, gasPrice, chainid, al, useLargeBlobs)
if err != nil {
return errors.Wrap(err, "Could not create blob cell tx")
}
signedTx, err := types.SignTx(tx, types.NewCancunSigner(chainid), fundedAccount.PrivateKey)
if err != nil {
return errors.Wrap(err, "Could not sign blob cell tx")
}
txs[index] = signedTx
for i := range uint64(10) {
index := i
g.Go(func() error {
tx, err := RandomBlobTx(client, f, fundedAccount.Address, nonce+index, gasPrice, chainid, al)
if err != nil {
logrus.WithError(err).Error("Could not create blob tx")
// In the event the transaction constructed is not valid, we continue with the routine
// rather than complete stop it.
//nolint:nilerr
return nil
})
}
} else {
logrus.Info("Sending blob transactions with sidecars")
// Reduced from 10 to 5 to reduce load and prevent builder/EL timeouts
for index := range uint64(5) {
g.Go(func() error {
tx, err := RandomBlobTx(client, fundedAccount.Address, nonce+index, gasPrice, chainid, al, useLargeBlobs)
if err != nil {
logrus.WithError(err).Error("Could not create blob tx")
// In the event the transaction constructed is not valid, we continue with the routine
// rather than complete stop it.
//nolint:nilerr
return nil
}
signedTx, err := types.SignTx(tx, types.NewCancunSigner(chainid), fundedAccount.PrivateKey)
if err != nil {
logrus.WithError(err).Error("Could not sign blob tx")
// We continue on in the event there is a reason we can't sign this
// transaction(unlikely).
//nolint:nilerr
return nil
}
txs[index] = signedTx
}
signedTx, err := types.SignTx(tx, types.NewCancunSigner(chainid), fundedAccount.PrivateKey)
if err != nil {
logrus.WithError(err).Error("Could not sign blob tx")
// We continue on in the event there is a reason we can't sign this
// transaction(unlikely).
//nolint:nilerr
return nil
})
}
}
txs[index] = signedTx
return nil
})
}
if err := g.Wait(); err != nil {
@@ -211,7 +169,6 @@ func SendTransaction(client *rpc.Client, key *ecdsa.PrivateKey, gasPrice *big.In
if tx == nil {
continue
}
err = backend.SendTransaction(context.Background(), tx)
if err != nil {
// Do nothing
@@ -224,20 +181,17 @@ func SendTransaction(client *rpc.Client, key *ecdsa.PrivateKey, gasPrice *big.In
return err
}
txs = make([]*types.Transaction, txCount)
for index := range txCount {
txs = make([]*types.Transaction, N)
for i := range N {
index := i
g.Go(func() error {
tx, err := randomValidTx(sender, nonce+index, gasPrice, chainid, al)
tx, err := txfuzz.RandomValidTx(client, f, sender, nonce+index, gasPrice, chainid, al)
if err != nil {
// In the event the transaction constructed is not valid, we continue with the routine
// rather than complete stop it.
//nolint:nilerr
return nil
}
// Clamp gas to avoid exceeding common EL per-tx gas caps (e.g. 16,777,216) due to EIP-7825: Transaction Gas Limit Cap
tx = clampTxGas(tx, 16_000_000)
signedTx, err := types.SignTx(tx, types.NewLondonSigner(chainid), key)
if err != nil {
// We continue on in the event there is a reason we can't sign this
@@ -283,13 +237,11 @@ func (t *TransactionGenerator) Stop() error {
return nil
}
func RandomBlobCellTx(rpc *rpc.Client, sender common.Address, nonce uint64, gasPrice, chainID *big.Int, al bool, useLargeBlobs bool) (*types.Transaction, error) {
func RandomBlobTx(rpc *rpc.Client, f *filler.Filler, sender common.Address, nonce uint64, gasPrice, chainID *big.Int, al bool) (*types.Transaction, error) {
// Set fields if non-nil
if rpc != nil {
client := ethclient.NewClient(rpc)
var err error
if gasPrice == nil {
gasPrice, err = client.SuggestGasPrice(context.Background())
if err != nil {
@@ -303,133 +255,27 @@ func RandomBlobCellTx(rpc *rpc.Client, sender common.Address, nonce uint64, gasP
}
}
}
gas := uint64(100000)
to := randomAddress()
// Generate random EVM bytecode (similar to what tx-fuzz RandomCode did)
code := generateRandomEVMCode(mathRand.Intn(128)) // #nosec G404
code := txfuzz.RandomCode(f)
value := big.NewInt(0)
if len(code) > 128 {
code = code[:128]
}
mod := 2
if al {
mod = 1
}
// Helper to get blob data based on config
getBlobData := func() ([]byte, error) {
if useLargeBlobs {
return randomBlobDataLarge()
}
return randomBlobData()
}
// #nosec G404 -- Test code uses deterministic randomness
switch mathRand.Intn(mod) {
case 0:
// Blob transaction with cell proofs (Version 1 sidecar)
tip, feecap, err := getCaps(rpc, gasPrice)
if err != nil {
return nil, errors.Wrap(err, "getCaps")
}
data, err := getBlobData()
if err != nil {
return nil, errors.Wrap(err, "getBlobData")
}
return New4844CellTx(nonce, &to, gas, chainID, tip, feecap, value, code, big.NewInt(1000000), data, make(types.AccessList, 0))
case 1:
// Blob transaction with cell proofs and access list
tx := types.NewTx(&types.LegacyTx{
Nonce: nonce,
To: &to,
Value: value,
Gas: gas,
GasPrice: gasPrice,
Data: code,
})
// Use legacy GasPrice for access list simulation to satisfy post-London requirement.
msg := ethereum.CallMsg{
From: sender,
To: tx.To(),
Gas: tx.Gas(),
GasPrice: gasPrice,
Value: tx.Value(),
Data: tx.Data(),
AccessList: nil,
}
geth := gethclient.New(rpc)
al, _, _, err := geth.CreateAccessList(context.Background(), msg)
if err != nil {
return nil, errors.Wrap(err, "CreateAccessList")
}
tip, feecap, err := getCaps(rpc, gasPrice)
if err != nil {
return nil, errors.Wrap(err, "getCaps")
}
data, err := getBlobData()
if err != nil {
return nil, errors.Wrap(err, "getBlobData")
}
return New4844CellTx(nonce, &to, gas, chainID, tip, feecap, value, code, big.NewInt(1000000), data, *al)
}
return nil, nil
}
func RandomBlobTx(rpc *rpc.Client, sender common.Address, nonce uint64, gasPrice, chainID *big.Int, al bool, useLargeBlobs bool) (*types.Transaction, error) {
// Set fields if non-nil
if rpc != nil {
client := ethclient.NewClient(rpc)
var err error
if gasPrice == nil {
gasPrice, err = client.SuggestGasPrice(context.Background())
if err != nil {
gasPrice = big.NewInt(1)
}
}
if chainID == nil {
chainID, err = client.ChainID(context.Background())
if err != nil {
chainID = big.NewInt(1)
}
}
}
gas := uint64(100000)
to := randomAddress()
// Generate random EVM bytecode (similar to what tx-fuzz RandomCode did)
code := generateRandomEVMCode(mathRand.Intn(128)) // #nosec G404
value := big.NewInt(0)
mod := 2
if al {
mod = 1
}
// Helper to get blob data based on config
getBlobData := func() ([]byte, error) {
if useLargeBlobs {
return randomBlobDataLarge()
}
return randomBlobData()
}
// #nosec G404 -- Test code uses deterministic randomness
switch mathRand.Intn(mod) {
switch f.Byte() % byte(mod) {
case 0:
// 4844 transaction without AL
tip, feecap, err := getCaps(rpc, gasPrice)
if err != nil {
return nil, errors.Wrap(err, "getCaps")
}
data, err := getBlobData()
data, err := randomBlobData()
if err != nil {
return nil, errors.Wrap(err, "getBlobData")
return nil, errors.Wrap(err, "randomBlobData")
}
return New4844Tx(nonce, &to, gas, chainID, tip, feecap, value, code, big.NewInt(1000000), data, make(types.AccessList, 0)), nil
case 1:
@@ -443,18 +289,18 @@ func RandomBlobTx(rpc *rpc.Client, sender common.Address, nonce uint64, gasPrice
Data: code,
})
// Use legacy GasPrice for access list simulation to satisfy post-London requirement.
// TODO: replace call with al, err := txfuzz.CreateAccessList(rpc, tx, sender) when txfuzz is fixed in new release
// an error occurs mentioning error="CreateAccessList: both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified"
msg := ethereum.CallMsg{
From: sender,
To: tx.To(),
Gas: tx.Gas(),
GasPrice: gasPrice,
GasPrice: tx.GasPrice(),
Value: tx.Value(),
Data: tx.Data(),
AccessList: nil,
}
geth := gethclient.New(rpc)
al, _, _, err := geth.CreateAccessList(context.Background(), msg)
if err != nil {
return nil, errors.Wrap(err, "CreateAccessList")
@@ -463,52 +309,15 @@ func RandomBlobTx(rpc *rpc.Client, sender common.Address, nonce uint64, gasPrice
if err != nil {
return nil, errors.Wrap(err, "getCaps")
}
data, err := getBlobData()
data, err := randomBlobData()
if err != nil {
return nil, errors.Wrap(err, "getBlobData")
return nil, errors.Wrap(err, "randomBlobData")
}
return New4844Tx(nonce, &to, gas, chainID, tip, feecap, value, code, big.NewInt(1000000), data, *al), nil
}
return nil, errors.New("asdf")
}
func New4844CellTx(nonce uint64, to *common.Address, gasLimit uint64, chainID, tip, feeCap, value *big.Int, code []byte, blobFeeCap *big.Int, blobData []byte, al types.AccessList) (*types.Transaction, error) {
blobs, comms, _, versionedHashes, err := EncodeBlobs(blobData)
if err != nil {
return nil, errors.Wrap(err, "failed to encode blobs")
}
// Create a Version 0 sidecar first
sidecar := &types.BlobTxSidecar{
Version: types.BlobSidecarVersion0,
Blobs: blobs,
Commitments: comms,
Proofs: make([]kzg4844.Proof, len(blobs)), // Placeholder, will be replaced by ToV1
}
// Convert to Version 1 which will compute and attach cell proofs
if err := sidecar.ToV1(); err != nil {
return nil, errors.Wrap(err, "failed to convert sidecar to V1")
}
tx := types.NewTx(&types.BlobTx{
ChainID: uint256.MustFromBig(chainID),
Nonce: nonce,
GasTipCap: uint256.MustFromBig(tip),
GasFeeCap: uint256.MustFromBig(feeCap),
Gas: gasLimit,
To: *to,
Value: uint256.MustFromBig(value),
Data: code,
AccessList: al,
BlobFeeCap: uint256.MustFromBig(blobFeeCap),
BlobHashes: versionedHashes,
Sidecar: sidecar,
})
return tx, nil
}
func New4844Tx(nonce uint64, to *common.Address, gasLimit uint64, chainID, tip, feeCap, value *big.Int, code []byte, blobFeeCap *big.Int, blobData []byte, al types.AccessList) *types.Transaction {
blobs, comms, proofs, versionedHashes, err := EncodeBlobs(blobData)
if err != nil {
@@ -535,91 +344,15 @@ func New4844Tx(nonce uint64, to *common.Address, gasLimit uint64, chainID, tip,
return tx
}
// clampTxGas returns a copy of tx with Gas reduced to cap if it exceeds cap.
// This avoids EL errors like "transaction gas limit too high" on networks with
// per-transaction gas caps (commonly ~16,777,216).
func clampTxGas(tx *types.Transaction, gasCap uint64) *types.Transaction {
if tx == nil || tx.Gas() <= gasCap {
return tx
}
to := tx.To()
switch tx.Type() {
case types.LegacyTxType:
return types.NewTx(&types.LegacyTx{
Nonce: tx.Nonce(),
To: to,
Value: tx.Value(),
Gas: gasCap,
GasPrice: tx.GasPrice(),
Data: tx.Data(),
})
case types.AccessListTxType:
return types.NewTx(&types.AccessListTx{
ChainID: tx.ChainId(),
Nonce: tx.Nonce(),
To: to,
Value: tx.Value(),
Gas: gasCap,
GasPrice: tx.GasPrice(),
Data: tx.Data(),
AccessList: tx.AccessList(),
})
case types.DynamicFeeTxType:
return types.NewTx(&types.DynamicFeeTx{
ChainID: tx.ChainId(),
Nonce: tx.Nonce(),
To: to,
Value: tx.Value(),
Gas: gasCap,
Data: tx.Data(),
AccessList: tx.AccessList(),
GasTipCap: tx.GasTipCap(),
GasFeeCap: tx.GasFeeCap(),
})
case types.BlobTxType:
// Leave blob txs unchanged here; blob tx construction paths set gas explicitly.
return tx
default:
return tx
}
}
// ensureMinBalance tops up dest account from miner if its balance is below minWei.
func ensureMinBalance(ctx context.Context, rpcCli *rpc.Client, backend *ethclient.Client, minerKey, destKey *keystore.Key, minWei *big.Int) error {
bal, err := backend.BalanceAt(ctx, destKey.Address, nil)
if err != nil {
return err
}
if bal.Cmp(minWei) >= 0 {
return nil
}
if err := fundAccount(rpcCli, minerKey, destKey); err != nil {
return err
}
if err := WaitForBlocks(ctx, backend, minerKey, 1); err != nil {
return errors.Wrap(err, "failed to mine block for top-up tx")
}
return nil
}
func encodeBlobs(data []byte) []kzg4844.Blob {
blobs := []kzg4844.Blob{{}}
blobIndex := 0
fieldIndex := -1
numOfElems := fieldparams.BlobLength / 32
// Allow up to 6 blobs per transaction to properly test BPO limits.
// With 10 blob txs per slot × 6 blobs = 60 max blobs submitted,
// which exceeds the highest BPO limit (21) and ensures we can hit it.
const maxBlobsPerTx = 6
for i := 0; i < len(data); i += 31 {
fieldIndex++
if fieldIndex == numOfElems {
if blobIndex >= maxBlobsPerTx-1 {
if blobIndex >= 1 {
break
}
blobs = append(blobs, kzg4844.Blob{})
@@ -672,7 +405,6 @@ func kZGToVersionedHash(kzg kzg4844.Commitment) common.Hash {
}
func randomBlobData() ([]byte, error) {
// Generate random data for up to 1 blob. This is used for pre-Fulu tests.
size := mathRand.Intn(fieldparams.BlobSize) // #nosec G404
data := make([]byte, size)
n, err := mathRand.Read(data) // #nosec G404
@@ -685,29 +417,6 @@ func randomBlobData() ([]byte, error) {
return data, nil
}
// randomBlobDataLarge generates 6 blobs worth of data for BPO testing.
// This is used post-Fulu to ensure we can test increased blob limits.
// With 5 blob txs per slot × 6 blobs = 30 max blobs submitted,
// which exceeds the highest BPO limit (21) and ensures we can hit it.
// The data is mostly zeros with only the first 1KB randomized for uniqueness,
// which is sufficient for testing without the overhead of generating ~786KB of random data.
func randomBlobDataLarge() ([]byte, error) {
const numBlobs = 6
size := (numBlobs-1)*fieldparams.BlobSize + 1
data := make([]byte, size) // Zero-initialized by Go
// Only randomize first 1KB for uniqueness - no need for full randomness in tests
const randomSize = 1024
n, err := mathRand.Read(data[:randomSize]) // #nosec G404
if err != nil {
return nil, err
}
if n != randomSize {
return nil, fmt.Errorf("could not create random blob data: %w", err)
}
return data, nil
}
func randomAddress() common.Address {
rNum := mathRand.Int31n(5) // #nosec G404
switch rNum {
@@ -759,8 +468,7 @@ func fundAccount(client *rpc.Client, sourceKey, destKey *keystore.Key) error {
if err != nil {
return err
}
// Increased funding to 100 million ETH to handle extended test runs with blob transactions
val, ok := big.NewInt(0).SetString("100000000000000000000000000", 10)
val, ok := big.NewInt(0).SetString("10000000000000000000000000", 10)
if !ok {
return errors.New("could not set big int for value")
}
@@ -771,132 +479,3 @@ func fundAccount(client *rpc.Client, sourceKey, destKey *keystore.Key) error {
}
return backend.SendTransaction(context.Background(), signedTx)
}
// generateRandomEVMCode generates random but valid-looking EVM bytecode
// This mimics what tx-fuzz's RandomCode did (which used FuzzyVM's generator)
func generateRandomEVMCode(maxLen int) []byte {
if maxLen == 0 {
return []byte{}
}
// Common EVM opcodes that are safe for testing
// Including: PUSH, DUP, SWAP, arithmetic, logic, and STOP
safeOpcodes := []byte{
0x00, // STOP
0x01, // ADD
0x02, // MUL
0x03, // SUB
0x04, // DIV
0x10, // LT
0x11, // GT
0x14, // EQ
0x16, // AND
0x17, // OR
0x18, // XOR
0x50, // POP
0x52, // MSTORE
0x54, // SLOAD
0x55, // SSTORE
0x56, // JUMP
0x57, // JUMPI
0x58, // PC
0x59, // MSIZE
0x5A, // GAS
0x60, // PUSH1
0x80, // DUP1
0x90, // SWAP1
}
code := make([]byte, 0, maxLen)
for i := 0; i < maxLen; i++ {
opcode := safeOpcodes[mathRand.Intn(len(safeOpcodes))] // #nosec G404
code = append(code, opcode)
// If PUSH1, add a random byte
if opcode == 0x60 && i+1 < maxLen {
code = append(code, byte(mathRand.Intn(256))) // #nosec G404
i++
}
}
return code
}
// randomValidTx generates a random valid transaction
// This replaces tx-fuzz's RandomValidTx functionality
func randomValidTx(sender common.Address, nonce uint64, gasPrice, chainID *big.Int, forceAccessList bool) (*types.Transaction, error) {
gas := uint64(21000 + mathRand.Intn(100000)) // #nosec G404
to := randomAddress()
code := generateRandomEVMCode(mathRand.Intn(256)) // #nosec G404
value := big.NewInt(0)
// Randomly choose transaction type
// 0: Legacy, 1: AccessList, 2: DynamicFee
txType := mathRand.Intn(3) // #nosec G404
if forceAccessList {
txType = 1 // Force AccessList type
}
switch txType {
case 0:
// Legacy transaction
return types.NewTx(&types.LegacyTx{
Nonce: nonce,
To: &to,
Value: value,
Gas: gas,
GasPrice: gasPrice,
Data: code,
}), nil
case 1:
// AccessList transaction
accessList := make(types.AccessList, 0)
// Optionally add some random access list entries
// #nosec G404 -- Test code uses deterministic randomness
if mathRand.Intn(2) == 0 {
// #nosec G404 -- Test code uses deterministic randomness
numEntries := mathRand.Intn(3) + 1
for range numEntries {
addr := randomAddress()
storageKeys := make([]common.Hash, mathRand.Intn(3)) // #nosec G404
for j := range storageKeys {
b := make([]byte, 32)
_, _ = mathRand.Read(b) // #nosec G404
storageKeys[j] = common.BytesToHash(b)
}
accessList = append(accessList, types.AccessTuple{
Address: addr,
StorageKeys: storageKeys,
})
}
}
return types.NewTx(&types.AccessListTx{
ChainID: chainID,
Nonce: nonce,
To: &to,
Value: value,
Gas: gas,
GasPrice: gasPrice,
Data: code,
AccessList: accessList,
}), nil
case 2:
// DynamicFee transaction (EIP-1559)
tip := new(big.Int).Div(gasPrice, big.NewInt(10)) // 10% tip
feeCap := new(big.Int).Add(gasPrice, tip)
accessList := make(types.AccessList, 0)
return types.NewTx(&types.DynamicFeeTx{
ChainID: chainID,
Nonce: nonce,
To: &to,
Value: value,
Gas: gas,
GasTipCap: tip,
GasFeeCap: feeCap,
Data: code,
AccessList: accessList,
}), nil
}
return nil, errors.New("invalid transaction type")
}

View File

@@ -21,7 +21,7 @@ func e2eMinimal(t *testing.T, cfg *params.BeaconChainConfig, cfgo ...types.E2ECo
require.NoError(t, e2eParams.Init(t, e2eParams.StandardBeaconCount))
var err error
epochsToRun := 18
epochsToRun := 16
epochStr, longRunning := os.LookupEnv("E2E_EPOCHS")
if longRunning {
epochsToRun, err = strconv.Atoi(epochStr)
@@ -35,10 +35,6 @@ func e2eMinimal(t *testing.T, cfg *params.BeaconChainConfig, cfgo ...types.E2ECo
}
tracingPort := e2eParams.TestParams.Ports.JaegerTracingPort
tracingEndpoint := fmt.Sprintf("127.0.0.1:%d", tracingPort)
// Default exit epoch used for voluntary exit tests.
// Can be overridden via WithExitEpoch option for shorter test runs.
exitEpoch := primitives.Epoch(7)
evals := []types.Evaluator{
ev.PeersConnect,
ev.HealthzCheck,
@@ -48,7 +44,10 @@ func e2eMinimal(t *testing.T, cfg *params.BeaconChainConfig, cfgo ...types.E2ECo
ev.FinalizationOccurs(3),
ev.VerifyBlockGraffiti,
ev.PeersCheck,
// Exit-related evaluators are added after processing options to allow custom exit epoch.
ev.ProposeVoluntaryExit,
ev.ValidatorsHaveExited,
ev.SubmitWithdrawal,
ev.ValidatorsHaveWithdrawn,
ev.ProcessesDepositsInBlocks,
ev.ActivatesDepositedValidators,
ev.DepositedValidatorsAreActive,
@@ -65,11 +64,6 @@ func e2eMinimal(t *testing.T, cfg *params.BeaconChainConfig, cfgo ...types.E2ECo
evals = addIfForkSet(evals, cfg.CapellaForkEpoch, ev.CapellaForkTransition)
evals = addIfForkSet(evals, cfg.DenebForkEpoch, ev.DenebForkTransition)
evals = addIfForkSet(evals, cfg.ElectraForkEpoch, ev.ElectraForkTransition)
evals = addIfForkSet(evals, cfg.FuluForkEpoch, ev.FuluForkTransition)
// Blob evaluators - run from Deneb onwards
evals = addIfForkSet(evals, cfg.DenebForkEpoch, ev.BlobsIncludedInBlocks)
// BPO (Blob Parameter Optimization) evaluator - runs from Fulu onwards
evals = addIfForkSet(evals, cfg.FuluForkEpoch, ev.BlobLimitsRespected)
testConfig := &types.E2EConfig{
BeaconFlags: []string{
@@ -93,18 +87,6 @@ func e2eMinimal(t *testing.T, cfg *params.BeaconChainConfig, cfgo ...types.E2ECo
for _, o := range cfgo {
o(testConfig)
}
// Add exit-related evaluators using custom exit epoch if configured, otherwise use default.
if testConfig.ExitEpoch > 0 {
exitEpoch = testConfig.ExitEpoch
}
testConfig.Evaluators = append(testConfig.Evaluators,
ev.ProposeVoluntaryExitAtEpoch(exitEpoch),
ev.ValidatorsHaveExitedAtEpoch(exitEpoch+1),
ev.SubmitWithdrawalAtEpoch(exitEpoch+1),
ev.ValidatorsHaveWithdrawnAfterExitAtEpoch(exitEpoch),
)
if testConfig.UseBuilder {
testConfig.Evaluators = append(testConfig.Evaluators, ev.BuilderIsActive)
}
@@ -158,11 +140,6 @@ func e2eMainnet(t *testing.T, usePrysmSh, useMultiClient bool, cfg *params.Beaco
evals = addIfForkSet(evals, cfg.CapellaForkEpoch, ev.CapellaForkTransition)
evals = addIfForkSet(evals, cfg.DenebForkEpoch, ev.DenebForkTransition)
evals = addIfForkSet(evals, cfg.ElectraForkEpoch, ev.ElectraForkTransition)
evals = addIfForkSet(evals, cfg.FuluForkEpoch, ev.FuluForkTransition)
// Blob evaluators - run from Deneb onwards
evals = addIfForkSet(evals, cfg.DenebForkEpoch, ev.BlobsIncludedInBlocks)
// BPO (Blob Parameter Optimization) evaluator - runs from Fulu onwards
evals = addIfForkSet(evals, cfg.FuluForkEpoch, ev.BlobLimitsRespected)
testConfig := &types.E2EConfig{
BeaconFlags: []string{
@@ -231,11 +208,6 @@ func scenarioEvals(cfg *params.BeaconChainConfig) []types.Evaluator {
evals = addIfForkSet(evals, cfg.CapellaForkEpoch, ev.CapellaForkTransition)
evals = addIfForkSet(evals, cfg.DenebForkEpoch, ev.DenebForkTransition)
evals = addIfForkSet(evals, cfg.ElectraForkEpoch, ev.ElectraForkTransition)
evals = addIfForkSet(evals, cfg.FuluForkEpoch, ev.FuluForkTransition)
// Blob evaluators - run from Deneb onwards
evals = addIfForkSet(evals, cfg.DenebForkEpoch, ev.BlobsIncludedInBlocks)
// BPO (Blob Parameter Optimization) evaluator - runs from Fulu onwards
evals = addIfForkSet(evals, cfg.FuluForkEpoch, ev.BlobLimitsRespected)
return evals
}
@@ -257,10 +229,5 @@ func scenarioEvalsMulti(cfg *params.BeaconChainConfig) []types.Evaluator {
evals = addIfForkSet(evals, cfg.CapellaForkEpoch, ev.CapellaForkTransition)
evals = addIfForkSet(evals, cfg.DenebForkEpoch, ev.DenebForkTransition)
evals = addIfForkSet(evals, cfg.ElectraForkEpoch, ev.ElectraForkTransition)
evals = addIfForkSet(evals, cfg.FuluForkEpoch, ev.FuluForkTransition)
// Blob evaluators - run from Deneb onwards
evals = addIfForkSet(evals, cfg.DenebForkEpoch, ev.BlobsIncludedInBlocks)
// BPO (Blob Parameter Optimization) evaluator - runs from Fulu onwards
evals = addIfForkSet(evals, cfg.FuluForkEpoch, ev.BlobLimitsRespected)
return evals
}

View File

@@ -225,9 +225,9 @@ func (r *testRunner) testDepositsAndTx(ctx context.Context, g *errgroup.Group,
if err := helpers.ComponentsStarted(ctx, []e2etypes.ComponentRunner{r.depositor}); err != nil {
return errors.Wrap(err, "testDepositsAndTx unable to run, depositor did not Start")
}
go func() {
if r.config.TestDeposits {
log.Info("Running deposit tests")
go func() {
if r.config.TestDeposits {
log.Info("Running deposit tests")
// The validators with an index < minGenesisActiveCount all have deposits already from the chain start.
// Skip all of those chain start validators by seeking to minGenesisActiveCount in the validator list
// for further deposit testing.
@@ -238,12 +238,9 @@ func (r *testRunner) testDepositsAndTx(ctx context.Context, g *errgroup.Group,
r.t.Error(errors.Wrap(err, "depositor.SendAndMine failed"))
}
}
}
// Only generate background transactions when relevant for the test.
if r.config.TestDeposits || r.config.TestFeature || r.config.UseBuilder {
r.testTxGeneration(ctx, g, keystorePath, []e2etypes.ComponentRunner{})
}
}()
}
r.testTxGeneration(ctx, g, keystorePath, []e2etypes.ComponentRunner{})
}()
if r.config.TestDeposits {
return depositCheckValidator.Start(ctx)
}
@@ -252,7 +249,7 @@ func (r *testRunner) testDepositsAndTx(ctx context.Context, g *errgroup.Group,
}
func (r *testRunner) testTxGeneration(ctx context.Context, g *errgroup.Group, keystorePath string, requiredNodes []e2etypes.ComponentRunner) {
txGenerator := eth1.NewTransactionGenerator(keystorePath, r.config.Seed, r.config.UseLargeBlobs)
txGenerator := eth1.NewTransactionGenerator(keystorePath, r.config.Seed)
r.comHandler.txGen = txGenerator
g.Go(func() error {
if err := helpers.ComponentsStarted(ctx, requiredNodes); err != nil {

View File

@@ -4,7 +4,6 @@ go_library(
name = "go_default_library",
testonly = True,
srcs = [
"blob_limits.go",
"builder.go",
"data.go",
"execution_engine.go",

View File

@@ -303,7 +303,7 @@ var (
}())),
"/validator/duties/sync/{param1}": newMetadata[structs.GetSyncCommitteeDutiesResponse](
v1PathTemplate,
withStart(params.E2ETestConfig().AltairForkEpoch),
withStart(params.AltairE2EForkEpoch),
withParams(func(currentEpoch primitives.Epoch) []string {
return []string{fmt.Sprintf("%v", currentEpoch)}
}),

View File

@@ -1,246 +0,0 @@
package evaluators
import (
"context"
"github.com/OffchainLabs/prysm/v7/config/params"
"github.com/OffchainLabs/prysm/v7/consensus-types/blocks"
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
"github.com/OffchainLabs/prysm/v7/runtime/version"
"github.com/OffchainLabs/prysm/v7/testing/endtoend/policies"
e2etypes "github.com/OffchainLabs/prysm/v7/testing/endtoend/types"
"github.com/OffchainLabs/prysm/v7/time/slots"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"google.golang.org/grpc"
"google.golang.org/protobuf/types/known/emptypb"
)
// Minimum blob utilization percentage required to pass the evaluator.
// We expect the transaction generator to produce enough blobs to hit at least this threshold.
const minBlobUtilizationPercent = 50
// BlobsIncludedInBlocks verifies that blocks contain blobs and that the blob count
// does not exceed the maximum allowed for the epoch (respecting BPO schedule).
var BlobsIncludedInBlocks = e2etypes.Evaluator{
Name: "blobs_included_in_blocks_epoch_%d",
Policy: func(currentEpoch primitives.Epoch) bool {
// Only run from Deneb onwards
return policies.OnwardsNthEpoch(params.BeaconConfig().DenebForkEpoch)(currentEpoch)
},
Evaluation: blobsIncludedInBlocks,
}
// BlobLimitsRespected verifies that the BPO (Blob Parameter Optimization) limits
// are correctly enforced after Fulu fork, checking that:
// 1. Blocks don't exceed MaxBlobsPerBlock for their respective epoch
// 2. We're utilizing the increased capacity (at least 50% utilization)
// 3. When BPO activates (limit increases), blocks actually use the new higher limit
var BlobLimitsRespected = e2etypes.Evaluator{
Name: "blob_limits_respected_epoch_%d",
Policy: func(currentEpoch primitives.Epoch) bool {
// Only run from Fulu onwards where BPO is active
return policies.OnwardsNthEpoch(params.BeaconConfig().FuluForkEpoch)(currentEpoch)
},
Evaluation: blobLimitsRespected,
}
func blobsIncludedInBlocks(_ *e2etypes.EvaluationContext, conns ...*grpc.ClientConn) error {
conn := conns[0]
client := ethpb.NewBeaconChainClient(conn)
chainHead, err := client.GetChainHead(context.Background(), &emptypb.Empty{})
if err != nil {
return errors.Wrap(err, "failed to get chain head")
}
// Check blocks from the previous epoch
epoch := chainHead.HeadEpoch
if epoch > 0 {
epoch = epoch - 1
}
// Skip if we're before Deneb
if epoch < params.BeaconConfig().DenebForkEpoch {
return nil
}
req := &ethpb.ListBlocksRequest{QueryFilter: &ethpb.ListBlocksRequest_Epoch{Epoch: epoch}}
blks, err := client.ListBeaconBlocks(context.Background(), req)
if err != nil {
return errors.Wrap(err, "failed to get blocks from beacon-chain")
}
blocksWithBlobs := 0
for _, ctr := range blks.BlockContainers {
blk, err := blocks.BeaconBlockContainerToSignedBeaconBlock(ctr)
if err != nil {
return errors.Wrap(err, "failed to convert block container to signed beacon block")
}
if blk == nil || blk.IsNil() {
continue
}
// Skip blocks before Deneb
if blk.Version() < version.Deneb {
continue
}
commitments, err := blk.Block().Body().BlobKzgCommitments()
if err != nil {
return errors.Wrap(err, "failed to get blob kzg commitments")
}
if len(commitments) > 0 {
blocksWithBlobs++
}
}
// We expect at least some blocks to have blobs since we're sending blob transactions
if blocksWithBlobs == 0 {
return errors.Errorf("no blocks with blobs found in epoch %d", epoch)
}
return nil
}
func blobLimitsRespected(_ *e2etypes.EvaluationContext, conns ...*grpc.ClientConn) error {
conn := conns[0]
nodeClient := ethpb.NewNodeClient(conn)
beaconClient := ethpb.NewBeaconChainClient(conn)
genesis, err := nodeClient.GetGenesis(context.Background(), &emptypb.Empty{})
if err != nil {
return errors.Wrap(err, "failed to get genesis")
}
currSlot := slots.CurrentSlot(genesis.GenesisTime.AsTime())
currEpoch := slots.ToEpoch(currSlot)
// Check the previous epoch to ensure blocks are finalized
epochToCheck := currEpoch
if epochToCheck > 0 {
epochToCheck = epochToCheck - 1
}
// Skip if we're before Fulu
if epochToCheck < params.BeaconConfig().FuluForkEpoch {
return nil
}
req := &ethpb.ListBlocksRequest{QueryFilter: &ethpb.ListBlocksRequest_Epoch{Epoch: epochToCheck}}
blks, err := beaconClient.ListBeaconBlocks(context.Background(), req)
if err != nil {
return errors.Wrap(err, "failed to get blocks from beacon-chain")
}
cfg := params.BeaconConfig()
maxBlobsForEpoch := cfg.MaxBlobsPerBlockAtEpoch(epochToCheck)
// Get the previous epoch's limit to detect BPO transitions
var prevEpochMaxBlobs int
if epochToCheck > 0 {
prevEpochMaxBlobs = cfg.MaxBlobsPerBlockAtEpoch(epochToCheck - 1)
}
// Check if this is a BPO transition epoch (limit increased from previous epoch)
isBPOTransitionEpoch := maxBlobsForEpoch > prevEpochMaxBlobs
var totalBlobs int
var maxBlobsInBlock int
var blockCount int
for _, ctr := range blks.BlockContainers {
blk, err := blocks.BeaconBlockContainerToSignedBeaconBlock(ctr)
if err != nil {
return errors.Wrap(err, "failed to convert block container to signed beacon block")
}
if blk == nil || blk.IsNil() {
continue
}
// Skip blocks before Deneb (shouldn't happen if we're checking post-Fulu epochs)
if blk.Version() < version.Deneb {
continue
}
slot := blk.Block().Slot()
blockEpoch := slots.ToEpoch(slot)
commitments, err := blk.Block().Body().BlobKzgCommitments()
if err != nil {
return errors.Wrap(err, "failed to get blob kzg commitments")
}
blobCount := len(commitments)
// Verify we don't exceed the limit
if blobCount > maxBlobsForEpoch {
return errors.Errorf(
"block at slot %d (epoch %d) has %d blobs, exceeding max allowed %d for this epoch",
slot, blockEpoch, blobCount, maxBlobsForEpoch,
)
}
totalBlobs += blobCount
blockCount++
if blobCount > maxBlobsInBlock {
maxBlobsInBlock = blobCount
}
}
// Calculate utilization
if blockCount == 0 {
return errors.Errorf("no blocks found in epoch %d", epochToCheck)
}
utilizationPercent := (maxBlobsInBlock * 100) / maxBlobsForEpoch
logrus.WithFields(logrus.Fields{
"epoch": epochToCheck,
"maxBlobsForEpoch": maxBlobsForEpoch,
"prevEpochMaxBlobs": prevEpochMaxBlobs,
"maxBlobsInBlock": maxBlobsInBlock,
"totalBlobs": totalBlobs,
"blockCount": blockCount,
"utilizationPercent": utilizationPercent,
"isBPOTransitionEpoch": isBPOTransitionEpoch,
}).Info("Blob utilization stats for epoch")
// For BPO transition epochs, verify that BPO actually activated by checking
// that at least one block exceeded the previous epoch's limit
if isBPOTransitionEpoch {
if maxBlobsInBlock <= prevEpochMaxBlobs {
return errors.Errorf(
"BPO transition at epoch %d: limit increased from %d to %d, but no block exceeded the old limit. "+
"Max blobs in any block was %d. This indicates BPO may not be working correctly or "+
"the transaction generator is not producing enough blobs to test the increased limit.",
epochToCheck, prevEpochMaxBlobs, maxBlobsForEpoch, maxBlobsInBlock,
)
}
logrus.WithFields(logrus.Fields{
"epoch": epochToCheck,
"prevLimit": prevEpochMaxBlobs,
"newLimit": maxBlobsForEpoch,
"maxBlobsInBlock": maxBlobsInBlock,
}).Info("BPO activation verified: blocks are using capacity beyond previous limit")
}
// For all BPO epochs (where limit > Electra's 9), verify we're utilizing the capacity
electraMax := cfg.DeprecatedMaxBlobsPerBlockElectra
if maxBlobsForEpoch > electraMax {
if utilizationPercent < minBlobUtilizationPercent {
return errors.Errorf(
"BPO epoch %d has max blobs %d but only achieved %d%% utilization (max blobs in any block: %d). "+
"Expected at least %d%% utilization to verify BPO is working. "+
"This may indicate the transaction generator is not producing enough blobs.",
epochToCheck, maxBlobsForEpoch, utilizationPercent, maxBlobsInBlock, minBlobUtilizationPercent,
)
}
}
return nil
}

View File

@@ -11,6 +11,7 @@ import (
"github.com/OffchainLabs/prysm/v7/testing/endtoend/policies"
e2etypes "github.com/OffchainLabs/prysm/v7/testing/endtoend/types"
"github.com/OffchainLabs/prysm/v7/time/slots"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/pkg/errors"
"google.golang.org/grpc"
"google.golang.org/protobuf/types/known/emptypb"
@@ -26,11 +27,6 @@ var BuilderIsActive = e2etypes.Evaluator{
Evaluation: builderActive,
}
// maxNonBuilderBlocks is the maximum number of blocks that can be built locally
// instead of by the builder before the test fails. This allows tolerance for
// occasional builder timeouts or failures.
const maxNonBuilderBlocks = 2
func builderActive(_ *e2etypes.EvaluationContext, conns ...*grpc.ClientConn) error {
conn := conns[0]
client := ethpb.NewNodeClient(conn)
@@ -53,10 +49,6 @@ func builderActive(_ *e2etypes.EvaluationContext, conns ...*grpc.ClientConn) err
if err != nil {
return err
}
nonBuilderBlocks := 0
builderBlocks := 0
blockCtrs, err := beaconClient.ListBeaconBlocks(context.Background(), &ethpb.ListBlocksRequest{QueryFilter: &ethpb.ListBlocksRequest_Epoch{Epoch: lowestBound}})
if err != nil {
return errors.Wrap(err, "failed to get beacon blocks")
@@ -92,18 +84,13 @@ func builderActive(_ *e2etypes.EvaluationContext, conns ...*grpc.ClientConn) err
continue
}
if string(execPayload.ExtraData()) != "prysm-builder" {
nonBuilderBlocks++
continue
return errors.Errorf("%s block with slot %d was not built by the builder. It has an extra data of %s and txRoot of %s", version.String(b.Version()), b.Block().Slot(), string(execPayload.ExtraData()), hexutil.Encode(txRoot))
}
builderBlocks++
if execPayload.GasLimit() == 0 {
return errors.Errorf("%s block with slot %d has a gas limit of 0, when it should be in the 30M range", version.String(b.Version()), b.Block().Slot())
}
}
if lowestBound == currEpoch {
if nonBuilderBlocks > maxNonBuilderBlocks {
return errors.Errorf("too many non-builder blocks: %d (max allowed: %d), builder blocks: %d", nonBuilderBlocks, maxNonBuilderBlocks, builderBlocks)
}
return nil
}
blockCtrs, err = beaconClient.ListBeaconBlocks(context.Background(), &ethpb.ListBlocksRequest{QueryFilter: &ethpb.ListBlocksRequest_Epoch{Epoch: currEpoch}})
@@ -140,16 +127,11 @@ func builderActive(_ *e2etypes.EvaluationContext, conns ...*grpc.ClientConn) err
continue
}
if string(execPayload.ExtraData()) != "prysm-builder" {
nonBuilderBlocks++
continue
return errors.Errorf("%s block with slot %d was not built by the builder. It has an extra data of %s and txRoot of %s", version.String(b.Version()), b.Block().Slot(), string(execPayload.ExtraData()), hexutil.Encode(txRoot))
}
builderBlocks++
if execPayload.GasLimit() == 0 {
return errors.Errorf("%s block with slot %d has a gas limit of 0, when it should be in the 30M range", version.String(b.Version()), b.Block().Slot())
}
}
if nonBuilderBlocks > maxNonBuilderBlocks {
return errors.Errorf("too many non-builder blocks: %d (max allowed: %d), builder blocks: %d", nonBuilderBlocks, maxNonBuilderBlocks, builderBlocks)
}
return nil
}

View File

@@ -88,28 +88,11 @@ var ElectraForkTransition = e2etypes.Evaluator{
Evaluation: electraForkOccurs,
}
// FuluForkTransition ensures that the fulu hard fork has occurred successfully
var FuluForkTransition = e2etypes.Evaluator{
Name: "fulu_fork_transition_%d",
Policy: func(e primitives.Epoch) bool {
// Only run if we started before Fulu
if e2etypes.GenesisFork() >= version.Fulu {
return false
}
fEpoch := params.BeaconConfig().FuluForkEpoch
return policies.OnEpoch(fEpoch)(e)
},
Evaluation: fuluForkOccurs,
}
func altairForkOccurs(_ *e2etypes.EvaluationContext, conns ...*grpc.ClientConn) error {
conn := conns[0]
client := ethpb.NewBeaconNodeValidatorClient(conn)
ctx, cancel := context.WithTimeout(context.Background(), streamDeadline)
defer cancel()
stream, err := client.StreamBlocksAltair(ctx, &ethpb.StreamBlocksRequest{VerifiedOnly: true})
if err != nil {
return errors.Wrap(err, "failed to get stream")
@@ -118,7 +101,6 @@ func altairForkOccurs(_ *e2etypes.EvaluationContext, conns ...*grpc.ClientConn)
if err != nil {
return err
}
if errors.Is(ctx.Err(), context.Canceled) {
return errors.New("context canceled prematurely")
}
@@ -139,24 +121,20 @@ func altairForkOccurs(_ *e2etypes.EvaluationContext, conns ...*grpc.ClientConn)
if err != nil {
return err
}
if err := blocks.BeaconBlockIsNil(blk); err != nil {
return err
}
if blk.Block().Slot() < fSlot {
return errors.Errorf("wanted a block >= %d but received %d", fSlot, blk.Block().Slot())
}
return nil
}
func bellatrixForkOccurs(_ *e2etypes.EvaluationContext, conns ...*grpc.ClientConn) error {
conn := conns[0]
client := ethpb.NewBeaconNodeValidatorClient(conn)
ctx, cancel := context.WithTimeout(context.Background(), streamDeadline)
defer cancel()
stream, err := client.StreamBlocksAltair(ctx, &ethpb.StreamBlocksRequest{VerifiedOnly: true})
if err != nil {
return errors.Wrap(err, "failed to get stream")
@@ -165,7 +143,6 @@ func bellatrixForkOccurs(_ *e2etypes.EvaluationContext, conns ...*grpc.ClientCon
if err != nil {
return err
}
if errors.Is(ctx.Err(), context.Canceled) {
return errors.New("context canceled prematurely")
}
@@ -189,7 +166,6 @@ func bellatrixForkOccurs(_ *e2etypes.EvaluationContext, conns ...*grpc.ClientCon
if err != nil {
return err
}
if err := blocks.BeaconBlockIsNil(blk); err != nil {
return err
}
@@ -212,7 +188,6 @@ func capellaForkOccurs(_ *e2etypes.EvaluationContext, conns ...*grpc.ClientConn)
if err != nil {
return err
}
if errors.Is(ctx.Err(), context.Canceled) {
return errors.New("context canceled prematurely")
}
@@ -234,7 +209,6 @@ func capellaForkOccurs(_ *e2etypes.EvaluationContext, conns ...*grpc.ClientConn)
if err != nil {
return err
}
if err := blocks.BeaconBlockIsNil(blk); err != nil {
return err
}
@@ -257,7 +231,6 @@ func denebForkOccurs(_ *e2etypes.EvaluationContext, conns ...*grpc.ClientConn) e
if err != nil {
return err
}
if errors.Is(ctx.Err(), context.Canceled) {
return errors.New("context canceled prematurely")
}
@@ -279,7 +252,6 @@ func denebForkOccurs(_ *e2etypes.EvaluationContext, conns ...*grpc.ClientConn) e
if err != nil {
return err
}
if err := blocks.BeaconBlockIsNil(blk); err != nil {
return err
}
@@ -302,7 +274,6 @@ func electraForkOccurs(_ *e2etypes.EvaluationContext, conns ...*grpc.ClientConn)
if err != nil {
return err
}
if errors.Is(ctx.Err(), context.Canceled) {
return errors.New("context canceled prematurely")
}
@@ -324,7 +295,6 @@ func electraForkOccurs(_ *e2etypes.EvaluationContext, conns ...*grpc.ClientConn)
if err != nil {
return err
}
if err := blocks.BeaconBlockIsNil(blk); err != nil {
return err
}
@@ -333,57 +303,3 @@ func electraForkOccurs(_ *e2etypes.EvaluationContext, conns ...*grpc.ClientConn)
}
return nil
}
func fuluForkOccurs(_ *e2etypes.EvaluationContext, conns ...*grpc.ClientConn) error {
conn := conns[0]
client := ethpb.NewBeaconNodeValidatorClient(conn)
ctx, cancel := context.WithTimeout(context.Background(), streamDeadline)
defer cancel()
stream, err := client.StreamBlocksAltair(ctx, &ethpb.StreamBlocksRequest{VerifiedOnly: true})
if err != nil {
return errors.Wrap(err, "failed to get stream")
}
fSlot, err := slots.EpochStart(params.BeaconConfig().FuluForkEpoch)
if err != nil {
return err
}
if errors.Is(ctx.Err(), context.Canceled) {
return errors.New("context canceled prematurely")
}
res, err := stream.Recv()
if err != nil {
return err
}
if res == nil || res.Block == nil {
return errors.New("nil block returned by beacon node")
}
if res.GetBlock() == nil {
return errors.New("nil block returned by beacon node")
}
if res.GetFuluBlock() == nil {
return errors.Errorf("non-fulu block returned after the fork with type %T", res.Block)
}
blk, err := blocks.NewSignedBeaconBlock(res.GetFuluBlock())
if err != nil {
return err
}
if err := blocks.BeaconBlockIsNil(blk); err != nil {
return err
}
if blk.Block().Slot() < fSlot {
return errors.Errorf("wanted a block at slot >= %d but received %d", fSlot, blk.Block().Slot())
}
return nil
}

View File

@@ -117,10 +117,7 @@ func metricsTest(_ *types.EvaluationContext, conns ...*grpc.ClientConn) error {
return err
}
timeSlot := slots.CurrentSlot(genesisResp.GenesisTime.AsTime())
// Allow 1 slot tolerance due to race between calculating current slot
// and fetching chain head - a slot boundary may occur between these calls.
// Check: chainHead.HeadSlot <= timeSlot <= chainHead.HeadSlot + 1
if uint64(chainHead.HeadSlot) > uint64(timeSlot) || uint64(timeSlot) > uint64(chainHead.HeadSlot)+1 {
if uint64(chainHead.HeadSlot) != uint64(timeSlot) {
return fmt.Errorf("expected metrics slot to equal chain head slot, expected %d, received %d", timeSlot, chainHead.HeadSlot)
}

View File

@@ -40,9 +40,7 @@ var depositsInBlockStart = params.E2ETestConfig().EpochsPerEth1VotingPeriod * 2
// deposits included + finalization + MaxSeedLookahead for activation.
var depositActivationStartEpoch = depositsInBlockStart + 2 + params.E2ETestConfig().MaxSeedLookahead
var depositEndEpoch = depositActivationStartEpoch + primitives.Epoch(math.Ceil(float64(depositValCount)/float64(params.E2ETestConfig().MinPerEpochChurnLimit)))
// Default exit submission epoch for standard tests.
var defaultExitSubmissionEpoch = primitives.Epoch(7)
var exitSubmissionEpoch = primitives.Epoch(7)
// ProcessesDepositsInBlocks ensures the expected amount of deposits are accepted into blocks.
// Note: This evaluator only works for pre-Electra genesis since Electra uses EIP-6110 deposit requests.
@@ -94,90 +92,55 @@ var DepositedValidatorsAreActive = e2etypes.Evaluator{
}
// ProposeVoluntaryExit sends a voluntary exit from randomly selected validator in the genesis set.
// Uses the default exit submission epoch (7).
var ProposeVoluntaryExit = ProposeVoluntaryExitAtEpoch(defaultExitSubmissionEpoch)
// ProposeVoluntaryExitAtEpoch sends a voluntary exit at the specified epoch.
var ProposeVoluntaryExitAtEpoch = func(epoch primitives.Epoch) e2etypes.Evaluator {
return e2etypes.Evaluator{
Name: "propose_voluntary_exit_epoch_%d",
Policy: policies.OnEpoch(epoch),
Evaluation: proposeVoluntaryExit,
}
var ProposeVoluntaryExit = e2etypes.Evaluator{
Name: "propose_voluntary_exit_epoch_%d",
Policy: policies.OnEpoch(exitSubmissionEpoch),
Evaluation: proposeVoluntaryExit,
}
// ValidatorsHaveExited checks the beacon state for the exited validator and ensures its marked as exited.
// Uses the default exit submission epoch + 1 (epoch 8).
var ValidatorsHaveExited = ValidatorsHaveExitedAtEpoch(defaultExitSubmissionEpoch + 1)
// ValidatorsHaveExitedAtEpoch checks validators have exited at the specified epoch.
var ValidatorsHaveExitedAtEpoch = func(epoch primitives.Epoch) e2etypes.Evaluator {
return e2etypes.Evaluator{
Name: "voluntary_has_exited_%d",
Policy: policies.OnEpoch(epoch),
Evaluation: validatorsHaveExited,
}
var ValidatorsHaveExited = e2etypes.Evaluator{
Name: "voluntary_has_exited_%d",
Policy: policies.OnEpoch(8),
Evaluation: validatorsHaveExited,
}
// SubmitWithdrawal sends a withdrawal from a previously exited validator.
// Uses default timing based on exit submission epoch.
var SubmitWithdrawal = SubmitWithdrawalAtEpoch(defaultExitSubmissionEpoch + 1)
// SubmitWithdrawalAtEpoch sends a withdrawal at the specified epoch.
// For pre-Deneb genesis, it runs around the Capella fork epoch instead.
var SubmitWithdrawalAtEpoch = func(epoch primitives.Epoch) e2etypes.Evaluator {
return e2etypes.Evaluator{
Name: "submit_withdrawal_epoch_%d",
Policy: func(currentEpoch primitives.Epoch) bool {
fEpoch := params.BeaconConfig().CapellaForkEpoch
// If Capella is disabled (starting at Deneb+), run at the specified epoch
if e2etypes.GenesisFork() >= version.Deneb {
return policies.OnEpoch(epoch)(currentEpoch)
}
return policies.BetweenEpochs(fEpoch-2, fEpoch+1)(currentEpoch)
},
Evaluation: submitWithdrawal,
}
var SubmitWithdrawal = e2etypes.Evaluator{
Name: "submit_withdrawal_epoch_%d",
Policy: func(currentEpoch primitives.Epoch) bool {
fEpoch := params.BeaconConfig().CapellaForkEpoch
// If Capella is disabled (starting at Deneb+), run after exit submission
if e2etypes.GenesisFork() >= version.Deneb {
return policies.OnEpoch(exitSubmissionEpoch + 1)(currentEpoch)
}
return policies.BetweenEpochs(fEpoch-2, fEpoch+1)(currentEpoch)
},
Evaluation: submitWithdrawal,
}
// ValidatorsHaveWithdrawn checks the beacon state for the withdrawn validator and ensures it has been withdrawn.
// Uses default timing based on exit submission epoch.
var ValidatorsHaveWithdrawn = ValidatorsHaveWithdrawnAfterExitAtEpoch(defaultExitSubmissionEpoch)
var ValidatorsHaveWithdrawn = e2etypes.Evaluator{
Name: "validator_has_withdrawn_%d",
Policy: func(currentEpoch primitives.Epoch) bool {
// TODO: Fix this for mainnet configs.
if params.BeaconConfig().ConfigName != params.EndToEndName {
return false
}
// Only run this for minimal setups after capella
fEpoch := params.BeaconConfig().CapellaForkEpoch
// If Capella is disabled (starting at Deneb+), run after withdrawal submission
var validWithdrawnEpoch primitives.Epoch
if e2etypes.GenesisFork() >= version.Deneb {
validWithdrawnEpoch = exitSubmissionEpoch + 2
} else {
validWithdrawnEpoch = fEpoch + 1
}
// ValidatorsHaveWithdrawnAfterExitAtEpoch checks validators have withdrawn after exiting at the specified epoch.
// For pre-Deneb genesis, it runs at CapellaForkEpoch + 1 instead.
var ValidatorsHaveWithdrawnAfterExitAtEpoch = func(exitSubmitEpoch primitives.Epoch) e2etypes.Evaluator {
return e2etypes.Evaluator{
Name: "validator_has_withdrawn_%d",
Policy: func(currentEpoch primitives.Epoch) bool {
// TODO: Fix this for mainnet configs.
if params.BeaconConfig().ConfigName != params.EndToEndName {
return false
}
// Only run this for minimal setups after capella
fEpoch := params.BeaconConfig().CapellaForkEpoch
// If Capella is disabled (starting at Deneb+), run after withdrawal submission
var validWithdrawnEpoch primitives.Epoch
if e2etypes.GenesisFork() >= version.Deneb {
// Exit submitted at exitSubmitEpoch
// Exit epoch = exitSubmitEpoch + 1 + MAX_SEED_LOOKAHEAD
// Withdrawable epoch = exit epoch + MIN_VALIDATOR_WITHDRAWABILITY_DELAY
// Add 1 more epoch for the sweep to process
exitEpoch := exitSubmitEpoch + 1 + primitives.Epoch(params.BeaconConfig().MaxSeedLookahead)
withdrawableEpoch := exitEpoch + primitives.Epoch(params.BeaconConfig().MinValidatorWithdrawabilityDelay)
validWithdrawnEpoch = withdrawableEpoch + 1
} else {
// For pre-Deneb genesis, give 2 epochs after Capella for:
// 1. BLS-to-exec changes to be processed (submitted in epoch before Capella)
// 2. Withdrawal sweep to reach all exited validators
validWithdrawnEpoch = fEpoch + 2
}
requiredPolicy := policies.OnEpoch(validWithdrawnEpoch)
return requiredPolicy(currentEpoch)
},
Evaluation: validatorsAreWithdrawn,
}
requiredPolicy := policies.OnEpoch(validWithdrawnEpoch)
return requiredPolicy(currentEpoch)
},
Evaluation: validatorsAreWithdrawn,
}
// ValidatorsVoteWithTheMajority verifies whether validator vote for eth1data using the majority algorithm.
@@ -388,7 +351,8 @@ func depositedValidatorsAreActive(ec *e2etypes.EvaluationContext, conns ...*grpc
continue // we aren't checking for this validator
}
// ignore voluntary exits when checking balance and active status
if _, exited := ec.ExitedVals[key]; exited {
exited := ec.ExitedVals[key]
if exited {
nexits++
delete(expected, key)
continue
@@ -493,7 +457,7 @@ func proposeVoluntaryExit(ec *e2etypes.EvaluationContext, conns ...*grpc.ClientC
return errors.Wrap(err, "could not propose exit")
}
pubk := bytesutil.ToBytes48(deposits[exitedIndex].Data.PublicKey)
ec.ExitedVals[pubk] = chainHead.HeadEpoch // Store submission epoch
ec.ExitedVals[pubk] = true
return nil
}
@@ -507,7 +471,7 @@ func proposeVoluntaryExit(ec *e2etypes.EvaluationContext, conns ...*grpc.ClientC
// Send an exit for a non-exited validator.
for i := 0; i < numOfExits; {
randIndex := primitives.ValidatorIndex(rand.Uint64() % params.BeaconConfig().MinGenesisActiveValidatorCount)
if _, alreadyExited := ec.ExitedVals[bytesutil.ToBytes48(privKeys[randIndex].PublicKey().Marshal())]; alreadyExited {
if ec.ExitedVals[bytesutil.ToBytes48(privKeys[randIndex].PublicKey().Marshal())] {
continue
}
if err := sendExit(randIndex); err != nil {
@@ -603,14 +567,6 @@ func validatorsVoteWithTheMajority(ec *e2etypes.EvaluationContext, conns ...*grp
b := blk.GetBlindedElectraBlock().Message
slot = b.Slot
vote = b.Body.Eth1Data.BlockHash
case *ethpb.BeaconBlockContainer_FuluBlock:
b := blk.GetFuluBlock().Block
slot = b.Slot
vote = b.Body.Eth1Data.BlockHash
case *ethpb.BeaconBlockContainer_BlindedFuluBlock:
b := blk.GetBlindedFuluBlock().Message
slot = b.Slot
vote = b.Body.Eth1Data.BlockHash
default:
return fmt.Errorf("block of type %T is unknown", blk.Block)
}
@@ -631,28 +587,20 @@ func validatorsVoteWithTheMajority(ec *e2etypes.EvaluationContext, conns ...*grp
}
if isFirstSlotInVotingPeriod {
ec.ExpectedEth1DataVote = vote
ec.Eth1DataMismatchCount = 0 // Reset for new voting period
return nil
}
if !bytes.Equal(vote, ec.ExpectedEth1DataVote) {
// Allow some tolerance for eth1data vote differences.
// Validators may have slightly different views of the eth1 chain
// as new blocks arrive during the voting period.
ec.Eth1DataMismatchCount++
// Allow up to 2 mismatches per voting period before failing.
if ec.Eth1DataMismatchCount > 2 {
for i := primitives.Slot(0); i < slot; i++ {
v, ok := ec.SeenVotes[i]
if ok {
fmt.Printf("vote at slot=%d = %#x\n", i, v)
} else {
fmt.Printf("did not see slot=%d\n", i)
}
for i := primitives.Slot(0); i < slot; i++ {
v, ok := ec.SeenVotes[i]
if ok {
fmt.Printf("vote at slot=%d = %#x\n", i, v)
} else {
fmt.Printf("did not see slot=%d\n", i)
}
return fmt.Errorf("incorrect eth1data vote for slot %d; expected: %#x vs voted: %#x (mismatch count: %d)",
slot, ec.ExpectedEth1DataVote, vote, ec.Eth1DataMismatchCount)
}
return fmt.Errorf("incorrect eth1data vote for slot %d; expected: %#x vs voted: %#x",
slot, ec.ExpectedEth1DataVote, vote)
}
}
return nil
@@ -696,12 +644,8 @@ func submitWithdrawal(ec *e2etypes.EvaluationContext, conns ...*grpc.ClientConn)
}
changes := make([]*structs.SignedBLSToExecutionChange, 0)
// Only send half the number of changes each time, to allow us to test
// at the fork boundary. When starting from Deneb+ at genesis, there's no
// fork boundary to test so we send all changes.
// at the fork boundary.
wantedChanges := numOfExits / 2
if e2etypes.GenesisFork() >= version.Deneb {
wantedChanges = numOfExits
}
for _, idx := range exitedIndices {
// Exit sending more change messages.
if len(changes) >= wantedChanges {

View File

@@ -26,11 +26,11 @@ import (
"google.golang.org/protobuf/types/known/emptypb"
)
var expectedParticipation = 0.98
var expectedParticipation = 0.99
var expectedMulticlientParticipation = 0.95
var expectedSyncParticipation = 0.95
var expectedSyncParticipation = 0.99
// ValidatorsAreActive ensures the expected amount of validators are active.
var ValidatorsAreActive = types.Evaluator{
@@ -62,7 +62,6 @@ var ValidatorSyncParticipation = types.Evaluator{
func validatorsAreActive(ec *types.EvaluationContext, conns ...*grpc.ClientConn) error {
conn := conns[0]
client := ethpb.NewBeaconChainClient(conn)
// Balances actually fluctuate but we just want to check initial balance.
validatorRequest := &ethpb.ListValidatorsRequest{
PageSize: int32(params.BeaconConfig().MinGenesisActiveValidatorCount),
@@ -73,26 +72,17 @@ func validatorsAreActive(ec *types.EvaluationContext, conns ...*grpc.ClientConn)
return errors.Wrap(err, "failed to get validators")
}
// Count should be MinGenesisActiveValidatorCount minus any validators that have exited.
// We determine actual exited count from the difference, as exits may be submitted but
// not yet processed, or affected by churn limits.
expectedCount := params.BeaconConfig().MinGenesisActiveValidatorCount
receivedCount := uint64(len(validators.ValidatorList))
maxExpected := params.BeaconConfig().MinGenesisActiveValidatorCount
minExpected := maxExpected - uint64(len(ec.ExitedVals))
if receivedCount > maxExpected {
return fmt.Errorf("validator count %d exceeds genesis count %d", receivedCount, maxExpected)
}
if receivedCount < minExpected {
return fmt.Errorf("validator count %d is less than expected minimum %d (genesis %d - %d submitted exits)",
receivedCount, minExpected, maxExpected, len(ec.ExitedVals))
if expectedCount != receivedCount {
return fmt.Errorf("expected validator count to be %d, received %d", expectedCount, receivedCount)
}
effBalanceLowCount := 0
exitEpochWrongCount := 0
withdrawEpochWrongCount := 0
for _, item := range validators.ValidatorList {
if _, exited := ec.ExitedVals[bytesutil.ToBytes48(item.Validator.PublicKey)]; exited {
if ec.ExitedVals[bytesutil.ToBytes48(item.Validator.PublicKey)] {
continue
}
if item.Validator.EffectiveBalance < params.BeaconConfig().MaxEffectiveBalance {
@@ -272,9 +262,9 @@ func validatorsSyncParticipation(_ *types.EvaluationContext, conns ...*grpc.Clie
// Skip fork slot.
continue
}
// Skip slots 1-2 at genesis - validators need time to ramp up after chain start
// due to doppelganger protection. This is a startup timing issue, not a fork transition issue.
if b.Block().Slot() < 3 {
// Skip slot 1 at genesis - validators need time to ramp up after chain start.
// This is a startup timing issue, not a fork transition issue.
if b.Block().Slot() == 1 {
continue
}
expectedParticipation := expectedSyncParticipation
@@ -310,34 +300,20 @@ func validatorsSyncParticipation(_ *types.EvaluationContext, conns ...*grpc.Clie
if b == nil || b.IsNil() {
return errors.New("nil block provided")
}
// Skip evaluation of fork transition slots as sync participation
// tends to drop briefly when transitioning between forks.
forkEpochs := []primitives.Epoch{
params.BeaconConfig().AltairForkEpoch,
params.BeaconConfig().BellatrixForkEpoch,
params.BeaconConfig().CapellaForkEpoch,
params.BeaconConfig().DenebForkEpoch,
params.BeaconConfig().ElectraForkEpoch,
params.BeaconConfig().FuluForkEpoch,
forkSlot, err := slots.EpochStart(params.BeaconConfig().AltairForkEpoch)
if err != nil {
return err
}
skipSlot := false
for _, forkEpoch := range forkEpochs {
// Skip fork epochs set to far future (not scheduled).
if forkEpoch == params.BeaconConfig().FarFutureEpoch {
continue
}
forkSlot, err := slots.EpochStart(forkEpoch)
if err != nil {
return err
}
// Skip the first two slots of each fork epoch.
if b.Block().Slot() == forkSlot || b.Block().Slot() == forkSlot+1 {
skipSlot = true
break
}
nexForkSlot, err := slots.EpochStart(params.BeaconConfig().BellatrixForkEpoch)
if err != nil {
return err
}
if skipSlot {
switch b.Block().Slot() {
case forkSlot, forkSlot + 1, nexForkSlot:
// Skip evaluation of the slot.
continue
default:
// no-op
}
syncAgg, err := b.Block().Body().SyncAggregate()
if err != nil {
@@ -382,14 +358,6 @@ func syncCompatibleBlockFromCtr(container *ethpb.BeaconBlockContainer) (interfac
if container.GetBlindedElectraBlock() != nil {
return blocks.NewSignedBeaconBlock(container.GetBlindedElectraBlock())
}
if container.GetFuluBlock() != nil {
return blocks.NewSignedBeaconBlock(container.GetFuluBlock())
}
if container.GetBlindedFuluBlock() != nil {
return blocks.NewSignedBeaconBlock(container.GetBlindedFuluBlock())
}
return nil, errors.New("no supported block type in container")
}

View File

@@ -10,7 +10,7 @@ import (
// Run mainnet e2e config with the current release validator against latest beacon node.
func TestEndToEnd_MainnetConfig_ValidatorAtCurrentRelease(t *testing.T) {
r := e2eMainnet(t, true, false, types.InitForkCfg(version.Bellatrix, version.Fulu, params.E2EMainnetTestConfig()))
r := e2eMainnet(t, true, false, types.InitForkCfg(version.Bellatrix, version.Electra, params.E2EMainnetTestConfig()))
r.run()
}

View File

@@ -8,38 +8,7 @@ import (
"github.com/OffchainLabs/prysm/v7/testing/endtoend/types"
)
// TestEndToEnd_MinimalConfig is the pre-submit e2e test from Electra to Fulu
// with compressed epochs. It runs 10 epochs with exit at epoch 4 (the earliest
// possible due to ShardCommitteePeriod=4), allowing all evaluators to complete:
// - Participation at epoch 2
// - Finalization at epoch 3
// - Fulu fork transition at epoch 2
// - BPO 1 at epoch 3 (15 blobs)
// - BPO 2 at epoch 4 (21 blobs)
// - Exit proposed at epoch 4
// - Exit confirmed at epoch 5
// - Withdrawal submitted at epoch 5
// - Withdrawal verified at epoch 8 (exit epoch 4 + 1 + MaxSeedLookahead + MinValidatorWithdrawabilityDelay + 1)
func TestEndToEnd_MinimalConfig(t *testing.T) {
cfg := params.E2ETestConfig()
cfg = types.InitForkCfg(version.Electra, version.Fulu, cfg)
// Set Fulu fork at epoch 2 for a quick fork transition test
cfg.FuluForkEpoch = 2
// Update BlobSchedule to use the new FuluForkEpoch for BPO testing
cfg.BlobSchedule = []params.BlobScheduleEntry{
{Epoch: cfg.DenebForkEpoch, MaxBlobsPerBlock: uint64(cfg.DeprecatedMaxBlobsPerBlock)},
{Epoch: cfg.ElectraForkEpoch, MaxBlobsPerBlock: uint64(cfg.DeprecatedMaxBlobsPerBlockElectra)},
// BPO (Blob Parameter Optimization) schedule for Fulu
{Epoch: cfg.FuluForkEpoch + 1, MaxBlobsPerBlock: 15},
{Epoch: cfg.FuluForkEpoch + 2, MaxBlobsPerBlock: 21},
}
cfg.InitializeForkSchedule()
r := e2eMinimal(t, cfg,
types.WithCheckpointSync(),
types.WithEpochs(10),
types.WithExitEpoch(4), // Minimum due to ShardCommitteePeriod=4
types.WithLargeBlobs(), // Use large blob transactions for BPO testing
)
r := e2eMinimal(t, types.InitForkCfg(version.Bellatrix, version.Electra, params.E2ETestConfig()), types.WithCheckpointSync())
r.run()
}

View File

@@ -1,17 +0,0 @@
package endtoend
import (
"testing"
"github.com/OffchainLabs/prysm/v7/config/params"
"github.com/OffchainLabs/prysm/v7/runtime/version"
"github.com/OffchainLabs/prysm/v7/testing/endtoend/types"
)
// TestEndToEnd_MinimalConfig_PostMerge is a post-submit test that runs the full
// e2e test from Bellatrix (post-merge) through all fork transitions to the latest fork.
// This test takes longer but provides comprehensive coverage of all forks.
func TestEndToEnd_MinimalConfig_PostMerge(t *testing.T) {
r := e2eMinimal(t, types.InitForkCfg(version.Bellatrix, version.Fulu, params.E2ETestConfig()), types.WithCheckpointSync())
r.run()
}

View File

@@ -59,15 +59,6 @@ func WithBuilder() E2EConfigOpt {
}
}
// WithLargeBlobs configures the transaction generator to use large blob
// transactions (6 blobs per tx) for testing BPO limits. Without this option,
// small blob transactions (1 blob per tx) are used by default.
func WithLargeBlobs() E2EConfigOpt {
return func(cfg *E2EConfig) {
cfg.UseLargeBlobs = true
}
}
func WithSSZOnly() E2EConfigOpt {
return func(cfg *E2EConfig) {
if err := os.Setenv(params.EnvNameOverrideAccept, api.OctetStreamMediaType); err != nil {
@@ -76,14 +67,6 @@ func WithSSZOnly() E2EConfigOpt {
}
}
// WithExitEpoch sets a custom epoch for voluntary exit submission.
// This affects ProposeVoluntaryExit, ValidatorsHaveExited, SubmitWithdrawal, and ValidatorsHaveWithdrawn evaluators.
func WithExitEpoch(e primitives.Epoch) E2EConfigOpt {
return func(cfg *E2EConfig) {
cfg.ExitEpoch = e
}
}
// E2EConfig defines the struct for all configurations needed for E2E testing.
type E2EConfig struct {
TestCheckpointSync bool
@@ -98,9 +81,7 @@ type E2EConfig struct {
UseValidatorCrossClient bool
UseBeaconRestApi bool
UseBuilder bool
UseLargeBlobs bool // Use large blob transactions (6 blobs per tx) for BPO testing
EpochsToRun uint64
ExitEpoch primitives.Epoch // Custom epoch for voluntary exit submission (0 means use default)
Seed int64
TracingSinkEndpoint string
Evaluators []Evaluator
@@ -114,9 +95,6 @@ type E2EConfig struct {
func GenesisFork() int {
cfg := params.BeaconConfig()
// Check from highest fork to lowest to find the genesis fork.
if cfg.FuluForkEpoch == 0 {
return version.Fulu
}
if cfg.ElectraForkEpoch == 0 {
return version.Electra
}
@@ -168,21 +146,16 @@ type DepositBalancer interface {
// EvaluationContext allows for additional data to be provided to evaluators that need extra state.
type EvaluationContext struct {
DepositBalancer
// ExitedVals maps validator pubkey to the epoch when their exit was submitted.
// The actual exit takes effect at: submission_epoch + 1 + MaxSeedLookahead
ExitedVals map[[48]byte]primitives.Epoch
ExitedVals map[[48]byte]bool
SeenVotes map[primitives.Slot][]byte
ExpectedEth1DataVote []byte
// Eth1DataMismatchCount tracks how many eth1data vote mismatches have been seen
// in the current voting period. Some tolerance is allowed for timing differences.
Eth1DataMismatchCount int
}
// NewEvaluationContext handles initializing internal datastructures (like maps) provided by the EvaluationContext.
func NewEvaluationContext(d DepositBalancer) *EvaluationContext {
return &EvaluationContext{
DepositBalancer: d,
ExitedVals: make(map[[48]byte]primitives.Epoch),
ExitedVals: make(map[[48]byte]bool),
SeenVotes: make(map[primitives.Slot][]byte),
}
}

Some files were not shown because too many files have changed in this diff Show More