Compare commits

...

11 Commits

Author SHA1 Message Date
Bastin
a7565164e4 Update hack/upgrade-version.sh
Co-authored-by: Preston Van Loon <pvanloon@offchainlabs.com>
2025-11-06 17:46:35 +01:00
Bastin
94d76a5685 update-go-ssz 2025-11-06 16:23:47 +01:00
Bastin
2ef2dd16ba version upgrade script 2025-11-06 16:23:07 +01:00
Preston Van Loon
d6005026e0 Remove deprecated flags for v7 release (#15986)
* Delete deprecated flags

* Changelog fragment

* E2E: Remove delete flag usage.

---------

Co-authored-by: Manu NALEPA <enalepa@offchainlabs.com>
2025-11-06 08:18:10 +00:00
kasey
33476f5d7b disable backfill if the checkpoint sync origin slot is in fulu (#15987)
Co-authored-by: Kasey Kirkham <kasey@users.noreply.github.com>
2025-11-06 07:45:12 +00:00
Preston Van Loon
35e9c1752e Remove test overrides for electra and fulu epochs in beacon-chain/rpc/eth/beacon/handlers_pool_test.go (#15988)
* Reverted all config.FuluForkEpoch = config.FarFutureEpoch from 8b6f187b15

* Fix tests by referencing electra epoch / slot values in requests and test setup

* Changelog fragment
2025-11-06 03:10:46 +00:00
james-prysm
8b6f187b15 Add support for fulu fork epoch and bpo schedule (#15975)
* wip

* fixing tests

* adding script to update workspace for eth clients

* updating test sepc to 1.6.0 and fixing broadcaster test

* fix specrefs

* more ethspecify fixes

* still trying to fix ethspecify

* fixing attestation tests

* fixing sha for consensus specs

* removing script for now until i have something more standard

* fixing more p2p tests

* fixing discovery tests

* attempting to fix discovery test flakeyness

* attempting to fix port binding issue

* more attempts to fix flakey tests

* Revert "more attempts to fix flakey tests"

This reverts commit 25e8183703.

* Revert "attempting to fix port binding issue"

This reverts commit 583df8000d.

* Revert "attempting to fix discovery test flakeyness"

This reverts commit 3c76525870.

* Revert "fixing discovery tests"

This reverts commit 8c701bf3b9.

* Revert "fixing more p2p tests"

This reverts commit 140d5db203.

* Revert "fixing attestation tests"

This reverts commit 26ded244cb.

* fixing attestation tests

* fixing more p2p tests

* fixing discovery tests

* attempting to fix discovery test flakeyness

* attempting to fix port binding issue

* more attempts to fix flakey tests

* changelog

* fixing import

* adding some missing dependencies, but  TestService_BroadcastAttestationWithDiscoveryAttempts is still failing

* attempting to fix test

* reverting test as it migrated to other pr

* reverting test

* fixing test from merge

* Fix `TestService_BroadcastAttestationWithDiscoveryAttempts`.

* Fix again `TestService_Start_OnlyStartsOnce`.

* fixing TestListenForNewNodes

* removing manual set of fulu epoch

* missed a few

* fixing subnet test

* Update beacon-chain/rpc/eth/config/handlers_test.go

Co-authored-by: Preston Van Loon <pvanloon@offchainlabs.com>

* removing a few more missed spots of reverting fulu epoch setting

* updating test name based on feedback

* fixing rest apis, they actually need the setting of the epoch due to the guard

---------

Co-authored-by: Manu NALEPA <enalepa@offchainlabs.com>
Co-authored-by: Preston Van Loon <pvanloon@offchainlabs.com>
2025-11-05 22:41:36 +00:00
Radosław Kapka
8ad547c969 Remove Beacon API endpoints that were deprecated in Electra (#15962)
* Remove Beacon API endpoints that were deprecated in Electra

* changelog <3

* build fix

* remove more stuff

* fix post-submit e2e and remove structs

* list endpoints in the changelog

---------

Co-authored-by: james-prysm <90280386+james-prysm@users.noreply.github.com>
2025-11-05 18:20:34 +00:00
Potuz
d3d7f67bec Use head for block validation when possible (#15972)
* Use head for block validation when possible

When validating blocks for pubsub, we always copy a state and advance
when we simply need to get a read only beacon state without a copy in
most cases since the head state normally works.

* fix test

* fix tests

* fix more tests

* fix more tests

* Add nil check to be safe

* fix more tests

* add test case

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
2025-11-05 17:54:38 +00:00
Potuz
9959782f1c Only use head if it's compatible with target (#15965)
* Only use head if it's compatible with target

* Allow blocks from the previous epoch to be viable for checkpoints

* Add feature flag to make it configurable

* fix tests

* @satushh's review

* Manu's nit

* Use fields in logs
2025-11-05 16:52:10 +00:00
Manu NALEPA
f0a099b275 Ensures the rate limitation is respected for by root blob and data column sidecars requests. (#15981)
* Set default value of `--blob-batch-limit` to 384.

So, using default values, `--blob-batch-limit * --blob-batch-limit-burst-factor = 384*3 = MAX_REQUEST_BLOB_SIDECARS = 1152.`

* `blobSidecarByRootRPCHandler`: Add rate limiting.

Bacause now the rate limiter validation is done before the request validation,
adapt `TestBlobsByRootValidation` consequently and add new specific tests for
`validateBlobByRootRequest` to cover the now untested case.

* Set default value of `--data-column-batch-limit-burst-factor` to 4.

So, using default values, `--data-column-batch-limit * --data-column-batch-limit-burst-factor = 4096*2 = MAX_REQUEST_DATA_COLUMN_SIDECARS_ELECTRA = 16384`.

* `validateDataColumnsByRootRequest`: Take a count instead of idents.

* `dataColumnSidecarByRootRPCHandler`: Add rate limiting.
2025-11-05 16:47:14 +00:00
68 changed files with 1885 additions and 4398 deletions

View File

@@ -253,16 +253,16 @@ filegroup(
url = "https://github.com/ethereum/EIPs/archive/5480440fe51742ed23342b68cf106cefd427e39d.tar.gz",
)
consensus_spec_version = "v1.6.0-beta.2"
consensus_spec_version = "v1.6.0"
load("@prysm//tools:download_spectests.bzl", "consensus_spec_tests")
consensus_spec_tests(
name = "consensus_spec_tests",
flavors = {
"general": "sha256-oEj0MTViJHjZo32nABK36gfvSXpbwkBk/jt6Mj7pWFI=",
"minimal": "sha256-cS4NPv6IRBoCSmWomQ8OEo8IsVNW9YawUFqoRZQBUj4=",
"mainnet": "sha256-BYuLndMPAh4p13IRJgNfVakrCVL69KRrNw2tdc3ETbE=",
"general": "sha256-54hTaUNF9nLg+hRr3oHoq0yjZpW3MNiiUUuCQu6Rajk=",
"minimal": "sha256-1JHIGg3gVMjvcGYRHR5cwdDgOvX47oR/MWp6gyAeZfA=",
"mainnet": "sha256-292h3W2Ffts0YExgDTyxYe9Os7R0bZIXuAaMO8P6kl4=",
},
version = consensus_spec_version,
)
@@ -278,7 +278,7 @@ filegroup(
visibility = ["//visibility:public"],
)
""",
integrity = "sha256-MForEP9dTe0z3ZkTHjX4H6waSkSTghf3gQHPwrSCCro=",
integrity = "sha256-VzBgrEokvYSMIIXVnSA5XS9I3m9oxpvToQGxC1N5lzw=",
strip_prefix = "consensus-specs-" + consensus_spec_version[1:],
url = "https://github.com/ethereum/consensus-specs/archive/refs/tags/%s.tar.gz" % consensus_spec_version,
)
@@ -327,9 +327,9 @@ filegroup(
visibility = ["//visibility:public"],
)
""",
integrity = "sha256-NZr/gsQK9rBHRnznlPBiNzJpK8MPMrfUa3f+QYqn1+g=",
strip_prefix = "mainnet-978f1794eada6f85bee76e4d2d5959a5fb8e0cc5",
url = "https://github.com/eth-clients/mainnet/archive/978f1794eada6f85bee76e4d2d5959a5fb8e0cc5.tar.gz",
integrity = "sha256-+mqMXyboedVw8Yp0v+U9GDz98QoC1SZET8mjaKPX+AI=",
strip_prefix = "mainnet-980aee8893a2291d473c38f63797d5bc370fa381",
url = "https://github.com/eth-clients/mainnet/archive/980aee8893a2291d473c38f63797d5bc370fa381.tar.gz",
)
http_archive(

View File

@@ -13,7 +13,6 @@ go_library(
"conversions_state.go",
"endpoints_beacon.go",
"endpoints_blob.go",
"endpoints_builder.go",
"endpoints_config.go",
"endpoints_debug.go",
"endpoints_events.go",

View File

@@ -1492,20 +1492,6 @@ func sszBytesToUint256String(b []byte) (string, error) {
return bi.String(), nil
}
func DepositSnapshotFromConsensus(ds *eth.DepositSnapshot) *DepositSnapshot {
finalized := make([]string, 0, len(ds.Finalized))
for _, f := range ds.Finalized {
finalized = append(finalized, hexutil.Encode(f))
}
return &DepositSnapshot{
Finalized: finalized,
DepositRoot: hexutil.Encode(ds.DepositRoot),
DepositCount: fmt.Sprintf("%d", ds.DepositCount),
ExecutionBlockHash: hexutil.Encode(ds.ExecutionHash),
ExecutionBlockHeight: fmt.Sprintf("%d", ds.ExecutionDepth),
}
}
func PendingDepositsFromConsensus(ds []*eth.PendingDeposit) []*PendingDeposit {
deposits := make([]*PendingDeposit, len(ds))
for i, d := range ds {

View File

@@ -9,24 +9,6 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil"
)
func TestDepositSnapshotFromConsensus(t *testing.T) {
ds := &eth.DepositSnapshot{
Finalized: [][]byte{{0xde, 0xad, 0xbe, 0xef}, {0xca, 0xfe, 0xba, 0xbe}},
DepositRoot: []byte{0xab, 0xcd},
DepositCount: 12345,
ExecutionHash: []byte{0x12, 0x34},
ExecutionDepth: 67890,
}
res := DepositSnapshotFromConsensus(ds)
require.NotNil(t, res)
require.DeepEqual(t, []string{"0xdeadbeef", "0xcafebabe"}, res.Finalized)
require.Equal(t, "0xabcd", res.DepositRoot)
require.Equal(t, "12345", res.DepositCount)
require.Equal(t, "0x1234", res.ExecutionBlockHash)
require.Equal(t, "67890", res.ExecutionBlockHeight)
}
func TestSignedBLSToExecutionChange_ToConsensus(t *testing.T) {
s := &SignedBLSToExecutionChange{Message: nil, Signature: ""}
_, err := s.ToConsensus()

View File

@@ -206,18 +206,6 @@ type WeakSubjectivityData struct {
StateRoot string `json:"state_root"`
}
type GetDepositSnapshotResponse struct {
Data *DepositSnapshot `json:"data"`
}
type DepositSnapshot struct {
Finalized []string `json:"finalized"`
DepositRoot string `json:"deposit_root"`
DepositCount string `json:"deposit_count"`
ExecutionBlockHash string `json:"execution_block_hash"`
ExecutionBlockHeight string `json:"execution_block_height"`
}
type GetIndividualVotesRequest struct {
Epoch string `json:"epoch"`
PublicKeys []string `json:"public_keys,omitempty"`

View File

@@ -1,14 +0,0 @@
package structs
type ExpectedWithdrawalsResponse struct {
Data []*ExpectedWithdrawal `json:"data"`
ExecutionOptimistic bool `json:"execution_optimistic"`
Finalized bool `json:"finalized"`
}
type ExpectedWithdrawal struct {
Address string `json:"address" hex:"true"`
Amount string `json:"amount"`
Index string `json:"index"`
ValidatorIndex string `json:"validator_index"`
}

View File

@@ -1,6 +1,7 @@
package blockchain
import (
"bytes"
"context"
"fmt"
"strconv"
@@ -16,6 +17,7 @@ import (
ethpb "github.com/OffchainLabs/prysm/v6/proto/prysm/v1alpha1"
"github.com/OffchainLabs/prysm/v6/time/slots"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
// The caller of this function must have a lock on forkchoice.
@@ -27,6 +29,20 @@ func (s *Service) getRecentPreState(ctx context.Context, c *ethpb.Checkpoint) st
if !s.cfg.ForkChoiceStore.IsCanonical([32]byte(c.Root)) {
return nil
}
// Only use head state if the head state is compatible with the target checkpoint.
headRoot, err := s.HeadRoot(ctx)
if err != nil {
return nil
}
headTarget, err := s.cfg.ForkChoiceStore.TargetRootForEpoch([32]byte(headRoot), c.Epoch)
if err != nil {
return nil
}
if !bytes.Equal(c.Root, headTarget[:]) {
return nil
}
// If the head state alone is enough, we can return it directly read only.
if c.Epoch == headEpoch {
st, err := s.HeadStateReadOnly(ctx)
if err != nil {
@@ -34,11 +50,13 @@ func (s *Service) getRecentPreState(ctx context.Context, c *ethpb.Checkpoint) st
}
return st
}
// Otherwise we need to advance the head state to the start of the target epoch.
// This point can only be reached if c.Root == headRoot and c.Epoch > headEpoch.
slot, err := slots.EpochStart(c.Epoch)
if err != nil {
return nil
}
// Try if we have already set the checkpoint cache
// Try if we have already set the checkpoint cache. This will be tried again if we fail here but the check is cheap anyway.
epochKey := strconv.FormatUint(uint64(c.Epoch), 10 /* base 10 */)
lock := async.NewMultilock(string(c.Root) + epochKey)
lock.Lock()
@@ -50,6 +68,7 @@ func (s *Service) getRecentPreState(ctx context.Context, c *ethpb.Checkpoint) st
if cachedState != nil && !cachedState.IsNil() {
return cachedState
}
// If we haven't advanced yet then process the slots from head state.
st, err := s.HeadState(ctx)
if err != nil {
return nil
@@ -114,6 +133,7 @@ func (s *Service) getAttPreState(ctx context.Context, c *ethpb.Checkpoint) (stat
}
// Fallback to state regeneration.
log.WithFields(logrus.Fields{"epoch": c.Epoch, "root": fmt.Sprintf("%#x", c.Root)}).Debug("Regenerating attestation pre-state")
baseState, err := s.cfg.StateGen.StateByRoot(ctx, bytesutil.ToBytes32(c.Root))
if err != nil {
return nil, errors.Wrapf(err, "could not get pre state for epoch %d", c.Epoch)

View File

@@ -86,7 +86,7 @@ func IsExecutionBlock(body interfaces.ReadOnlyBeaconBlockBody) (bool, error) {
// def is_execution_enabled(state: BeaconState, body: ReadOnlyBeaconBlockBody) -> bool:
//
// return is_merge_block(state, body) or is_merge_complete(state)
func IsExecutionEnabled(st state.BeaconState, body interfaces.ReadOnlyBeaconBlockBody) (bool, error) {
func IsExecutionEnabled(st state.ReadOnlyBeaconState, body interfaces.ReadOnlyBeaconBlockBody) (bool, error) {
if st == nil || body == nil {
return false, errors.New("nil state or block body")
}

View File

@@ -51,16 +51,20 @@ func Test_commitmentsToCheck(t *testing.T) {
name: "commitments within da",
block: func(t *testing.T) blocks.ROBlock {
d := util.NewBeaconBlockFulu()
d.Block.Body.BlobKzgCommitments = commits[:maxBlobs]
d.Block.Slot = fulu + 100
mb := params.GetNetworkScheduleEntry(slots.ToEpoch(d.Block.Slot)).MaxBlobsPerBlock
d.Block.Body.BlobKzgCommitments = commits[:mb]
sb, err := blocks.NewSignedBeaconBlock(d)
require.NoError(t, err)
rb, err := blocks.NewROBlock(sb)
require.NoError(t, err)
return rb
},
commits: commits[:maxBlobs],
slot: fulu + 100,
commits: func() [][]byte {
mb := params.GetNetworkScheduleEntry(slots.ToEpoch(fulu + 100)).MaxBlobsPerBlock
return commits[:mb]
}(),
slot: fulu + 100,
},
{
name: "commitments outside da",

View File

@@ -27,6 +27,7 @@ go_library(
"//beacon-chain/forkchoice:go_default_library",
"//beacon-chain/forkchoice/types: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/blocks:go_default_library",

View File

@@ -8,6 +8,7 @@ import (
"github.com/OffchainLabs/prysm/v6/beacon-chain/forkchoice"
forkchoicetypes "github.com/OffchainLabs/prysm/v6/beacon-chain/forkchoice/types"
"github.com/OffchainLabs/prysm/v6/beacon-chain/state"
"github.com/OffchainLabs/prysm/v6/config/features"
fieldparams "github.com/OffchainLabs/prysm/v6/config/fieldparams"
"github.com/OffchainLabs/prysm/v6/config/params"
consensus_blocks "github.com/OffchainLabs/prysm/v6/consensus-types/blocks"
@@ -239,9 +240,12 @@ func (f *ForkChoice) IsViableForCheckpoint(cp *forkchoicetypes.Checkpoint) (bool
if node.slot == epochStart {
return true, nil
}
nodeEpoch := slots.ToEpoch(node.slot)
if nodeEpoch >= cp.Epoch {
return false, nil
if !features.Get().DisableLastEpochTargets {
// Allow any node from the checkpoint epoch - 1 to be viable.
nodeEpoch := slots.ToEpoch(node.slot)
if nodeEpoch+1 == cp.Epoch {
return true, nil
}
}
for _, child := range node.children {
if child.slot > epochStart {

View File

@@ -813,9 +813,10 @@ func TestForkChoiceIsViableForCheckpoint(t *testing.T) {
require.NoError(t, err)
require.Equal(t, true, viable)
// Last epoch blocks are still viable
viable, err = f.IsViableForCheckpoint(&forkchoicetypes.Checkpoint{Root: blk.Root(), Epoch: 1})
require.NoError(t, err)
require.Equal(t, false, viable)
require.Equal(t, true, viable)
// No Children but impossible checkpoint
viable, err = f.IsViableForCheckpoint(&forkchoicetypes.Checkpoint{Root: blk2.Root()})
@@ -835,9 +836,10 @@ func TestForkChoiceIsViableForCheckpoint(t *testing.T) {
require.NoError(t, err)
require.Equal(t, false, viable)
// Last epoch blocks are still viable
viable, err = f.IsViableForCheckpoint(&forkchoicetypes.Checkpoint{Root: blk2.Root(), Epoch: 1})
require.NoError(t, err)
require.Equal(t, false, viable)
require.Equal(t, true, viable)
st, blk4, err := prepareForkchoiceState(ctx, params.BeaconConfig().SlotsPerEpoch, [32]byte{'d'}, blk2.Root(), [32]byte{'D'}, 0, 0)
require.NoError(t, err)
@@ -848,9 +850,10 @@ func TestForkChoiceIsViableForCheckpoint(t *testing.T) {
require.NoError(t, err)
require.Equal(t, false, viable)
// Last epoch blocks are still viable
viable, err = f.IsViableForCheckpoint(&forkchoicetypes.Checkpoint{Root: blk2.Root(), Epoch: 1})
require.NoError(t, err)
require.Equal(t, false, viable)
require.Equal(t, true, viable)
// Boundary block
viable, err = f.IsViableForCheckpoint(&forkchoicetypes.Checkpoint{Root: blk4.Root(), Epoch: 1})

View File

@@ -13,6 +13,7 @@ import (
"github.com/OffchainLabs/prysm/v6/beacon-chain/blockchain/kzg"
"github.com/OffchainLabs/prysm/v6/beacon-chain/core/helpers"
"github.com/OffchainLabs/prysm/v6/beacon-chain/core/peerdas"
testDB "github.com/OffchainLabs/prysm/v6/beacon-chain/db/testing"
"github.com/OffchainLabs/prysm/v6/beacon-chain/p2p/peers"
"github.com/OffchainLabs/prysm/v6/beacon-chain/p2p/peers/scorers"
p2ptest "github.com/OffchainLabs/prysm/v6/beacon-chain/p2p/testing"
@@ -218,19 +219,30 @@ func TestService_BroadcastAttestation(t *testing.T) {
func TestService_BroadcastAttestationWithDiscoveryAttempts(t *testing.T) {
const port = uint(2000)
// The DB has to be shared in all peers to avoid the
// duplicate metrics collector registration attempted.
// However, we don't care for this test.
db := testDB.SetupDB(t)
// Setup bootnode.
cfg := &Config{PingInterval: testPingInterval}
cfg := &Config{PingInterval: testPingInterval, DB: db}
cfg.UDPPort = uint(port)
_, pkey := createAddrAndPrivKey(t)
ipAddr := net.ParseIP("127.0.0.1")
genesisTime := time.Now()
genesisValidatorsRoot := make([]byte, 32)
s := &Service{
cfg: cfg,
genesisTime: genesisTime,
genesisValidatorsRoot: genesisValidatorsRoot,
custodyInfo: &custodyInfo{},
ctx: t.Context(),
custodyInfoSet: make(chan struct{}),
}
close(s.custodyInfoSet)
bootListener, err := s.createListener(ipAddr, pkey)
require.NoError(t, err)
defer bootListener.Close()
@@ -245,6 +257,7 @@ func TestService_BroadcastAttestationWithDiscoveryAttempts(t *testing.T) {
Discv5BootStrapAddrs: []string{bootNode.String()},
MaxPeers: 2,
PingInterval: testPingInterval,
DB: db,
}
// Setup 2 different hosts
for i := uint(1); i <= 2; i++ {
@@ -259,7 +272,12 @@ func TestService_BroadcastAttestationWithDiscoveryAttempts(t *testing.T) {
genesisTime: genesisTime,
genesisValidatorsRoot: genesisValidatorsRoot,
custodyInfo: &custodyInfo{},
ctx: t.Context(),
custodyInfoSet: make(chan struct{}),
}
close(s.custodyInfoSet)
listener, err := s.startDiscoveryV5(ipAddr, pkey)
// Set for 2nd peer
if i == 2 {
@@ -711,18 +729,26 @@ func TestService_BroadcastDataColumn(t *testing.T) {
// Create a host.
_, pkey, ipAddr := createHost(t, port)
// Create a shared DB for the service
db := testDB.SetupDB(t)
// Create and close the custody info channel immediately since custodyInfo is already set
custodyInfoSet := make(chan struct{})
close(custodyInfoSet)
service := &Service{
ctx: ctx,
host: p1.BHost,
pubsub: p1.PubSub(),
joinedTopics: map[string]*pubsub.Topic{},
cfg: &Config{},
cfg: &Config{DB: db},
genesisTime: time.Now(),
genesisValidatorsRoot: bytesutil.PadTo([]byte{'A'}, 32),
subnetsLock: make(map[uint64]*sync.RWMutex),
subnetsLockLock: sync.Mutex{},
peers: peers.NewStatus(ctx, &peers.StatusConfig{ScorerParams: &scorers.Config{}}),
custodyInfo: &custodyInfo{},
custodyInfoSet: custodyInfoSet,
}
// Create a listener.

View File

@@ -136,20 +136,26 @@ func setNodeSubnets(localNode *enode.LocalNode, attSubnets []uint64) {
}
func TestCreateListener(t *testing.T) {
port := 1024
ipAddr, pkey := createAddrAndPrivKey(t)
db := testDB.SetupDB(t)
custodyInfoSet := make(chan struct{})
close(custodyInfoSet)
s := &Service{
ctx: t.Context(),
genesisTime: time.Now(),
genesisValidatorsRoot: bytesutil.PadTo([]byte{'A'}, 32),
cfg: &Config{UDPPort: uint(port)},
cfg: &Config{UDPPort: 2200, DB: db},
custodyInfo: &custodyInfo{},
custodyInfoSet: custodyInfoSet,
}
listener, err := s.createListener(ipAddr, pkey)
require.NoError(t, err)
defer listener.Close()
assert.Equal(t, true, listener.Self().IP().Equal(ipAddr), "IP address is not the expected type")
assert.Equal(t, port, listener.Self().UDP(), "Incorrect port number")
assert.Equal(t, 2200, listener.Self().UDP(), "Incorrect port number")
pubkey := listener.Self().Pubkey()
XisSame := pkey.PublicKey.X.Cmp(pubkey.X) == 0
@@ -161,15 +167,21 @@ func TestCreateListener(t *testing.T) {
}
func TestStartDiscV5_DiscoverAllPeers(t *testing.T) {
port := 2000
ipAddr, pkey := createAddrAndPrivKey(t)
genesisTime := time.Now()
genesisValidatorsRoot := make([]byte, 32)
db := testDB.SetupDB(t)
custodyInfoSet := make(chan struct{})
close(custodyInfoSet)
s := &Service{
cfg: &Config{UDPPort: uint(port), PingInterval: testPingInterval, DisableLivenessCheck: true},
ctx: t.Context(),
cfg: &Config{UDPPort: 6000, PingInterval: testPingInterval, DisableLivenessCheck: true, DB: db}, // Use high port to reduce conflicts
genesisTime: genesisTime,
genesisValidatorsRoot: genesisValidatorsRoot,
custodyInfo: &custodyInfo{},
custodyInfoSet: custodyInfoSet,
}
bootListener, err := s.createListener(ipAddr, pkey)
require.NoError(t, err)
@@ -183,19 +195,26 @@ func TestStartDiscV5_DiscoverAllPeers(t *testing.T) {
var listeners []*listenerWrapper
for i := 1; i <= 5; i++ {
port = 3000 + i
port := 6000 + i // Use unique high ports for peer discovery
cfg := &Config{
Discv5BootStrapAddrs: []string{bootNode.String()},
UDPPort: uint(port),
PingInterval: testPingInterval,
DisableLivenessCheck: true,
DB: db,
}
ipAddr, pkey := createAddrAndPrivKey(t)
custodyInfoSetLoop := make(chan struct{})
close(custodyInfoSetLoop)
s = &Service{
ctx: t.Context(),
cfg: cfg,
genesisTime: genesisTime,
genesisValidatorsRoot: genesisValidatorsRoot,
custodyInfo: &custodyInfo{},
custodyInfoSet: custodyInfoSetLoop,
}
listener, err := s.startDiscoveryV5(ipAddr, pkey)
assert.NoError(t, err, "Could not start discovery for node")
@@ -220,16 +239,6 @@ func TestStartDiscV5_DiscoverAllPeers(t *testing.T) {
}
func TestCreateLocalNode(t *testing.T) {
params.SetupTestConfigCleanup(t)
// Set the fulu fork epoch to something other than the far future epoch.
initFuluForkEpoch := params.BeaconConfig().FuluForkEpoch
params.BeaconConfig().FuluForkEpoch = 42
defer func() {
params.BeaconConfig().FuluForkEpoch = initFuluForkEpoch
}()
testCases := []struct {
name string
cfg *Config
@@ -264,11 +273,11 @@ func TestCreateLocalNode(t *testing.T) {
for _, tt := range testCases {
t.Run(tt.name, func(t *testing.T) {
// Define ports.
// Define ports. Use unique ports since this test validates ENR content.
const (
udpPort = 2000
tcpPort = 3000
quicPort = 3000
udpPort = 3100
tcpPort = 3101
quicPort = 3102
)
custodyRequirement := params.BeaconConfig().CustodyRequirement
@@ -344,13 +353,19 @@ func TestCreateLocalNode(t *testing.T) {
}
func TestRebootDiscoveryListener(t *testing.T) {
port := 1024
ipAddr, pkey := createAddrAndPrivKey(t)
db := testDB.SetupDB(t)
custodyInfoSet := make(chan struct{})
close(custodyInfoSet)
s := &Service{
ctx: t.Context(),
genesisTime: time.Now(),
genesisValidatorsRoot: bytesutil.PadTo([]byte{'A'}, 32),
cfg: &Config{UDPPort: uint(port)},
cfg: &Config{UDPPort: 0, DB: db}, // Use 0 to let OS assign an available port
custodyInfo: &custodyInfo{},
custodyInfoSet: custodyInfoSet,
}
createListener := func() (*discover.UDPv5, error) {
@@ -379,11 +394,17 @@ func TestRebootDiscoveryListener(t *testing.T) {
func TestMultiAddrsConversion_InvalidIPAddr(t *testing.T) {
addr := net.ParseIP("invalidIP")
_, pkey := createAddrAndPrivKey(t)
custodyInfoSet := make(chan struct{})
close(custodyInfoSet)
s := &Service{
ctx: t.Context(),
genesisTime: time.Now(),
genesisValidatorsRoot: bytesutil.PadTo([]byte{'A'}, 32),
cfg: &Config{},
custodyInfo: &custodyInfo{},
custodyInfoSet: custodyInfoSet,
}
node, err := s.createLocalNode(pkey, addr, 0, 0, 0)
require.NoError(t, err)
@@ -394,15 +415,23 @@ func TestMultiAddrsConversion_InvalidIPAddr(t *testing.T) {
func TestMultiAddrConversion_OK(t *testing.T) {
hook := logTest.NewGlobal()
ipAddr, pkey := createAddrAndPrivKey(t)
db := testDB.SetupDB(t)
custodyInfoSet := make(chan struct{})
close(custodyInfoSet)
s := &Service{
ctx: t.Context(),
cfg: &Config{
UDPPort: 2000,
TCPPort: 3000,
QUICPort: 3000,
UDPPort: 0, // Use 0 to let OS assign an available port
TCPPort: 0,
QUICPort: 0,
DB: db,
},
genesisTime: time.Now(),
genesisValidatorsRoot: bytesutil.PadTo([]byte{'A'}, 32),
custodyInfo: &custodyInfo{},
custodyInfoSet: custodyInfoSet,
}
listener, err := s.createListener(ipAddr, pkey)
require.NoError(t, err)
@@ -472,13 +501,20 @@ func TestHostIsResolved(t *testing.T) {
"2001:4860:4860::8844": true,
}
db := testDB.SetupDB(t)
custodyInfoSet := make(chan struct{})
close(custodyInfoSet)
s := &Service{
ctx: t.Context(),
cfg: &Config{
HostDNS: host,
DB: db,
},
genesisTime: time.Now(),
genesisValidatorsRoot: bytesutil.PadTo([]byte{'A'}, 32),
custodyInfo: &custodyInfo{},
custodyInfoSet: custodyInfoSet,
}
ip, key := createAddrAndPrivKey(t)
list, err := s.createListener(ip, key)
@@ -540,15 +576,21 @@ func TestOutboundPeerThreshold(t *testing.T) {
}
func TestUDPMultiAddress(t *testing.T) {
port := 6500
ipAddr, pkey := createAddrAndPrivKey(t)
genesisTime := time.Now()
genesisValidatorsRoot := make([]byte, 32)
db := testDB.SetupDB(t)
custodyInfoSet := make(chan struct{})
close(custodyInfoSet)
s := &Service{
cfg: &Config{UDPPort: uint(port)},
ctx: t.Context(),
cfg: &Config{UDPPort: 2500, DB: db},
genesisTime: genesisTime,
genesisValidatorsRoot: genesisValidatorsRoot,
custodyInfo: &custodyInfo{},
custodyInfoSet: custodyInfoSet,
}
createListener := func() (*discover.UDPv5, error) {
@@ -562,7 +604,7 @@ func TestUDPMultiAddress(t *testing.T) {
multiAddresses, err := s.DiscoveryAddresses()
require.NoError(t, err)
require.Equal(t, true, len(multiAddresses) > 0)
assert.Equal(t, true, strings.Contains(multiAddresses[0].String(), fmt.Sprintf("%d", port)))
assert.Equal(t, true, strings.Contains(multiAddresses[0].String(), fmt.Sprintf("%d", 2500)))
assert.Equal(t, true, strings.Contains(multiAddresses[0].String(), "udp"))
}
@@ -912,7 +954,7 @@ func TestRefreshPersistentSubnets(t *testing.T) {
actualPingCount++
return nil
},
cfg: &Config{UDPPort: 2000, DB: testDB.SetupDB(t)},
cfg: &Config{UDPPort: 0, DB: testDB.SetupDB(t)}, // Use 0 to let OS assign an available port
peers: p2p.Peers(),
genesisTime: time.Now().Add(-time.Duration(tc.epochSinceGenesis*secondsPerEpoch) * time.Second),
genesisValidatorsRoot: bytesutil.PadTo([]byte{'A'}, 32),

View File

@@ -58,14 +58,13 @@ func TestService_Stop_DontPanicIfDv5ListenerIsNotInited(t *testing.T) {
}
func TestService_Start_OnlyStartsOnce(t *testing.T) {
params.SetupTestConfigCleanup(t)
hook := logTest.NewGlobal()
cs := startup.NewClockSynchronizer()
cfg := &Config{
UDPPort: 2000,
TCPPort: 3000,
QUICPort: 3000,
UDPPort: 0, // Use 0 to let OS assign an available port
TCPPort: 0,
QUICPort: 0,
ClockWaiter: cs,
DB: testDB.SetupDB(t),
}
@@ -73,6 +72,7 @@ func TestService_Start_OnlyStartsOnce(t *testing.T) {
require.NoError(t, err)
s.dv5Listener = testp2p.NewMockListener(nil, nil)
s.custodyInfo = &custodyInfo{}
close(s.custodyInfoSet)
exitRoutine := make(chan bool)
go func() {
s.Start()
@@ -111,9 +111,9 @@ func TestService_Start_NoDiscoverFlag(t *testing.T) {
cs := startup.NewClockSynchronizer()
cfg := &Config{
UDPPort: 2000,
TCPPort: 3000,
QUICPort: 3000,
UDPPort: 0, // Use 0 to let OS assign an available port
TCPPort: 0,
QUICPort: 0,
StateNotifier: &mock.MockStateNotifier{},
NoDiscovery: true, // <-- no s.dv5Listener is created
ClockWaiter: cs,
@@ -147,12 +147,11 @@ func TestService_Start_NoDiscoverFlag(t *testing.T) {
func TestListenForNewNodes(t *testing.T) {
const (
port = uint(2000)
bootPort = uint(2200) // Use specific port for bootnode ENR
testPollingPeriod = 1 * time.Second
peerCount = 5
)
params.SetupTestConfigCleanup(t)
db := testDB.SetupDB(t)
// Setup bootnode.
@@ -160,7 +159,7 @@ func TestListenForNewNodes(t *testing.T) {
StateNotifier: &mock.MockStateNotifier{},
PingInterval: testPingInterval,
DisableLivenessCheck: true,
UDPPort: port,
UDPPort: bootPort,
DB: db,
}
@@ -171,10 +170,13 @@ func TestListenForNewNodes(t *testing.T) {
s := &Service{
cfg: cfg,
ctx: t.Context(),
genesisTime: genesisTime,
genesisValidatorsRoot: gvr[:],
custodyInfo: &custodyInfo{},
custodyInfoSet: make(chan struct{}),
}
close(s.custodyInfoSet)
bootListener, err := s.createListener(ipAddr, pkey)
require.NoError(t, err)
@@ -199,25 +201,29 @@ func TestListenForNewNodes(t *testing.T) {
hosts := make([]host.Host, 0, peerCount)
for i := uint(1); i <= peerCount; i++ {
peerPort := bootPort + i
cfg = &Config{
Discv5BootStrapAddrs: []string{bootNode.String()},
PingInterval: testPingInterval,
DisableLivenessCheck: true,
MaxPeers: peerCount,
ClockWaiter: cs,
UDPPort: port + i,
TCPPort: port + i,
UDPPort: peerPort,
TCPPort: peerPort,
DB: db,
}
h, pkey, ipAddr := createHost(t, port+i)
h, pkey, ipAddr := createHost(t, peerPort)
s := &Service{
cfg: cfg,
ctx: t.Context(),
genesisTime: genesisTime,
genesisValidatorsRoot: gvr[:],
custodyInfo: &custodyInfo{},
custodyInfoSet: make(chan struct{}),
}
close(s.custodyInfoSet)
listener, err := s.startDiscoveryV5(ipAddr, pkey)
require.NoError(t, err, "Could not start discovery for node")
@@ -247,6 +253,7 @@ func TestListenForNewNodes(t *testing.T) {
s, err = NewService(t.Context(), cfg)
require.NoError(t, err)
s.custodyInfo = &custodyInfo{}
close(s.custodyInfoSet)
go s.Start()
@@ -270,7 +277,6 @@ func TestListenForNewNodes(t *testing.T) {
}
func TestPeer_Disconnect(t *testing.T) {
params.SetupTestConfigCleanup(t)
h1, _, _ := createHost(t, 5000)
defer func() {
if err := h1.Close(); err != nil {

View File

@@ -69,10 +69,13 @@ func TestStartDiscV5_FindAndDialPeersWithSubnet(t *testing.T) {
bootNodeService := &Service{
cfg: &Config{UDPPort: 2000, TCPPort: 3000, QUICPort: 3000, DisableLivenessCheck: true, PingInterval: testPingInterval},
ctx: ctx,
genesisTime: genesisTime,
genesisValidatorsRoot: params.BeaconConfig().GenesisValidatorsRoot[:],
custodyInfo: &custodyInfo{},
custodyInfoSet: make(chan struct{}),
}
close(bootNodeService.custodyInfoSet)
bootNodeForkDigest, err := bootNodeService.currentForkDigest()
require.NoError(t, err)
@@ -102,6 +105,7 @@ func TestStartDiscV5_FindAndDialPeersWithSubnet(t *testing.T) {
PingInterval: testPingInterval,
DisableLivenessCheck: true,
DB: db,
DataDir: t.TempDir(), // Unique data dir for each peer
})
require.NoError(t, err)
@@ -109,6 +113,7 @@ func TestStartDiscV5_FindAndDialPeersWithSubnet(t *testing.T) {
service.genesisTime = genesisTime
service.genesisValidatorsRoot = params.BeaconConfig().GenesisValidatorsRoot[:]
service.custodyInfo = &custodyInfo{}
close(service.custodyInfoSet)
nodeForkDigest, err := service.currentForkDigest()
require.NoError(t, err)
@@ -152,6 +157,7 @@ func TestStartDiscV5_FindAndDialPeersWithSubnet(t *testing.T) {
TCPPort: 3010,
QUICPort: 3010,
DB: db,
DataDir: t.TempDir(), // Unique data dir for test service
}
service, err := NewService(t.Context(), cfg)
@@ -160,6 +166,7 @@ func TestStartDiscV5_FindAndDialPeersWithSubnet(t *testing.T) {
service.genesisTime = genesisTime
service.genesisValidatorsRoot = params.BeaconConfig().GenesisValidatorsRoot[:]
service.custodyInfo = &custodyInfo{}
close(service.custodyInfoSet)
service.Start()
defer func() {

View File

@@ -33,7 +33,6 @@ go_library(
"//beacon-chain/rpc/core:go_default_library",
"//beacon-chain/rpc/eth/beacon:go_default_library",
"//beacon-chain/rpc/eth/blob:go_default_library",
"//beacon-chain/rpc/eth/builder:go_default_library",
"//beacon-chain/rpc/eth/config:go_default_library",
"//beacon-chain/rpc/eth/debug:go_default_library",
"//beacon-chain/rpc/eth/events:go_default_library",

View File

@@ -8,7 +8,6 @@ import (
"github.com/OffchainLabs/prysm/v6/beacon-chain/rpc/core"
"github.com/OffchainLabs/prysm/v6/beacon-chain/rpc/eth/beacon"
"github.com/OffchainLabs/prysm/v6/beacon-chain/rpc/eth/blob"
"github.com/OffchainLabs/prysm/v6/beacon-chain/rpc/eth/builder"
"github.com/OffchainLabs/prysm/v6/beacon-chain/rpc/eth/config"
"github.com/OffchainLabs/prysm/v6/beacon-chain/rpc/eth/debug"
"github.com/OffchainLabs/prysm/v6/beacon-chain/rpc/eth/events"
@@ -90,7 +89,6 @@ func (s *Service) endpoints(
) []endpoint {
endpoints := make([]endpoint, 0)
endpoints = append(endpoints, s.rewardsEndpoints(blocker, stater, rewardFetcher)...)
endpoints = append(endpoints, s.builderEndpoints(stater)...)
endpoints = append(endpoints, s.blobEndpoints(blocker)...)
endpoints = append(endpoints, s.validatorEndpoints(validatorServer, stater, coreService, rewardFetcher)...)
endpoints = append(endpoints, s.nodeEndpoints()...)
@@ -160,29 +158,6 @@ func (s *Service) rewardsEndpoints(blocker lookup.Blocker, stater lookup.Stater,
}
}
func (s *Service) builderEndpoints(stater lookup.Stater) []endpoint {
server := &builder.Server{
FinalizationFetcher: s.cfg.FinalizationFetcher,
OptimisticModeFetcher: s.cfg.OptimisticModeFetcher,
Stater: stater,
}
const namespace = "builder"
return []endpoint{
{
// Deprecated: use SSE from /eth/v1/events for `Payload Attributes` instead
template: "/eth/v1/builder/states/{state_id}/expected_withdrawals",
name: namespace + ".ExpectedWithdrawals",
middleware: []middleware.Middleware{
middleware.AcceptHeaderHandler([]string{api.JsonMediaType, api.OctetStreamMediaType}),
middleware.AcceptEncodingHeaderHandler(),
},
handler: server.ExpectedWithdrawals,
methods: []string{http.MethodGet},
},
}
}
func (s *Service) blobEndpoints(blocker lookup.Blocker) []endpoint {
server := &blob.Server{
Blocker: blocker,
@@ -248,17 +223,6 @@ func (s *Service) validatorEndpoints(
const namespace = "validator"
return []endpoint{
{
// Deprecated: use /eth/v2/validator/aggregate_attestation instead
template: "/eth/v1/validator/aggregate_attestation",
name: namespace + ".GetAggregateAttestation",
middleware: []middleware.Middleware{
middleware.AcceptHeaderHandler([]string{api.JsonMediaType}),
middleware.AcceptEncodingHeaderHandler(),
},
handler: server.GetAggregateAttestation,
methods: []string{http.MethodGet},
},
{
template: "/eth/v2/validator/aggregate_attestation",
name: namespace + ".GetAggregateAttestationV2",
@@ -280,18 +244,6 @@ func (s *Service) validatorEndpoints(
handler: server.SubmitContributionAndProofs,
methods: []string{http.MethodPost},
},
{
// Deprecated: use /eth/v2/validator/aggregate_and_proofs instead
template: "/eth/v1/validator/aggregate_and_proofs",
name: namespace + ".SubmitAggregateAndProofs",
middleware: []middleware.Middleware{
middleware.ContentTypeHandler([]string{api.JsonMediaType}),
middleware.AcceptHeaderHandler([]string{api.JsonMediaType}),
middleware.AcceptEncodingHeaderHandler(),
},
handler: server.SubmitAggregateAndProofs,
methods: []string{http.MethodPost},
},
{
template: "/eth/v2/validator/aggregate_and_proofs",
name: namespace + ".SubmitAggregateAndProofsV2",
@@ -618,30 +570,6 @@ func (s *Service) beaconEndpoints(
handler: server.GetRandao,
methods: []string{http.MethodGet},
},
{
// Deprecated: use /eth/v2/beacon/blocks instead
template: "/eth/v1/beacon/blocks",
name: namespace + ".PublishBlock",
middleware: []middleware.Middleware{
middleware.ContentTypeHandler([]string{api.JsonMediaType, api.OctetStreamMediaType}),
middleware.AcceptHeaderHandler([]string{api.JsonMediaType}),
middleware.AcceptEncodingHeaderHandler(),
},
handler: server.PublishBlock,
methods: []string{http.MethodPost},
},
{
// Deprecated: use /eth/v2/beacon/blinded_blocks instead
template: "/eth/v1/beacon/blinded_blocks",
name: namespace + ".PublishBlindedBlock",
middleware: []middleware.Middleware{
middleware.ContentTypeHandler([]string{api.JsonMediaType, api.OctetStreamMediaType}),
middleware.AcceptHeaderHandler([]string{api.JsonMediaType}),
middleware.AcceptEncodingHeaderHandler(),
},
handler: server.PublishBlindedBlock,
methods: []string{http.MethodPost},
},
{
template: "/eth/v2/beacon/blocks",
name: namespace + ".PublishBlockV2",
@@ -674,17 +602,6 @@ func (s *Service) beaconEndpoints(
handler: server.GetBlockV2,
methods: []string{http.MethodGet},
},
{
// Deprecated: use /eth/v2/beacon/blocks/{block_id}/attestations instead
template: "/eth/v1/beacon/blocks/{block_id}/attestations",
name: namespace + ".GetBlockAttestations",
middleware: []middleware.Middleware{
middleware.AcceptHeaderHandler([]string{api.JsonMediaType}),
middleware.AcceptEncodingHeaderHandler(),
},
handler: server.GetBlockAttestations,
methods: []string{http.MethodGet},
},
{
template: "/eth/v2/beacon/blocks/{block_id}/attestations",
name: namespace + ".GetBlockAttestationsV2",
@@ -715,17 +632,6 @@ func (s *Service) beaconEndpoints(
handler: server.GetBlockRoot,
methods: []string{http.MethodGet},
},
{
// Deprecated: use /eth/v2/beacon/pool/attestations instead
template: "/eth/v1/beacon/pool/attestations",
name: namespace + ".ListAttestations",
middleware: []middleware.Middleware{
middleware.AcceptHeaderHandler([]string{api.JsonMediaType}),
middleware.AcceptEncodingHeaderHandler(),
},
handler: server.ListAttestations,
methods: []string{http.MethodGet},
},
{
template: "/eth/v2/beacon/pool/attestations",
name: namespace + ".ListAttestationsV2",
@@ -736,17 +642,6 @@ func (s *Service) beaconEndpoints(
handler: server.ListAttestationsV2,
methods: []string{http.MethodGet},
},
{
template: "/eth/v1/beacon/pool/attestations",
name: namespace + ".SubmitAttestations",
middleware: []middleware.Middleware{
middleware.ContentTypeHandler([]string{api.JsonMediaType}),
middleware.AcceptHeaderHandler([]string{api.JsonMediaType}),
middleware.AcceptEncodingHeaderHandler(),
},
handler: server.SubmitAttestations,
methods: []string{http.MethodPost},
},
{
template: "/eth/v2/beacon/pool/attestations",
name: namespace + ".SubmitAttestationsV2",
@@ -811,17 +706,6 @@ func (s *Service) beaconEndpoints(
handler: server.SubmitBLSToExecutionChanges,
methods: []string{http.MethodPost},
},
{
// Deprecated: use /eth/v2/beacon/pool/attester_slashings instead
template: "/eth/v1/beacon/pool/attester_slashings",
name: namespace + ".GetAttesterSlashings",
middleware: []middleware.Middleware{
middleware.AcceptHeaderHandler([]string{api.JsonMediaType}),
middleware.AcceptEncodingHeaderHandler(),
},
handler: server.GetAttesterSlashings,
methods: []string{http.MethodGet},
},
{
template: "/eth/v2/beacon/pool/attester_slashings",
name: namespace + ".GetAttesterSlashingsV2",
@@ -832,17 +716,6 @@ func (s *Service) beaconEndpoints(
handler: server.GetAttesterSlashingsV2,
methods: []string{http.MethodGet},
},
{
template: "/eth/v1/beacon/pool/attester_slashings",
name: namespace + ".SubmitAttesterSlashings",
middleware: []middleware.Middleware{
middleware.ContentTypeHandler([]string{api.JsonMediaType}),
middleware.AcceptHeaderHandler([]string{api.JsonMediaType}),
middleware.AcceptEncodingHeaderHandler(),
},
handler: server.SubmitAttesterSlashings,
methods: []string{http.MethodPost},
},
{
template: "/eth/v2/beacon/pool/attester_slashings",
name: namespace + ".SubmitAttesterSlashingsV2",
@@ -957,17 +830,6 @@ func (s *Service) beaconEndpoints(
handler: server.GetValidatorIdentities,
methods: []string{http.MethodPost},
},
{
// Deprecated: no longer needed post Electra
template: "/eth/v1/beacon/deposit_snapshot",
name: namespace + ".GetDepositSnapshot",
middleware: []middleware.Middleware{
middleware.AcceptHeaderHandler([]string{api.JsonMediaType}),
middleware.AcceptEncodingHeaderHandler(),
},
handler: server.GetDepositSnapshot,
methods: []string{http.MethodGet},
},
{
template: "/eth/v1/beacon/states/{state_id}/pending_deposits",
name: namespace + ".GetPendingDeposits",

View File

@@ -35,20 +35,14 @@ func Test_endpoints(t *testing.T) {
"/eth/v1/beacon/states/{state_id}/proposer_lookahead": {http.MethodGet},
"/eth/v1/beacon/headers": {http.MethodGet},
"/eth/v1/beacon/headers/{block_id}": {http.MethodGet},
"/eth/v1/beacon/blinded_blocks": {http.MethodPost},
"/eth/v2/beacon/blinded_blocks": {http.MethodPost},
"/eth/v1/beacon/blocks": {http.MethodPost},
"/eth/v2/beacon/blocks": {http.MethodPost},
"/eth/v2/beacon/blocks/{block_id}": {http.MethodGet},
"/eth/v1/beacon/blocks/{block_id}/root": {http.MethodGet},
"/eth/v1/beacon/blocks/{block_id}/attestations": {http.MethodGet},
"/eth/v2/beacon/blocks/{block_id}/attestations": {http.MethodGet},
"/eth/v1/beacon/blob_sidecars/{block_id}": {http.MethodGet},
"/eth/v1/beacon/deposit_snapshot": {http.MethodGet},
"/eth/v1/beacon/blinded_blocks/{block_id}": {http.MethodGet},
"/eth/v1/beacon/pool/attestations": {http.MethodGet, http.MethodPost},
"/eth/v2/beacon/pool/attestations": {http.MethodGet, http.MethodPost},
"/eth/v1/beacon/pool/attester_slashings": {http.MethodGet, http.MethodPost},
"/eth/v2/beacon/pool/attester_slashings": {http.MethodGet, http.MethodPost},
"/eth/v1/beacon/pool/proposer_slashings": {http.MethodGet, http.MethodPost},
"/eth/v1/beacon/pool/sync_committees": {http.MethodPost},
@@ -64,10 +58,6 @@ func Test_endpoints(t *testing.T) {
"/eth/v1/beacon/light_client/optimistic_update": {http.MethodGet},
}
builderRoutes := map[string][]string{
"/eth/v1/builder/states/{state_id}/expected_withdrawals": {http.MethodGet},
}
blobRoutes := map[string][]string{
"/eth/v1/beacon/blob_sidecars/{block_id}": {http.MethodGet},
"/eth/v1/beacon/blobs/{block_id}": {http.MethodGet},
@@ -106,9 +96,7 @@ func Test_endpoints(t *testing.T) {
"/eth/v1/validator/duties/sync/{epoch}": {http.MethodPost},
"/eth/v3/validator/blocks/{slot}": {http.MethodGet},
"/eth/v1/validator/attestation_data": {http.MethodGet},
"/eth/v1/validator/aggregate_attestation": {http.MethodGet},
"/eth/v2/validator/aggregate_attestation": {http.MethodGet},
"/eth/v1/validator/aggregate_and_proofs": {http.MethodPost},
"/eth/v2/validator/aggregate_and_proofs": {http.MethodPost},
"/eth/v1/validator/beacon_committee_subscriptions": {http.MethodPost},
"/eth/v1/validator/sync_committee_subscriptions": {http.MethodPost},
@@ -182,7 +170,7 @@ func Test_endpoints(t *testing.T) {
}
expectedRoutes := make(map[string][]string)
for _, m := range []map[string][]string{
beaconRoutes, builderRoutes, configRoutes, debugRoutes, eventsRoutes,
beaconRoutes, configRoutes, debugRoutes, eventsRoutes,
nodeRoutes, validatorRoutes, rewardsRoutes, blobRoutes,
prysmValidatorRoutes, prysmNodeRoutes, prysmBeaconRoutes,
} {

View File

@@ -20,7 +20,6 @@ go_library(
"//beacon-chain/blockchain:go_default_library",
"//beacon-chain/blockchain/kzg:go_default_library",
"//beacon-chain/cache:go_default_library",
"//beacon-chain/cache/depositsnapshot:go_default_library",
"//beacon-chain/core/altair:go_default_library",
"//beacon-chain/core/blocks:go_default_library",
"//beacon-chain/core/feed:go_default_library",
@@ -86,7 +85,6 @@ go_test(
"//api/server/structs:go_default_library",
"//beacon-chain/blockchain/kzg:go_default_library",
"//beacon-chain/blockchain/testing:go_default_library",
"//beacon-chain/cache/depositsnapshot:go_default_library",
"//beacon-chain/core/signing:go_default_library",
"//beacon-chain/core/time:go_default_library",
"//beacon-chain/core/transition:go_default_library",

View File

@@ -14,7 +14,6 @@ import (
"github.com/OffchainLabs/prysm/v6/api"
"github.com/OffchainLabs/prysm/v6/api/server/structs"
"github.com/OffchainLabs/prysm/v6/beacon-chain/blockchain/kzg"
"github.com/OffchainLabs/prysm/v6/beacon-chain/cache/depositsnapshot"
corehelpers "github.com/OffchainLabs/prysm/v6/beacon-chain/core/helpers"
"github.com/OffchainLabs/prysm/v6/beacon-chain/core/transition"
"github.com/OffchainLabs/prysm/v6/beacon-chain/db/filters"
@@ -293,35 +292,6 @@ func (s *Server) getBlockResponseBodyJson(ctx context.Context, blk interfaces.Re
}, nil
}
// Deprecated: use GetBlockAttestationsV2 instead
// GetBlockAttestations retrieves attestation included in requested block.
func (s *Server) GetBlockAttestations(w http.ResponseWriter, r *http.Request) {
ctx, span := trace.StartSpan(r.Context(), "beacon.GetBlockAttestations")
defer span.End()
blk, isOptimistic, root := s.blockData(ctx, w, r)
if blk == nil {
return
}
consensusAtts := blk.Block().Body().Attestations()
atts := make([]*structs.Attestation, len(consensusAtts))
for i, att := range consensusAtts {
a, ok := att.(*eth.Attestation)
if ok {
atts[i] = structs.AttFromConsensus(a)
} else {
httputil.HandleError(w, fmt.Sprintf("unable to convert consensus attestations of type %T", att), http.StatusInternalServerError)
return
}
}
resp := &structs.GetBlockAttestationsResponse{
Data: atts,
ExecutionOptimistic: isOptimistic,
Finalized: s.FinalizationFetcher.IsFinalized(ctx, root),
}
httputil.WriteJson(w, resp)
}
// GetBlockAttestationsV2 retrieves attestation included in requested block.
func (s *Server) GetBlockAttestationsV2(w http.ResponseWriter, r *http.Request) {
ctx, span := trace.StartSpan(r.Context(), "beacon.GetBlockAttestationsV2")
@@ -396,28 +366,6 @@ func (s *Server) blockData(ctx context.Context, w http.ResponseWriter, r *http.R
return blk, isOptimistic, root
}
// Deprecated: use PublishBlindedBlockV2 instead
// PublishBlindedBlock instructs the beacon node to use the components of the `SignedBlindedBeaconBlock` to construct
// and publish a SignedBeaconBlock by swapping out the transactions_root for the corresponding full list of `transactions`.
// The beacon node should broadcast a newly constructed SignedBeaconBlock to the beacon network, to be included in the
// beacon chain. The beacon node is not required to validate the signed BeaconBlock, and a successful response (20X)
// only indicates that the broadcast has been successful. The beacon node is expected to integrate the new block into
// its state, and therefore validate the block internally, however blocks which fail the validation are still broadcast
// but a different status code is returned (202). Pre-Bellatrix, this endpoint will accept a SignedBeaconBlock. After
// Deneb, this additionally instructs the beacon node to broadcast all given signed blobs.
func (s *Server) PublishBlindedBlock(w http.ResponseWriter, r *http.Request) {
ctx, span := trace.StartSpan(r.Context(), "beacon.PublishBlindedBlock")
defer span.End()
if shared.IsSyncing(r.Context(), w, s.SyncChecker, s.HeadFetcher, s.TimeFetcher, s.OptimisticModeFetcher) {
return
}
if httputil.IsRequestSsz(r) {
s.publishBlindedBlockSSZ(ctx, w, r, false)
} else {
s.publishBlindedBlock(ctx, w, r, false)
}
}
// PublishBlindedBlockV2 instructs the beacon node to use the components of the `SignedBlindedBeaconBlock` to construct and publish a
// `SignedBeaconBlock` by swapping out the `transactions_root` for the corresponding full list of `transactions`.
// The beacon node should broadcast a newly constructed `SignedBeaconBlock` to the beacon network,
@@ -627,28 +575,6 @@ func decodeBlindedBellatrixJSON(body []byte) (*eth.GenericSignedBeaconBlock, err
)
}
// Deprecated: use PublishBlockV2 instead
// PublishBlock instructs the beacon node to broadcast a newly signed beacon block to the beacon network,
// to be included in the beacon chain. A success response (20x) indicates that the block
// passed gossip validation and was successfully broadcast onto the network.
// The beacon node is also expected to integrate the block into state, but may broadcast it
// before doing so, so as to aid timely delivery of the block. Should the block fail full
// validation, a separate success response code (202) is used to indicate that the block was
// successfully broadcast but failed integration. After Deneb, this additionally instructs the
// beacon node to broadcast all given signed blobs.
func (s *Server) PublishBlock(w http.ResponseWriter, r *http.Request) {
ctx, span := trace.StartSpan(r.Context(), "beacon.PublishBlock")
defer span.End()
if shared.IsSyncing(r.Context(), w, s.SyncChecker, s.HeadFetcher, s.TimeFetcher, s.OptimisticModeFetcher) {
return
}
if httputil.IsRequestSsz(r) {
s.publishBlockSSZ(ctx, w, r, false)
} else {
s.publishBlock(ctx, w, r, false)
}
}
// PublishBlockV2 instructs the beacon node to broadcast a newly signed beacon block to the beacon network,
// to be included in the beacon chain. A success response (20x) indicates that the block
// passed gossip validation and was successfully broadcast onto the network.
@@ -1589,48 +1515,6 @@ func (s *Server) GetGenesis(w http.ResponseWriter, r *http.Request) {
httputil.WriteJson(w, resp)
}
// Deprecated: no longer needed post Electra
// GetDepositSnapshot retrieves the EIP-4881 Deposit Tree Snapshot. Either a JSON or,
// if the Accept header was added, bytes serialized by SSZ will be returned.
func (s *Server) GetDepositSnapshot(w http.ResponseWriter, r *http.Request) {
ctx, span := trace.StartSpan(r.Context(), "beacon.GetDepositSnapshot")
defer span.End()
eth1data, err := s.BeaconDB.ExecutionChainData(ctx)
if err != nil {
httputil.HandleError(w, "Could not retrieve execution chain data: "+err.Error(), http.StatusInternalServerError)
return
}
if eth1data == nil {
httputil.HandleError(w, "Could not retrieve execution chain data: empty Eth1Data", http.StatusInternalServerError)
return
}
snapshot := eth1data.DepositSnapshot
if snapshot == nil || len(snapshot.Finalized) == 0 {
httputil.HandleError(w, "No finalized snapshot available", http.StatusNotFound)
return
}
if len(snapshot.Finalized) > depositsnapshot.DepositContractDepth {
httputil.HandleError(w, "Retrieved invalid deposit snapshot", http.StatusInternalServerError)
return
}
if httputil.RespondWithSsz(r) {
sszData, err := snapshot.MarshalSSZ()
if err != nil {
httputil.HandleError(w, "Could not marshal deposit snapshot into SSZ: "+err.Error(), http.StatusInternalServerError)
return
}
httputil.WriteSsz(w, sszData)
return
}
httputil.WriteJson(
w,
&structs.GetDepositSnapshotResponse{
Data: structs.DepositSnapshotFromConsensus(snapshot),
},
)
}
// Broadcast blob sidecars even if the block of the same slot has been imported.
// To ensure safety, we will only broadcast blob sidecars if the header references the same block that was previously seen.
// Otherwise, a proposer could get slashed through a different blob sidecar header reference.

View File

@@ -34,58 +34,6 @@ import (
const broadcastBLSChangesRateLimit = 128
// Deprecated: use ListAttestationsV2 instead
// ListAttestations retrieves attestations known by the node but
// not necessarily incorporated into any block. Allows filtering by committee index or slot.
func (s *Server) ListAttestations(w http.ResponseWriter, r *http.Request) {
_, span := trace.StartSpan(r.Context(), "beacon.ListAttestations")
defer span.End()
rawSlot, slot, ok := shared.UintFromQuery(w, r, "slot", false)
if !ok {
return
}
rawCommitteeIndex, committeeIndex, ok := shared.UintFromQuery(w, r, "committee_index", false)
if !ok {
return
}
var attestations []eth.Att
if features.Get().EnableExperimentalAttestationPool {
attestations = s.AttestationCache.GetAll()
} else {
attestations = s.AttestationsPool.AggregatedAttestations()
unaggAtts := s.AttestationsPool.UnaggregatedAttestations()
attestations = append(attestations, unaggAtts...)
}
filteredAtts := make([]*structs.Attestation, 0, len(attestations))
for _, a := range attestations {
var includeAttestation bool
att, ok := a.(*eth.Attestation)
if !ok {
httputil.HandleError(w, fmt.Sprintf("Unable to convert attestation of type %T", a), http.StatusInternalServerError)
return
}
includeAttestation = shouldIncludeAttestation(att, rawSlot, slot, rawCommitteeIndex, committeeIndex)
if includeAttestation {
attStruct := structs.AttFromConsensus(att)
filteredAtts = append(filteredAtts, attStruct)
}
}
attsData, err := json.Marshal(filteredAtts)
if err != nil {
httputil.HandleError(w, "Could not marshal attestations: "+err.Error(), http.StatusInternalServerError)
return
}
httputil.WriteJson(w, &structs.ListAttestationsResponse{
Data: attsData,
})
}
// ListAttestationsV2 retrieves attestations known by the node but
// not necessarily incorporated into any block. Allows filtering by committee index or slot.
func (s *Server) ListAttestationsV2(w http.ResponseWriter, r *http.Request) {
@@ -176,49 +124,6 @@ func shouldIncludeAttestation(
return committeeIndexMatch && slotMatch
}
// SubmitAttestations submits an attestation object to node. If the attestation passes all validation
// constraints, node MUST publish the attestation on an appropriate subnet.
func (s *Server) SubmitAttestations(w http.ResponseWriter, r *http.Request) {
ctx, span := trace.StartSpan(r.Context(), "beacon.SubmitAttestations")
defer span.End()
var req structs.SubmitAttestationsRequest
err := json.NewDecoder(r.Body).Decode(&req.Data)
switch {
case errors.Is(err, io.EOF):
httputil.HandleError(w, "No data submitted", http.StatusBadRequest)
return
case err != nil:
httputil.HandleError(w, "Could not decode request body: "+err.Error(), http.StatusBadRequest)
return
}
attFailures, failedBroadcasts, err := s.handleAttestations(ctx, req.Data)
if err != nil {
httputil.HandleError(w, err.Error(), http.StatusBadRequest)
return
}
if len(attFailures) > 0 {
failuresErr := &server.IndexedErrorContainer{
Code: http.StatusBadRequest,
Message: server.ErrIndexedValidationFail,
Failures: attFailures,
}
httputil.WriteError(w, failuresErr)
return
}
if len(failedBroadcasts) > 0 {
failuresErr := &server.IndexedErrorContainer{
Code: http.StatusInternalServerError,
Message: server.ErrIndexedBroadcastFail,
Failures: failedBroadcasts,
}
httputil.WriteError(w, failuresErr)
return
}
}
// SubmitAttestationsV2 submits an attestation object to node. If the attestation passes all validation
// constraints, node MUST publish the attestation on an appropriate subnet.
func (s *Server) SubmitAttestationsV2(w http.ResponseWriter, r *http.Request) {
@@ -749,36 +654,6 @@ func (s *Server) ListBLSToExecutionChanges(w http.ResponseWriter, r *http.Reques
})
}
// Deprecated: use GetAttesterSlashingsV2 instead
// GetAttesterSlashings retrieves attester slashings known by the node but
// not necessarily incorporated into any block.
func (s *Server) GetAttesterSlashings(w http.ResponseWriter, r *http.Request) {
ctx, span := trace.StartSpan(r.Context(), "beacon.GetAttesterSlashings")
defer span.End()
headState, err := s.ChainInfoFetcher.HeadStateReadOnly(ctx)
if err != nil {
httputil.HandleError(w, "Could not get head state: "+err.Error(), http.StatusInternalServerError)
return
}
sourceSlashings := s.SlashingsPool.PendingAttesterSlashings(ctx, headState, true /* return unlimited slashings */)
slashings := make([]*structs.AttesterSlashing, len(sourceSlashings))
for i, slashing := range sourceSlashings {
as, ok := slashing.(*eth.AttesterSlashing)
if !ok {
httputil.HandleError(w, fmt.Sprintf("Unable to convert slashing of type %T", slashing), http.StatusInternalServerError)
return
}
slashings[i] = structs.AttesterSlashingFromConsensus(as)
}
attBytes, err := json.Marshal(slashings)
if err != nil {
httputil.HandleError(w, fmt.Sprintf("Failed to marshal slashings: %v", err), http.StatusInternalServerError)
return
}
httputil.WriteJson(w, &structs.GetAttesterSlashingsResponse{Data: attBytes})
}
// GetAttesterSlashingsV2 retrieves attester slashings known by the node but
// not necessarily incorporated into any block, supporting both AttesterSlashing and AttesterSlashingElectra.
func (s *Server) GetAttesterSlashingsV2(w http.ResponseWriter, r *http.Request) {
@@ -830,31 +705,6 @@ func (s *Server) GetAttesterSlashingsV2(w http.ResponseWriter, r *http.Request)
httputil.WriteJson(w, resp)
}
// SubmitAttesterSlashings submits an attester slashing object to node's pool and
// if passes validation node MUST broadcast it to network.
func (s *Server) SubmitAttesterSlashings(w http.ResponseWriter, r *http.Request) {
ctx, span := trace.StartSpan(r.Context(), "beacon.SubmitAttesterSlashings")
defer span.End()
var req structs.AttesterSlashing
err := json.NewDecoder(r.Body).Decode(&req)
switch {
case errors.Is(err, io.EOF):
httputil.HandleError(w, "No data submitted", http.StatusBadRequest)
return
case err != nil:
httputil.HandleError(w, "Could not decode request body: "+err.Error(), http.StatusBadRequest)
return
}
slashing, err := req.ToConsensus()
if err != nil {
httputil.HandleError(w, "Could not convert request slashing to consensus slashing: "+err.Error(), http.StatusBadRequest)
return
}
s.submitAttesterSlashing(w, ctx, slashing)
}
// SubmitAttesterSlashingsV2 submits an attester slashing object to node's pool and
// if passes validation node MUST broadcast it to network.
func (s *Server) SubmitAttesterSlashingsV2(w http.ResponseWriter, r *http.Request) {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,47 +0,0 @@
load("@prysm//tools/go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = [
"handlers.go",
"server.go",
],
importpath = "github.com/OffchainLabs/prysm/v6/beacon-chain/rpc/eth/builder",
visibility = ["//visibility:public"],
deps = [
"//api/server/structs:go_default_library",
"//beacon-chain/blockchain:go_default_library",
"//beacon-chain/core/helpers:go_default_library",
"//beacon-chain/core/transition:go_default_library",
"//beacon-chain/rpc/lookup:go_default_library",
"//config/params:go_default_library",
"//consensus-types/primitives:go_default_library",
"//network/httputil:go_default_library",
"//proto/engine/v1:go_default_library",
"//time/slots:go_default_library",
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
"@com_github_pkg_errors//:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = ["handlers_test.go"],
embed = [":go_default_library"],
deps = [
"//api/server/structs:go_default_library",
"//beacon-chain/blockchain/testing:go_default_library",
"//beacon-chain/rpc/testutil:go_default_library",
"//beacon-chain/state:go_default_library",
"//config/params:go_default_library",
"//consensus-types/primitives:go_default_library",
"//crypto/bls:go_default_library",
"//network/httputil:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//testing/assert:go_default_library",
"//testing/require:go_default_library",
"//testing/util:go_default_library",
"//time/slots:go_default_library",
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
],
)

View File

@@ -1,132 +0,0 @@
package builder
import (
"fmt"
"net/http"
"strconv"
"github.com/OffchainLabs/prysm/v6/api/server/structs"
"github.com/OffchainLabs/prysm/v6/beacon-chain/core/helpers"
"github.com/OffchainLabs/prysm/v6/beacon-chain/core/transition"
"github.com/OffchainLabs/prysm/v6/config/params"
"github.com/OffchainLabs/prysm/v6/consensus-types/primitives"
"github.com/OffchainLabs/prysm/v6/network/httputil"
enginev1 "github.com/OffchainLabs/prysm/v6/proto/engine/v1"
"github.com/OffchainLabs/prysm/v6/time/slots"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/pkg/errors"
)
// Deprecated: use SSE from events for `payload attributes` instead
// ExpectedWithdrawals get the withdrawals computed from the specified state, that will be included in the block that gets built on the specified state.
func (s *Server) ExpectedWithdrawals(w http.ResponseWriter, r *http.Request) {
// Retrieve beacon state
stateId := r.PathValue("state_id")
if stateId == "" {
httputil.WriteError(w, &httputil.DefaultJsonError{
Message: "state_id is required in URL params",
Code: http.StatusBadRequest,
})
return
}
st, err := s.Stater.State(r.Context(), []byte(stateId))
if err != nil {
httputil.WriteError(w, handleWrapError(err, "could not retrieve state", http.StatusNotFound))
return
}
queryParam := r.URL.Query().Get("proposal_slot")
var proposalSlot primitives.Slot
if queryParam != "" {
pSlot, err := strconv.ParseUint(queryParam, 10, 64)
if err != nil {
httputil.WriteError(w, handleWrapError(err, "invalid proposal slot value", http.StatusBadRequest))
return
}
proposalSlot = primitives.Slot(pSlot)
} else {
proposalSlot = st.Slot() + 1
}
// Perform sanity checks on proposal slot before computing state
capellaStart, err := slots.EpochStart(params.BeaconConfig().CapellaForkEpoch)
if err != nil {
httputil.WriteError(w, handleWrapError(err, "could not calculate Capella start slot", http.StatusInternalServerError))
return
}
if proposalSlot < capellaStart {
httputil.WriteError(w, &httputil.DefaultJsonError{
Message: "expected withdrawals are not supported before Capella fork",
Code: http.StatusBadRequest,
})
return
}
if proposalSlot <= st.Slot() {
httputil.WriteError(w, &httputil.DefaultJsonError{
Message: fmt.Sprintf("proposal slot must be bigger than state slot. proposal slot: %d, state slot: %d", proposalSlot, st.Slot()),
Code: http.StatusBadRequest,
})
return
}
lookAheadLimit := uint64(params.BeaconConfig().SlotsPerEpoch.Mul(uint64(params.BeaconConfig().MaxSeedLookahead)))
if st.Slot().Add(lookAheadLimit) <= proposalSlot {
httputil.WriteError(w, &httputil.DefaultJsonError{
Message: fmt.Sprintf("proposal slot cannot be >= %d slots ahead of state slot", lookAheadLimit),
Code: http.StatusBadRequest,
})
return
}
// Get metadata for response
isOptimistic, err := s.OptimisticModeFetcher.IsOptimistic(r.Context())
if err != nil {
httputil.WriteError(w, handleWrapError(err, "could not get optimistic mode info", http.StatusInternalServerError))
return
}
root, err := helpers.BlockRootAtSlot(st, slots.PrevSlot(st.Slot()))
if err != nil {
httputil.WriteError(w, handleWrapError(err, "could not get block root", http.StatusInternalServerError))
return
}
var blockRoot = [32]byte(root)
isFinalized := s.FinalizationFetcher.IsFinalized(r.Context(), blockRoot)
// Advance state forward to proposal slot
st, err = transition.ProcessSlots(r.Context(), st, proposalSlot)
if err != nil {
httputil.WriteError(w, &httputil.DefaultJsonError{
Message: "could not process slots",
Code: http.StatusInternalServerError,
})
return
}
withdrawals, _, err := st.ExpectedWithdrawals()
if err != nil {
httputil.WriteError(w, &httputil.DefaultJsonError{
Message: "could not get expected withdrawals",
Code: http.StatusInternalServerError,
})
return
}
httputil.WriteJson(w, &structs.ExpectedWithdrawalsResponse{
ExecutionOptimistic: isOptimistic,
Finalized: isFinalized,
Data: buildExpectedWithdrawalsData(withdrawals),
})
}
func buildExpectedWithdrawalsData(withdrawals []*enginev1.Withdrawal) []*structs.ExpectedWithdrawal {
data := make([]*structs.ExpectedWithdrawal, len(withdrawals))
for i, withdrawal := range withdrawals {
data[i] = &structs.ExpectedWithdrawal{
Address: hexutil.Encode(withdrawal.Address),
Amount: strconv.FormatUint(withdrawal.Amount, 10),
Index: strconv.FormatUint(withdrawal.Index, 10),
ValidatorIndex: strconv.FormatUint(uint64(withdrawal.ValidatorIndex), 10),
}
}
return data
}
func handleWrapError(err error, message string, code int) *httputil.DefaultJsonError {
return &httputil.DefaultJsonError{
Message: errors.Wrap(err, message).Error(),
Code: code,
}
}

View File

@@ -1,210 +0,0 @@
package builder
import (
"bytes"
"encoding/json"
"net/http"
"net/http/httptest"
"strconv"
"testing"
"github.com/OffchainLabs/prysm/v6/api/server/structs"
mock "github.com/OffchainLabs/prysm/v6/beacon-chain/blockchain/testing"
"github.com/OffchainLabs/prysm/v6/beacon-chain/rpc/testutil"
"github.com/OffchainLabs/prysm/v6/beacon-chain/state"
"github.com/OffchainLabs/prysm/v6/config/params"
"github.com/OffchainLabs/prysm/v6/consensus-types/primitives"
"github.com/OffchainLabs/prysm/v6/crypto/bls"
"github.com/OffchainLabs/prysm/v6/network/httputil"
eth "github.com/OffchainLabs/prysm/v6/proto/prysm/v1alpha1"
"github.com/OffchainLabs/prysm/v6/testing/assert"
"github.com/OffchainLabs/prysm/v6/testing/require"
"github.com/OffchainLabs/prysm/v6/testing/util"
"github.com/OffchainLabs/prysm/v6/time/slots"
"github.com/ethereum/go-ethereum/common/hexutil"
)
func TestExpectedWithdrawals_BadRequest(t *testing.T) {
st, err := util.NewBeaconStateCapella()
slotsAhead := 5000
require.NoError(t, err)
capellaSlot, err := slots.EpochStart(params.BeaconConfig().CapellaForkEpoch)
require.NoError(t, err)
currentSlot := capellaSlot + primitives.Slot(slotsAhead)
require.NoError(t, st.SetSlot(currentSlot))
mockChainService := &mock.ChainService{Optimistic: true}
testCases := []struct {
name string
path string
urlParams map[string]string
state state.BeaconState
errorMessage string
}{
{
name: "no state_id url params",
path: "/eth/v1/builder/states/{state_id}/expected_withdrawals?proposal_slot" +
strconv.FormatUint(uint64(currentSlot), 10),
urlParams: map[string]string{},
state: nil,
errorMessage: "state_id is required in URL params",
},
{
name: "invalid proposal slot value",
path: "/eth/v1/builder/states/{state_id}/expected_withdrawals?proposal_slot=aaa",
urlParams: map[string]string{"state_id": "head"},
state: st,
errorMessage: "invalid proposal slot value",
},
{
name: "proposal slot < Capella start slot",
path: "/eth/v1/builder/states/{state_id}/expected_withdrawals?proposal_slot=" +
strconv.FormatUint(uint64(capellaSlot)-1, 10),
urlParams: map[string]string{"state_id": "head"},
state: st,
errorMessage: "expected withdrawals are not supported before Capella fork",
},
{
name: "proposal slot == Capella start slot",
path: "/eth/v1/builder/states/{state_id}/expected_withdrawals?proposal_slot=" +
strconv.FormatUint(uint64(capellaSlot), 10),
urlParams: map[string]string{"state_id": "head"},
state: st,
errorMessage: "proposal slot must be bigger than state slot",
},
{
name: "Proposal slot >= 128 slots ahead of state slot",
path: "/eth/v1/builder/states/{state_id}/expected_withdrawals?proposal_slot=" +
strconv.FormatUint(uint64(currentSlot+128), 10),
urlParams: map[string]string{"state_id": "head"},
state: st,
errorMessage: "proposal slot cannot be >= 128 slots ahead of state slot",
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
s := &Server{
FinalizationFetcher: mockChainService,
OptimisticModeFetcher: mockChainService,
Stater: &testutil.MockStater{BeaconState: testCase.state},
}
request := httptest.NewRequest("GET", testCase.path, nil)
request.SetPathValue("state_id", testCase.urlParams["state_id"])
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.ExpectedWithdrawals(writer, request)
assert.Equal(t, http.StatusBadRequest, writer.Code)
e := &httputil.DefaultJsonError{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), e))
assert.Equal(t, http.StatusBadRequest, e.Code)
assert.StringContains(t, testCase.errorMessage, e.Message)
})
}
}
func TestExpectedWithdrawals(t *testing.T) {
st, err := util.NewBeaconStateCapella()
slotsAhead := 5000
require.NoError(t, err)
capellaSlot, err := slots.EpochStart(params.BeaconConfig().CapellaForkEpoch)
require.NoError(t, err)
currentSlot := capellaSlot + primitives.Slot(slotsAhead)
require.NoError(t, st.SetSlot(currentSlot))
mockChainService := &mock.ChainService{Optimistic: true}
t.Run("get correct expected withdrawals", func(t *testing.T) {
params.SetupTestConfigCleanup(t)
cfg := params.BeaconConfig().Copy()
cfg.MaxValidatorsPerWithdrawalsSweep = 16
params.OverrideBeaconConfig(cfg)
// Update state with updated validator fields
valCount := 17
validators := make([]*eth.Validator, 0, valCount)
balances := make([]uint64, 0, valCount)
for i := 0; i < valCount; i++ {
blsKey, err := bls.RandKey()
require.NoError(t, err)
val := &eth.Validator{
PublicKey: blsKey.PublicKey().Marshal(),
WithdrawalCredentials: make([]byte, 32),
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
WithdrawableEpoch: params.BeaconConfig().FarFutureEpoch,
EffectiveBalance: params.BeaconConfig().MaxEffectiveBalance,
}
val.WithdrawalCredentials[0] = params.BeaconConfig().ETH1AddressWithdrawalPrefixByte
validators = append(validators, val)
balances = append(balances, params.BeaconConfig().MaxEffectiveBalance)
}
epoch := slots.ToEpoch(st.Slot())
// Fully withdrawable now with more than 0 balance
validators[5].WithdrawableEpoch = epoch
// Fully withdrawable now but 0 balance
validators[10].WithdrawableEpoch = epoch
balances[10] = 0
// Partially withdrawable now but fully withdrawable after 1 epoch
validators[14].WithdrawableEpoch = epoch + 1
balances[14] += params.BeaconConfig().MinDepositAmount
// Partially withdrawable
validators[15].WithdrawableEpoch = epoch + 2
balances[15] += params.BeaconConfig().MinDepositAmount
// Above sweep bound
validators[16].WithdrawableEpoch = epoch + 1
balances[16] += params.BeaconConfig().MinDepositAmount
require.NoError(t, st.SetValidators(validators))
require.NoError(t, st.SetBalances(balances))
inactivityScores := make([]uint64, valCount)
for i := range inactivityScores {
inactivityScores[i] = 10
}
require.NoError(t, st.SetInactivityScores(inactivityScores))
s := &Server{
FinalizationFetcher: mockChainService,
OptimisticModeFetcher: mockChainService,
Stater: &testutil.MockStater{BeaconState: st},
}
request := httptest.NewRequest(
"GET", "/eth/v1/builder/states/{state_id}/expected_withdrawals?proposal_slot="+
strconv.FormatUint(uint64(currentSlot+params.BeaconConfig().SlotsPerEpoch), 10), nil)
request.SetPathValue("state_id", "head")
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
s.ExpectedWithdrawals(writer, request)
assert.Equal(t, http.StatusOK, writer.Code)
resp := &structs.ExpectedWithdrawalsResponse{}
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
assert.Equal(t, true, resp.ExecutionOptimistic)
assert.Equal(t, false, resp.Finalized)
assert.Equal(t, 3, len(resp.Data))
expectedWithdrawal1 := &structs.ExpectedWithdrawal{
Index: strconv.FormatUint(0, 10),
ValidatorIndex: strconv.FormatUint(5, 10),
Address: hexutil.Encode(validators[5].WithdrawalCredentials[12:]),
// Decreased due to epoch processing when state advanced forward
Amount: strconv.FormatUint(31998257885, 10),
}
expectedWithdrawal2 := &structs.ExpectedWithdrawal{
Index: strconv.FormatUint(1, 10),
ValidatorIndex: strconv.FormatUint(14, 10),
Address: hexutil.Encode(validators[14].WithdrawalCredentials[12:]),
// MaxEffectiveBalance + MinDepositAmount + decrease after epoch processing
Amount: strconv.FormatUint(32998257885, 10),
}
expectedWithdrawal3 := &structs.ExpectedWithdrawal{
Index: strconv.FormatUint(2, 10),
ValidatorIndex: strconv.FormatUint(15, 10),
Address: hexutil.Encode(validators[15].WithdrawalCredentials[12:]),
// MinDepositAmount + decrease after epoch processing
Amount: strconv.FormatUint(998257885, 10),
}
require.DeepEqual(t, expectedWithdrawal1, resp.Data[0])
require.DeepEqual(t, expectedWithdrawal2, resp.Data[1])
require.DeepEqual(t, expectedWithdrawal3, resp.Data[2])
})
}

View File

@@ -1,12 +0,0 @@
package builder
import (
"github.com/OffchainLabs/prysm/v6/beacon-chain/blockchain"
"github.com/OffchainLabs/prysm/v6/beacon-chain/rpc/lookup"
)
type Server struct {
FinalizationFetcher blockchain.FinalizationFetcher
OptimisticModeFetcher blockchain.OptimisticModeFetcher
Stater lookup.Stater
}

View File

@@ -568,10 +568,9 @@ func TestGetSpec(t *testing.T) {
case "SYNC_MESSAGE_DUE_BPS":
assert.Equal(t, "104", v)
case "BLOB_SCHEDULE":
// BLOB_SCHEDULE should be an empty slice when no schedule is defined
blobSchedule, ok := v.([]interface{})
assert.Equal(t, true, ok)
assert.Equal(t, 0, len(blobSchedule))
assert.Equal(t, 2, len(blobSchedule))
default:
t.Errorf("Incorrect key: %s", k)
}

View File

@@ -44,38 +44,6 @@ import (
"google.golang.org/grpc/status"
)
// Deprecated: use GetAggregateAttestationV2 instead
// GetAggregateAttestation aggregates all attestations matching the given attestation data root and slot, returning the aggregated result.
func (s *Server) GetAggregateAttestation(w http.ResponseWriter, r *http.Request) {
_, span := trace.StartSpan(r.Context(), "validator.GetAggregateAttestation")
defer span.End()
_, attDataRoot, ok := shared.HexFromQuery(w, r, "attestation_data_root", fieldparams.RootLength, true)
if !ok {
return
}
_, slot, ok := shared.UintFromQuery(w, r, "slot", true)
if !ok {
return
}
agg := s.aggregatedAttestation(w, primitives.Slot(slot), attDataRoot, 0)
if agg == nil {
return
}
typedAgg, ok := agg.(*ethpbalpha.Attestation)
if !ok {
httputil.HandleError(w, fmt.Sprintf("Attestation is not of type %T", &ethpbalpha.Attestation{}), http.StatusInternalServerError)
return
}
data, err := json.Marshal(structs.AttFromConsensus(typedAgg))
if err != nil {
httputil.HandleError(w, "Could not marshal attestation: "+err.Error(), http.StatusInternalServerError)
return
}
httputil.WriteJson(w, &structs.AggregateAttestationResponse{Data: data})
}
// GetAggregateAttestationV2 aggregates all attestations matching the given attestation data root and slot, returning the aggregated result.
func (s *Server) GetAggregateAttestationV2(w http.ResponseWriter, r *http.Request) {
_, span := trace.StartSpan(r.Context(), "validator.GetAggregateAttestationV2")
@@ -326,58 +294,6 @@ func (s *Server) SubmitContributionAndProofs(w http.ResponseWriter, r *http.Requ
}
}
// Deprecated: use SubmitAggregateAndProofsV2 instead
// SubmitAggregateAndProofs verifies given aggregate and proofs and publishes them on appropriate gossipsub topic.
func (s *Server) SubmitAggregateAndProofs(w http.ResponseWriter, r *http.Request) {
ctx, span := trace.StartSpan(r.Context(), "validator.SubmitAggregateAndProofs")
defer span.End()
var req structs.SubmitAggregateAndProofsRequest
err := json.NewDecoder(r.Body).Decode(&req.Data)
switch {
case errors.Is(err, io.EOF):
httputil.HandleError(w, "No data submitted", http.StatusBadRequest)
return
case err != nil:
httputil.HandleError(w, "Could not decode request body: "+err.Error(), http.StatusBadRequest)
return
}
if len(req.Data) == 0 {
httputil.HandleError(w, "No data submitted", http.StatusBadRequest)
return
}
broadcastFailed := false
for _, item := range req.Data {
var signedAggregate structs.SignedAggregateAttestationAndProof
err := json.Unmarshal(item, &signedAggregate)
if err != nil {
httputil.HandleError(w, "Could not decode item: "+err.Error(), http.StatusBadRequest)
return
}
consensusItem, err := signedAggregate.ToConsensus()
if err != nil {
httputil.HandleError(w, "Could not convert request aggregate to consensus aggregate: "+err.Error(), http.StatusBadRequest)
return
}
rpcError := s.CoreService.SubmitSignedAggregateSelectionProof(ctx, consensusItem)
if rpcError != nil {
var broadcastFailedErr *server.BroadcastFailedError
ok := errors.As(rpcError.Err, &broadcastFailedErr)
if ok {
broadcastFailed = true
} else {
httputil.HandleError(w, rpcError.Err.Error(), core.ErrorReasonToHTTP(rpcError.Reason))
return
}
}
}
if broadcastFailed {
httputil.HandleError(w, "Could not broadcast one or more signed aggregated attestations", http.StatusInternalServerError)
}
}
// SubmitAggregateAndProofsV2 verifies given aggregate and proofs and publishes them on appropriate gossipsub topic.
func (s *Server) SubmitAggregateAndProofsV2(w http.ResponseWriter, r *http.Request) {
ctx, span := trace.StartSpan(r.Context(), "validator.SubmitAggregateAndProofsV2")

File diff suppressed because it is too large Load Diff

View File

@@ -81,6 +81,7 @@ go_test(
"//runtime/interop:go_default_library",
"//testing/require:go_default_library",
"//testing/util:go_default_library",
"//time/slots:go_default_library",
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
"@com_github_libp2p_go_libp2p//core/peer:go_default_library",
"@com_github_pkg_errors//:go_default_library",

View File

@@ -249,6 +249,18 @@ func (s *Service) scheduleTodos() {
}
}
// fuluOrigin checks whether the origin block (ie the checkpoint sync block from which backfill
// syncs backwards) is in an unsupported fork, enabling the backfill service to shut down rather than
// run with buggy behavior.
// This will be removed once DataColumnSidecar support is released.
func fuluOrigin(cfg *params.BeaconChainConfig, status *dbval.BackfillStatus) bool {
originEpoch := slots.ToEpoch(primitives.Slot(status.OriginSlot))
if originEpoch < cfg.FuluForkEpoch {
return false
}
return true
}
// Start begins the runloop of backfill.Service in the current goroutine.
func (s *Service) Start() {
if !s.enabled {
@@ -281,6 +293,12 @@ func (s *Service) Start() {
return
}
status := s.store.status()
if fuluOrigin(params.BeaconConfig(), status) {
log.WithField("originSlot", s.store.status().OriginSlot).
Warn("backfill disabled; DataColumnSidecar currently unsupported, for updates follow https://github.com/OffchainLabs/prysm/issues/15982")
s.markComplete()
return
}
// Exit early if there aren't going to be any batches to backfill.
if primitives.Slot(status.LowSlot) <= s.ms(s.clock.CurrentSlot()) {
log.WithField("minimumRequiredSlot", s.ms(s.clock.CurrentSlot())).
@@ -289,6 +307,7 @@ func (s *Service) Start() {
s.markComplete()
return
}
s.verifier, s.ctxMap, err = s.initVerifier(ctx)
if err != nil {
log.WithError(err).Error("Unable to initialize backfill verifier")

View File

@@ -15,6 +15,7 @@ import (
"github.com/OffchainLabs/prysm/v6/proto/dbval"
"github.com/OffchainLabs/prysm/v6/testing/require"
"github.com/OffchainLabs/prysm/v6/testing/util"
"github.com/OffchainLabs/prysm/v6/time/slots"
)
type mockMinimumSlotter struct {
@@ -131,3 +132,41 @@ func TestBackfillMinSlotDefault(t *testing.T) {
require.Equal(t, specMin, s.ms(current))
})
}
func TestFuluOrigin(t *testing.T) {
cfg := params.BeaconConfig()
fuluEpoch := cfg.FuluForkEpoch
fuluSlot, err := slots.EpochStart(fuluEpoch)
require.NoError(t, err)
cases := []struct {
name string
origin primitives.Slot
isFulu bool
}{
{
name: "before fulu",
origin: fuluSlot - 1,
isFulu: false,
},
{
name: "at fulu",
origin: fuluSlot,
isFulu: true,
},
{
name: "after fulu",
origin: fuluSlot + 1,
isFulu: true,
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
status := &dbval.BackfillStatus{
OriginSlot: uint64(tc.origin),
}
result := fuluOrigin(cfg, status)
require.Equal(t, tc.isFulu, result)
})
}
}

View File

@@ -43,15 +43,16 @@ func TestRegularSyncBeaconBlockSubscriber_ProcessPendingBlocks1(t *testing.T) {
db := dbtest.SetupDB(t)
p1 := p2ptest.NewTestP2P(t)
mockChain := &mock.ChainService{
FinalizedCheckPoint: &ethpb.Checkpoint{
Epoch: 0,
},
}
r := &Service{
cfg: &config{
p2p: p1,
beaconDB: db,
chain: &mock.ChainService{
FinalizedCheckPoint: &ethpb.Checkpoint{
Epoch: 0,
},
},
chain: mockChain,
clock: startup.NewClock(time.Unix(0, 0), [32]byte{}),
stateGen: stategen.New(db, doublylinkedtree.New()),
},
@@ -64,6 +65,12 @@ func TestRegularSyncBeaconBlockSubscriber_ProcessPendingBlocks1(t *testing.T) {
util.SaveBlock(t, t.Context(), r.cfg.beaconDB, b0)
b0Root, err := b0.Block.HashTreeRoot()
require.NoError(t, err)
// Setup head state for blockVerifyingState logic
st, err := util.NewBeaconState()
require.NoError(t, err)
mockChain.Root = b0Root[:]
mockChain.State = st
b3 := util.NewBeaconBlock()
b3.Block.Slot = 3
b3.Block.ParentRoot = b0Root[:]
@@ -115,16 +122,17 @@ func TestRegularSyncBeaconBlockSubscriber_OptimisticStatus(t *testing.T) {
db := dbtest.SetupDB(t)
p1 := p2ptest.NewTestP2P(t)
mockChain := &mock.ChainService{
Optimistic: true,
FinalizedCheckPoint: &ethpb.Checkpoint{
Epoch: 0,
},
}
r := &Service{
cfg: &config{
p2p: p1,
beaconDB: db,
chain: &mock.ChainService{
Optimistic: true,
FinalizedCheckPoint: &ethpb.Checkpoint{
Epoch: 0,
},
},
chain: mockChain,
clock: startup.NewClock(time.Unix(0, 0), [32]byte{}),
stateGen: stategen.New(db, doublylinkedtree.New()),
},
@@ -137,6 +145,12 @@ func TestRegularSyncBeaconBlockSubscriber_OptimisticStatus(t *testing.T) {
util.SaveBlock(t, t.Context(), r.cfg.beaconDB, b0)
b0Root, err := b0.Block.HashTreeRoot()
require.NoError(t, err)
// Setup head state for blockVerifyingState logic
st, err := util.NewBeaconState()
require.NoError(t, err)
mockChain.Root = b0Root[:]
mockChain.State = st
b3 := util.NewBeaconBlock()
b3.Block.Slot = 3
b3.Block.ParentRoot = b0Root[:]
@@ -189,16 +203,17 @@ func TestRegularSyncBeaconBlockSubscriber_ExecutionEngineTimesOut(t *testing.T)
p1 := p2ptest.NewTestP2P(t)
fcs := doublylinkedtree.New()
mockChain := &mock.ChainService{
FinalizedCheckPoint: &ethpb.Checkpoint{
Epoch: 0,
},
ReceiveBlockMockErr: execution.ErrHTTPTimeout,
}
r := &Service{
cfg: &config{
p2p: p1,
beaconDB: db,
chain: &mock.ChainService{
FinalizedCheckPoint: &ethpb.Checkpoint{
Epoch: 0,
},
ReceiveBlockMockErr: execution.ErrHTTPTimeout,
},
chain: mockChain,
clock: startup.NewClock(time.Unix(0, 0), [32]byte{}),
stateGen: stategen.New(db, fcs),
},
@@ -211,6 +226,12 @@ func TestRegularSyncBeaconBlockSubscriber_ExecutionEngineTimesOut(t *testing.T)
util.SaveBlock(t, t.Context(), r.cfg.beaconDB, b0)
b0Root, err := b0.Block.HashTreeRoot()
require.NoError(t, err)
// Setup head state for blockVerifyingState logic
st, err := util.NewBeaconState()
require.NoError(t, err)
mockChain.Root = b0Root[:]
mockChain.State = st
b3 := util.NewBeaconBlock()
b3.Block.Slot = 3
b3.Block.ParentRoot = b0Root[:]
@@ -412,6 +433,14 @@ func TestRegularSyncBeaconBlockSubscriber_ProcessPendingBlocks_2Chains(t *testin
util.SaveBlock(t, t.Context(), r.cfg.beaconDB, b0)
b0Root, err := b0.Block.HashTreeRoot()
require.NoError(t, err)
// Setup head state for blockVerifyingState logic
st, err := util.NewBeaconState()
require.NoError(t, err)
mockChain := r.cfg.chain.(*mock.ChainService)
mockChain.Root = b0Root[:]
mockChain.State = st
b1 := util.NewBeaconBlock()
b1.Block.Slot = 1
b1.Block.ParentRoot = b0Root[:]
@@ -741,10 +770,8 @@ func TestService_ProcessPendingBlockOnCorrectSlot(t *testing.T) {
proposerIdx, err := helpers.BeaconProposerIndex(ctx, copied)
require.NoError(t, err)
st, err := util.NewBeaconState()
require.NoError(t, err)
mockChain.Root = bRoot[:]
mockChain.State = st
mockChain.State = beaconState
b1 := util.NewBeaconBlock()
b1.Block.ParentRoot = bRoot[:]
@@ -819,10 +846,8 @@ func TestService_ProcessBadPendingBlocks(t *testing.T) {
proposerIdx, err := helpers.BeaconProposerIndex(ctx, copied)
require.NoError(t, err)
st, err := util.NewBeaconState()
require.NoError(t, err)
mockChain.Root = bRoot[:]
mockChain.State = st
mockChain.State = beaconState
b1 := util.NewBeaconBlock()
b1.Block.ParentRoot = bRoot[:]

View File

@@ -37,6 +37,11 @@ func (s *Service) blobSidecarByRootRPCHandler(ctx context.Context, msg interface
}
blobIdents := *ref
if err := s.rateLimiter.validateRequest(stream, uint64(len(blobIdents))); err != nil {
return errors.Wrap(err, "rate limiter validate request")
}
cs := s.cfg.clock.CurrentSlot()
remotePeer := stream.Conn().RemotePeer()
if err := validateBlobByRootRequest(blobIdents, cs); err != nil {
@@ -44,6 +49,7 @@ func (s *Service) blobSidecarByRootRPCHandler(ctx context.Context, msg interface
s.writeErrorResponseToStream(responseCodeInvalidRequest, err.Error(), stream)
return err
}
// Sort the identifiers so that requests for the same blob root will be adjacent, minimizing db lookups.
sort.Sort(blobIdents)

View File

@@ -238,7 +238,7 @@ func TestBlobsByRootValidation(t *testing.T) {
{
name: "exceeds req max",
nblocks: int(params.BeaconConfig().MaxRequestBlobSidecars) + 1,
err: p2pTypes.ErrMaxBlobReqExceeded,
err: p2pTypes.ErrRateLimited,
},
}
for _, c := range cases {
@@ -270,3 +270,64 @@ func TestBlobsByRootOK(t *testing.T) {
})
}
}
func TestValidateBlobByRootRequest(t *testing.T) {
params.SetupTestConfigCleanup(t)
cfg := params.BeaconConfig()
// Helper function to create blob identifiers
createBlobIdents := func(count int) p2pTypes.BlobSidecarsByRootReq {
idents := make([]*ethpb.BlobIdentifier, count)
for i := 0; i < count; i++ {
idents[i] = &ethpb.BlobIdentifier{
BlockRoot: make([]byte, 32),
Index: uint64(i),
}
}
return idents
}
tests := []struct {
name string
blobIdents p2pTypes.BlobSidecarsByRootReq
slot types.Slot
expectedErr error
}{
{
name: "pre-Electra: at max limit",
blobIdents: createBlobIdents(int(cfg.MaxRequestBlobSidecars)),
slot: util.SlotAtEpoch(t, cfg.ElectraForkEpoch-1),
expectedErr: nil,
},
{
name: "pre-Electra: exceeds max limit by 1",
blobIdents: createBlobIdents(int(cfg.MaxRequestBlobSidecars) + 1),
slot: util.SlotAtEpoch(t, cfg.ElectraForkEpoch-1),
expectedErr: p2pTypes.ErrMaxBlobReqExceeded,
},
{
name: "Electra: at max limit",
blobIdents: createBlobIdents(int(cfg.MaxRequestBlobSidecarsElectra)),
slot: util.SlotAtEpoch(t, cfg.ElectraForkEpoch),
expectedErr: nil,
},
{
name: "Electra: exceeds Electra max limit by 1",
blobIdents: createBlobIdents(int(cfg.MaxRequestBlobSidecarsElectra) + 1),
slot: util.SlotAtEpoch(t, cfg.ElectraForkEpoch),
expectedErr: p2pTypes.ErrMaxBlobReqExceeded,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := validateBlobByRootRequest(tt.blobIdents, tt.slot)
if tt.expectedErr != nil {
require.ErrorIs(t, err, tt.expectedErr)
return
}
require.NoError(t, err)
})
}
}

View File

@@ -49,8 +49,18 @@ func (s *Service) dataColumnSidecarByRootRPCHandler(ctx context.Context, msg int
SetRPCStreamDeadlines(stream)
// Count the total number of requested data column sidecars.
totalRequested := 0
for _, ident := range requestedColumnIdents {
totalRequested += len(ident.Columns)
}
if err := s.rateLimiter.validateRequest(stream, uint64(totalRequested)); err != nil {
return errors.Wrap(err, "rate limiter validate request")
}
// Penalize peers that send invalid requests.
if err := validateDataColumnsByRootRequest(requestedColumnIdents); err != nil {
if err := validateDataColumnsByRootRequest(totalRequested); err != nil {
s.downscorePeer(remotePeer, "dataColumnSidecarByRootRPCHandlerValidationError")
s.writeErrorResponseToStream(responseCodeInvalidRequest, err.Error(), stream)
return errors.Wrap(err, "validate data columns by root request")
@@ -154,13 +164,8 @@ func (s *Service) dataColumnSidecarByRootRPCHandler(ctx context.Context, msg int
}
// validateDataColumnsByRootRequest checks if the request for data column sidecars is valid.
func validateDataColumnsByRootRequest(colIdents types.DataColumnsByRootIdentifiers) error {
total := uint64(0)
for _, id := range colIdents {
total += uint64(len(id.Columns))
}
if total > params.BeaconConfig().MaxRequestDataColumnSidecars {
func validateDataColumnsByRootRequest(count int) error {
if uint64(count) > params.BeaconConfig().MaxRequestDataColumnSidecars {
return types.ErrMaxDataColumnReqExceeded
}

View File

@@ -1,7 +1,6 @@
package sync
import (
"context"
"io"
"math"
"sync"
@@ -12,10 +11,10 @@ import (
"github.com/OffchainLabs/prysm/v6/beacon-chain/db/filesystem"
testDB "github.com/OffchainLabs/prysm/v6/beacon-chain/db/testing"
"github.com/OffchainLabs/prysm/v6/beacon-chain/p2p"
"github.com/OffchainLabs/prysm/v6/beacon-chain/p2p/encoder"
p2ptest "github.com/OffchainLabs/prysm/v6/beacon-chain/p2p/testing"
"github.com/OffchainLabs/prysm/v6/beacon-chain/p2p/types"
"github.com/OffchainLabs/prysm/v6/beacon-chain/startup"
"github.com/OffchainLabs/prysm/v6/cmd/beacon-chain/flags"
fieldparams "github.com/OffchainLabs/prysm/v6/config/fieldparams"
"github.com/OffchainLabs/prysm/v6/config/params"
"github.com/OffchainLabs/prysm/v6/consensus-types/blocks"
@@ -36,7 +35,10 @@ func TestDataColumnSidecarsByRootRPCHandler(t *testing.T) {
params.BeaconConfig().InitializeForkSchedule()
ctxMap, err := ContextByteVersionsForValRoot(params.BeaconConfig().GenesisValidatorsRoot)
require.NoError(t, err)
ctx := context.Background()
ctx := t.Context()
protocolID := protocol.ID(p2p.RPCDataColumnSidecarsByRootTopicV1) + "/" + encoder.ProtocolSuffixSSZSnappy
t.Run("wrong message type", func(t *testing.T) {
service := &Service{}
err := service.dataColumnSidecarByRootRPCHandler(t.Context(), nil, nil)
@@ -50,9 +52,7 @@ func TestDataColumnSidecarsByRootRPCHandler(t *testing.T) {
params.OverrideBeaconConfig(cfg)
localP2P := p2ptest.NewTestP2P(t)
service := &Service{cfg: &config{p2p: localP2P}}
protocolID := protocol.ID(p2p.RPCDataColumnSidecarsByRootTopicV1)
service := &Service{cfg: &config{p2p: localP2P}, rateLimiter: newRateLimiter(localP2P)}
remoteP2P := p2ptest.NewTestP2P(t)
var wg sync.WaitGroup
@@ -83,12 +83,6 @@ func TestDataColumnSidecarsByRootRPCHandler(t *testing.T) {
})
t.Run("nominal", func(t *testing.T) {
resetFlags := flags.Get()
gFlags := new(flags.GlobalFlags)
gFlags.DataColumnBatchLimit = 2
flags.Init(gFlags)
defer flags.Init(resetFlags)
// Setting the ticker to 0 will cause the ticker to panic.
// Setting it to the minimum value instead.
refTickerDelay := tickerDelay
@@ -151,7 +145,6 @@ func TestDataColumnSidecarsByRootRPCHandler(t *testing.T) {
rateLimiter: newRateLimiter(localP2P),
}
protocolID := protocol.ID(p2p.RPCDataColumnSidecarsByRootTopicV1)
remoteP2P := p2ptest.NewTestP2P(t)
var wg sync.WaitGroup
@@ -226,68 +219,22 @@ func TestDataColumnSidecarsByRootRPCHandler(t *testing.T) {
}
func TestValidateDataColumnsByRootRequest(t *testing.T) {
const max = 10
params.SetupTestConfigCleanup(t)
config := params.BeaconConfig()
maxCols := uint64(10) // Set a small value for testing
config.MaxRequestDataColumnSidecars = maxCols
params.OverrideBeaconConfig(config)
cfg := params.BeaconConfig()
cfg.MaxRequestDataColumnSidecars = max
params.OverrideBeaconConfig(cfg)
tests := []struct {
name string
colIdents types.DataColumnsByRootIdentifiers
expectedErr error
}{
{
name: "Invalid request - multiple identifiers exceed max",
colIdents: types.DataColumnsByRootIdentifiers{
{
BlockRoot: make([]byte, fieldparams.RootLength),
Columns: make([]uint64, maxCols/2+1),
},
{
BlockRoot: make([]byte, fieldparams.RootLength),
Columns: make([]uint64, maxCols/2+1),
},
},
expectedErr: types.ErrMaxDataColumnReqExceeded,
},
{
name: "Valid request - less than max",
colIdents: types.DataColumnsByRootIdentifiers{
{
BlockRoot: make([]byte, fieldparams.RootLength),
Columns: make([]uint64, maxCols-1),
},
},
expectedErr: nil,
},
{
name: "Valid request - multiple identifiers sum to max",
colIdents: types.DataColumnsByRootIdentifiers{
{
BlockRoot: make([]byte, fieldparams.RootLength),
Columns: make([]uint64, maxCols/2),
},
{
BlockRoot: make([]byte, fieldparams.RootLength),
Columns: make([]uint64, maxCols/2),
},
},
expectedErr: nil,
},
}
t.Run("invalid", func(t *testing.T) {
err := validateDataColumnsByRootRequest(max + 1)
require.ErrorIs(t, err, types.ErrMaxDataColumnReqExceeded)
})
// Run tests
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := validateDataColumnsByRootRequest(tt.colIdents)
if tt.expectedErr == nil {
require.NoError(t, err)
} else {
require.ErrorIs(t, err, tt.expectedErr)
}
})
}
t.Run("valid", func(t *testing.T) {
err := validateDataColumnsByRootRequest(max)
require.NoError(t, err)
})
}
func TestDataColumnsRPCMinValidSlot(t *testing.T) {

View File

@@ -20,7 +20,7 @@ func TestMain(m *testing.M) {
BlobBatchLimit: 32,
BlobBatchLimitBurstFactor: 2,
DataColumnBatchLimit: 4096,
DataColumnBatchLimitBurstFactor: 2,
DataColumnBatchLimitBurstFactor: 4,
})
defer func() {
flags.Init(resetFlags)

View File

@@ -1,6 +1,7 @@
package sync
import (
"bytes"
"context"
"fmt"
"time"
@@ -105,10 +106,6 @@ func (s *Service) validateBeaconBlockPubSub(ctx context.Context, pid peer.ID, ms
}()
}
if err := validateDenebBeaconBlock(blk.Block()); err != nil {
return pubsub.ValidationReject, err
}
// Verify the block is the first block received for the proposer for the slot.
if s.hasSeenBlockIndexSlot(blk.Block().Slot(), blk.Block().ProposerIndex()) {
// Attempt to detect and broadcast equivocation before ignoring
@@ -262,12 +259,15 @@ func (s *Service) validateBeaconBlock(ctx context.Context, blk interfaces.ReadOn
return err
}
parentState, err := s.validatePhase0Block(ctx, blk, blockRoot)
verifyingState, err := s.validatePhase0Block(ctx, blk, blockRoot)
if err != nil {
return err
}
if verifyingState == nil {
return errors.New("could not get verifying state")
}
if err = s.validateBellatrixBeaconBlock(ctx, parentState, blk.Block()); err != nil {
if err = s.validateBellatrixBeaconBlock(ctx, verifyingState, blk.Block()); err != nil {
if errors.Is(err, ErrOptimisticParent) {
return err
}
@@ -282,31 +282,25 @@ func (s *Service) validateBeaconBlock(ctx context.Context, blk interfaces.ReadOn
// - Checks that the parent is in our forkchoice tree.
// - Validates that the proposer signature is valid.
// - Validates that the proposer index is valid.
func (s *Service) validatePhase0Block(ctx context.Context, blk interfaces.ReadOnlySignedBeaconBlock, blockRoot [32]byte) (state.BeaconState, error) {
// Returns a state that has compatible Randao Mix and active validator indices as the block's parent state advanced to the block's slot.
// This state can be used for further block validations.
func (s *Service) validatePhase0Block(ctx context.Context, blk interfaces.ReadOnlySignedBeaconBlock, blockRoot [32]byte) (state.ReadOnlyBeaconState, error) {
if !s.cfg.chain.InForkchoice(blk.Block().ParentRoot()) {
s.setBadBlock(ctx, blockRoot)
return nil, blockchain.ErrNotDescendantOfFinalized
}
parentState, err := s.cfg.stateGen.StateByRoot(ctx, blk.Block().ParentRoot())
verifyingState, err := s.blockVerifyingState(ctx, blk)
if err != nil {
return nil, err
}
if err := blocks.VerifyBlockSignatureUsingCurrentFork(parentState, blk, blockRoot); err != nil {
if err := blocks.VerifyBlockSignatureUsingCurrentFork(verifyingState, blk, blockRoot); err != nil {
if errors.Is(err, blocks.ErrInvalidSignature) {
s.setBadBlock(ctx, blockRoot)
}
return nil, err
}
// In the event the block is more than an epoch ahead from its
// parent state, we have to advance the state forward.
parentRoot := blk.Block().ParentRoot()
parentState, err = transition.ProcessSlotsUsingNextSlotCache(ctx, parentState, parentRoot[:], blk.Block().Slot())
if err != nil {
return nil, err
}
idx, err := helpers.BeaconProposerIndex(ctx, parentState)
idx, err := helpers.BeaconProposerIndexAtSlot(ctx, verifyingState, blk.Block().Slot())
if err != nil {
return nil, err
}
@@ -314,7 +308,59 @@ func (s *Service) validatePhase0Block(ctx context.Context, blk interfaces.ReadOn
s.setBadBlock(ctx, blockRoot)
return nil, errors.New("incorrect proposer index")
}
return parentState, nil
return verifyingState, nil
}
// blockVerifyingState returns the appropriate state to verify the signature and proposer index of the given block.
// The returned state is guaranteed to be at the same epoch as the block's epoch, and have the same randao mix and active validator indices as the
// block's parent state advanced to the block's slot.
func (s *Service) blockVerifyingState(ctx context.Context, blk interfaces.ReadOnlySignedBeaconBlock) (state.ReadOnlyBeaconState, error) {
headRoot, err := s.cfg.chain.HeadRoot(ctx)
if err != nil {
return nil, err
}
parentRoot := blk.Block().ParentRoot()
blockSlot := blk.Block().Slot()
blockEpoch := slots.ToEpoch(blockSlot)
headSlot := s.cfg.chain.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 blockEpoch == headEpoch {
return s.cfg.chain.HeadStateReadOnly(ctx)
}
// Otherwise, we need to process the head state to the block's slot
headState, err := s.cfg.chain.HeadState(ctx)
if err != nil {
return nil, err
}
return transition.ProcessSlotsUsingNextSlotCache(ctx, headState, headRoot, blockSlot)
}
// If head and block are in the same epoch and head is compatible with the parent's target, then use head
if blockEpoch == headEpoch {
headTarget, err := s.cfg.chain.TargetRootForEpoch([32]byte(headRoot), blockEpoch)
if err != nil {
return nil, err
}
parentTarget, err := s.cfg.chain.TargetRootForEpoch([32]byte(parentRoot), blockEpoch)
if err != nil {
return nil, err
}
if bytes.Equal(headTarget[:], parentTarget[:]) {
return s.cfg.chain.HeadStateReadOnly(ctx)
}
}
// Otherwise retrieve the the parent state and advance it to the block's slot
parentState, err := s.cfg.stateGen.StateByRoot(ctx, parentRoot)
if err != nil {
return nil, err
}
parentEpoch := slots.ToEpoch(parentState.Slot())
if blockEpoch == parentEpoch {
return parentState, nil
}
return transition.ProcessSlotsUsingNextSlotCache(ctx, parentState, parentRoot[:], blockSlot)
}
func validateDenebBeaconBlock(blk interfaces.ReadOnlyBeaconBlock) error {
@@ -336,6 +382,8 @@ func validateDenebBeaconBlock(blk interfaces.ReadOnlyBeaconBlock) error {
}
// validateBellatrixBeaconBlock validates the block for the Bellatrix fork.
// The verifying state is used only to check if the chain is execution enabled.
//
// spec code:
//
// If the execution is enabled for the block -- i.e. is_execution_enabled(state, block.body) then validate the following:
@@ -348,14 +396,14 @@ func validateDenebBeaconBlock(blk interfaces.ReadOnlyBeaconBlock) error {
// otherwise:
// [IGNORE] The block's parent (defined by block.parent_root) passes all validation (including execution
// node verification of the block.body.execution_payload).
func (s *Service) validateBellatrixBeaconBlock(ctx context.Context, parentState state.BeaconState, blk interfaces.ReadOnlyBeaconBlock) error {
func (s *Service) validateBellatrixBeaconBlock(ctx context.Context, verifyingState state.ReadOnlyBeaconState, blk interfaces.ReadOnlyBeaconBlock) error {
// Error if block and state are not the same version
if parentState.Version() != blk.Version() {
if verifyingState.Version() != blk.Version() {
return errors.New("block and state are not the same version")
}
body := blk.Body()
executionEnabled, err := blocks.IsExecutionEnabled(parentState, body)
executionEnabled, err := blocks.IsExecutionEnabled(verifyingState, body)
if err != nil {
return err
}
@@ -363,7 +411,7 @@ func (s *Service) validateBellatrixBeaconBlock(ctx context.Context, parentState
return nil
}
t, err := slots.StartTime(parentState.GenesisTime(), blk.Slot())
t, err := slots.StartTime(verifyingState.GenesisTime(), blk.Slot())
if err != nil {
return err
}

View File

@@ -73,7 +73,9 @@ func TestValidateBeaconBlockPubSub_InvalidSignature(t *testing.T) {
Epoch: 0,
Root: make([]byte, 32),
},
DB: db,
DB: db,
State: beaconState,
Root: bRoot[:],
}
r := &Service{
cfg: &config{
@@ -137,7 +139,9 @@ func TestValidateBeaconBlockPubSub_InvalidSignature_MarksBlockAsBad(t *testing.T
Epoch: 0,
Root: make([]byte, 32),
},
DB: db,
DB: db,
State: beaconState,
Root: bRoot[:],
}
r := &Service{
cfg: &config{
@@ -1301,7 +1305,10 @@ func TestValidateBeaconBlockPubSub_ValidExecutionPayload(t *testing.T) {
FinalizedCheckPoint: &ethpb.Checkpoint{
Epoch: 0,
Root: make([]byte, 32),
}}
},
State: beaconState,
Root: bRoot[:],
}
r := &Service{
cfg: &config{
beaconDB: db,
@@ -1536,7 +1543,10 @@ func Test_validateBeaconBlockProcessingWhenParentIsOptimistic(t *testing.T) {
FinalizedCheckPoint: &ethpb.Checkpoint{
Epoch: 0,
Root: make([]byte, 32),
}}
},
State: beaconState,
Root: bRoot[:],
}
r := &Service{
cfg: &config{
beaconDB: db,
@@ -1814,3 +1824,88 @@ func TestDetectAndBroadcastEquivocation(t *testing.T) {
require.ErrorIs(t, err, ErrSlashingSignatureFailure)
})
}
func TestBlockVerifyingState_SameEpochAsParent(t *testing.T) {
ctx := t.Context()
db := dbtest.SetupDB(t)
// Create a genesis state
beaconState, _ := util.DeterministicGenesisState(t, 100)
// Create parent block at slot 1
parentBlock := util.NewBeaconBlock()
parentBlock.Block.Slot = 1
util.SaveBlock(t, ctx, db, parentBlock)
parentRoot, err := parentBlock.Block.HashTreeRoot()
require.NoError(t, err)
// Save parent state at slot 1 (epoch 0)
parentState := beaconState.Copy()
require.NoError(t, parentState.SetSlot(1))
require.NoError(t, db.SaveState(ctx, parentState, parentRoot))
require.NoError(t, db.SaveStateSummary(ctx, &ethpb.StateSummary{Root: parentRoot[:]}))
// Create a different head block at a later epoch
headBlock := util.NewBeaconBlock()
headBlock.Block.Slot = 40 // Different epoch (epoch 1)
headBlock.Block.ParentRoot = parentRoot[:] // Head descends from parent
util.SaveBlock(t, ctx, db, headBlock)
headRoot, err := headBlock.Block.HashTreeRoot()
require.NoError(t, err)
headState := beaconState.Copy()
require.NoError(t, headState.SetSlot(40))
require.NoError(t, db.SaveState(ctx, headState, headRoot))
// Create a block at slot 2 (same epoch 0 as parent)
block := util.NewBeaconBlock()
block.Block.Slot = 2
block.Block.ParentRoot = parentRoot[:]
signedBlock, err := blocks.NewSignedBeaconBlock(block)
require.NoError(t, err)
forkchoiceStore := doublylinkedtree.New()
stateGen := stategen.New(db, forkchoiceStore)
// Insert parent block into forkchoice
signedParentBlock, err := blocks.NewSignedBeaconBlock(parentBlock)
require.NoError(t, err)
roParentBlock, err := blocks.NewROBlockWithRoot(signedParentBlock, parentRoot)
require.NoError(t, err)
require.NoError(t, forkchoiceStore.InsertNode(ctx, parentState, roParentBlock))
// Insert head block into forkchoice
signedHeadBlock, err := blocks.NewSignedBeaconBlock(headBlock)
require.NoError(t, err)
roHeadBlock, err := blocks.NewROBlockWithRoot(signedHeadBlock, headRoot)
require.NoError(t, err)
require.NoError(t, forkchoiceStore.InsertNode(ctx, headState, roHeadBlock))
chainService := &mock.ChainService{
DB: db,
Root: headRoot[:], // Head is different from parent
State: headState, // Set head state so HeadSlot() returns correct value
FinalizedCheckPoint: &ethpb.Checkpoint{
Epoch: 0,
Root: parentRoot[:],
},
ForkChoiceStore: forkchoiceStore,
}
r := &Service{
cfg: &config{
beaconDB: db,
chain: chainService,
stateGen: stateGen,
},
}
// Call blockVerifyingState - should return parent state without processing
result, err := r.blockVerifyingState(ctx, signedBlock)
require.NoError(t, err)
require.NotNil(t, result)
// Verify that the returned state is at slot 1 (parent state slot)
// This confirms that the branch at line 361 was taken (returning parentState directly)
assert.Equal(t, primitives.Slot(1), result.Slot())
}

View File

@@ -0,0 +1,3 @@
### Added
- Bash script for automating the version upgrade process.

View File

@@ -0,0 +1,8 @@
### Added
- Fulu fork epoch for mainnet configurations set for December 3, 2025, 09:49:11pm UTC
- Added BPO schedules for December 9, 2025, 02:21:11pm UTC and January 7, 2026, 01:01:11am UTC
### Changed
- updated consensus spec to 1.6.0 from 1.6.0-beta.2

View File

@@ -0,0 +1,2 @@
### Fixed
- Backfill disabled if checkpoint sync origin is after fulu fork due to lack of DataColumnSidecar support in backfill. To track the availability of fulu-compatible backfill please watch https://github.com/OffchainLabs/prysm/issues/15982

View File

@@ -0,0 +1,2 @@
### Fixed
- Ensures the rate limitation is respected for by root blob and data column sidecars requests.

View File

@@ -0,0 +1,3 @@
### Fixed
- Use head only if its compatible with target for attestation validation.

View File

@@ -0,0 +1,3 @@
### Changed
- Use head state for block pubsub validation when possible.

View File

@@ -0,0 +1,3 @@
### Ignored
- Fix test setup to properly reference electra rather than unset the fulu epoch

View File

@@ -0,0 +1,3 @@
### Removed
- Remove Beacon API endpoints that were deprecated in Electra: `GET /eth/v1/beacon/deposit_snapshot`, `GET /eth/v1/beacon/blocks/{block_id}/attestations`, `GET /eth/v1/beacon/pool/attestations`, `POST /eth/v1/beacon/pool/attestations`, `GET /eth/v1/beacon/pool/attester_slashings`, `POST /eth/v1/beacon/pool/attester_slashings`, `GET /eth/v1/validator/aggregate_attestation`, `POST /eth/v1/validator/aggregate_and_proofs`, `POST /eth/v1/beacon/blocks`, `POST /eth/v1/beacon/blinded_blocks`, `GET /eth/v1/builder/states/{state_id}/expected_withdrawals`.

View File

@@ -0,0 +1,21 @@
### Removed
- Deprecated flag `--enable-optional-engine-methods` has been removed.
- Deprecated flag `--disable-build-block-parallel` has been removed.
- Deprecated flag `--disable-reorg-late-blocks` has been removed.
- Deprecated flag `--disable-optional-engine-methods` has been removed.
- Deprecated flag `--disable-aggregate-parallel` has been removed.
- Deprecated flag `--enable-eip-4881` has been removed.
- Deprecated flag `--disable-eip-4881` has been removed.
- Deprecated flag `--enable-verbose-sig-verification` has been removed.
- Deprecated flag `--enable-debug-rpc-endpoints` has been removed.
- Deprecated flag `--beacon-rpc-gateway-provider` has been removed.
- Deprecated flag `--disable-grpc-gateway` has been removed.
- Deprecated flag `--enable-experimental-state` has been removed.
- Deprecated flag `--enable-committee-aware-packing` has been removed.
- Deprecated flag `--interop-genesis-time` has been removed.
- Deprecated flag `--interop-num-validators` has been removed (from beacon-chain only; still available in validator client).
- Deprecated flag `--enable-quic` has been removed.
- Deprecated flag `--attest-timely` has been removed.
- Deprecated flag `--disable-experimental-state` has been removed.
- Deprecated flag `--p2p-metadata` has been removed.

View File

@@ -204,7 +204,7 @@ var (
BlobBatchLimit = &cli.IntFlag{
Name: "blob-batch-limit",
Usage: "The amount of blobs the local peer is bounded to request and respond to in a batch.",
Value: 192,
Value: 384,
}
// BlobBatchLimitBurstFactor specifies the factor by which blob batch size may increase.
BlobBatchLimitBurstFactor = &cli.IntFlag{
@@ -222,7 +222,7 @@ var (
DataColumnBatchLimitBurstFactor = &cli.IntFlag{
Name: "data-column-batch-limit-burst-factor",
Usage: "The factor by which data column batch limit may increase on burst.",
Value: 2,
Value: 4,
}
// DisableDebugRPCEndpoints disables the debug Beacon API namespace.
DisableDebugRPCEndpoints = &cli.BoolFlag{

View File

@@ -69,6 +69,7 @@ type Flags struct {
DisableResourceManager bool // Disables running the node with libp2p's resource manager.
DisableStakinContractCheck bool // Disables check for deposit contract when proposing blocks
DisableLastEpochTargets bool // Disables processing of states for attestations to old blocks.
EnableVerboseSigVerification bool // EnableVerboseSigVerification specifies whether to verify individual signature if batch verification fails
@@ -274,11 +275,14 @@ func ConfigureBeaconChain(ctx *cli.Context) error {
logEnabled(forceHeadFlag)
cfg.ForceHead = ctx.String(forceHeadFlag.Name)
}
if ctx.IsSet(blacklistRoots.Name) {
logEnabled(blacklistRoots)
cfg.BlacklistedRoots = parseBlacklistedRoots(ctx.StringSlice(blacklistRoots.Name))
}
if ctx.IsSet(disableLastEpochTargets.Name) {
logEnabled(disableLastEpochTargets)
cfg.DisableLastEpochTargets = true
}
cfg.AggregateIntervals = [3]time.Duration{aggregateFirstInterval.Value, aggregateSecondInterval.Value, aggregateThirdInterval.Value}
Init(cfg)

View File

@@ -14,129 +14,10 @@ var (
Usage: deprecatedUsage,
Hidden: true,
}
deprecatedEnableOptionalEngineMethods = &cli.BoolFlag{
Name: "enable-optional-engine-methods",
Usage: deprecatedUsage,
Hidden: true,
}
deprecatedDisableBuildBlockParallel = &cli.BoolFlag{
Name: "disable-build-block-parallel",
Usage: deprecatedUsage,
Hidden: true,
}
deprecatedDisableReorgLateBlocks = &cli.BoolFlag{
Name: "disable-reorg-late-blocks",
Usage: deprecatedUsage,
Hidden: true,
}
deprecatedDisableOptionalEngineMethods = &cli.BoolFlag{
Name: "disable-optional-engine-methods",
Usage: deprecatedUsage,
Hidden: true,
}
deprecatedDisableAggregateParallel = &cli.BoolFlag{
Name: "disable-aggregate-parallel",
Usage: deprecatedUsage,
Hidden: true,
}
deprecatedEnableEIP4881 = &cli.BoolFlag{
Name: "enable-eip-4881",
Usage: deprecatedUsage,
Hidden: true,
}
deprecatedDisableEIP4881 = &cli.BoolFlag{
Name: "disable-eip-4881",
Usage: deprecatedUsage,
Hidden: true,
}
deprecatedVerboseSigVerification = &cli.BoolFlag{
Name: "enable-verbose-sig-verification",
Usage: deprecatedUsage,
Hidden: true,
}
deprecatedEnableDebugRPCEndpoints = &cli.BoolFlag{
Name: "enable-debug-rpc-endpoints",
Usage: deprecatedUsage,
Hidden: true,
}
deprecatedBeaconRPCGatewayProviderFlag = &cli.StringFlag{
Name: "beacon-rpc-gateway-provider",
Usage: deprecatedUsage,
Hidden: true,
}
deprecatedDisableGRPCGateway = &cli.BoolFlag{
Name: "disable-grpc-gateway",
Usage: deprecatedUsage,
Hidden: true,
}
deprecatedEnableExperimentalState = &cli.BoolFlag{
Name: "enable-experimental-state",
Usage: deprecatedUsage,
Hidden: true,
}
deprecatedEnableCommitteeAwarePacking = &cli.BoolFlag{
Name: "enable-committee-aware-packing",
Usage: deprecatedUsage,
Hidden: true,
}
deprecatedInteropGenesisTimeFlag = &cli.Uint64Flag{
Name: "interop-genesis-time",
Usage: deprecatedUsage,
Hidden: true,
}
deprecatedInteropNumValidatorsFlag = &cli.Uint64Flag{
Name: "interop-num-validators",
Usage: deprecatedUsage,
Hidden: true,
}
deprecatedEnableQuic = &cli.BoolFlag{
Name: "enable-quic",
Usage: deprecatedUsage,
Hidden: true,
}
deprecatedAttestTimely = &cli.BoolFlag{
Name: "attest-timely",
Usage: deprecatedUsage,
Hidden: true,
}
deprecatedDisableExperimentalState = &cli.BoolFlag{
Name: "disable-experimental-state",
Usage: deprecatedUsage,
Hidden: true,
}
deprecatedP2PMetadata = &cli.StringFlag{
Name: "p2p-metadata",
Usage: deprecatedUsage,
Hidden: true,
}
)
// Deprecated flags for both the beacon node and validator client.
var deprecatedFlags = []cli.Flag{
exampleDeprecatedFeatureFlag,
deprecatedEnableOptionalEngineMethods,
deprecatedDisableBuildBlockParallel,
deprecatedDisableReorgLateBlocks,
deprecatedDisableOptionalEngineMethods,
deprecatedDisableAggregateParallel,
deprecatedEnableEIP4881,
deprecatedDisableEIP4881,
deprecatedVerboseSigVerification,
deprecatedEnableDebugRPCEndpoints,
deprecatedBeaconRPCGatewayProviderFlag,
deprecatedDisableGRPCGateway,
deprecatedEnableExperimentalState,
deprecatedEnableCommitteeAwarePacking,
deprecatedInteropGenesisTimeFlag,
deprecatedEnableQuic,
deprecatedAttestTimely,
deprecatedDisableExperimentalState,
deprecatedP2PMetadata,
}
var deprecatedFlags = []cli.Flag{}
var upcomingDeprecation = []cli.Flag{
enableHistoricalSpaceRepresentation,
@@ -144,6 +25,4 @@ var upcomingDeprecation = []cli.Flag{
// deprecatedBeaconFlags contains flags that are still used by other components
// and therefore cannot be added to deprecatedFlags
var deprecatedBeaconFlags = []cli.Flag{
deprecatedInteropNumValidatorsFlag,
}
var deprecatedBeaconFlags = []cli.Flag{}

View File

@@ -197,6 +197,11 @@ var (
Usage: "(Work in progress): Enables the web portal for the validator client.",
Value: false,
}
// disableLastEpochTargets is a flag to disable processing of attestations for old blocks.
disableLastEpochTargets = &cli.BoolFlag{
Name: "disable-last-epoch-targets",
Usage: "Disables processing of last epoch targets.",
}
)
// devModeFlags holds list of flags that are set when development mode is on.
@@ -257,6 +262,7 @@ var BeaconChainFlags = combinedFlags([]cli.Flag{
enableExperimentalAttestationPool,
forceHeadFlag,
blacklistRoots,
disableLastEpochTargets,
}, deprecatedBeaconFlags, deprecatedFlags, upcomingDeprecation)
func combinedFlags(flags ...[]cli.Flag) []cli.Flag {

View File

@@ -130,10 +130,10 @@ func TestNextForkData(t *testing.T) {
wantedEpoch: cfg.BellatrixForkEpoch,
},
{
name: "after last bpo - should be far future epoch and 0x00000000",
name: "post last full fork, fulu bpo 1",
currEpoch: params.LastForkEpoch() + 1,
wantedForkVersion: [4]byte(cfg.ElectraForkVersion),
wantedEpoch: cfg.ElectraForkEpoch,
wantedForkVersion: [4]byte(cfg.FuluForkVersion),
wantedEpoch: cfg.BlobSchedule[0].Epoch,
},
}
for _, tt := range tests {

View File

@@ -30,7 +30,7 @@ const (
// Electra Fork Epoch for mainnet config
mainnetElectraForkEpoch = 364032 // May 7, 2025, 10:05:11 UTC
// Fulu Fork Epoch for mainnet config
mainnetFuluForkEpoch = math.MaxUint64 // Far future / to be defined
mainnetFuluForkEpoch = 411392 // December 3, 2025, 09:49:11pm UTC
)
var mainnetNetworkConfig = &NetworkConfig{
@@ -338,7 +338,16 @@ var mainnetBeaconConfig = &BeaconChainConfig{
SubnetsPerNode: 2,
NodeIdBits: 256,
BlobSchedule: []BlobScheduleEntry{},
BlobSchedule: []BlobScheduleEntry{
{
Epoch: 412672, // December 9, 2025, 02:21:11pm UTC
MaxBlobsPerBlock: 15,
},
{
Epoch: 419072, // January 7, 2026, 01:01:11am UTC
MaxBlobsPerBlock: 21,
},
},
}
// MainnetTestConfig provides a version of the mainnet config that has a different name

79
hack/upgrade-version.sh Normal file
View File

@@ -0,0 +1,79 @@
#!/usr/bin/env bash
set -euo pipefail
# ====== config ======
OLD="OffchainLabs/prysm/v6"
NEW="OffchainLabs/prysm/v7"
# files by extension (recursive)
EXTENSIONS=("bazel" "go" "proto")
# explicit files (relative to project root)
EXPLICIT_FILES=(
"hack/update-mockgen.sh"
"hack/update-go-pbs.sh"
"go.mod"
".deepsource.toml"
)
# commands to run at the end, in order
COMMANDS=(
'bazel run //:gazelle -- update-repos -from_file=go.mod -to_macro=deps.bzl%prysm_deps -prune=true'
'go mod tidy && go get ./...'
'bazel clean --expunge --async'
'./hack/update-go-pbs.sh'
'./hack/update-go-ssz.sh'
'go build ./... && bazel build //cmd/beacon-chain //cmd/validator'
)
# ====================
# Detect BSD vs GNU sed (macOS vs Linux)
if sed --version >/dev/null 2>&1; then
SED_INPLACE=("sed" "-i")
else
# macOS / BSD sed needs an empty string after -i
SED_INPLACE=("sed" "-i" "")
fi
escape_sed() {
# escape / in pattern and replacement so sed doesn't choke
printf '%s' "$1" | sed 's/[\/&]/\\&/g'
}
OLD_ESCAPED=$(escape_sed "$OLD")
NEW_ESCAPED=$(escape_sed "$NEW")
############################################
# 1) walk directory by extensions
############################################
for ext in "${EXTENSIONS[@]}"; do
while IFS= read -r -d '' file; do
"${SED_INPLACE[@]}" "s/${OLD_ESCAPED}/${NEW_ESCAPED}/g" "$file"
echo "updated (by ext): $file"
done < <(find . -type f -name "*.${ext}" -print0)
done
############################################
# 2) specific files
############################################
for f in "${EXPLICIT_FILES[@]}"; do
if [[ -f "$f" ]]; then
"${SED_INPLACE[@]}" "s/${OLD_ESCAPED}/${NEW_ESCAPED}/g" "$f"
echo "updated (explicit): $f"
else
echo "warn: explicit file not found: $f" >&2
fi
done
############################################
# 3) run commands in order
############################################
for cmd in "${COMMANDS[@]}"; do
echo "==> running: $cmd"
# use bash -c so we can have && in commands
bash -c "$cmd"
echo "==> done: $cmd"
done
echo
echo "✅ version upgrade was successful."

View File

@@ -1,4 +1,4 @@
version: v1.6.0-beta.2
version: v1.6.0
style: full
specrefs:

View File

@@ -108,8 +108,16 @@
search: BlobSchedule\s+\[]BlobScheduleEntry
regex: true
spec: |
<spec config_var="BLOB_SCHEDULE" fork="fulu" hash="f3f1064a">
<spec config_var="BLOB_SCHEDULE" fork="fulu" hash="07879110">
BLOB_SCHEDULE: tuple[frozendict[str, Any], ...] = (
frozendict({
"EPOCH": 412672,
"MAX_BLOBS_PER_BLOCK": 15,
}),
frozendict({
"EPOCH": 419072,
"MAX_BLOBS_PER_BLOCK": 21,
}),
)
</spec>
@@ -266,8 +274,8 @@
search: FuluForkEpoch\s+primitives.Epoch
regex: true
spec: |
<spec config_var="FULU_FORK_EPOCH" fork="fulu" hash="673334be">
FULU_FORK_EPOCH: Epoch = 18446744073709551615
<spec config_var="FULU_FORK_EPOCH" fork="fulu" hash="af10fa3c">
FULU_FORK_EPOCH: Epoch = 411392
</spec>
- name: FULU_FORK_VERSION

View File

@@ -136,10 +136,6 @@ func (c *componentHandler) setup() {
})
c.eth1Nodes = eth1Nodes
if config.TestCheckpointSync {
appendDebugEndpoints(config)
}
var builders *components.BuilderSet
var proxies *eth1.ProxySet
if config.UseBuilder {
@@ -290,10 +286,3 @@ func PIDsFromMultiComponentRunner(runner e2etypes.MultipleComponentRunners) []in
}
return pids
}
func appendDebugEndpoints(cfg *e2etypes.E2EConfig) {
debug := []string{
"--enable-debug-rpc-endpoints",
}
cfg.BeaconFlags = append(cfg.BeaconFlags, debug...)
}

View File

@@ -136,7 +136,7 @@ var getRequests = map[string]endpoint{
v2PathTemplate,
withSanityCheckOnly()),
"/beacon/pool/attester_slashings": newMetadata[structs.GetAttesterSlashingsResponse](
v1PathTemplate,
v2PathTemplate,
withSanityCheckOnly()),
"/beacon/pool/proposer_slashings": newMetadata[structs.GetProposerSlashingsResponse](
v1PathTemplate,
@@ -147,12 +147,6 @@ var getRequests = map[string]endpoint{
"/beacon/pool/bls_to_execution_changes": newMetadata[structs.BLSToExecutionChangesPoolResponse](
v1PathTemplate,
withSanityCheckOnly()),
"/builder/states/{param1}/expected_withdrawals": newMetadata[structs.ExpectedWithdrawalsResponse](
v1PathTemplate,
withStart(params.CapellaE2EForkEpoch),
withParams(func(_ primitives.Epoch) []string {
return []string{"head"}
})),
"/config/fork_schedule": newMetadata[structs.GetForkScheduleResponse](
v1PathTemplate,
withCustomEval(func(p interface{}, lh interface{}) error {
@@ -208,7 +202,7 @@ var getRequests = map[string]endpoint{
withCustomEval(func(p interface{}, _ interface{}) error {
pResp, ok := p.(*structs.GetVersionResponse)
if !ok {
return fmt.Errorf(msgWrongJSON, &structs.ListAttestationsResponse{}, p)
return fmt.Errorf(msgWrongJSON, &structs.GetVersionResponse{}, p)
}
if pResp.Data == nil {
return errEmptyPrysmData

View File

@@ -4,10 +4,8 @@ import (
"bytes"
"context"
"encoding/json"
"net/http"
"github.com/OffchainLabs/prysm/v6/beacon-chain/core/helpers"
"github.com/OffchainLabs/prysm/v6/network/httputil"
ethpb "github.com/OffchainLabs/prysm/v6/proto/prysm/v1alpha1"
"github.com/OffchainLabs/prysm/v6/runtime/version"
"github.com/OffchainLabs/prysm/v6/time/slots"
@@ -31,25 +29,8 @@ func (c *beaconApiValidatorClient) proposeAttestation(ctx context.Context, attes
bytes.NewBuffer(marshalledAttestation),
nil,
)
errJson := &httputil.DefaultJsonError{}
if err != nil {
// TODO: remove this when v2 becomes default
if !errors.As(err, &errJson) {
return nil, err
}
if errJson.Code != http.StatusNotFound {
return nil, errJson
}
log.Debug("Endpoint /eth/v2/beacon/pool/attestations is not supported, falling back to older endpoints for submit attestation.")
if err = c.jsonRestHandler.Post(
ctx,
"/eth/v1/beacon/pool/attestations",
nil,
bytes.NewBuffer(marshalledAttestation),
nil,
); err != nil {
return nil, err
}
return nil, err
}
attestationDataRoot, err := attestation.Data.HashTreeRoot()

View File

@@ -4,13 +4,11 @@ import (
"bytes"
"encoding/json"
"errors"
"net/http"
"testing"
"github.com/OffchainLabs/prysm/v6/beacon-chain/core/helpers"
"github.com/OffchainLabs/prysm/v6/config/params"
"github.com/OffchainLabs/prysm/v6/consensus-types/primitives"
"github.com/OffchainLabs/prysm/v6/network/httputil"
ethpb "github.com/OffchainLabs/prysm/v6/proto/prysm/v1alpha1"
"github.com/OffchainLabs/prysm/v6/runtime/version"
"github.com/OffchainLabs/prysm/v6/testing/assert"
@@ -150,72 +148,6 @@ func TestProposeAttestation(t *testing.T) {
}
}
func TestProposeAttestationFallBack(t *testing.T) {
attestation := &ethpb.Attestation{
AggregationBits: testhelpers.FillByteSlice(4, 74),
Data: &ethpb.AttestationData{
Slot: 75,
CommitteeIndex: 76,
BeaconBlockRoot: testhelpers.FillByteSlice(32, 38),
Source: &ethpb.Checkpoint{
Epoch: 78,
Root: testhelpers.FillByteSlice(32, 79),
},
Target: &ethpb.Checkpoint{
Epoch: 80,
Root: testhelpers.FillByteSlice(32, 81),
},
},
Signature: testhelpers.FillByteSlice(96, 82),
}
ctrl := gomock.NewController(t)
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
var marshalledAttestations []byte
if helpers.ValidateNilAttestation(attestation) == nil {
b, err := json.Marshal(jsonifyAttestations([]*ethpb.Attestation{attestation}))
require.NoError(t, err)
marshalledAttestations = b
}
ctx := t.Context()
headers := map[string]string{"Eth-Consensus-Version": version.String(attestation.Version())}
jsonRestHandler.EXPECT().Post(
gomock.Any(),
"/eth/v2/beacon/pool/attestations",
headers,
bytes.NewBuffer(marshalledAttestations),
nil,
).Return(
&httputil.DefaultJsonError{
Code: http.StatusNotFound,
},
).Times(1)
jsonRestHandler.EXPECT().Post(
gomock.Any(),
"/eth/v1/beacon/pool/attestations",
nil,
bytes.NewBuffer(marshalledAttestations),
nil,
).Return(
nil,
).Times(1)
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
proposeResponse, err := validatorClient.proposeAttestation(ctx, attestation)
require.NoError(t, err)
require.NotNil(t, proposeResponse)
expectedAttestationDataRoot, err := attestation.Data.HashTreeRoot()
require.NoError(t, err)
// Make sure that the attestation data root is set
assert.DeepEqual(t, expectedAttestationDataRoot[:], proposeResponse.AttestationDataRoot)
}
func TestProposeAttestationElectra(t *testing.T) {
params.SetupTestConfigCleanup(t)
params.BeaconConfig().ElectraForkEpoch = 0

View File

@@ -3,7 +3,6 @@ package beacon_api
import (
"context"
"encoding/json"
"net/http"
"net/url"
"strconv"
@@ -11,7 +10,6 @@ import (
"github.com/OffchainLabs/prysm/v6/api/server/structs"
"github.com/OffchainLabs/prysm/v6/beacon-chain/core/helpers"
"github.com/OffchainLabs/prysm/v6/consensus-types/primitives"
"github.com/OffchainLabs/prysm/v6/network/httputil"
ethpb "github.com/OffchainLabs/prysm/v6/proto/prysm/v1alpha1"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/pkg/errors"
@@ -132,23 +130,8 @@ func (c *beaconApiValidatorClient) aggregateAttestation(
var aggregateAttestationResponse structs.AggregateAttestationResponse
err := c.jsonRestHandler.Get(ctx, endpoint, &aggregateAttestationResponse)
errJson := &httputil.DefaultJsonError{}
if err != nil {
// TODO: remove this when v2 becomes default
if !errors.As(err, &errJson) {
return nil, err
}
if errJson.Code != http.StatusNotFound {
return nil, errJson
}
log.Debug("Endpoint /eth/v2/validator/aggregate_attestation is not supported, falling back to older endpoints for get aggregated attestation.")
params = url.Values{}
params.Add("slot", strconv.FormatUint(uint64(slot), 10))
params.Add("attestation_data_root", hexutil.Encode(attestationDataRoot))
oldEndpoint := apiutil.BuildURL("/eth/v1/validator/aggregate_attestation", params)
if err = c.jsonRestHandler.Get(ctx, oldEndpoint, &aggregateAttestationResponse); err != nil {
return nil, err
}
return nil, err
}
return &aggregateAttestationResponse, nil

View File

@@ -4,12 +4,10 @@ import (
"encoding/json"
"errors"
"fmt"
"net/http"
"testing"
"github.com/OffchainLabs/prysm/v6/api/server/structs"
"github.com/OffchainLabs/prysm/v6/consensus-types/primitives"
"github.com/OffchainLabs/prysm/v6/network/httputil"
ethpb "github.com/OffchainLabs/prysm/v6/proto/prysm/v1alpha1"
"github.com/OffchainLabs/prysm/v6/testing/assert"
"github.com/OffchainLabs/prysm/v6/testing/require"
@@ -187,129 +185,6 @@ func TestSubmitAggregateSelectionProof(t *testing.T) {
}
}
func TestSubmitAggregateSelectionProofFallBack(t *testing.T) {
const (
pubkeyStr = "0x8000091c2ae64ee414a54c1cc1fc67dec663408bc636cb86756e0200e41a75c8f86603f104f02c856983d2783116be13"
syncingEndpoint = "/eth/v1/node/syncing"
attestationDataEndpoint = "/eth/v1/validator/attestation_data"
aggregateAttestationEndpoint = "/eth/v1/validator/aggregate_attestation"
aggregateAttestationV2Endpoint = "/eth/v2/validator/aggregate_attestation"
validatorIndex = primitives.ValidatorIndex(55293)
slotSignature = "0x8776a37d6802c4797d113169c5fcfda50e68a32058eb6356a6f00d06d7da64c841a00c7c38b9b94a204751eca53707bd03523ce4797827d9bacff116a6e776a20bbccff4b683bf5201b610797ed0502557a58a65c8395f8a1649b976c3112d15"
slot = primitives.Slot(123)
committeeIndex = primitives.CommitteeIndex(1)
committeesAtSlot = uint64(1)
)
attestationDataResponse := generateValidAttestation(uint64(slot), uint64(committeeIndex))
attestationDataProto, err := attestationDataResponse.Data.ToConsensus()
require.NoError(t, err)
attestationDataRootBytes, err := attestationDataProto.HashTreeRoot()
require.NoError(t, err)
aggregateAttestation := &ethpb.Attestation{
AggregationBits: testhelpers.FillByteSlice(4, 74),
Data: attestationDataProto,
Signature: testhelpers.FillByteSlice(96, 82),
}
ctrl := gomock.NewController(t)
defer ctrl.Finish()
ctx := t.Context()
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
// Call node syncing endpoint to check if head is optimistic.
jsonRestHandler.EXPECT().Get(
gomock.Any(),
syncingEndpoint,
&structs.SyncStatusResponse{},
).SetArg(
2,
structs.SyncStatusResponse{
Data: &structs.SyncStatusResponseData{
IsOptimistic: false,
},
},
).Return(
nil,
).Times(1)
// Call attestation data to get attestation data root to query aggregate attestation.
jsonRestHandler.EXPECT().Get(
gomock.Any(),
fmt.Sprintf("%s?committee_index=%d&slot=%d", attestationDataEndpoint, committeeIndex, slot),
&structs.GetAttestationDataResponse{},
).SetArg(
2,
attestationDataResponse,
).Return(
nil,
).Times(1)
attestationJSON, err := json.Marshal(jsonifyAttestation(aggregateAttestation))
require.NoError(t, err)
// Call attestation data to get attestation data root to query aggregate attestation.
jsonRestHandler.EXPECT().Get(
gomock.Any(),
fmt.Sprintf("%s?attestation_data_root=%s&committee_index=%d&slot=%d", aggregateAttestationV2Endpoint, hexutil.Encode(attestationDataRootBytes[:]), committeeIndex, slot),
&structs.AggregateAttestationResponse{},
).Return(
&httputil.DefaultJsonError{
Code: http.StatusNotFound,
},
).Times(1)
// Call attestation data to get attestation data root to query aggregate attestation.
jsonRestHandler.EXPECT().Get(
gomock.Any(),
fmt.Sprintf("%s?attestation_data_root=%s&slot=%d", aggregateAttestationEndpoint, hexutil.Encode(attestationDataRootBytes[:]), slot),
&structs.AggregateAttestationResponse{},
).SetArg(
2,
structs.AggregateAttestationResponse{
Data: attestationJSON,
},
).Return(
nil,
).Times(1)
pubkey, err := hexutil.Decode(pubkeyStr)
require.NoError(t, err)
slotSignatureBytes, err := hexutil.Decode(slotSignature)
require.NoError(t, err)
expectedResponse := &ethpb.AggregateSelectionResponse{
AggregateAndProof: &ethpb.AggregateAttestationAndProof{
AggregatorIndex: primitives.ValidatorIndex(55293),
Aggregate: aggregateAttestation,
SelectionProof: slotSignatureBytes,
},
}
validatorClient := &beaconApiValidatorClient{
jsonRestHandler: jsonRestHandler,
stateValidatorsProvider: beaconApiStateValidatorsProvider{
jsonRestHandler: jsonRestHandler,
},
dutiesProvider: beaconApiDutiesProvider{
jsonRestHandler: jsonRestHandler,
},
}
actualResponse, err := validatorClient.submitAggregateSelectionProof(ctx, &ethpb.AggregateSelectionRequest{
Slot: slot,
CommitteeIndex: committeeIndex,
PublicKey: pubkey,
SlotSignature: slotSignatureBytes,
}, validatorIndex, committeesAtSlot)
require.NoError(t, err)
assert.DeepEqual(t, expectedResponse, actualResponse)
}
func TestSubmitAggregateSelectionProofElectra(t *testing.T) {
const (
pubkeyStr = "0x8000091c2ae64ee414a54c1cc1fc67dec663408bc636cb86756e0200e41a75c8f86603f104f02c856983d2783116be13"

View File

@@ -4,10 +4,8 @@ import (
"bytes"
"context"
"encoding/json"
"net/http"
"github.com/OffchainLabs/prysm/v6/api/server/structs"
"github.com/OffchainLabs/prysm/v6/network/httputil"
ethpb "github.com/OffchainLabs/prysm/v6/proto/prysm/v1alpha1"
"github.com/OffchainLabs/prysm/v6/runtime/version"
"github.com/OffchainLabs/prysm/v6/time/slots"
@@ -21,25 +19,8 @@ func (c *beaconApiValidatorClient) submitSignedAggregateSelectionProof(ctx conte
}
headers := map[string]string{"Eth-Consensus-Version": version.String(in.SignedAggregateAndProof.Version())}
err = c.jsonRestHandler.Post(ctx, "/eth/v2/validator/aggregate_and_proofs", headers, bytes.NewBuffer(body), nil)
errJson := &httputil.DefaultJsonError{}
if err != nil {
// TODO: remove this when v2 becomes default
if !errors.As(err, &errJson) {
return nil, err
}
if errJson.Code != http.StatusNotFound {
return nil, errJson
}
log.Debug("Endpoint /eth/v2/validator/aggregate_and_proofs is not supported, falling back to older endpoints for publish aggregate and proofs.")
if err = c.jsonRestHandler.Post(
ctx,
"/eth/v1/validator/aggregate_and_proofs",
nil,
bytes.NewBuffer(body),
nil,
); err != nil {
return nil, err
}
return nil, err
}
attestationDataRoot, err := in.SignedAggregateAndProof.Message.Aggregate.Data.HashTreeRoot()

View File

@@ -3,12 +3,10 @@ package beacon_api
import (
"bytes"
"encoding/json"
"net/http"
"testing"
"github.com/OffchainLabs/prysm/v6/api/server/structs"
"github.com/OffchainLabs/prysm/v6/config/params"
"github.com/OffchainLabs/prysm/v6/network/httputil"
ethpb "github.com/OffchainLabs/prysm/v6/proto/prysm/v1alpha1"
"github.com/OffchainLabs/prysm/v6/runtime/version"
"github.com/OffchainLabs/prysm/v6/testing/assert"
@@ -80,50 +78,6 @@ func TestSubmitSignedAggregateSelectionProof_BadRequest(t *testing.T) {
assert.ErrorContains(t, "bad request", err)
}
func TestSubmitSignedAggregateSelectionProof_Fallback(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
signedAggregateAndProof := generateSignedAggregateAndProofJson()
marshalledSignedAggregateSignedAndProof, err := json.Marshal([]*structs.SignedAggregateAttestationAndProof{jsonifySignedAggregateAndProof(signedAggregateAndProof)})
require.NoError(t, err)
ctx := t.Context()
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
headers := map[string]string{"Eth-Consensus-Version": version.String(signedAggregateAndProof.Message.Version())}
jsonRestHandler.EXPECT().Post(
gomock.Any(),
"/eth/v2/validator/aggregate_and_proofs",
headers,
bytes.NewBuffer(marshalledSignedAggregateSignedAndProof),
nil,
).Return(
&httputil.DefaultJsonError{
Code: http.StatusNotFound,
},
).Times(1)
jsonRestHandler.EXPECT().Post(
gomock.Any(),
"/eth/v1/validator/aggregate_and_proofs",
nil,
bytes.NewBuffer(marshalledSignedAggregateSignedAndProof),
nil,
).Return(
nil,
).Times(1)
attestationDataRoot, err := signedAggregateAndProof.Message.Aggregate.Data.HashTreeRoot()
require.NoError(t, err)
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
resp, err := validatorClient.submitSignedAggregateSelectionProof(ctx, &ethpb.SignedAggregateSubmitRequest{
SignedAggregateAndProof: signedAggregateAndProof,
})
require.NoError(t, err)
assert.DeepEqual(t, attestationDataRoot[:], resp.AttestationDataRoot)
}
func TestSubmitSignedAggregateSelectionProofElectra_Valid(t *testing.T) {
params.SetupTestConfigCleanup(t)
params.BeaconConfig().ElectraForkEpoch = 0