mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 21:38:05 -05:00
Compare commits
11 Commits
alternate-
...
version-up
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a7565164e4 | ||
|
|
94d76a5685 | ||
|
|
2ef2dd16ba | ||
|
|
d6005026e0 | ||
|
|
33476f5d7b | ||
|
|
35e9c1752e | ||
|
|
8b6f187b15 | ||
|
|
8ad547c969 | ||
|
|
d3d7f67bec | ||
|
|
9959782f1c | ||
|
|
f0a099b275 |
16
WORKSPACE
16
WORKSPACE
@@ -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(
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -9,24 +9,6 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
)
|
||||
|
||||
func TestDepositSnapshotFromConsensus(t *testing.T) {
|
||||
ds := ð.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()
|
||||
|
||||
@@ -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"`
|
||||
|
||||
@@ -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"`
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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})
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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,
|
||||
} {
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
@@ -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",
|
||||
],
|
||||
)
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
@@ -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 := ð.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])
|
||||
})
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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", ðpbalpha.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
@@ -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",
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,15 +43,16 @@ func TestRegularSyncBeaconBlockSubscriber_ProcessPendingBlocks1(t *testing.T) {
|
||||
db := dbtest.SetupDB(t)
|
||||
|
||||
p1 := p2ptest.NewTestP2P(t)
|
||||
mockChain := &mock.ChainService{
|
||||
FinalizedCheckPoint: ðpb.Checkpoint{
|
||||
Epoch: 0,
|
||||
},
|
||||
}
|
||||
r := &Service{
|
||||
cfg: &config{
|
||||
p2p: p1,
|
||||
beaconDB: db,
|
||||
chain: &mock.ChainService{
|
||||
FinalizedCheckPoint: ðpb.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: ðpb.Checkpoint{
|
||||
Epoch: 0,
|
||||
},
|
||||
}
|
||||
r := &Service{
|
||||
cfg: &config{
|
||||
p2p: p1,
|
||||
beaconDB: db,
|
||||
chain: &mock.ChainService{
|
||||
Optimistic: true,
|
||||
FinalizedCheckPoint: ðpb.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: ðpb.Checkpoint{
|
||||
Epoch: 0,
|
||||
},
|
||||
ReceiveBlockMockErr: execution.ErrHTTPTimeout,
|
||||
}
|
||||
r := &Service{
|
||||
cfg: &config{
|
||||
p2p: p1,
|
||||
beaconDB: db,
|
||||
chain: &mock.ChainService{
|
||||
FinalizedCheckPoint: ðpb.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[:]
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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] = ðpb.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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -20,7 +20,7 @@ func TestMain(m *testing.M) {
|
||||
BlobBatchLimit: 32,
|
||||
BlobBatchLimitBurstFactor: 2,
|
||||
DataColumnBatchLimit: 4096,
|
||||
DataColumnBatchLimitBurstFactor: 2,
|
||||
DataColumnBatchLimitBurstFactor: 4,
|
||||
})
|
||||
defer func() {
|
||||
flags.Init(resetFlags)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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: ðpb.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: ðpb.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, ðpb.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: ðpb.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())
|
||||
}
|
||||
|
||||
3
changelog/bastin_version-upgrade-script.md
Normal file
3
changelog/bastin_version-upgrade-script.md
Normal file
@@ -0,0 +1,3 @@
|
||||
### Added
|
||||
|
||||
- Bash script for automating the version upgrade process.
|
||||
8
changelog/james-prysm_fulu-fork-epoch.md
Normal file
8
changelog/james-prysm_fulu-fork-epoch.md
Normal 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
|
||||
2
changelog/kasey_disable-backfill-if-fulu.md
Normal file
2
changelog/kasey_disable-backfill-if-fulu.md
Normal 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
|
||||
2
changelog/manu-rate-limit.md
Normal file
2
changelog/manu-rate-limit.md
Normal file
@@ -0,0 +1,2 @@
|
||||
### Fixed
|
||||
- Ensures the rate limitation is respected for by root blob and data column sidecars requests.
|
||||
3
changelog/potuz_head_target_compat.md
Normal file
3
changelog/potuz_head_target_compat.md
Normal file
@@ -0,0 +1,3 @@
|
||||
### Fixed
|
||||
|
||||
- Use head only if its compatible with target for attestation validation.
|
||||
3
changelog/potuz_use_head_block_validation.md
Normal file
3
changelog/potuz_use_head_block_validation.md
Normal file
@@ -0,0 +1,3 @@
|
||||
### Changed
|
||||
|
||||
- Use head state for block pubsub validation when possible.
|
||||
3
changelog/pvl-fulu-test-fix.md
Normal file
3
changelog/pvl-fulu-test-fix.md
Normal file
@@ -0,0 +1,3 @@
|
||||
### Ignored
|
||||
|
||||
- Fix test setup to properly reference electra rather than unset the fulu epoch
|
||||
3
changelog/radek_v7-remove-apis.md
Normal file
3
changelog/radek_v7-remove-apis.md
Normal 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`.
|
||||
21
changelog/remove-deprecated-flags.md
Normal file
21
changelog/remove-deprecated-flags.md
Normal 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.
|
||||
@@ -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{
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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{}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
79
hack/upgrade-version.sh
Normal 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."
|
||||
@@ -1,4 +1,4 @@
|
||||
version: v1.6.0-beta.2
|
||||
version: v1.6.0
|
||||
style: full
|
||||
|
||||
specrefs:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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...)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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 := ðpb.Attestation{
|
||||
AggregationBits: testhelpers.FillByteSlice(4, 74),
|
||||
Data: ðpb.AttestationData{
|
||||
Slot: 75,
|
||||
CommitteeIndex: 76,
|
||||
BeaconBlockRoot: testhelpers.FillByteSlice(32, 38),
|
||||
Source: ðpb.Checkpoint{
|
||||
Epoch: 78,
|
||||
Root: testhelpers.FillByteSlice(32, 79),
|
||||
},
|
||||
Target: ðpb.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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 := ðpb.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 := ðpb.AggregateSelectionResponse{
|
||||
AggregateAndProof: ðpb.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, ðpb.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"
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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, ðpb.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
|
||||
|
||||
Reference in New Issue
Block a user