Compare commits

...

11 Commits

Author SHA1 Message Date
Manu NALEPA
663b53829c Lock the subnet mutex only when trying to find peers, so the mutex is not locked when dialing peers. 2025-09-14 21:18:11 +02:00
kasey
4b09dd4aa5 prevent ConnManager from pruning peers excessively (#15681)
* prevent ConnManager from pruning peers excessively

* manu feedback

---------

Co-authored-by: Kasey Kirkham <kasey@users.noreply.github.com>
Co-authored-by: james-prysm <90280386+james-prysm@users.noreply.github.com>
2025-09-14 00:03:48 +00:00
Preston Van Loon
1dab5a9f8a Feature: --p2p-colocation-whitelist flag to allow certain IPs to bypass colocation restrictions (#15685)
* Add flag for colocation whitelisting. --p2p-ip-colocation-whitelist

This change updates the peer IP colocation checking to respect the
configured CIDR whitelist (--p2p-ip-colocation-whitelist flag).

Changes:
- Added IPColocationWhitelist field to peers.StatusConfig
- Added ipColocationWhitelist field to Status struct to store parsed IPNets
- Parse CIDR strings into net.IPNet in NewStatus constructor
- Updated isfromBadIP method to skip colocation limits for whitelisted IPs
- Pass IPColocationWhitelist from Service config when creating Status

The IP colocation whitelist allows operators to exempt specific IP ranges
from the colocation limit, useful for deployments with known trusted
address ranges or legitimate node clustering.

Only check if an IP is in the whitelist when the colocation limit
is actually exceeded, rather than checking for every IP. This is
more efficient and matches the intended behavior.

* Changelog fragment

* Apply suggestion from @nalepae

Co-authored-by: Manu NALEPA <enalepa@offchainlabs.com>

* Apply suggestion from @nalepae

Co-authored-by: Manu NALEPA <enalepa@offchainlabs.com>

* @kasey feedback: Move IP colocation parsing to the node construction

---------

Co-authored-by: Manu NALEPA <enalepa@offchainlabs.com>
2025-09-12 16:03:54 +00:00
Potuz
d681232fe6 Fix setup forkchoice (#15684)
* Insert the head block to forkchoice

* Add test

* Rename file

* add changelog

* Remove test
2025-09-12 13:58:00 +00:00
Preston Van Loon
1d24f89c96 Downscore peers when they give invalid responses during initial sync (#15686)
* Downscore peers when they give invalid responses during initial sync.

* @nalepae feedback on adding a log message after downscoring
2025-09-12 13:29:08 +00:00
kasey
967193e6a2 update go-libp2p-pubsub=v0.14.2, go-libp2p=v0.39.1 (#15677)
Co-authored-by: Kasey Kirkham <kasey@users.noreply.github.com>
2025-09-11 21:04:56 +00:00
terence
9e40551852 Implement KZG proof batch verification (option 2) - uses worker pool (#15617)
* Implement KZG proof batch verification for data column gossip validation

* Manu's feedback

* Add tests

* Update beacon-chain/sync/batch_verifier.go

Co-authored-by: Manu NALEPA <enalepa@offchainlabs.com>

* Update beacon-chain/sync/batch_verifier.go

Co-authored-by: Manu NALEPA <enalepa@offchainlabs.com>

* Update beacon-chain/sync/kzg_batch_verifier_test.go

Co-authored-by: Manu NALEPA <enalepa@offchainlabs.com>

* Update beacon-chain/sync/kzg_batch_verifier_test.go

Co-authored-by: Manu NALEPA <enalepa@offchainlabs.com>

* Update beacon-chain/sync/kzg_batch_verifier_test.go

Co-authored-by: Manu NALEPA <enalepa@offchainlabs.com>

* Update beacon-chain/sync/kzg_batch_verifier_test.go

Co-authored-by: Manu NALEPA <enalepa@offchainlabs.com>

* Update beacon-chain/sync/kzg_batch_verifier_test.go

Co-authored-by: Manu NALEPA <enalepa@offchainlabs.com>

* Fix tests

* Kasey's feedback

* `validateWithKzgBatchVerifier`: Give up after a full slot.

Before this commit:
After 100 ms, an un-batched verification is launched concurrently to the batched one.
As a result, a stressed node could start to be even more stressed by the multiple verifications.
Also, it is always hard to choose a correct timeout value.
100ms may be OK for a given node with a given BPO version, and not ok for the same node with a BPO version with 10x more blobs.

However, we know this gossip validation won't be useful after a full slot duration.

After this commit:
After a full slot duration, we just ignore the incoming gossip message.
It's important to ignore it and not to reject it, since rejecting it would downscore the peer sending this message.

---------

Co-authored-by: Manu NALEPA <enalepa@offchainlabs.com>
2025-09-11 15:52:25 +00:00
fernantho
a8cab58f7e SSZ-QL: populate variable-length metadata in AnalyzeObject (#15676)
* increased AnalyzeObject functionality to return a fully populated SSZInfo object including runtime lengths for lists

* reverted formatting change

---------

Co-authored-by: Radosław Kapka <rkapka@wp.pl>
2025-09-11 11:39:02 +00:00
james-prysm
df86f57507 deprecate /prysm/v1/beacon/blobs (#15643)
* adding in check for post fulu support

* gaz

* fixing unit tests and typo

* gaz

* manu feedback

* addressing manu's suggestion

* fixing tests
2025-09-10 21:44:50 +00:00
james-prysm
9b0a3e9632 default validator client rest mode to ssz for post block (#15645)
* wip

* fixing tests

* updating unit tests for coverage

* gofmt

* changelog

* consolidated propose beacon block tests and improved code coverage

* gaz

* partial review feedback

* continuation fixing feedback on functions

* adding log

* only fall back on 406

* fixing broken tests

* Update validator/client/beacon-api/propose_beacon_block.go

Co-authored-by: Radosław Kapka <rkapka@wp.pl>

* Update validator/client/beacon-api/propose_beacon_block.go

Co-authored-by: Radosław Kapka <rkapka@wp.pl>

* Update validator/client/beacon-api/propose_beacon_block.go

Co-authored-by: Radosław Kapka <rkapka@wp.pl>

* Update validator/client/beacon-api/propose_beacon_block.go

Co-authored-by: Radosław Kapka <rkapka@wp.pl>

* Update validator/client/beacon-api/propose_beacon_block.go

Co-authored-by: Radosław Kapka <rkapka@wp.pl>

* radek review feedback

* more feedback

* radek suggestion

* fixing unit tests

---------

Co-authored-by: Radosław Kapka <rkapka@wp.pl>
2025-09-10 21:08:11 +00:00
kasey
5e079aa62c justin's suggestion to unwedge CI (#15679)
Co-authored-by: Kasey Kirkham <kasey@users.noreply.github.com>
2025-09-10 20:31:29 +00:00
64 changed files with 2406 additions and 1525 deletions

View File

@@ -27,7 +27,7 @@ go_library(
"receive_block.go",
"receive_data_column.go",
"service.go",
"setup_forchoice.go",
"setup_forkchoice.go",
"tracked_proposer.go",
"weak_subjectivity_checks.go",
],

View File

@@ -78,7 +78,10 @@ func (s *Service) setupForkchoiceTree(st state.BeaconState) error {
}
s.cfg.ForkChoiceStore.Lock()
defer s.cfg.ForkChoiceStore.Unlock()
return s.cfg.ForkChoiceStore.InsertChain(s.ctx, chain)
if err := s.cfg.ForkChoiceStore.InsertChain(s.ctx, chain); err != nil {
return errors.Wrap(err, "could not insert forkchoice chain")
}
return s.cfg.ForkChoiceStore.InsertNode(s.ctx, st, chain[0].Block)
}
func (s *Service) buildForkchoiceChain(ctx context.Context, head interfaces.ReadOnlySignedBeaconBlock) ([]*forkchoicetypes.BlockAndCheckpoints, error) {

View File

@@ -124,5 +124,5 @@ func Test_setupForkchoiceTree_Head(t *testing.T) {
require.NotEqual(t, fRoot, root)
require.Equal(t, root, service.startupHeadRoot())
require.NoError(t, service.setupForkchoiceTree(st))
require.Equal(t, 2, service.cfg.ForkChoiceStore.NodeCount())
require.Equal(t, 3, service.cfg.ForkChoiceStore.NodeCount())
}

View File

@@ -621,35 +621,55 @@ func (b *BeaconNode) startStateGen(ctx context.Context, bfs coverage.AvailableBl
return nil
}
func parseIPNetStrings(ipWhitelist []string) ([]*net.IPNet, error) {
ipNets := make([]*net.IPNet, 0, len(ipWhitelist))
for _, cidr := range ipWhitelist {
_, ipNet, err := net.ParseCIDR(cidr)
if err != nil {
log.WithError(err).WithField("cidr", cidr).Error("Invalid CIDR in IP colocation whitelist")
return nil, err
}
ipNets = append(ipNets, ipNet)
log.WithField("cidr", cidr).Info("Added IP to colocation whitelist")
}
return ipNets, nil
}
func (b *BeaconNode) registerP2P(cliCtx *cli.Context) error {
bootstrapNodeAddrs, dataDir, err := registration.P2PPreregistration(cliCtx)
if err != nil {
return errors.Wrapf(err, "could not register p2p service")
}
colocationWhitelist, err := parseIPNetStrings(slice.SplitCommaSeparated(cliCtx.StringSlice(cmd.P2PColocationWhitelist.Name)))
if err != nil {
return fmt.Errorf("failed to register p2p service: %w", err)
}
svc, err := p2p.NewService(b.ctx, &p2p.Config{
NoDiscovery: cliCtx.Bool(cmd.NoDiscovery.Name),
StaticPeers: slice.SplitCommaSeparated(cliCtx.StringSlice(cmd.StaticPeers.Name)),
Discv5BootStrapAddrs: p2p.ParseBootStrapAddrs(bootstrapNodeAddrs),
RelayNodeAddr: cliCtx.String(cmd.RelayNode.Name),
DataDir: dataDir,
DiscoveryDir: filepath.Join(dataDir, "discovery"),
LocalIP: cliCtx.String(cmd.P2PIP.Name),
HostAddress: cliCtx.String(cmd.P2PHost.Name),
HostDNS: cliCtx.String(cmd.P2PHostDNS.Name),
PrivateKey: cliCtx.String(cmd.P2PPrivKey.Name),
StaticPeerID: cliCtx.Bool(cmd.P2PStaticID.Name),
QUICPort: cliCtx.Uint(cmd.P2PQUICPort.Name),
TCPPort: cliCtx.Uint(cmd.P2PTCPPort.Name),
UDPPort: cliCtx.Uint(cmd.P2PUDPPort.Name),
MaxPeers: cliCtx.Uint(cmd.P2PMaxPeers.Name),
QueueSize: cliCtx.Uint(cmd.PubsubQueueSize.Name),
AllowListCIDR: cliCtx.String(cmd.P2PAllowList.Name),
DenyListCIDR: slice.SplitCommaSeparated(cliCtx.StringSlice(cmd.P2PDenyList.Name)),
EnableUPnP: cliCtx.Bool(cmd.EnableUPnPFlag.Name),
StateNotifier: b,
DB: b.db,
ClockWaiter: b.clockWaiter,
NoDiscovery: cliCtx.Bool(cmd.NoDiscovery.Name),
StaticPeers: slice.SplitCommaSeparated(cliCtx.StringSlice(cmd.StaticPeers.Name)),
Discv5BootStrapAddrs: p2p.ParseBootStrapAddrs(bootstrapNodeAddrs),
RelayNodeAddr: cliCtx.String(cmd.RelayNode.Name),
DataDir: dataDir,
DiscoveryDir: filepath.Join(dataDir, "discovery"),
LocalIP: cliCtx.String(cmd.P2PIP.Name),
HostAddress: cliCtx.String(cmd.P2PHost.Name),
HostDNS: cliCtx.String(cmd.P2PHostDNS.Name),
PrivateKey: cliCtx.String(cmd.P2PPrivKey.Name),
StaticPeerID: cliCtx.Bool(cmd.P2PStaticID.Name),
QUICPort: cliCtx.Uint(cmd.P2PQUICPort.Name),
TCPPort: cliCtx.Uint(cmd.P2PTCPPort.Name),
UDPPort: cliCtx.Uint(cmd.P2PUDPPort.Name),
MaxPeers: cliCtx.Uint(cmd.P2PMaxPeers.Name),
QueueSize: cliCtx.Uint(cmd.PubsubQueueSize.Name),
AllowListCIDR: cliCtx.String(cmd.P2PAllowList.Name),
DenyListCIDR: slice.SplitCommaSeparated(cliCtx.StringSlice(cmd.P2PDenyList.Name)),
IPColocationWhitelist: colocationWhitelist,
EnableUPnP: cliCtx.Bool(cmd.EnableUPnPFlag.Name),
StateNotifier: b,
DB: b.db,
ClockWaiter: b.clockWaiter,
})
if err != nil {
return err

View File

@@ -262,3 +262,46 @@ func TestCORS(t *testing.T) {
})
}
}
func TestParseIPNetStrings(t *testing.T) {
tests := []struct {
name string
whitelist []string
wantCount int
wantError string
}{
{
name: "empty whitelist",
whitelist: []string{},
wantCount: 0,
},
{
name: "single IP whitelist",
whitelist: []string{"192.168.1.1/32"},
wantCount: 1,
},
{
name: "multiple IPs whitelist",
whitelist: []string{"192.168.1.0/24", "10.0.0.0/8", "34.42.19.170/32"},
wantCount: 3,
},
{
name: "invalid CIDR returns error",
whitelist: []string{"192.168.1.0/24", "invalid-cidr", "10.0.0.0/8"},
wantCount: 0,
wantError: "invalid CIDR address",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := parseIPNetStrings(tt.whitelist)
assert.Equal(t, tt.wantCount, len(result))
if len(tt.wantError) == 0 {
assert.Equal(t, nil, err)
} else {
assert.ErrorContains(t, tt.wantError, err)
}
})
}
}

View File

@@ -95,6 +95,7 @@ go_library(
"@com_github_libp2p_go_libp2p//core/peer:go_default_library",
"@com_github_libp2p_go_libp2p//core/peerstore:go_default_library",
"@com_github_libp2p_go_libp2p//core/protocol:go_default_library",
"@com_github_libp2p_go_libp2p//p2p/net/connmgr:go_default_library",
"@com_github_libp2p_go_libp2p//p2p/security/noise:go_default_library",
"@com_github_libp2p_go_libp2p//p2p/transport/quic:go_default_library",
"@com_github_libp2p_go_libp2p//p2p/transport/tcp:go_default_library",
@@ -184,6 +185,7 @@ go_test(
"@com_github_ethereum_go_ethereum//p2p/enr:go_default_library",
"@com_github_golang_snappy//:go_default_library",
"@com_github_libp2p_go_libp2p//:go_default_library",
"@com_github_libp2p_go_libp2p//core/connmgr:go_default_library",
"@com_github_libp2p_go_libp2p//core/crypto:go_default_library",
"@com_github_libp2p_go_libp2p//core/host:go_default_library",
"@com_github_libp2p_go_libp2p//core/network:go_default_library",

View File

@@ -123,9 +123,6 @@ func (s *Service) internalBroadcastAttestation(ctx context.Context, subnet uint6
if !hasPeer {
attestationBroadcastAttempts.Inc()
if err := func() error {
s.subnetLocker(subnet).Lock()
defer s.subnetLocker(subnet).Unlock()
if err := s.FindAndDialPeersWithSubnets(ctx, AttestationSubnetTopicFormat, forkDigest, minimumPeersPerSubnetForBroadcast, map[uint64]bool{subnet: true}); err != nil {
return errors.Wrap(err, "find peers with subnets")
}

View File

@@ -1,6 +1,7 @@
package p2p
import (
"net"
"time"
statefeed "github.com/OffchainLabs/prysm/v6/beacon-chain/core/feed/state"
@@ -10,34 +11,56 @@ import (
// This is the default queue size used if we have specified an invalid one.
const defaultPubsubQueueSize = 600
const (
// defaultConnManagerPruneAbove sets the number of peers where ConnectionManager
// will begin to internally prune peers. This value is set based on the internal
// value of the libp2p DefaultConectionManager "high water mark". The "low water mark"
// is the number of peers where ConnManager will stop pruning. This value is computed
// by subtracting connManagerPruneAmount from the high water mark.
defaultConnManagerPruneAbove = 192
connManagerPruneAmount = 32
)
// Config for the p2p service. These parameters are set from application level flags
// to initialize the p2p service.
type Config struct {
NoDiscovery bool
EnableUPnP bool
StaticPeerID bool
DisableLivenessCheck bool
StaticPeers []string
Discv5BootStrapAddrs []string
RelayNodeAddr string
LocalIP string
HostAddress string
HostDNS string
PrivateKey string
DataDir string
DiscoveryDir string
QUICPort uint
TCPPort uint
UDPPort uint
PingInterval time.Duration
MaxPeers uint
QueueSize uint
AllowListCIDR string
DenyListCIDR []string
StateNotifier statefeed.Notifier
DB db.ReadOnlyDatabaseWithSeqNum
ClockWaiter startup.ClockWaiter
NoDiscovery bool
EnableUPnP bool
StaticPeerID bool
DisableLivenessCheck bool
StaticPeers []string
Discv5BootStrapAddrs []string
RelayNodeAddr string
LocalIP string
HostAddress string
HostDNS string
PrivateKey string
DataDir string
DiscoveryDir string
QUICPort uint
TCPPort uint
UDPPort uint
PingInterval time.Duration
MaxPeers uint
QueueSize uint
AllowListCIDR string
DenyListCIDR []string
IPColocationWhitelist []*net.IPNet
StateNotifier statefeed.Notifier
DB db.ReadOnlyDatabaseWithSeqNum
ClockWaiter startup.ClockWaiter
}
// connManagerLowHigh picks the low and high water marks for the connection manager based
// on the MaxPeers setting. The high water mark will be at least the default high water mark
// (192), or MaxPeers + 32, whichever is higher. The low water mark is set to be 32 less than
// the high water mark. This is done to ensure the ConnManager never prunes peers that the
// node has connected to based on the MaxPeers setting.
func (cfg *Config) connManagerLowHigh() (int, int) {
maxPeersPlusMargin := int(cfg.MaxPeers) + connManagerPruneAmount
high := max(maxPeersPlusMargin, defaultConnManagerPruneAbove)
low := high - connManagerPruneAmount
return low, high
}
// validateConfig validates whether the values provided are accurate and will set

View File

@@ -3,6 +3,7 @@ package p2p
import (
"context"
"math"
"net"
"reflect"
"strings"
"time"
@@ -75,7 +76,7 @@ var (
tenEpochs = 10 * oneEpochDuration()
)
func peerScoringParams() (*pubsub.PeerScoreParams, *pubsub.PeerScoreThresholds) {
func peerScoringParams(colocationWhitelist []*net.IPNet) (*pubsub.PeerScoreParams, *pubsub.PeerScoreThresholds) {
thresholds := &pubsub.PeerScoreThresholds{
GossipThreshold: -4000,
PublishThreshold: -8000,
@@ -83,6 +84,7 @@ func peerScoringParams() (*pubsub.PeerScoreParams, *pubsub.PeerScoreThresholds)
AcceptPXThreshold: 100,
OpportunisticGraftThreshold: 5,
}
scoreParams := &pubsub.PeerScoreParams{
Topics: make(map[string]*pubsub.TopicScoreParams),
TopicScoreCap: 32.72,
@@ -92,7 +94,7 @@ func peerScoringParams() (*pubsub.PeerScoreParams, *pubsub.PeerScoreThresholds)
AppSpecificWeight: 1,
IPColocationFactorWeight: -35.11,
IPColocationFactorThreshold: 10,
IPColocationFactorWhitelist: nil,
IPColocationFactorWhitelist: colocationWhitelist,
BehaviourPenaltyWeight: -15.92,
BehaviourPenaltyThreshold: 6,
BehaviourPenaltyDecay: scoreDecay(tenEpochs),

View File

@@ -13,6 +13,7 @@ import (
mplex "github.com/libp2p/go-libp2p-mplex"
"github.com/libp2p/go-libp2p/core/network"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/libp2p/go-libp2p/p2p/net/connmgr"
"github.com/libp2p/go-libp2p/p2p/security/noise"
libp2pquic "github.com/libp2p/go-libp2p/p2p/transport/quic"
libp2ptcp "github.com/libp2p/go-libp2p/p2p/transport/tcp"
@@ -58,6 +59,22 @@ func MultiAddressBuilder(ip net.IP, tcpPort, quicPort uint) ([]ma.Multiaddr, err
return multiaddrs, nil
}
// setConnManagerOption sets the connection manager option for libp2p based on the
// MaxPeers setting in the p2p config. If MaxPeers is set to a value higher than the
// default high water mark, we create a new connection manager with a high water mark
// that is higher than MaxPeers. Otherwise, we do not set a connection manager option
// and allow the libp2p fallback defaults to be applied. Rationale below:
// see: https://github.com/OffchainLabs/prysm/issues/15607
func setConnManagerOption(cfg *Config, opts []libp2p.Option) ([]libp2p.Option, error) {
low, high := cfg.connManagerLowHigh()
cm, err := connmgr.NewConnManager(low, high)
if err != nil {
return nil, errors.Wrap(err, "new ConnManager")
}
opts = append(opts, libp2p.ConnectionManager(cm))
return opts, nil
}
// buildOptions for the libp2p host.
func (s *Service) buildOptions(ip net.IP, priKey *ecdsa.PrivateKey) ([]libp2p.Option, error) {
cfg := s.cfg
@@ -84,7 +101,6 @@ func (s *Service) buildOptions(ip net.IP, priKey *ecdsa.PrivateKey) ([]libp2p.Op
if err != nil {
return nil, errors.Wrapf(err, "cannot get ID from public key: %s", ifaceKey.GetPublic().Type().String())
}
log.Infof("Running node with peer id of %s ", id.String())
options := []libp2p.Option{
@@ -98,6 +114,10 @@ func (s *Service) buildOptions(ip net.IP, priKey *ecdsa.PrivateKey) ([]libp2p.Op
libp2p.Security(noise.ID, noise.New),
libp2p.Ping(false), // Disable Ping Service.
}
options, err = setConnManagerOption(s.cfg, options)
if err != nil {
return nil, errors.Wrap(err, "set connection manager option")
}
if features.Get().EnableQUIC {
options = append(options, libp2p.Transport(libp2pquic.NewTransport))

View File

@@ -18,6 +18,7 @@ import (
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/p2p/enr"
"github.com/libp2p/go-libp2p"
"github.com/libp2p/go-libp2p/core/connmgr"
"github.com/libp2p/go-libp2p/core/crypto"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/libp2p/go-libp2p/core/protocol"
@@ -134,6 +135,59 @@ func TestDefaultMultiplexers(t *testing.T) {
assert.Equal(t, protocol.ID("/mplex/6.7.0"), cfg.Muxers[1].ID)
}
func TestSetConnManagerOption(t *testing.T) {
cases := []struct {
name string
maxPeers uint
highWater int
}{
{
name: "MaxPeers lower than default high water mark",
maxPeers: defaultConnManagerPruneAbove - 1,
highWater: defaultConnManagerPruneAbove,
},
{
name: "MaxPeers equal to default high water mark",
maxPeers: defaultConnManagerPruneAbove,
highWater: defaultConnManagerPruneAbove,
},
{
name: "MaxPeers higher than default high water mark",
maxPeers: defaultConnManagerPruneAbove + 1,
highWater: defaultConnManagerPruneAbove + 1 + connManagerPruneAmount,
},
}
for _, tt := range cases {
t.Run(tt.name, func(t *testing.T) {
cfg := &Config{MaxPeers: tt.maxPeers}
opts, err := setConnManagerOption(cfg, []libp2p.Option{})
assert.NoError(t, err)
_, high := cfg.connManagerLowHigh()
require.Equal(t, true, high > int(cfg.MaxPeers))
var libCfg libp2p.Config
require.NoError(t, libCfg.Apply(append(opts, libp2p.FallbackDefaults)...))
checkLimit(t, libCfg.ConnManager, high)
})
}
}
type connLimitGetter int
func (m connLimitGetter) GetConnLimit() int {
return int(m)
}
// CheckLimit will return an error if the result of calling lg.GetConnLimit is greater than
// the high water mark. So by checking the result of calling it with a value equal to and lower
// than the expected value, we can determine the value it holds internally.
func checkLimit(t *testing.T, cm connmgr.ConnManager, expected int) {
require.NoError(t, cm.CheckLimit(connLimitGetter(expected)), "Connection manager limit check failed")
if err := cm.CheckLimit(connLimitGetter(expected - 1)); err == nil {
t.Errorf("connection manager limit is below the expected value of %d", expected)
}
}
func TestMultiAddressBuilderWithID(t *testing.T) {
testCases := []struct {
name string

View File

@@ -25,6 +25,7 @@ package peers
import (
"context"
"math"
"net"
"sort"
"strings"
"time"
@@ -87,11 +88,12 @@ const (
// Status is the structure holding the peer status information.
type Status struct {
ctx context.Context
scorers *scorers.Service
store *peerdata.Store
ipTracker map[string]uint64
rand *rand.Rand
ctx context.Context
scorers *scorers.Service
store *peerdata.Store
ipTracker map[string]uint64
rand *rand.Rand
ipColocationWhitelist []*net.IPNet
}
// StatusConfig represents peer status service params.
@@ -100,6 +102,8 @@ type StatusConfig struct {
PeerLimit int
// ScorerParams holds peer scorer configuration params.
ScorerParams *scorers.Config
// IPColocationWhitelist contains CIDR ranges that are exempt from IP colocation limits.
IPColocationWhitelist []*net.IPNet
}
// NewStatus creates a new status entity.
@@ -107,11 +111,13 @@ func NewStatus(ctx context.Context, config *StatusConfig) *Status {
store := peerdata.NewStore(ctx, &peerdata.StoreConfig{
MaxPeers: maxLimitBuffer + config.PeerLimit,
})
return &Status{
ctx: ctx,
store: store,
scorers: scorers.NewService(ctx, store, config.ScorerParams),
ipTracker: map[string]uint64{},
ctx: ctx,
store: store,
scorers: scorers.NewService(ctx, store, config.ScorerParams),
ipTracker: map[string]uint64{},
ipColocationWhitelist: config.IPColocationWhitelist,
// Random generator used to calculate dial backoff period.
// It is ok to use deterministic generator, no need for true entropy.
rand: rand.NewDeterministicGenerator(),
@@ -1046,6 +1052,13 @@ func (p *Status) isfromBadIP(pid peer.ID) error {
if val, ok := p.ipTracker[ip.String()]; ok {
if val > CollocationLimit {
// Check if IP is in the whitelist
for _, ipNet := range p.ipColocationWhitelist {
if ipNet.Contains(ip) {
// IP is whitelisted, skip colocation limit check
return nil
}
}
return errors.Errorf(
"colocation limit exceeded: got %d - limit %d for peer %v with IP %v",
val, CollocationLimit, pid, ip.String(),

View File

@@ -145,7 +145,7 @@ func (s *Service) pubsubOptions() []pubsub.Option {
pubsub.WithPeerOutboundQueueSize(int(s.cfg.QueueSize)),
pubsub.WithMaxMessageSize(int(MaxMessageSize())), // lint:ignore uintcast -- Max Message Size is a config value and is naturally bounded by networking limitations.
pubsub.WithValidateQueueSize(int(s.cfg.QueueSize)),
pubsub.WithPeerScore(peerScoringParams()),
pubsub.WithPeerScore(peerScoringParams(s.cfg.IPColocationWhitelist)),
pubsub.WithPeerScoreInspect(s.peerInspector, time.Minute),
pubsub.WithGossipSubParams(pubsubGossipParam()),
pubsub.WithRawTracer(gossipTracer{host: s.host}),

View File

@@ -178,7 +178,8 @@ func NewService(ctx context.Context, cfg *Config) (*Service, error) {
s.pubsub = gs
s.peers = peers.NewStatus(ctx, &peers.StatusConfig{
PeerLimit: int(s.cfg.MaxPeers),
PeerLimit: int(s.cfg.MaxPeers),
IPColocationWhitelist: s.cfg.IPColocationWhitelist,
ScorerParams: &scorers.Config{
BadResponsesScorerConfig: &scorers.BadResponsesScorerConfig{
Threshold: maxBadResponses,

View File

@@ -110,6 +110,11 @@ func (s *Service) FindAndDialPeersWithSubnets(
}
peersToDial, err := func() ([]*enode.Node, error) {
for subnet := range defectiveSubnets {
s.subnetLocker(subnet).Lock()
defer s.subnetLocker(subnet).Unlock()
}
ctx, cancel := context.WithTimeout(ctx, batchPeriod)
defer cancel()

View File

@@ -1230,6 +1230,7 @@ func (s *Service) prysmBeaconEndpoints(
methods: []string{http.MethodGet},
},
{
// Warning: no longer supported post Fulu fork
template: "/prysm/v1/beacon/blobs",
name: namespace + ".PublishBlobs",
middleware: []middleware.Middleware{

View File

@@ -186,6 +186,7 @@ func (s *Server) GetChainHead(w http.ResponseWriter, r *http.Request) {
httputil.WriteJson(w, response)
}
// Warning: no longer supported post Fulu blobs
func (s *Server) PublishBlobs(w http.ResponseWriter, r *http.Request) {
ctx, span := trace.StartSpan(r.Context(), "beacon.PublishBlobs")
defer span.End()
@@ -215,6 +216,15 @@ func (s *Server) PublishBlobs(w http.ResponseWriter, r *http.Request) {
httputil.HandleError(w, "Could not decode blob sidecar: "+err.Error(), http.StatusBadRequest)
return
}
scEpoch := slots.ToEpoch(sc.SignedBlockHeader.Header.Slot)
if scEpoch < params.BeaconConfig().DenebForkEpoch {
httputil.HandleError(w, "Blob sidecars not supported for pre deneb", http.StatusBadRequest)
return
}
if scEpoch > params.BeaconConfig().FuluForkEpoch {
httputil.HandleError(w, "Blob sidecars not supported for post fulu blobs", http.StatusBadRequest)
return
}
readOnlySc, err := blocks.NewROBlobWithRoot(sc, bytesutil.ToBytes32(root))
if err != nil {

View File

@@ -1017,6 +1017,11 @@ func TestPublishBlobs_BadBlockRoot(t *testing.T) {
}
func TestPublishBlobs(t *testing.T) {
params.SetupTestConfigCleanup(t)
cfg := params.BeaconConfig().Copy()
cfg.DenebForkEpoch = 0 // Set Deneb fork to epoch 0 so slot 0 is valid
params.OverrideBeaconConfig(cfg)
server := &Server{
BlobReceiver: &chainMock.ChainService{},
Broadcaster: &mockp2p.MockBroadcaster{},
@@ -1032,3 +1037,94 @@ func TestPublishBlobs(t *testing.T) {
assert.Equal(t, len(server.BlobReceiver.(*chainMock.ChainService).Blobs), 1)
assert.Equal(t, server.Broadcaster.(*mockp2p.MockBroadcaster).BroadcastCalled.Load(), true)
}
func TestPublishBlobs_PreDeneb(t *testing.T) {
params.SetupTestConfigCleanup(t)
cfg := params.BeaconConfig().Copy()
cfg.DenebForkEpoch = 10 // Set Deneb fork to epoch 10, so slot 0 is pre-Deneb
params.OverrideBeaconConfig(cfg)
server := &Server{
BlobReceiver: &chainMock.ChainService{},
Broadcaster: &mockp2p.MockBroadcaster{},
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader([]byte(rpctesting.PublishBlobsRequest)))
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
server.PublishBlobs(writer, request)
assert.Equal(t, http.StatusBadRequest, writer.Code)
assert.StringContains(t, "Blob sidecars not supported for pre deneb", writer.Body.String())
assert.Equal(t, len(server.BlobReceiver.(*chainMock.ChainService).Blobs), 0)
assert.Equal(t, server.Broadcaster.(*mockp2p.MockBroadcaster).BroadcastCalled.Load(), false)
}
func TestPublishBlobs_PostFulu(t *testing.T) {
params.SetupTestConfigCleanup(t)
cfg := params.BeaconConfig().Copy()
cfg.DenebForkEpoch = 0
cfg.FuluForkEpoch = 0 // Set Fulu fork to epoch 0
params.OverrideBeaconConfig(cfg)
server := &Server{
BlobReceiver: &chainMock.ChainService{},
Broadcaster: &mockp2p.MockBroadcaster{},
SyncChecker: &mockSync.Sync{IsSyncing: false},
}
// Create a custom request with a slot in epoch 1 (which is > FuluForkEpoch of 0)
postFuluRequest := fmt.Sprintf(`{
"block_root" : "0x0000000000000000000000000000000000000000000000000000000000000000",
"blob_sidecars" : {
"sidecars" : [
{
"blob" : "%s",
"index" : "0",
"kzg_commitment" : "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"kzg_commitment_inclusion_proof" : [
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000",
"0x0000000000000000000000000000000000000000000000000000000000000000"
],
"kzg_proof" : "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"signed_block_header" : {
"message" : {
"body_root" : "0x0000000000000000000000000000000000000000000000000000000000000000",
"parent_root" : "0x0000000000000000000000000000000000000000000000000000000000000000",
"proposer_index" : "0",
"slot" : "33",
"state_root" : "0x0000000000000000000000000000000000000000000000000000000000000000"
},
"signature" : "0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
}
}
]
}
}`, rpctesting.Blob)
request := httptest.NewRequest(http.MethodPost, "http://foo.example", bytes.NewReader([]byte(postFuluRequest)))
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
server.PublishBlobs(writer, request)
assert.Equal(t, http.StatusBadRequest, writer.Code)
assert.StringContains(t, "Blob sidecars not supported for post fulu blobs", writer.Body.String())
assert.Equal(t, len(server.BlobReceiver.(*chainMock.ChainService).Blobs), 0)
assert.Equal(t, server.Broadcaster.(*mockp2p.MockBroadcaster).BroadcastCalled.Load(), false)
}

View File

@@ -171,6 +171,7 @@ go_test(
"decode_pubsub_test.go",
"error_test.go",
"fork_watcher_test.go",
"kzg_batch_verifier_test.go",
"pending_attestations_queue_test.go",
"pending_blocks_queue_test.go",
"rate_limiter_test.go",

View File

@@ -4,6 +4,9 @@ import (
"context"
"time"
"github.com/OffchainLabs/prysm/v6/beacon-chain/core/peerdas"
"github.com/OffchainLabs/prysm/v6/config/params"
"github.com/OffchainLabs/prysm/v6/consensus-types/blocks"
"github.com/OffchainLabs/prysm/v6/crypto/bls"
"github.com/OffchainLabs/prysm/v6/monitoring/tracing"
"github.com/OffchainLabs/prysm/v6/monitoring/tracing/trace"
@@ -18,6 +21,11 @@ type signatureVerifier struct {
resChan chan error
}
type kzgVerifier struct {
dataColumns []blocks.RODataColumn
resChan chan error
}
// A routine that runs in the background to perform batch
// verifications of incoming messages from gossip.
func (s *Service) verifierRoutine() {
@@ -47,6 +55,32 @@ func (s *Service) verifierRoutine() {
}
}
// A routine that runs in the background to perform batch
// KZG verifications by draining the channel and processing all pending requests.
func (s *Service) kzgVerifierRoutine() {
for {
kzgBatch := make([]*kzgVerifier, 0, 1)
select {
case <-s.ctx.Done():
return
case kzg := <-s.kzgChan:
kzgBatch = append(kzgBatch, kzg)
}
for {
select {
case <-s.ctx.Done():
return
case kzg := <-s.kzgChan:
kzgBatch = append(kzgBatch, kzg)
continue
default:
verifyKzgBatch(kzgBatch)
}
break
}
}
}
func (s *Service) validateWithBatchVerifier(ctx context.Context, message string, set *bls.SignatureBatch) (pubsub.ValidationResult, error) {
_, span := trace.StartSpan(ctx, "sync.validateWithBatchVerifier")
defer span.End()
@@ -56,7 +90,6 @@ func (s *Service) validateWithBatchVerifier(ctx context.Context, message string,
s.signatureChan <- verificationSet
resErr := <-resChan
close(resChan)
// If verification fails we fallback to individual verification
// of each signature set.
if resErr != nil {
@@ -120,3 +153,62 @@ func performBatchAggregation(aggSet *bls.SignatureBatch) (*bls.SignatureBatch, e
}
return aggSet, nil
}
func (s *Service) validateWithKzgBatchVerifier(ctx context.Context, dataColumns []blocks.RODataColumn) (pubsub.ValidationResult, error) {
_, span := trace.StartSpan(ctx, "sync.validateWithKzgBatchVerifier")
defer span.End()
timeout := time.Duration(params.BeaconConfig().SecondsPerSlot) * time.Second
resChan := make(chan error)
verificationSet := &kzgVerifier{dataColumns: dataColumns, resChan: resChan}
s.kzgChan <- verificationSet
ctx, cancel := context.WithTimeout(ctx, timeout)
defer cancel()
select {
case <-ctx.Done():
return pubsub.ValidationIgnore, ctx.Err() // parent context canceled, give up
case err := <-resChan:
if err != nil {
log.WithError(err).Trace("Could not perform batch verification")
tracing.AnnotateError(span, err)
return s.validateUnbatchedColumnsKzg(ctx, dataColumns)
}
}
return pubsub.ValidationAccept, nil
}
func (s *Service) validateUnbatchedColumnsKzg(ctx context.Context, columns []blocks.RODataColumn) (pubsub.ValidationResult, error) {
_, span := trace.StartSpan(ctx, "sync.validateUnbatchedColumnsKzg")
defer span.End()
if err := peerdas.VerifyDataColumnsSidecarKZGProofs(columns); err != nil {
err = errors.Wrap(err, "could not verify")
tracing.AnnotateError(span, err)
return pubsub.ValidationReject, err
}
return pubsub.ValidationAccept, nil
}
func verifyKzgBatch(kzgBatch []*kzgVerifier) {
if len(kzgBatch) == 0 {
return
}
allDataColumns := make([]blocks.RODataColumn, 0, len(kzgBatch))
for _, kzgVerifier := range kzgBatch {
allDataColumns = append(allDataColumns, kzgVerifier.dataColumns...)
}
var verificationErr error
err := peerdas.VerifyDataColumnsSidecarKZGProofs(allDataColumns)
if err != nil {
verificationErr = errors.Wrap(err, "batch KZG verification failed")
}
// Send the same result to all verifiers in the batch
for _, verifier := range kzgBatch {
verifier.resChan <- verificationErr
}
}

View File

@@ -456,6 +456,9 @@ func (f *blocksFetcher) fetchBlocksFromPeer(
"count": req.Count,
"step": req.Step,
}).WithError(err).Debug("Could not request blocks by range from peer")
if errors.Is(err, prysmsync.ErrInvalidFetchedData) {
f.downscorePeer(p, err)
}
continue
}
f.p2p.Peers().Scorers().BlockProviderScorer().Touch(p)
@@ -876,6 +879,12 @@ func dedupPeers(peers []peer.ID) []peer.ID {
return newPeerList
}
// downscorePeer increments the bad responses score for the peer and logs the event.
func (f *blocksFetcher) downscorePeer(peerID peer.ID, reason error) {
newScore := f.p2p.Peers().Scorers().BadResponsesScorer().Increment(peerID)
log.WithFields(logrus.Fields{"peerID": peerID, "reason": reason, "newScore": newScore}).Debug("Downscore peer")
}
// findFirstFuluIndex returns the index of the first block with a version >= Fulu.
// It returns an error if blocks are not correctly sorted by version regarding Fulu.
func findFirstFuluIndex(bwScs []blocks.BlockWithROSidecars) (int, error) {

View File

@@ -13,6 +13,7 @@ import (
"github.com/OffchainLabs/prysm/v6/beacon-chain/db/filesystem"
dbtest "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/peers/peerdata"
p2ptest "github.com/OffchainLabs/prysm/v6/beacon-chain/p2p/testing"
"github.com/OffchainLabs/prysm/v6/beacon-chain/startup"
beaconsync "github.com/OffchainLabs/prysm/v6/beacon-chain/sync"
@@ -910,6 +911,55 @@ func TestBlocksFetcher_requestBlocksFromPeerReturningInvalidBlocks(t *testing.T)
}
}
func TestBlocksFetcher_requestBlocksDownscoreOnInvalidData(t *testing.T) {
p1 := p2ptest.NewTestP2P(t)
p2 := p2ptest.NewTestP2P(t)
p1.Connect(p2)
topic := p2p.RPCBlocksByRangeTopicV1
protocol := libp2pcore.ProtocolID(topic + p1.Encoding().ProtocolSuffix())
ctx, cancel := context.WithCancel(t.Context())
defer cancel()
fetcher := newBlocksFetcher(ctx, &blocksFetcherConfig{
p2p: p1,
chain: &mock.ChainService{Genesis: time.Now(), ValidatorsRoot: [32]byte{}},
})
fetcher.rateLimiter = leakybucket.NewCollector(0.000001, 640, 1*time.Second, false)
// Set up handler that returns blocks in wrong order (will trigger ErrInvalidFetchedData)
p2.BHost.SetStreamHandler(protocol, func(stream network.Stream) {
blk := util.NewBeaconBlock()
blk.Block.Slot = 163
tor := startup.NewClock(time.Now(), [32]byte{})
wsb, err := blocks.NewSignedBeaconBlock(blk)
require.NoError(t, err)
assert.NoError(t, beaconsync.WriteBlockChunk(stream, tor, p1.Encoding(), wsb))
blk = util.NewBeaconBlock()
blk.Block.Slot = 162
wsb, err = blocks.NewSignedBeaconBlock(blk)
require.NoError(t, err)
assert.NoError(t, beaconsync.WriteBlockChunk(stream, tor, p1.Encoding(), wsb))
assert.NoError(t, stream.Close())
})
// Verify the peer has no bad responses before the request
scoreBeforeRequest, err := p1.Peers().Scorers().BadResponsesScorer().Count(p2.PeerID())
require.ErrorIs(t, err, peerdata.ErrPeerUnknown)
assert.Equal(t, -1, scoreBeforeRequest)
// Use fetchBlocksFromPeer which includes the downscoring logic
_, _, err = fetcher.fetchBlocksFromPeer(ctx, 100, 64, []peer.ID{p2.PeerID()})
assert.ErrorContains(t, errNoPeersAvailable.Error(), err)
// Verify the peer was downscored
scoreAfterRequest, err := p1.Peers().Scorers().BadResponsesScorer().Count(p2.PeerID())
assert.NoError(t, err)
assert.Equal(t, 1, scoreAfterRequest)
}
func TestTimeToWait(t *testing.T) {
tests := []struct {
name string

View File

@@ -0,0 +1,327 @@
package sync
import (
"context"
"sync"
"testing"
"time"
"github.com/OffchainLabs/prysm/v6/beacon-chain/blockchain/kzg"
"github.com/OffchainLabs/prysm/v6/consensus-types/blocks"
ethpb "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"
pubsub "github.com/libp2p/go-libp2p-pubsub"
)
func TestValidateWithKzgBatchVerifier(t *testing.T) {
err := kzg.Start()
require.NoError(t, err)
tests := []struct {
name string
dataColumns []blocks.RODataColumn
expectedResult pubsub.ValidationResult
expectError bool
}{
{
name: "single valid data column",
dataColumns: createValidTestDataColumns(t, 1),
expectedResult: pubsub.ValidationAccept,
expectError: false,
},
{
name: "multiple valid data columns",
dataColumns: createValidTestDataColumns(t, 3),
expectedResult: pubsub.ValidationAccept,
expectError: false,
},
{
name: "single invalid data column",
dataColumns: createInvalidTestDataColumns(t, 1),
expectedResult: pubsub.ValidationReject,
expectError: true,
},
{
name: "empty data column slice",
dataColumns: []blocks.RODataColumn{},
expectedResult: pubsub.ValidationAccept,
expectError: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
service := &Service{
ctx: ctx,
kzgChan: make(chan *kzgVerifier, 100),
}
go service.kzgVerifierRoutine()
result, err := service.validateWithKzgBatchVerifier(ctx, tt.dataColumns)
require.Equal(t, tt.expectedResult, result)
if tt.expectError {
assert.NotNil(t, err)
} else {
assert.NoError(t, err)
}
})
}
}
func TestVerifierRoutine(t *testing.T) {
err := kzg.Start()
require.NoError(t, err)
t.Run("processes single request", func(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
service := &Service{
ctx: ctx,
kzgChan: make(chan *kzgVerifier, 100),
}
go service.kzgVerifierRoutine()
dataColumns := createValidTestDataColumns(t, 1)
resChan := make(chan error, 1)
service.kzgChan <- &kzgVerifier{dataColumns: dataColumns, resChan: resChan}
select {
case err := <-resChan:
require.NoError(t, err)
case <-time.After(time.Second):
t.Fatal("timeout waiting for verification result")
}
})
t.Run("batches multiple requests", func(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
service := &Service{
ctx: ctx,
kzgChan: make(chan *kzgVerifier, 100),
}
go service.kzgVerifierRoutine()
const numRequests = 5
resChans := make([]chan error, numRequests)
for i := range numRequests {
dataColumns := createValidTestDataColumns(t, 1)
resChan := make(chan error, 1)
resChans[i] = resChan
service.kzgChan <- &kzgVerifier{dataColumns: dataColumns, resChan: resChan}
}
for i := range numRequests {
select {
case err := <-resChans[i]:
require.NoError(t, err)
case <-time.After(time.Second):
t.Fatalf("timeout waiting for verification result %d", i)
}
}
})
t.Run("context cancellation stops routine", func(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
service := &Service{
ctx: ctx,
kzgChan: make(chan *kzgVerifier, 100),
}
routineDone := make(chan struct{})
go func() {
service.kzgVerifierRoutine()
close(routineDone)
}()
cancel()
select {
case <-routineDone:
case <-time.After(time.Second):
t.Fatal("timeout waiting for routine to exit")
}
})
}
func TestVerifyKzgBatch(t *testing.T) {
err := kzg.Start()
require.NoError(t, err)
t.Run("all valid data columns succeed", func(t *testing.T) {
dataColumns := createValidTestDataColumns(t, 3)
resChan := make(chan error, 1)
kzgVerifiers := []*kzgVerifier{{dataColumns: dataColumns, resChan: resChan}}
verifyKzgBatch(kzgVerifiers)
select {
case err := <-resChan:
require.NoError(t, err)
case <-time.After(time.Second):
t.Fatal("timeout waiting for batch verification")
}
})
t.Run("invalid proofs fail entire batch", func(t *testing.T) {
validColumns := createValidTestDataColumns(t, 1)
invalidColumns := createInvalidTestDataColumns(t, 1)
allColumns := append(validColumns, invalidColumns...)
resChan := make(chan error, 1)
kzgVerifiers := []*kzgVerifier{{dataColumns: allColumns, resChan: resChan}}
verifyKzgBatch(kzgVerifiers)
select {
case err := <-resChan:
assert.NotNil(t, err)
case <-time.After(time.Second):
t.Fatal("timeout waiting for batch verification")
}
})
t.Run("empty batch handling", func(t *testing.T) {
verifyKzgBatch([]*kzgVerifier{})
})
}
func TestKzgBatchVerifierConcurrency(t *testing.T) {
err := kzg.Start()
require.NoError(t, err)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
service := &Service{
ctx: ctx,
kzgChan: make(chan *kzgVerifier, 100),
}
go service.kzgVerifierRoutine()
const numGoroutines = 10
const numRequestsPerGoroutine = 5
var wg sync.WaitGroup
wg.Add(numGoroutines)
// Multiple goroutines sending verification requests simultaneously
for i := range numGoroutines {
go func(goroutineID int) {
defer wg.Done()
for range numRequestsPerGoroutine {
dataColumns := createValidTestDataColumns(t, 1)
result, err := service.validateWithKzgBatchVerifier(ctx, dataColumns)
require.Equal(t, pubsub.ValidationAccept, result)
require.NoError(t, err)
}
}(i)
}
wg.Wait()
}
func TestKzgBatchVerifierFallback(t *testing.T) {
err := kzg.Start()
require.NoError(t, err)
t.Run("fallback handles mixed valid/invalid batch correctly", func(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
service := &Service{
ctx: ctx,
kzgChan: make(chan *kzgVerifier, 100),
}
go service.kzgVerifierRoutine()
validColumns := createValidTestDataColumns(t, 1)
invalidColumns := createInvalidTestDataColumns(t, 1)
result, err := service.validateWithKzgBatchVerifier(ctx, validColumns)
require.Equal(t, pubsub.ValidationAccept, result)
require.NoError(t, err)
result, err = service.validateWithKzgBatchVerifier(ctx, invalidColumns)
require.Equal(t, pubsub.ValidationReject, result)
assert.NotNil(t, err)
})
t.Run("empty data columns fallback", func(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
service := &Service{
ctx: ctx,
kzgChan: make(chan *kzgVerifier, 100),
}
go service.kzgVerifierRoutine()
result, err := service.validateWithKzgBatchVerifier(ctx, []blocks.RODataColumn{})
require.Equal(t, pubsub.ValidationAccept, result)
require.NoError(t, err)
})
}
func createValidTestDataColumns(t *testing.T, count int) []blocks.RODataColumn {
_, roSidecars, _ := util.GenerateTestFuluBlockWithSidecars(t, count)
if len(roSidecars) >= count {
return roSidecars[:count]
}
return roSidecars
}
func createInvalidTestDataColumns(t *testing.T, count int) []blocks.RODataColumn {
dataColumns := createValidTestDataColumns(t, count)
if len(dataColumns) > 0 {
sidecar := dataColumns[0].DataColumnSidecar
if len(sidecar.Column) > 0 && len(sidecar.Column[0]) > 0 {
corruptedSidecar := &ethpb.DataColumnSidecar{
Index: sidecar.Index,
KzgCommitments: make([][]byte, len(sidecar.KzgCommitments)),
KzgProofs: make([][]byte, len(sidecar.KzgProofs)),
KzgCommitmentsInclusionProof: make([][]byte, len(sidecar.KzgCommitmentsInclusionProof)),
SignedBlockHeader: sidecar.SignedBlockHeader,
Column: make([][]byte, len(sidecar.Column)),
}
for i, commitment := range sidecar.KzgCommitments {
corruptedSidecar.KzgCommitments[i] = make([]byte, len(commitment))
copy(corruptedSidecar.KzgCommitments[i], commitment)
}
for i, proof := range sidecar.KzgProofs {
corruptedSidecar.KzgProofs[i] = make([]byte, len(proof))
copy(corruptedSidecar.KzgProofs[i], proof)
}
for i, proof := range sidecar.KzgCommitmentsInclusionProof {
corruptedSidecar.KzgCommitmentsInclusionProof[i] = make([]byte, len(proof))
copy(corruptedSidecar.KzgCommitmentsInclusionProof[i], proof)
}
for i, col := range sidecar.Column {
corruptedSidecar.Column[i] = make([]byte, len(col))
copy(corruptedSidecar.Column[i], col)
}
corruptedSidecar.Column[0][0] ^= 0xFF // Flip bits to corrupt
corruptedRO, err := blocks.NewRODataColumn(corruptedSidecar)
require.NoError(t, err)
dataColumns[0] = corruptedRO
}
}
return dataColumns
}

View File

@@ -164,6 +164,7 @@ type Service struct {
syncContributionBitsOverlapLock sync.RWMutex
syncContributionBitsOverlapCache *lru.Cache
signatureChan chan *signatureVerifier
kzgChan chan *kzgVerifier
clockWaiter startup.ClockWaiter
initialSyncComplete chan struct{}
verifierWaiter *verification.InitializerWaiter
@@ -203,6 +204,10 @@ func NewService(ctx context.Context, opts ...Option) *Service {
}
// Initialize signature channel with configured limit
r.signatureChan = make(chan *signatureVerifier, r.cfg.batchVerifierLimit)
// Initialize KZG channel with fixed buffer size of 100.
// This buffer size is designed to handle burst traffic of data column gossip messages:
// - Data columns arrive less frequently than attestations (default batchVerifierLimit=1000)
r.kzgChan = make(chan *kzgVerifier, 100)
// Correctly remove it from our seen pending block map.
// The eviction method always assumes that the mutex is held.
r.slotToPendingBlocks.OnEvicted(func(s string, i interface{}) {
@@ -255,6 +260,7 @@ func (s *Service) Start() {
s.newColumnsVerifier = newDataColumnsVerifierFromInitializer(v)
go s.verifierRoutine()
go s.kzgVerifierRoutine()
go s.startDiscoveryAndSubscriptions()
go s.processDataColumnLogs()

View File

@@ -145,9 +145,12 @@ func (s *Service) validateDataColumn(ctx context.Context, pid peer.ID, msg *pubs
}
// [REJECT] The sidecar's column data is valid as verified by `verify_data_column_sidecar_kzg_proofs(sidecar)`.
if err := verifier.SidecarKzgProofVerified(); err != nil {
return pubsub.ValidationReject, err
validationResult, err := s.validateWithKzgBatchVerifier(ctx, roDataColumns)
if validationResult != pubsub.ValidationAccept {
return validationResult, err
}
// Mark KZG verification as satisfied since we did it via batch verifier
verifier.SatisfyRequirement(verification.RequireSidecarKzgProofVerified)
// [IGNORE] The sidecar is the first sidecar for the tuple `(block_header.slot, block_header.proposer_index, sidecar.index)`
// with valid header signature, sidecar inclusion proof, and kzg proof.

View File

@@ -7,6 +7,7 @@ import (
"testing"
"time"
"github.com/OffchainLabs/prysm/v6/beacon-chain/blockchain/kzg"
mock "github.com/OffchainLabs/prysm/v6/beacon-chain/blockchain/testing"
"github.com/OffchainLabs/prysm/v6/beacon-chain/p2p"
p2ptest "github.com/OffchainLabs/prysm/v6/beacon-chain/p2p/testing"
@@ -25,6 +26,9 @@ import (
)
func TestValidateDataColumn(t *testing.T) {
err := kzg.Start()
require.NoError(t, err)
ctx := t.Context()
t.Run("from self", func(t *testing.T) {
@@ -63,10 +67,14 @@ func TestValidateDataColumn(t *testing.T) {
clock := startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot)
service := &Service{
cfg: &config{p2p: p, initialSync: &mockSync.Sync{}, clock: clock, chain: chainService},
cfg: &config{p2p: p, initialSync: &mockSync.Sync{}, clock: clock, chain: chainService, batchVerifierLimit: 10},
ctx: ctx,
newColumnsVerifier: newDataColumnsVerifier,
seenDataColumnCache: newSlotAwareCache(seenDataColumnSize),
kzgChan: make(chan *kzgVerifier, 100),
}
// Start the KZG verifier routine for batch verification
go service.kzgVerifierRoutine()
// Encode a `beaconBlock` message instead of expected.
buf := new(bytes.Buffer)
@@ -177,12 +185,6 @@ func TestValidateDataColumn(t *testing.T) {
expectedResult: pubsub.ValidationReject,
expectedError: genericError,
},
{
name: "sidecar kzg proof verified",
verifier: testNewDataColumnSidecarsVerifier(verification.MockDataColumnsVerifier{ErrSidecarKzgProofVerified: genericError}),
expectedResult: pubsub.ValidationReject,
expectedError: genericError,
},
{
name: "sidecar proposer expected",
verifier: testNewDataColumnSidecarsVerifier(verification.MockDataColumnsVerifier{ErrSidecarProposerExpected: genericError}),

View File

@@ -0,0 +1,3 @@
### Added
- Populate sszInfo of variable-length fields in AnalyzeObjects

View File

@@ -0,0 +1,3 @@
### Changed
- Deprecated and added error to /prysm/v1/beacon/blobs endpoint for post Fulu fork.

View File

@@ -0,0 +1,3 @@
### Changed
- Switching default of validator client rest call for submit block from JSON to SSZ. Fallback json will be attempted.

View File

@@ -0,0 +1,2 @@
### Fixed
- mitigate potential supernode clustering due to libp2p ConnManager pruning of non-supernodes, see https://github.com/OffchainLabs/prysm/issues/15607.

View File

@@ -0,0 +1,2 @@
### Ignored
- Unwedge ethspecify.

View File

@@ -0,0 +1,2 @@
### Changed
- Upgraded gossipsub to v0.14.2 and libp2p to v0.39.1.

View File

@@ -0,0 +1,2 @@
### Fixed
- Lock the subnet mutex only when trying to find peers, so the mutex is not locked when dialing peers.

View File

@@ -0,0 +1,3 @@
### Fixed
- Fixed an off-by-one in forkchoice startup.

View File

@@ -0,0 +1,3 @@
### Changed
- Prysm will now downscore peers that return invalid block_by_range responses.

View File

@@ -0,0 +1,3 @@
### Added
- Added flag `--p2p-colocation-whitelist` to accept CIDRs which will bypass the p2p colocation restrictions.

View File

@@ -0,0 +1,3 @@
### Added
- KZG proof batch verification for data column gossip validation

View File

@@ -105,6 +105,7 @@ var appFlags = []cli.Flag{
cmd.P2PStaticID,
cmd.P2PAllowList,
cmd.P2PDenyList,
cmd.P2PColocationWhitelist,
cmd.PubsubQueueSize,
cmd.DataDirFlag,
cmd.VerbosityFlag,

View File

@@ -84,6 +84,7 @@ var appHelpFlagGroups = []flagGroup{
cmd.NoDiscovery,
cmd.P2PAllowList,
cmd.P2PDenyList,
cmd.P2PColocationWhitelist,
cmd.P2PHost,
cmd.P2PHostDNS,
cmd.P2PIP,

View File

@@ -180,6 +180,13 @@ var (
"192.168.0.0/16 would deny connections from peers on your local network only. The " +
"default is to accept all connections.",
}
// P2PColocationWhitelist defines a list of CIDR addresses to exempt from IP colocation restrictions.
P2PColocationWhitelist = &cli.StringSliceFlag{
Name: "p2p-colocation-whitelist",
Usage: "CIDR addresses to exempt from gossip sub IP colocation restrictions. " +
"Can be specified multiple times. Example: " +
"192.168.1.1/32 would exempt that specific IP from colocation restrictions.",
}
PubsubQueueSize = &cli.IntFlag{
Name: "pubsub-queue-size",
Usage: "The size of the pubsub validation and outbound queue for the node.",

176
deps.bzl
View File

@@ -1009,17 +1009,11 @@ def prysm_deps():
sum = "h1:dXFJfIHVvUcpSgDOV+Ne6t7jXri8Tfv2uOLHUZ2XNuo=",
version = "v0.10.0",
)
go_repository(
name = "com_github_go_kit_log",
importpath = "github.com/go-kit/log",
sum = "h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU=",
version = "v0.2.1",
)
go_repository(
name = "com_github_go_logfmt_logfmt",
importpath = "github.com/go-logfmt/logfmt",
sum = "h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA=",
version = "v0.5.1",
sum = "h1:TrB8swr/68K7m9CcGut2g3UOihhbcbiMAYiuTXdEih4=",
version = "v0.5.0",
)
go_repository(
name = "com_github_go_logr_logr",
@@ -1272,8 +1266,8 @@ def prysm_deps():
go_repository(
name = "com_github_google_pprof",
importpath = "github.com/google/pprof",
sum = "h1:FKHo8hFI3A+7w0aUQuYXQ+6EN5stWmeY/AZqtM8xk9k=",
version = "v0.0.0-20240727154555-813a5fbdbec8",
sum = "h1:wlQI2cYY0BsWmmPPAnxfQ8SDW0S3Jasn+4B8kXFxprg=",
version = "v0.0.0-20250202011525-fc3143867406",
)
go_repository(
name = "com_github_google_renameio",
@@ -1644,8 +1638,8 @@ def prysm_deps():
go_repository(
name = "com_github_ipfs_go_cid",
importpath = "github.com/ipfs/go-cid",
sum = "h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s=",
version = "v0.4.1",
sum = "h1:goEKKhaGm0ul11IHA7I6p1GmKz8kEYniqFopaB5Otwg=",
version = "v0.5.0",
)
go_repository(
name = "com_github_ipfs_go_datastore",
@@ -1856,8 +1850,8 @@ def prysm_deps():
go_repository(
name = "com_github_klauspost_compress",
importpath = "github.com/klauspost/compress",
sum = "h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=",
version = "v1.17.9",
sum = "h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=",
version = "v1.17.11",
)
go_repository(
name = "com_github_klauspost_cpuid",
@@ -1868,8 +1862,8 @@ def prysm_deps():
go_repository(
name = "com_github_klauspost_cpuid_v2",
importpath = "github.com/klauspost/cpuid/v2",
sum = "h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=",
version = "v2.2.8",
sum = "h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=",
version = "v2.2.9",
)
go_repository(
name = "com_github_klauspost_reedsolomon",
@@ -1892,8 +1886,8 @@ def prysm_deps():
go_repository(
name = "com_github_koron_go_ssdp",
importpath = "github.com/koron/go-ssdp",
sum = "h1:1IDwrghSKYM7yLf7XCzbByg2sJ/JcNOZRXS2jczTwz0=",
version = "v0.0.4",
sum = "h1:E1iSMxIs4WqxTbIBLtmNBeOOC+1sCIXQeqTWVnpmwhk=",
version = "v0.0.5",
)
go_repository(
name = "com_github_korovkin_limiter",
@@ -1970,8 +1964,8 @@ def prysm_deps():
go_repository(
name = "com_github_libp2p_go_flow_metrics",
importpath = "github.com/libp2p/go-flow-metrics",
sum = "h1:0iPhMI8PskQwzh57jB9WxIuIOQ0r+15PChFGkx3Q3WM=",
version = "v0.1.0",
sum = "h1:EIZzjmeOE6c8Dav0sNv35vhZxATIXWZg6j/C08XmmDw=",
version = "v0.2.0",
)
go_repository(
name = "com_github_libp2p_go_libp2p",
@@ -1980,8 +1974,8 @@ def prysm_deps():
],
build_file_proto_mode = "disable_global",
importpath = "github.com/libp2p/go-libp2p",
sum = "h1:DoABsaHO0VXwH6pwCs2F6XKAXWYjFMO4HFBoVxTnF9g=",
version = "v0.36.5",
sum = "h1:1Ur6rPCf3GR+g8jkrnaQaM0ha2IGespsnNlCqJLLALE=",
version = "v0.39.1",
)
go_repository(
name = "com_github_libp2p_go_libp2p_asn_util",
@@ -1999,8 +1993,8 @@ def prysm_deps():
name = "com_github_libp2p_go_libp2p_pubsub",
build_file_proto_mode = "disable_global",
importpath = "github.com/libp2p/go-libp2p-pubsub",
sum = "h1:RmFQ2XAy3zQtbt2iNPy7Tt0/3fwTnHpCQSSnmGnt1Ps=",
version = "v0.13.0",
sum = "h1:nT5lFHPQOFJcp9CW8hpKtvbpQNdl2udJuzLQWbgRum8=",
version = "v0.14.2",
)
go_repository(
name = "com_github_libp2p_go_libp2p_testing",
@@ -2029,8 +2023,8 @@ def prysm_deps():
go_repository(
name = "com_github_libp2p_go_netroute",
importpath = "github.com/libp2p/go-netroute",
sum = "h1:V8kVrpD8GK0Riv15/7VN6RbUQ3URNZVosw7H2v9tksU=",
version = "v0.2.1",
sum = "h1:Dejd8cQ47Qx2kRABg6lPwknU7+nBnFRpko45/fFPuZ8=",
version = "v0.2.2",
)
go_repository(
name = "com_github_libp2p_go_reuseport",
@@ -2041,8 +2035,8 @@ def prysm_deps():
go_repository(
name = "com_github_libp2p_go_yamux_v4",
importpath = "github.com/libp2p/go-yamux/v4",
sum = "h1:FfDR4S1wj6Bw2Pqbc8Uz7pCxeRBPbwsBbEdfwiCypkQ=",
version = "v4.0.1",
sum = "h1:nrLh89LN/LEiqcFiqdKDRHjGstN300C1269K/EX0CPU=",
version = "v4.0.2",
)
go_repository(
name = "com_github_libp2p_zeroconf_v2",
@@ -2185,8 +2179,8 @@ def prysm_deps():
go_repository(
name = "com_github_miekg_dns",
importpath = "github.com/miekg/dns",
sum = "h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ=",
version = "v1.1.62",
sum = "h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY=",
version = "v1.1.63",
)
go_repository(
name = "com_github_mikioh_tcp",
@@ -2329,14 +2323,14 @@ def prysm_deps():
go_repository(
name = "com_github_multiformats_go_multiaddr",
importpath = "github.com/multiformats/go-multiaddr",
sum = "h1:BCBzs61E3AGHcYYTv8dqRH43ZfyrqM8RXVPT8t13tLQ=",
version = "v0.13.0",
sum = "h1:bfrHrJhrRuh/NXH5mCnemjpbGjzRw/b+tJFOD41g2tU=",
version = "v0.14.0",
)
go_repository(
name = "com_github_multiformats_go_multiaddr_dns",
importpath = "github.com/multiformats/go-multiaddr-dns",
sum = "h1:P76EJ3qzBXpUXZ3twdCDx/kvagMsNo0LMFXpyms/zgU=",
version = "v0.4.0",
sum = "h1:whi/uCLbDS3mSEUMb1MsoT4uzUeZB0N32yzufqS0i5M=",
version = "v0.4.1",
)
go_repository(
name = "com_github_multiformats_go_multiaddr_fmt",
@@ -2368,8 +2362,8 @@ def prysm_deps():
go_repository(
name = "com_github_multiformats_go_multistream",
importpath = "github.com/multiformats/go-multistream",
sum = "h1:5htLSLl7lvJk3xx3qT/8Zm9J4K8vEOf/QGkvOGQAyiE=",
version = "v0.5.0",
sum = "h1:ZaHKbsL404720283o4c/IHQXiS6gb8qAN5EIJ4PN5EA=",
version = "v0.6.0",
)
go_repository(
name = "com_github_multiformats_go_varint",
@@ -2482,14 +2476,14 @@ def prysm_deps():
go_repository(
name = "com_github_onsi_ginkgo_v2",
importpath = "github.com/onsi/ginkgo/v2",
sum = "h1:PE84V2mHqoT1sglvHc8ZdQtPcwmvvt29WLEEO3xmdZw=",
version = "v2.20.0",
sum = "h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU=",
version = "v2.22.2",
)
go_repository(
name = "com_github_onsi_gomega",
importpath = "github.com/onsi/gomega",
sum = "h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=",
version = "v1.34.1",
sum = "h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8=",
version = "v1.36.2",
)
go_repository(
name = "com_github_op_go_logging",
@@ -2644,8 +2638,8 @@ def prysm_deps():
go_repository(
name = "com_github_pion_datachannel",
importpath = "github.com/pion/datachannel",
sum = "h1:ph1P1NsGkazkjrvyMfhRBUAWMxugJjq2HfQifaOoSNo=",
version = "v1.5.8",
sum = "h1:ly0Q26K1i6ZkGf42W7D4hQYR90pZwzFOjTq5AuCKk4o=",
version = "v1.5.10",
)
go_repository(
name = "com_github_pion_dtls_v2",
@@ -2653,23 +2647,35 @@ def prysm_deps():
sum = "h1:KP7H5/c1EiVAAKUmXyCzPiQe5+bCJrpOeKg/L05dunk=",
version = "v2.2.12",
)
go_repository(
name = "com_github_pion_dtls_v3",
importpath = "github.com/pion/dtls/v3",
sum = "h1:44CZekewMzfrn9pmGrj5BNnTMDCFwr+6sLH+cCuLM7U=",
version = "v3.0.4",
)
go_repository(
name = "com_github_pion_ice_v2",
importpath = "github.com/pion/ice/v2",
sum = "h1:Ic1ppYCj4tUOcPAp76U6F3fVrlSw8A9JtRXLqw6BbUM=",
version = "v2.3.34",
sum = "h1:ObIdaNDu1rCo7hObhs34YSBcO7fjslJMZV0ux+uZWh0=",
version = "v2.3.37",
)
go_repository(
name = "com_github_pion_ice_v4",
importpath = "github.com/pion/ice/v4",
sum = "h1:jmM9HwI9lfetQV/39uD0nY4y++XZNPhvzIPCb8EwxUM=",
version = "v4.0.6",
)
go_repository(
name = "com_github_pion_interceptor",
importpath = "github.com/pion/interceptor",
sum = "h1:au5rlVHsgmxNi+v/mjOPazbW1SHzfx7/hYOEYQnUcxA=",
version = "v0.1.30",
sum = "h1:aRA8Zpab/wE7/c0O3fh1PqY0AJI3fCSEM5lRWJVorwI=",
version = "v0.1.37",
)
go_repository(
name = "com_github_pion_logging",
importpath = "github.com/pion/logging",
sum = "h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=",
version = "v0.2.2",
sum = "h1:gHuf0zpoh1GW67Nr6Gj4cv5Z9ZscU7g/EaoC/Ke/igI=",
version = "v0.2.3",
)
go_repository(
name = "com_github_pion_mdns",
@@ -2677,6 +2683,12 @@ def prysm_deps():
sum = "h1:CiMYlY+O0azojWDmxdNr7ADGrnZ+V6Ilfner+6mSVK8=",
version = "v0.0.12",
)
go_repository(
name = "com_github_pion_mdns_v2",
importpath = "github.com/pion/mdns/v2",
sum = "h1:c9kM8ewCgjslaAmicYMFQIde2H9/lrZpjBkN8VwoVtM=",
version = "v2.0.7",
)
go_repository(
name = "com_github_pion_randutil",
importpath = "github.com/pion/randutil",
@@ -2686,32 +2698,32 @@ def prysm_deps():
go_repository(
name = "com_github_pion_rtcp",
importpath = "github.com/pion/rtcp",
sum = "h1:KCkGV3vJ+4DAJmvP0vaQShsb0xkRfWkO540Gy102KyE=",
version = "v1.2.14",
sum = "h1:LZQi2JbdipLOj4eBjK4wlVoQWfrZbh3Q6eHtWtJBZBo=",
version = "v1.2.15",
)
go_repository(
name = "com_github_pion_rtp",
importpath = "github.com/pion/rtp",
sum = "h1:E2HX740TZKaqdcPmf4pw6ZZuG8u5RlMMt+l3dxeu6Wk=",
version = "v1.8.9",
sum = "h1:17xjnY5WO5hgO6SD3/NTIUPvSFw/PbLsIJyz1r1yNIk=",
version = "v1.8.11",
)
go_repository(
name = "com_github_pion_sctp",
importpath = "github.com/pion/sctp",
sum = "h1:dSE4wX6uTJBcNm8+YlMg7lw1wqyKHggsP5uKbdj+NZw=",
version = "v1.8.33",
sum = "h1:qwtKvNK1Wc5tHMIYgTDJhfZk7vATGVHhXbUDfHbYwzA=",
version = "v1.8.35",
)
go_repository(
name = "com_github_pion_sdp_v3",
importpath = "github.com/pion/sdp/v3",
sum = "h1:pX++dCHoHUwq43kuwf3PyJfHlwIj4hXA7Vrifiq0IJY=",
version = "v3.0.9",
sum = "h1:6MChLE/1xYB+CjumMw+gZ9ufp2DPApuVSnDT8t5MIgA=",
version = "v3.0.10",
)
go_repository(
name = "com_github_pion_srtp_v2",
importpath = "github.com/pion/srtp/v2",
sum = "h1:HNNny4s+OUmG280ETrCdgFndp4ufx3/uy85EawYEhTk=",
version = "v2.0.20",
name = "com_github_pion_srtp_v3",
importpath = "github.com/pion/srtp/v3",
sum = "h1:2Z6vDVxzrX3UHEgrUyIGM4rRouoC7v+NiF1IHtp9B5M=",
version = "v3.0.4",
)
go_repository(
name = "com_github_pion_stun",
@@ -2725,6 +2737,12 @@ def prysm_deps():
sum = "h1:A5+wXKLAypxQri59+tmQKVs7+l6mMM+3d+eER9ifRU0=",
version = "v2.0.0",
)
go_repository(
name = "com_github_pion_stun_v3",
importpath = "github.com/pion/stun/v3",
sum = "h1:4h1gwhWLWuZWOJIJR9s2ferRO+W3zA/b6ijOI6mKzUw=",
version = "v3.0.0",
)
go_repository(
name = "com_github_pion_transport_v2",
importpath = "github.com/pion/transport/v2",
@@ -2744,10 +2762,16 @@ def prysm_deps():
version = "v2.1.6",
)
go_repository(
name = "com_github_pion_webrtc_v3",
importpath = "github.com/pion/webrtc/v3",
sum = "h1:Rf4u6n6U5t5sUxhYPQk/samzU/oDv7jk6BA5hyO2F9I=",
version = "v3.3.0",
name = "com_github_pion_turn_v4",
importpath = "github.com/pion/turn/v4",
sum = "h1:qxplo3Rxa9Yg1xXDxxH8xaqcyGUtbHYw4QSCvmFWvhM=",
version = "v4.0.0",
)
go_repository(
name = "com_github_pion_webrtc_v4",
importpath = "github.com/pion/webrtc/v4",
sum = "h1:T1ZmnT9qxIJIt4d8XoiMOBrTClGHDDXNg9e/fh018Qc=",
version = "v4.0.8",
)
go_repository(
name = "com_github_pkg_diff",
@@ -2800,8 +2824,8 @@ def prysm_deps():
go_repository(
name = "com_github_prometheus_client_golang",
importpath = "github.com/prometheus/client_golang",
sum = "h1:jBzTZ7B099Rg24tny+qngoynol8LtVYlA2bqx3vEloI=",
version = "v1.20.0",
sum = "h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=",
version = "v1.20.5",
)
go_repository(
name = "com_github_prometheus_client_model",
@@ -2812,8 +2836,8 @@ def prysm_deps():
go_repository(
name = "com_github_prometheus_common",
importpath = "github.com/prometheus/common",
sum = "h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=",
version = "v0.55.0",
sum = "h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=",
version = "v0.62.0",
)
go_repository(
name = "com_github_prometheus_procfs",
@@ -2891,8 +2915,8 @@ def prysm_deps():
"gazelle:exclude tools.go",
],
importpath = "github.com/quic-go/quic-go",
sum = "h1:wsKXZPeGWpMpCGSWqOcqpW2wZYic/8T3aqiOID0/KWE=",
version = "v0.48.2",
sum = "h1:w5iJHXwHxs1QxyBv1EHKuC50GX5to8mJAxvtnttJp94=",
version = "v0.49.0",
)
go_repository(
name = "com_github_quic_go_webtransport_go",
@@ -3504,8 +3528,8 @@ def prysm_deps():
go_repository(
name = "com_github_wlynxg_anet",
importpath = "github.com/wlynxg/anet",
sum = "h1:0de1OFQxnNqAu+x2FAKKCVIrnfGKQbs7FQz++tB0+Uw=",
version = "v0.0.4",
sum = "h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU=",
version = "v0.0.5",
)
go_repository(
name = "com_github_x448_float16",
@@ -4757,8 +4781,8 @@ def prysm_deps():
go_repository(
name = "org_golang_x_exp",
importpath = "golang.org/x/exp",
sum = "h1:ELnwvuAXPNtPk1TJRuGkI9fDTwym6AYBu0qzT8AcHdI=",
version = "v0.0.0-20240808152545-0cdaa3abc0fa",
sum = "h1:KL/ZBHXgKGVmuZBZ01Lt57yE5ws8ZPSkkihmEyq7FXc=",
version = "v0.0.0-20250128182459-e0ece0dbea4c",
)
go_repository(
name = "org_golang_x_exp_typeparams",
@@ -4882,8 +4906,8 @@ def prysm_deps():
go_repository(
name = "org_uber_go_fx",
importpath = "go.uber.org/fx",
sum = "h1:iPW+OPxv0G8w75OemJ1RAnTUrF55zOJlXlo1TbJ0Buw=",
version = "v1.22.2",
sum = "h1:lIr/gYWQGfTwGcSXWXu4vP5Ws6iqnNEIY+F/aFzCKTg=",
version = "v1.23.0",
)
go_repository(
name = "org_uber_go_goleak",

View File

@@ -18,6 +18,12 @@ func AnalyzeObject(obj any) (*sszInfo, error) {
return nil, fmt.Errorf("could not analyze type %s: %w", value.Type().Name(), err)
}
// Populate variable-length information using the actual value.
err = PopulateVariableLengthInfo(info, value.Interface())
if err != nil {
return nil, fmt.Errorf("could not populate variable length info: %w", err)
}
return info, nil
}

View File

@@ -171,11 +171,9 @@ func TestCalculateOffsetAndLength(t *testing.T) {
path, err := query.ParsePath(tt.path)
require.NoError(t, err)
info, err := query.AnalyzeObject(&sszquerypb.VariableTestContainer{})
require.NoError(t, err)
testContainer := createVariableTestContainer()
err = query.PopulateVariableLengthInfo(info, testContainer)
info, err := query.AnalyzeObject(testContainer)
require.NoError(t, err)
_, offset, length, err := query.CalculateOffsetAndLength(info, path)

67
go.mod
View File

@@ -41,9 +41,9 @@ require (
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213
github.com/kisielk/errcheck v1.8.0
github.com/kr/pretty v0.3.1
github.com/libp2p/go-libp2p v0.36.5
github.com/libp2p/go-libp2p v0.39.1
github.com/libp2p/go-libp2p-mplex v0.9.0
github.com/libp2p/go-libp2p-pubsub v0.13.0
github.com/libp2p/go-libp2p-pubsub v0.14.2
github.com/libp2p/go-mplex v0.7.0
github.com/logrusorgru/aurora v2.0.3+incompatible
github.com/manifoldco/promptui v0.7.0
@@ -51,12 +51,12 @@ require (
github.com/minio/highwayhash v1.0.2
github.com/minio/sha256-simd v1.0.1
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826
github.com/multiformats/go-multiaddr v0.13.0
github.com/multiformats/go-multiaddr v0.14.0
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/paulbellamy/ratecounter v0.2.0
github.com/pborman/uuid v1.2.1
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.20.0
github.com/prometheus/client_golang v1.20.5
github.com/prometheus/client_model v0.6.1
github.com/prometheus/prom2json v1.3.0
github.com/prysmaticlabs/fastssz v0.0.0-20241008181541-518c4ce73516
@@ -89,7 +89,7 @@ require (
go.uber.org/automaxprocs v1.5.2
go.uber.org/mock v0.5.2
golang.org/x/crypto v0.36.0
golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa
golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c
golang.org/x/sync v0.12.0
golang.org/x/tools v0.30.0
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1
@@ -150,7 +150,7 @@ require (
github.com/godbus/dbus/v5 v5.1.0 // indirect
github.com/gofrs/flock v0.8.1 // indirect
github.com/google/gopacket v1.1.19 // indirect
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 // indirect
github.com/google/pprof v0.0.0-20250202011525-fc3143867406 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
github.com/graph-gophers/graphql-go v1.3.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.25.1 // indirect
@@ -163,30 +163,30 @@ require (
github.com/influxdata/influxdb-client-go/v2 v2.4.0 // indirect
github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c // indirect
github.com/influxdata/line-protocol v0.0.0-20210311194329-9aa0e372d097 // indirect
github.com/ipfs/go-cid v0.4.1 // indirect
github.com/ipfs/go-cid v0.5.0 // indirect
github.com/jackpal/go-nat-pmp v1.0.2 // indirect
github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect
github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a // indirect
github.com/klauspost/compress v1.17.9 // indirect
github.com/klauspost/cpuid/v2 v2.2.8 // indirect
github.com/koron/go-ssdp v0.0.4 // indirect
github.com/klauspost/compress v1.17.11 // indirect
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
github.com/koron/go-ssdp v0.0.5 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/leodido/go-urn v1.2.3 // indirect
github.com/libp2p/go-buffer-pool v0.1.0 // indirect
github.com/libp2p/go-flow-metrics v0.1.0 // indirect
github.com/libp2p/go-flow-metrics v0.2.0 // indirect
github.com/libp2p/go-libp2p-asn-util v0.4.1 // indirect
github.com/libp2p/go-msgio v0.3.0 // indirect
github.com/libp2p/go-nat v0.2.0 // indirect
github.com/libp2p/go-netroute v0.2.1 // indirect
github.com/libp2p/go-netroute v0.2.2 // indirect
github.com/libp2p/go-reuseport v0.4.0 // indirect
github.com/libp2p/go-yamux/v4 v4.0.1 // indirect
github.com/libp2p/go-yamux/v4 v4.0.2 // indirect
github.com/lunixbochs/vtclean v1.0.0 // indirect
github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/miekg/dns v1.1.62 // indirect
github.com/miekg/dns v1.1.63 // indirect
github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect
github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
@@ -198,42 +198,47 @@ require (
github.com/mr-tron/base58 v1.2.0 // indirect
github.com/multiformats/go-base32 v0.1.0 // indirect
github.com/multiformats/go-base36 v0.2.0 // indirect
github.com/multiformats/go-multiaddr-dns v0.4.0 // indirect
github.com/multiformats/go-multiaddr-dns v0.4.1 // indirect
github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect
github.com/multiformats/go-multibase v0.2.0 // indirect
github.com/multiformats/go-multicodec v0.9.0 // indirect
github.com/multiformats/go-multihash v0.2.3 // indirect
github.com/multiformats/go-multistream v0.5.0 // indirect
github.com/multiformats/go-multistream v0.6.0 // indirect
github.com/multiformats/go-varint v0.0.7 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/onsi/ginkgo/v2 v2.20.0 // indirect
github.com/onsi/ginkgo/v2 v2.22.2 // indirect
github.com/opencontainers/runtime-spec v1.2.0 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect
github.com/pion/datachannel v1.5.8 // indirect
github.com/pion/datachannel v1.5.10 // indirect
github.com/pion/dtls/v2 v2.2.12 // indirect
github.com/pion/ice/v2 v2.3.34 // indirect
github.com/pion/interceptor v0.1.30 // indirect
github.com/pion/logging v0.2.2 // indirect
github.com/pion/dtls/v3 v3.0.4 // indirect
github.com/pion/ice/v2 v2.3.37 // indirect
github.com/pion/ice/v4 v4.0.6 // indirect
github.com/pion/interceptor v0.1.37 // indirect
github.com/pion/logging v0.2.3 // indirect
github.com/pion/mdns v0.0.12 // indirect
github.com/pion/mdns/v2 v2.0.7 // indirect
github.com/pion/randutil v0.1.0 // indirect
github.com/pion/rtcp v1.2.14 // indirect
github.com/pion/rtp v1.8.9 // indirect
github.com/pion/sctp v1.8.33 // indirect
github.com/pion/sdp/v3 v3.0.9 // indirect
github.com/pion/srtp/v2 v2.0.20 // indirect
github.com/pion/rtcp v1.2.15 // indirect
github.com/pion/rtp v1.8.11 // indirect
github.com/pion/sctp v1.8.35 // indirect
github.com/pion/sdp/v3 v3.0.10 // indirect
github.com/pion/srtp/v3 v3.0.4 // indirect
github.com/pion/stun v0.6.1 // indirect
github.com/pion/stun/v2 v2.0.0 // indirect
github.com/pion/stun/v3 v3.0.0 // indirect
github.com/pion/transport/v2 v2.2.10 // indirect
github.com/pion/transport/v3 v3.0.7 // indirect
github.com/pion/turn/v2 v2.1.6 // indirect
github.com/pion/webrtc/v3 v3.3.0 // indirect
github.com/pion/turn/v4 v4.0.0 // indirect
github.com/pion/webrtc/v4 v4.0.8 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/common v0.55.0 // indirect
github.com/prometheus/common v0.62.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/quic-go/qpack v0.5.1 // indirect
github.com/quic-go/quic-go v0.48.2 // indirect
github.com/quic-go/quic-go v0.49.0 // indirect
github.com/quic-go/webtransport-go v0.8.1-0.20241018022711-4ac2c9250e66 // indirect
github.com/raulk/go-watchdog v1.3.0 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
@@ -246,7 +251,7 @@ require (
github.com/tklauser/go-sysconf v0.3.13 // indirect
github.com/tklauser/numcpus v0.7.0 // indirect
github.com/wealdtech/go-eth2-types/v2 v2.8.2 // indirect
github.com/wlynxg/anet v0.0.4 // indirect
github.com/wlynxg/anet v0.0.5 // indirect
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
github.com/yusufpapurcu/wmi v1.2.3 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
@@ -254,7 +259,7 @@ require (
go.opentelemetry.io/otel/metric v1.35.0 // indirect
go.opentelemetry.io/proto/otlp v1.5.0 // indirect
go.uber.org/dig v1.18.0 // indirect
go.uber.org/fx v1.22.2 // indirect
go.uber.org/fx v1.23.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/exp/typeparams v0.0.0-20231108232855-2478ac86f678 // indirect

141
go.sum
View File

@@ -413,8 +413,8 @@ github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg=
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 h1:FKHo8hFI3A+7w0aUQuYXQ+6EN5stWmeY/AZqtM8xk9k=
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=
github.com/google/pprof v0.0.0-20250202011525-fc3143867406 h1:wlQI2cYY0BsWmmPPAnxfQ8SDW0S3Jasn+4B8kXFxprg=
github.com/google/pprof v0.0.0-20250202011525-fc3143867406/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@@ -511,8 +511,8 @@ github.com/influxdata/influxdb1-client v0.0.0-20220302092344-a9ab5670611c/go.mod
github.com/influxdata/line-protocol v0.0.0-20200327222509-2487e7298839/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo=
github.com/influxdata/line-protocol v0.0.0-20210311194329-9aa0e372d097 h1:vilfsDSy7TDxedi9gyBkMvAirat/oRcL0lFdJBf6tdM=
github.com/influxdata/line-protocol v0.0.0-20210311194329-9aa0e372d097/go.mod h1:xaLFMmpvUxqXtVkUJfg9QmT88cDaCJ3ZKgdZ78oO8Qo=
github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s=
github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk=
github.com/ipfs/go-cid v0.5.0 h1:goEKKhaGm0ul11IHA7I6p1GmKz8kEYniqFopaB5Otwg=
github.com/ipfs/go-cid v0.5.0/go.mod h1:0L7vmeNXpQpUS9vt+yEARkJ8rOg43DF3iPgn4GIN0mk=
github.com/ipfs/go-log/v2 v2.5.1 h1:1XdUzF7048prq4aBjDQQ4SL5RxftpRGdXhNRwKSAlcY=
github.com/ipfs/go-log/v2 v2.5.1/go.mod h1:prSpmC1Gpllc9UYWxDiZDreBYw7zp4Iqp1kOLU9U5UI=
github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=
@@ -547,17 +547,17 @@ github.com/kisielk/errcheck v1.8.0/go.mod h1:1kLL+jV4e+CFfueBmI1dSK2ADDyQnlrnrY/
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.9.8/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.10.1/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
github.com/klauspost/cpuid v1.2.3/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
github.com/klauspost/reedsolomon v1.9.3/go.mod h1:CwCi+NUr9pqSVktrkN+Ondf06rkhYZ/pcNv7fu+8Un4=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/koron/go-ssdp v0.0.4 h1:1IDwrghSKYM7yLf7XCzbByg2sJ/JcNOZRXS2jczTwz0=
github.com/koron/go-ssdp v0.0.4/go.mod h1:oDXq+E5IL5q0U8uSBcoAXzTzInwy5lEgC91HoKtbmZk=
github.com/koron/go-ssdp v0.0.5 h1:E1iSMxIs4WqxTbIBLtmNBeOOC+1sCIXQeqTWVnpmwhk=
github.com/koron/go-ssdp v0.0.5/go.mod h1:Qm59B7hpKpDqfyRNWRNr00jGwLdXjDyZh6y7rH6VS0w=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
@@ -581,16 +581,16 @@ github.com/leodido/go-urn v1.2.3 h1:6BE2vPT0lqoz3fmOesHZiaiFh7889ssCo2GMvLCfiuA=
github.com/leodido/go-urn v1.2.3/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8=
github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg=
github.com/libp2p/go-flow-metrics v0.1.0 h1:0iPhMI8PskQwzh57jB9WxIuIOQ0r+15PChFGkx3Q3WM=
github.com/libp2p/go-flow-metrics v0.1.0/go.mod h1:4Xi8MX8wj5aWNDAZttg6UPmc0ZrnFNsMtpsYUClFtro=
github.com/libp2p/go-libp2p v0.36.5 h1:DoABsaHO0VXwH6pwCs2F6XKAXWYjFMO4HFBoVxTnF9g=
github.com/libp2p/go-libp2p v0.36.5/go.mod h1:CpszAtXxHYOcyvB7K8rSHgnNlh21eKjYbEfLoMerbEI=
github.com/libp2p/go-flow-metrics v0.2.0 h1:EIZzjmeOE6c8Dav0sNv35vhZxATIXWZg6j/C08XmmDw=
github.com/libp2p/go-flow-metrics v0.2.0/go.mod h1:st3qqfu8+pMfh+9Mzqb2GTiwrAGjIPszEjZmtksN8Jc=
github.com/libp2p/go-libp2p v0.39.1 h1:1Ur6rPCf3GR+g8jkrnaQaM0ha2IGespsnNlCqJLLALE=
github.com/libp2p/go-libp2p v0.39.1/go.mod h1:3zicI8Lp7Isun+Afo/JOACUbbJqqR2owK6RQWFsVAbI=
github.com/libp2p/go-libp2p-asn-util v0.4.1 h1:xqL7++IKD9TBFMgnLPZR6/6iYhawHKHl950SO9L6n94=
github.com/libp2p/go-libp2p-asn-util v0.4.1/go.mod h1:d/NI6XZ9qxw67b4e+NgpQexCIiFYJjErASrYW4PFDN8=
github.com/libp2p/go-libp2p-mplex v0.9.0 h1:R58pDRAmuBXkYugbSSXR9wrTX3+1pFM1xP2bLuodIq8=
github.com/libp2p/go-libp2p-mplex v0.9.0/go.mod h1:ro1i4kuwiFT+uMPbIDIFkcLs1KRbNp0QwnUXM+P64Og=
github.com/libp2p/go-libp2p-pubsub v0.13.0 h1:RmFQ2XAy3zQtbt2iNPy7Tt0/3fwTnHpCQSSnmGnt1Ps=
github.com/libp2p/go-libp2p-pubsub v0.13.0/go.mod h1:m0gpUOyrXKXdE7c8FNQ9/HLfWbxaEw7xku45w+PaqZo=
github.com/libp2p/go-libp2p-pubsub v0.14.2 h1:nT5lFHPQOFJcp9CW8hpKtvbpQNdl2udJuzLQWbgRum8=
github.com/libp2p/go-libp2p-pubsub v0.14.2/go.mod h1:MKPU5vMI8RRFyTP0HfdsF9cLmL1nHAeJm44AxJGJx44=
github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA=
github.com/libp2p/go-libp2p-testing v0.12.0/go.mod h1:KcGDRXyN7sQCllucn1cOOS+Dmm7ujhfEyXQL5lvkcPg=
github.com/libp2p/go-mplex v0.7.0 h1:BDhFZdlk5tbr0oyFq/xv/NPGfjbnrsDam1EvutpBDbY=
@@ -599,12 +599,12 @@ github.com/libp2p/go-msgio v0.3.0 h1:mf3Z8B1xcFN314sWX+2vOTShIE0Mmn2TXn3YCUQGNj0
github.com/libp2p/go-msgio v0.3.0/go.mod h1:nyRM819GmVaF9LX3l03RMh10QdOroF++NBbxAb0mmDM=
github.com/libp2p/go-nat v0.2.0 h1:Tyz+bUFAYqGyJ/ppPPymMGbIgNRH+WqC5QrT5fKrrGk=
github.com/libp2p/go-nat v0.2.0/go.mod h1:3MJr+GRpRkyT65EpVPBstXLvOlAPzUVlG6Pwg9ohLJk=
github.com/libp2p/go-netroute v0.2.1 h1:V8kVrpD8GK0Riv15/7VN6RbUQ3URNZVosw7H2v9tksU=
github.com/libp2p/go-netroute v0.2.1/go.mod h1:hraioZr0fhBjG0ZRXJJ6Zj2IVEVNx6tDTFQfSmcq7mQ=
github.com/libp2p/go-netroute v0.2.2 h1:Dejd8cQ47Qx2kRABg6lPwknU7+nBnFRpko45/fFPuZ8=
github.com/libp2p/go-netroute v0.2.2/go.mod h1:Rntq6jUAH0l9Gg17w5bFGhcC9a+vk4KNXs6s7IljKYE=
github.com/libp2p/go-reuseport v0.4.0 h1:nR5KU7hD0WxXCJbmw7r2rhRYruNRl2koHw8fQscQm2s=
github.com/libp2p/go-reuseport v0.4.0/go.mod h1:ZtI03j/wO5hZVDFo2jKywN6bYKWLOy8Se6DrI2E1cLU=
github.com/libp2p/go-yamux/v4 v4.0.1 h1:FfDR4S1wj6Bw2Pqbc8Uz7pCxeRBPbwsBbEdfwiCypkQ=
github.com/libp2p/go-yamux/v4 v4.0.1/go.mod h1:NWjl8ZTLOGlozrXSOZ/HlfG++39iKNnM5wwmtQP1YB4=
github.com/libp2p/go-yamux/v4 v4.0.2 h1:nrLh89LN/LEiqcFiqdKDRHjGstN300C1269K/EX0CPU=
github.com/libp2p/go-yamux/v4 v4.0.2/go.mod h1:C808cCRgOs1iBwY4S71T5oxgMxgLmqUw56qh4AeBW2o=
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
@@ -653,8 +653,8 @@ github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1f
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ=
github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ=
github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY=
github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs=
github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c h1:bzE/A84HN25pxAuk9Eej1Kz9OUelF97nAc82bDquQI8=
github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c/go.mod h1:0SQS9kMwD2VsyFEB++InYyBJroV/FRmBgcydeSUcJms=
github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b h1:z78hV3sbSMAUoyUMM0I83AUIT6Hu17AWfgjzIbtrYFc=
@@ -703,10 +703,10 @@ github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYg
github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0=
github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4=
github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo=
github.com/multiformats/go-multiaddr v0.13.0 h1:BCBzs61E3AGHcYYTv8dqRH43ZfyrqM8RXVPT8t13tLQ=
github.com/multiformats/go-multiaddr v0.13.0/go.mod h1:sBXrNzucqkFJhvKOiwwLyqamGa/P5EIXNPLovyhQCII=
github.com/multiformats/go-multiaddr-dns v0.4.0 h1:P76EJ3qzBXpUXZ3twdCDx/kvagMsNo0LMFXpyms/zgU=
github.com/multiformats/go-multiaddr-dns v0.4.0/go.mod h1:7hfthtB4E4pQwirrz+J0CcDUfbWzTqEzVyYKKIKpgkc=
github.com/multiformats/go-multiaddr v0.14.0 h1:bfrHrJhrRuh/NXH5mCnemjpbGjzRw/b+tJFOD41g2tU=
github.com/multiformats/go-multiaddr v0.14.0/go.mod h1:6EkVAxtznq2yC3QT5CM1UTAwG0GTP3EWAIcjHuzQ+r4=
github.com/multiformats/go-multiaddr-dns v0.4.1 h1:whi/uCLbDS3mSEUMb1MsoT4uzUeZB0N32yzufqS0i5M=
github.com/multiformats/go-multiaddr-dns v0.4.1/go.mod h1:7hfthtB4E4pQwirrz+J0CcDUfbWzTqEzVyYKKIKpgkc=
github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E=
github.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDuPVjyWNZxToGUh/HwTZYJo=
github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g=
@@ -716,8 +716,8 @@ github.com/multiformats/go-multicodec v0.9.0/go.mod h1:L3QTQvMIaVBkXOXXtVmYE+LI1
github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew=
github.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7BFvVU9RSh+U=
github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM=
github.com/multiformats/go-multistream v0.5.0 h1:5htLSLl7lvJk3xx3qT/8Zm9J4K8vEOf/QGkvOGQAyiE=
github.com/multiformats/go-multistream v0.5.0/go.mod h1:n6tMZiwiP2wUsR8DgfDWw1dydlEqV3l6N3/GBsX6ILA=
github.com/multiformats/go-multistream v0.6.0 h1:ZaHKbsL404720283o4c/IHQXiS6gb8qAN5EIJ4PN5EA=
github.com/multiformats/go-multistream v0.6.0/go.mod h1:MOyoG5otO24cHIg8kf9QW2/NozURlkP/rvi2FQJyCPg=
github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8=
github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
@@ -748,14 +748,14 @@ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
github.com/onsi/ginkgo/v2 v2.20.0 h1:PE84V2mHqoT1sglvHc8ZdQtPcwmvvt29WLEEO3xmdZw=
github.com/onsi/ginkgo/v2 v2.20.0/go.mod h1:lG9ey2Z29hR41WMVthyJBGUBcBhGOtoPF2VFMvBXFCI=
github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU=
github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8=
github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY=
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
github.com/openconfig/gnmi v0.0.0-20190823184014-89b2bf29312c/go.mod h1:t+O9It+LKzfOAhKTT5O0ehDix+MTqbtT0T9t+7zzOvc=
github.com/openconfig/reference v0.0.0-20190727015836-8dfd928c9696/go.mod h1:ym2A+zigScwkSEb/cVQB0/ZMpU3rqiH6X7WRRsxgOGw=
@@ -798,39 +798,45 @@ github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi
github.com/pierrec/lz4 v2.4.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
github.com/pion/datachannel v1.5.8 h1:ph1P1NsGkazkjrvyMfhRBUAWMxugJjq2HfQifaOoSNo=
github.com/pion/datachannel v1.5.8/go.mod h1:PgmdpoaNBLX9HNzNClmdki4DYW5JtI7Yibu8QzbL3tI=
github.com/pion/datachannel v1.5.10 h1:ly0Q26K1i6ZkGf42W7D4hQYR90pZwzFOjTq5AuCKk4o=
github.com/pion/datachannel v1.5.10/go.mod h1:p/jJfC9arb29W7WrxyKbepTU20CFgyx5oLo8Rs4Py/M=
github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s=
github.com/pion/dtls/v2 v2.2.12 h1:KP7H5/c1EiVAAKUmXyCzPiQe5+bCJrpOeKg/L05dunk=
github.com/pion/dtls/v2 v2.2.12/go.mod h1:d9SYc9fch0CqK90mRk1dC7AkzzpwJj6u2GU3u+9pqFE=
github.com/pion/ice/v2 v2.3.34 h1:Ic1ppYCj4tUOcPAp76U6F3fVrlSw8A9JtRXLqw6BbUM=
github.com/pion/ice/v2 v2.3.34/go.mod h1:mBF7lnigdqgtB+YHkaY/Y6s6tsyRyo4u4rPGRuOjUBQ=
github.com/pion/interceptor v0.1.30 h1:au5rlVHsgmxNi+v/mjOPazbW1SHzfx7/hYOEYQnUcxA=
github.com/pion/interceptor v0.1.30/go.mod h1:RQuKT5HTdkP2Fi0cuOS5G5WNymTjzXaGF75J4k7z2nc=
github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY=
github.com/pion/dtls/v3 v3.0.4 h1:44CZekewMzfrn9pmGrj5BNnTMDCFwr+6sLH+cCuLM7U=
github.com/pion/dtls/v3 v3.0.4/go.mod h1:R373CsjxWqNPf6MEkfdy3aSe9niZvL/JaKlGeFphtMg=
github.com/pion/ice/v2 v2.3.37 h1:ObIdaNDu1rCo7hObhs34YSBcO7fjslJMZV0ux+uZWh0=
github.com/pion/ice/v2 v2.3.37/go.mod h1:mBF7lnigdqgtB+YHkaY/Y6s6tsyRyo4u4rPGRuOjUBQ=
github.com/pion/ice/v4 v4.0.6 h1:jmM9HwI9lfetQV/39uD0nY4y++XZNPhvzIPCb8EwxUM=
github.com/pion/ice/v4 v4.0.6/go.mod h1:y3M18aPhIxLlcO/4dn9X8LzLLSma84cx6emMSu14FGw=
github.com/pion/interceptor v0.1.37 h1:aRA8Zpab/wE7/c0O3fh1PqY0AJI3fCSEM5lRWJVorwI=
github.com/pion/interceptor v0.1.37/go.mod h1:JzxbJ4umVTlZAf+/utHzNesY8tmRkM2lVmkS82TTj8Y=
github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms=
github.com/pion/logging v0.2.3 h1:gHuf0zpoh1GW67Nr6Gj4cv5Z9ZscU7g/EaoC/Ke/igI=
github.com/pion/logging v0.2.3/go.mod h1:z8YfknkquMe1csOrxK5kc+5/ZPAzMxbKLX5aXpbpC90=
github.com/pion/mdns v0.0.12 h1:CiMYlY+O0azojWDmxdNr7ADGrnZ+V6Ilfner+6mSVK8=
github.com/pion/mdns v0.0.12/go.mod h1:VExJjv8to/6Wqm1FXK+Ii/Z9tsVk/F5sD/N70cnYFbk=
github.com/pion/mdns/v2 v2.0.7 h1:c9kM8ewCgjslaAmicYMFQIde2H9/lrZpjBkN8VwoVtM=
github.com/pion/mdns/v2 v2.0.7/go.mod h1:vAdSYNAT0Jy3Ru0zl2YiW3Rm/fJCwIeM0nToenfOJKA=
github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA=
github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8=
github.com/pion/rtcp v1.2.12/go.mod h1:sn6qjxvnwyAkkPzPULIbVqSKI5Dv54Rv7VG0kNxh9L4=
github.com/pion/rtcp v1.2.14 h1:KCkGV3vJ+4DAJmvP0vaQShsb0xkRfWkO540Gy102KyE=
github.com/pion/rtcp v1.2.14/go.mod h1:sn6qjxvnwyAkkPzPULIbVqSKI5Dv54Rv7VG0kNxh9L4=
github.com/pion/rtp v1.8.3/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU=
github.com/pion/rtp v1.8.9 h1:E2HX740TZKaqdcPmf4pw6ZZuG8u5RlMMt+l3dxeu6Wk=
github.com/pion/rtp v1.8.9/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU=
github.com/pion/sctp v1.8.33 h1:dSE4wX6uTJBcNm8+YlMg7lw1wqyKHggsP5uKbdj+NZw=
github.com/pion/sctp v1.8.33/go.mod h1:beTnqSzewI53KWoG3nqB282oDMGrhNxBdb+JZnkCwRM=
github.com/pion/sdp/v3 v3.0.9 h1:pX++dCHoHUwq43kuwf3PyJfHlwIj4hXA7Vrifiq0IJY=
github.com/pion/sdp/v3 v3.0.9/go.mod h1:B5xmvENq5IXJimIO4zfp6LAe1fD9N+kFv+V/1lOdz8M=
github.com/pion/srtp/v2 v2.0.20 h1:HNNny4s+OUmG280ETrCdgFndp4ufx3/uy85EawYEhTk=
github.com/pion/srtp/v2 v2.0.20/go.mod h1:0KJQjA99A6/a0DOVTu1PhDSw0CXF2jTkqOoMg3ODqdA=
github.com/pion/rtcp v1.2.15 h1:LZQi2JbdipLOj4eBjK4wlVoQWfrZbh3Q6eHtWtJBZBo=
github.com/pion/rtcp v1.2.15/go.mod h1:jlGuAjHMEXwMUHK78RgX0UmEJFV4zUKOFHR7OP+D3D0=
github.com/pion/rtp v1.8.11 h1:17xjnY5WO5hgO6SD3/NTIUPvSFw/PbLsIJyz1r1yNIk=
github.com/pion/rtp v1.8.11/go.mod h1:8uMBJj32Pa1wwx8Fuv/AsFhn8jsgw+3rUC2PfoBZ8p4=
github.com/pion/sctp v1.8.35 h1:qwtKvNK1Wc5tHMIYgTDJhfZk7vATGVHhXbUDfHbYwzA=
github.com/pion/sctp v1.8.35/go.mod h1:EcXP8zCYVTRy3W9xtOF7wJm1L1aXfKRQzaM33SjQlzg=
github.com/pion/sdp/v3 v3.0.10 h1:6MChLE/1xYB+CjumMw+gZ9ufp2DPApuVSnDT8t5MIgA=
github.com/pion/sdp/v3 v3.0.10/go.mod h1:88GMahN5xnScv1hIMTqLdu/cOcUkj6a9ytbncwMCq2E=
github.com/pion/srtp/v3 v3.0.4 h1:2Z6vDVxzrX3UHEgrUyIGM4rRouoC7v+NiF1IHtp9B5M=
github.com/pion/srtp/v3 v3.0.4/go.mod h1:1Jx3FwDoxpRaTh1oRV8A/6G1BnFL+QI82eK4ms8EEJQ=
github.com/pion/stun v0.6.1 h1:8lp6YejULeHBF8NmV8e2787BogQhduZugh5PdhDyyN4=
github.com/pion/stun v0.6.1/go.mod h1:/hO7APkX4hZKu/D0f2lHzNyvdkTGtIy3NDmLR7kSz/8=
github.com/pion/stun/v2 v2.0.0 h1:A5+wXKLAypxQri59+tmQKVs7+l6mMM+3d+eER9ifRU0=
github.com/pion/stun/v2 v2.0.0/go.mod h1:22qRSh08fSEttYUmJZGlriq9+03jtVmXNODgLccj8GQ=
github.com/pion/stun/v3 v3.0.0 h1:4h1gwhWLWuZWOJIJR9s2ferRO+W3zA/b6ijOI6mKzUw=
github.com/pion/stun/v3 v3.0.0/go.mod h1:HvCN8txt8mwi4FBvS3EmDghW6aQJ24T+y+1TKjB5jyU=
github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g=
github.com/pion/transport/v2 v2.2.3/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0=
github.com/pion/transport/v2 v2.2.4/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0=
github.com/pion/transport/v2 v2.2.10 h1:ucLBLE8nuxiHfvkFKnkDQRYWYfp8ejf4YBOPfaQpw6Q=
github.com/pion/transport/v2 v2.2.10/go.mod h1:sq1kSLWs+cHW9E+2fJP95QudkzbK7wscs8yYgQToO5E=
@@ -840,8 +846,10 @@ github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uP
github.com/pion/turn/v2 v2.1.3/go.mod h1:huEpByKKHix2/b9kmTAM3YoX6MKP+/D//0ClgUYR2fY=
github.com/pion/turn/v2 v2.1.6 h1:Xr2niVsiPTB0FPtt+yAWKFUkU1eotQbGgpTIld4x1Gc=
github.com/pion/turn/v2 v2.1.6/go.mod h1:huEpByKKHix2/b9kmTAM3YoX6MKP+/D//0ClgUYR2fY=
github.com/pion/webrtc/v3 v3.3.0 h1:Rf4u6n6U5t5sUxhYPQk/samzU/oDv7jk6BA5hyO2F9I=
github.com/pion/webrtc/v3 v3.3.0/go.mod h1:hVmrDJvwhEertRWObeb1xzulzHGeVUoPlWvxdGzcfU0=
github.com/pion/turn/v4 v4.0.0 h1:qxplo3Rxa9Yg1xXDxxH8xaqcyGUtbHYw4QSCvmFWvhM=
github.com/pion/turn/v4 v4.0.0/go.mod h1:MuPDkm15nYSklKpN8vWJ9W2M0PlyQZqYt1McGuxG7mA=
github.com/pion/webrtc/v4 v4.0.8 h1:T1ZmnT9qxIJIt4d8XoiMOBrTClGHDDXNg9e/fh018Qc=
github.com/pion/webrtc/v4 v4.0.8/go.mod h1:HHBeUVBAC+j4ZFnYhovEFStF02Arb1EyD4G7e7HBTJw=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@@ -864,8 +872,8 @@ github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeD
github.com/prometheus/client_golang v1.4.1/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
github.com/prometheus/client_golang v1.9.0/go.mod h1:FqZLKOZnGdFAhOK4nqGHa7D66IdsO+O441Eve7ptJDU=
github.com/prometheus/client_golang v1.20.0 h1:jBzTZ7B099Rg24tny+qngoynol8LtVYlA2bqx3vEloI=
github.com/prometheus/client_golang v1.20.0/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
@@ -881,8 +889,8 @@ github.com/prometheus/common v0.7.0/go.mod h1:DjGbpBbp5NYNiECxcL/VnbXCCaQpKd3tt2
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16Clt/msog/s=
github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc=
github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8=
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
@@ -908,8 +916,8 @@ github.com/prysmaticlabs/protoc-gen-go-cast v0.0.0-20230228205207-28762a7b9294 h
github.com/prysmaticlabs/protoc-gen-go-cast v0.0.0-20230228205207-28762a7b9294/go.mod h1:ZVEbRdnMkGhp/pu35zq4SXxtvUwWK0J1MATtekZpH2Y=
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
github.com/quic-go/quic-go v0.48.2 h1:wsKXZPeGWpMpCGSWqOcqpW2wZYic/8T3aqiOID0/KWE=
github.com/quic-go/quic-go v0.48.2/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs=
github.com/quic-go/quic-go v0.49.0 h1:w5iJHXwHxs1QxyBv1EHKuC50GX5to8mJAxvtnttJp94=
github.com/quic-go/quic-go v0.49.0/go.mod h1:s2wDnmCdooUQBmQfpUSTCYBl1/D4FcqbULMMkASvR6s=
github.com/quic-go/webtransport-go v0.8.1-0.20241018022711-4ac2c9250e66 h1:4WFk6u3sOT6pLa1kQ50ZVdm8BQFgJNA117cepZxtLIg=
github.com/quic-go/webtransport-go v0.8.1-0.20241018022711-4ac2c9250e66/go.mod h1:Vp72IJajgeOL6ddqrAhmp7IM9zbTcgkQxD/YdxrVwMw=
github.com/r3labs/sse/v2 v2.10.0 h1:hFEkLLFY4LDifoHdiCN/LlGBAdVJYsANaLqNYa1l/v0=
@@ -1011,7 +1019,6 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/supranational/blst v0.3.14 h1:xNMoHRJOTwMn63ip6qoWJ2Ymgvj7E2b9jY2FAwY+qRo=
@@ -1061,8 +1068,8 @@ github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4 v1.1.3/go.mod h1:qiIima
github.com/wealdtech/go-eth2-wallet-types/v2 v2.8.2 h1:264/meVYWt1wFw6Mtn+xwkZkXjID42gNra4rycoiDXI=
github.com/wealdtech/go-eth2-wallet-types/v2 v2.8.2/go.mod h1:k6kmiKWSWBTd4OxFifTEkPaBLhZspnO2KFD5XJY9nqg=
github.com/wlynxg/anet v0.0.3/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA=
github.com/wlynxg/anet v0.0.4 h1:0de1OFQxnNqAu+x2FAKKCVIrnfGKQbs7FQz++tB0+Uw=
github.com/wlynxg/anet v0.0.4/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA=
github.com/wlynxg/anet v0.0.5 h1:J3VJGi1gvo0JwZ/P1/Yc/8p63SoW98B5dHkYDmpgvvU=
github.com/wlynxg/anet v0.0.5/go.mod h1:eay5PRQr7fIVAMbTbchTnO9gG65Hg/uYGdc7mguHxoA=
github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I=
github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
@@ -1122,8 +1129,8 @@ go.uber.org/automaxprocs v1.5.2 h1:2LxUOGiR3O6tw8ui5sZa2LAaHnsviZdVOUZw4fvbnME=
go.uber.org/automaxprocs v1.5.2/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
go.uber.org/dig v1.18.0 h1:imUL1UiY0Mg4bqbFfsRQO5G4CGRBec/ZujWTvSVp3pw=
go.uber.org/dig v1.18.0/go.mod h1:Us0rSJiThwCv2GteUN0Q7OKvU7n5J4dxZ9JKUXozFdE=
go.uber.org/fx v1.22.2 h1:iPW+OPxv0G8w75OemJ1RAnTUrF55zOJlXlo1TbJ0Buw=
go.uber.org/fx v1.22.2/go.mod h1:o/D9n+2mLP6v1EG+qsdT1O8wKopYAsqZasju97SDFCU=
go.uber.org/fx v1.23.0 h1:lIr/gYWQGfTwGcSXWXu4vP5Ws6iqnNEIY+F/aFzCKTg=
go.uber.org/fx v1.23.0/go.mod h1:o/D9n+2mLP6v1EG+qsdT1O8wKopYAsqZasju97SDFCU=
go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
@@ -1179,8 +1186,8 @@ golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa h1:ELnwvuAXPNtPk1TJRuGkI9fDTwym6AYBu0qzT8AcHdI=
golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ=
golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c h1:KL/ZBHXgKGVmuZBZ01Lt57yE5ws8ZPSkkihmEyq7FXc=
golang.org/x/exp v0.0.0-20250128182459-e0ece0dbea4c/go.mod h1:tujkw807nyEEAamNbDrEGzRav+ilXA7PCRAd6xsmwiU=
golang.org/x/exp/typeparams v0.0.0-20231108232855-2478ac86f678 h1:1P7xPZEwZMoBoz0Yze5Nx2/4pxj6nw9ZqHWXqP0iRgQ=
golang.org/x/exp/typeparams v0.0.0-20231108232855-2478ac86f678/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=

View File

@@ -3737,8 +3737,6 @@
sources:
- file: beacon-chain/core/validators/validator.go
search: func InitiateValidatorExit(
- file: beacon-chain/core/validators/validator.go
search: if s.Version() < version.Electra {
spec: |
<spec fn="initiate_validator_exit" fork="phase0" hash="0483a058">
def initiate_validator_exit(state: BeaconState, index: ValidatorIndex) -> None:

View File

@@ -91,18 +91,6 @@ go_test(
"index_test.go",
"prepare_beacon_proposer_test.go",
"propose_attestation_test.go",
"propose_beacon_block_altair_test.go",
"propose_beacon_block_bellatrix_test.go",
"propose_beacon_block_blinded_bellatrix_test.go",
"propose_beacon_block_blinded_capella_test.go",
"propose_beacon_block_blinded_deneb_test.go",
"propose_beacon_block_blinded_electra_test.go",
"propose_beacon_block_blinded_fulu_test.go",
"propose_beacon_block_capella_test.go",
"propose_beacon_block_deneb_test.go",
"propose_beacon_block_electra_test.go",
"propose_beacon_block_fulu_test.go",
"propose_beacon_block_phase0_test.go",
"propose_beacon_block_test.go",
"propose_exit_test.go",
"prysm_beacon_chain_client_test.go",

View File

@@ -1,14 +1,18 @@
package beacon_api
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"testing"
"github.com/OffchainLabs/prysm/v6/api/server/structs"
rpctesting "github.com/OffchainLabs/prysm/v6/beacon-chain/rpc/eth/shared/testing"
"github.com/OffchainLabs/prysm/v6/config/params"
"github.com/OffchainLabs/prysm/v6/consensus-types/primitives"
"github.com/OffchainLabs/prysm/v6/encoding/bytesutil"
"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"
@@ -136,15 +140,14 @@ func TestBeaconApiValidatorClient_ProposeBeaconBlockValid(t *testing.T) {
ctx := t.Context()
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
jsonRestHandler.EXPECT().Post(
jsonRestHandler.EXPECT().PostSSZ(
gomock.Any(),
"/eth/v2/beacon/blocks",
map[string]string{"Eth-Consensus-Version": "phase0"},
gomock.Any(),
nil,
gomock.Any(),
).Return(
nil,
).Times(2)
nil, nil, nil,
).Times(1)
validatorClient := beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
expectedResp, expectedErr := validatorClient.proposeBeaconBlock(
@@ -153,34 +156,38 @@ func TestBeaconApiValidatorClient_ProposeBeaconBlockValid(t *testing.T) {
Block: generateSignedPhase0Block(),
},
)
resp, err := validatorClient.ProposeBeaconBlock(
ctx,
&ethpb.GenericSignedBeaconBlock{
Block: generateSignedPhase0Block(),
},
)
assert.DeepEqual(t, expectedErr, err)
assert.DeepEqual(t, expectedResp, resp)
require.NoError(t, expectedErr)
require.NotNil(t, expectedResp)
}
func TestBeaconApiValidatorClient_ProposeBeaconBlockError(t *testing.T) {
func TestBeaconApiValidatorClient_ProposeBeaconBlockError_ThenPass(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
ctx := t.Context()
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
jsonRestHandler.EXPECT().PostSSZ(
gomock.Any(),
"/eth/v2/beacon/blocks",
gomock.Any(),
gomock.Any(),
).Return(
nil, nil, &httputil.DefaultJsonError{
Code: http.StatusNotAcceptable,
Message: "SSZ not supported",
},
).Times(1)
jsonRestHandler.EXPECT().Post(
gomock.Any(),
"/eth/v2/beacon/blocks",
map[string]string{"Eth-Consensus-Version": "phase0"},
gomock.Any(),
nil,
gomock.Any(),
gomock.Any(),
).Return(
errors.New("foo error"),
).Times(2)
nil,
).Times(1)
validatorClient := beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
expectedResp, expectedErr := validatorClient.proposeBeaconBlock(
@@ -189,16 +196,351 @@ func TestBeaconApiValidatorClient_ProposeBeaconBlockError(t *testing.T) {
Block: generateSignedPhase0Block(),
},
)
require.NoError(t, expectedErr)
require.NotNil(t, expectedResp)
}
resp, err := validatorClient.ProposeBeaconBlock(
ctx,
&ethpb.GenericSignedBeaconBlock{
Block: generateSignedPhase0Block(),
func TestBeaconApiValidatorClient_ProposeBeaconBlockAllTypes(t *testing.T) {
tests := []struct {
name string
block *ethpb.GenericSignedBeaconBlock
expectedPath string
wantErr bool
errorMessage string
}{
{
name: "Phase0 block",
block: &ethpb.GenericSignedBeaconBlock{
Block: generateSignedPhase0Block(),
},
expectedPath: "/eth/v2/beacon/blocks",
},
)
{
name: "Altair block",
block: &ethpb.GenericSignedBeaconBlock{
Block: generateSignedAltairBlock(),
},
expectedPath: "/eth/v2/beacon/blocks",
},
{
name: "Bellatrix block",
block: &ethpb.GenericSignedBeaconBlock{
Block: generateSignedBellatrixBlock(),
},
expectedPath: "/eth/v2/beacon/blocks",
},
{
name: "Capella block",
block: &ethpb.GenericSignedBeaconBlock{
Block: generateSignedCapellaBlock(),
},
expectedPath: "/eth/v2/beacon/blocks",
},
{
name: "Blinded Bellatrix block",
block: &ethpb.GenericSignedBeaconBlock{
Block: generateSignedBlindedBellatrixBlock(),
},
expectedPath: "/eth/v2/beacon/blinded_blocks",
},
{
name: "Blinded Capella block",
block: &ethpb.GenericSignedBeaconBlock{
Block: generateSignedBlindedCapellaBlock(),
},
expectedPath: "/eth/v2/beacon/blinded_blocks",
},
{
name: "Deneb block",
block: &ethpb.GenericSignedBeaconBlock{
Block: generateSignedDenebBlock(),
},
expectedPath: "/eth/v2/beacon/blocks",
},
{
name: "Blinded Deneb block",
block: &ethpb.GenericSignedBeaconBlock{
Block: generateSignedBlindedDenebBlock(),
},
expectedPath: "/eth/v2/beacon/blinded_blocks",
},
{
name: "Electra block",
block: &ethpb.GenericSignedBeaconBlock{
Block: generateSignedElectraBlock(),
},
expectedPath: "/eth/v2/beacon/blocks",
},
{
name: "Blinded Electra block",
block: &ethpb.GenericSignedBeaconBlock{
Block: generateSignedBlindedElectraBlock(),
},
expectedPath: "/eth/v2/beacon/blinded_blocks",
},
{
name: "Fulu block",
block: &ethpb.GenericSignedBeaconBlock{
Block: generateSignedFuluBlock(),
},
expectedPath: "/eth/v2/beacon/blocks",
},
{
name: "Blinded Fulu block",
block: &ethpb.GenericSignedBeaconBlock{
Block: generateSignedBlindedFuluBlock(),
},
expectedPath: "/eth/v2/beacon/blinded_blocks",
},
{
name: "Unsupported block type",
block: &ethpb.GenericSignedBeaconBlock{
Block: nil,
},
wantErr: true,
errorMessage: "unsupported block type",
},
}
assert.ErrorContains(t, expectedErr.Error(), err)
assert.DeepEqual(t, expectedResp, resp)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
ctx := t.Context()
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
if !tt.wantErr {
jsonRestHandler.EXPECT().PostSSZ(
gomock.Any(),
tt.expectedPath,
gomock.Any(),
gomock.Any(),
).Return(nil, nil, nil).Times(1)
}
validatorClient := beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
resp, err := validatorClient.proposeBeaconBlock(ctx, tt.block)
if tt.wantErr {
require.ErrorContains(t, tt.errorMessage, err)
assert.Equal(t, (*ethpb.ProposeResponse)(nil), resp)
} else {
require.NoError(t, err)
require.NotNil(t, resp)
}
})
}
}
func TestBeaconApiValidatorClient_ProposeBeaconBlockHTTPErrors(t *testing.T) {
tests := []struct {
name string
sszError error
expectJSON bool
errorMessage string
}{
{
name: "HTTP 202 Accepted - block broadcast but failed validation",
sszError: &httputil.DefaultJsonError{
Code: http.StatusAccepted,
Message: "block broadcast but failed validation",
},
expectJSON: false, // No fallback for non-406 errors
errorMessage: "failed to submit block ssz",
},
{
name: "Other HTTP error",
sszError: &httputil.DefaultJsonError{
Code: http.StatusBadRequest,
Message: "bad request",
},
expectJSON: false, // No fallback for non-406 errors
errorMessage: "failed to submit block ssz",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
ctx := t.Context()
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
jsonRestHandler.EXPECT().PostSSZ(
gomock.Any(),
"/eth/v2/beacon/blocks",
gomock.Any(),
gomock.Any(),
).Return(nil, nil, tt.sszError).Times(1)
if tt.expectJSON {
// When SSZ fails, it falls back to JSON
jsonRestHandler.EXPECT().Post(
gomock.Any(),
"/eth/v2/beacon/blocks",
gomock.Any(),
gomock.Any(),
gomock.Any(),
).Return(tt.sszError).Times(1)
}
validatorClient := beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
_, err := validatorClient.proposeBeaconBlock(
ctx,
&ethpb.GenericSignedBeaconBlock{
Block: generateSignedPhase0Block(),
},
)
require.ErrorContains(t, tt.errorMessage, err)
})
}
}
func TestBeaconApiValidatorClient_ProposeBeaconBlockJSONFallback(t *testing.T) {
tests := []struct {
name string
block *ethpb.GenericSignedBeaconBlock
expectedPath string
jsonError error
wantErr bool
}{
{
name: "Phase0 block JSON fallback success",
block: &ethpb.GenericSignedBeaconBlock{
Block: generateSignedPhase0Block(),
},
expectedPath: "/eth/v2/beacon/blocks",
},
{
name: "Altair block JSON fallback success",
block: &ethpb.GenericSignedBeaconBlock{
Block: generateSignedAltairBlock(),
},
expectedPath: "/eth/v2/beacon/blocks",
},
{
name: "Bellatrix block JSON fallback success",
block: &ethpb.GenericSignedBeaconBlock{
Block: generateSignedBellatrixBlock(),
},
expectedPath: "/eth/v2/beacon/blocks",
},
{
name: "Capella block JSON fallback success",
block: &ethpb.GenericSignedBeaconBlock{
Block: generateSignedCapellaBlock(),
},
expectedPath: "/eth/v2/beacon/blocks",
},
{
name: "Blinded Bellatrix block JSON fallback success",
block: &ethpb.GenericSignedBeaconBlock{
Block: generateSignedBlindedBellatrixBlock(),
},
expectedPath: "/eth/v2/beacon/blinded_blocks",
},
{
name: "Blinded Capella block JSON fallback success",
block: &ethpb.GenericSignedBeaconBlock{
Block: generateSignedBlindedCapellaBlock(),
},
expectedPath: "/eth/v2/beacon/blinded_blocks",
},
{
name: "Deneb block JSON fallback success",
block: &ethpb.GenericSignedBeaconBlock{
Block: generateSignedDenebBlock(),
},
expectedPath: "/eth/v2/beacon/blocks",
},
{
name: "Blinded Deneb block JSON fallback success",
block: &ethpb.GenericSignedBeaconBlock{
Block: generateSignedBlindedDenebBlock(),
},
expectedPath: "/eth/v2/beacon/blinded_blocks",
},
{
name: "Electra block JSON fallback success",
block: &ethpb.GenericSignedBeaconBlock{
Block: generateSignedElectraBlock(),
},
expectedPath: "/eth/v2/beacon/blocks",
},
{
name: "Blinded Electra block JSON fallback success",
block: &ethpb.GenericSignedBeaconBlock{
Block: generateSignedBlindedElectraBlock(),
},
expectedPath: "/eth/v2/beacon/blinded_blocks",
},
{
name: "Fulu block JSON fallback success",
block: &ethpb.GenericSignedBeaconBlock{
Block: generateSignedFuluBlock(),
},
expectedPath: "/eth/v2/beacon/blocks",
},
{
name: "Blinded Fulu block JSON fallback success",
block: &ethpb.GenericSignedBeaconBlock{
Block: generateSignedBlindedFuluBlock(),
},
expectedPath: "/eth/v2/beacon/blinded_blocks",
},
{
name: "JSON fallback fails",
block: &ethpb.GenericSignedBeaconBlock{
Block: generateSignedPhase0Block(),
},
expectedPath: "/eth/v2/beacon/blocks",
jsonError: errors.New("json post failed"),
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
ctx := t.Context()
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
// SSZ call fails with 406 to trigger JSON fallback
jsonRestHandler.EXPECT().PostSSZ(
gomock.Any(),
tt.expectedPath,
gomock.Any(),
gomock.Any(),
).Return(nil, nil, &httputil.DefaultJsonError{
Code: http.StatusNotAcceptable,
Message: "SSZ not supported",
}).Times(1)
// JSON fallback
jsonRestHandler.EXPECT().Post(
gomock.Any(),
tt.expectedPath,
gomock.Any(),
gomock.Any(),
gomock.Any(),
).Return(tt.jsonError).Times(1)
validatorClient := beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
resp, err := validatorClient.proposeBeaconBlock(ctx, tt.block)
if tt.wantErr {
assert.NotNil(t, err)
assert.Equal(t, (*ethpb.ProposeResponse)(nil), resp)
} else {
require.NoError(t, err)
require.NotNil(t, resp)
}
})
}
}
func TestBeaconApiValidatorClient_Host(t *testing.T) {
@@ -229,3 +571,88 @@ func TestBeaconApiValidatorClient_Host(t *testing.T) {
host = validatorClient.Host()
require.Equal(t, hosts[1], host)
}
// Helper functions for generating test blocks for newer consensus versions
func generateSignedDenebBlock() *ethpb.GenericSignedBeaconBlock_Deneb {
var blockContents structs.SignedBeaconBlockContentsDeneb
if err := json.Unmarshal([]byte(rpctesting.DenebBlockContents), &blockContents); err != nil {
panic(err)
}
genericBlock, err := blockContents.ToGeneric()
if err != nil {
panic(err)
}
return &ethpb.GenericSignedBeaconBlock_Deneb{
Deneb: genericBlock.GetDeneb(),
}
}
func generateSignedBlindedDenebBlock() *ethpb.GenericSignedBeaconBlock_BlindedDeneb {
var blindedBlock structs.SignedBlindedBeaconBlockDeneb
if err := json.Unmarshal([]byte(rpctesting.BlindedDenebBlock), &blindedBlock); err != nil {
panic(err)
}
genericBlock, err := blindedBlock.ToGeneric()
if err != nil {
panic(err)
}
return &ethpb.GenericSignedBeaconBlock_BlindedDeneb{
BlindedDeneb: genericBlock.GetBlindedDeneb(),
}
}
func generateSignedElectraBlock() *ethpb.GenericSignedBeaconBlock_Electra {
var blockContents structs.SignedBeaconBlockContentsElectra
if err := json.Unmarshal([]byte(rpctesting.ElectraBlockContents), &blockContents); err != nil {
panic(err)
}
genericBlock, err := blockContents.ToGeneric()
if err != nil {
panic(err)
}
return &ethpb.GenericSignedBeaconBlock_Electra{
Electra: genericBlock.GetElectra(),
}
}
func generateSignedBlindedElectraBlock() *ethpb.GenericSignedBeaconBlock_BlindedElectra {
var blindedBlock structs.SignedBlindedBeaconBlockElectra
if err := json.Unmarshal([]byte(rpctesting.BlindedElectraBlock), &blindedBlock); err != nil {
panic(err)
}
genericBlock, err := blindedBlock.ToGeneric()
if err != nil {
panic(err)
}
return &ethpb.GenericSignedBeaconBlock_BlindedElectra{
BlindedElectra: genericBlock.GetBlindedElectra(),
}
}
func generateSignedFuluBlock() *ethpb.GenericSignedBeaconBlock_Fulu {
var blockContents structs.SignedBeaconBlockContentsFulu
if err := json.Unmarshal([]byte(rpctesting.FuluBlockContents), &blockContents); err != nil {
panic(err)
}
genericBlock, err := blockContents.ToGeneric()
if err != nil {
panic(err)
}
return &ethpb.GenericSignedBeaconBlock_Fulu{
Fulu: genericBlock.GetFulu(),
}
}
func generateSignedBlindedFuluBlock() *ethpb.GenericSignedBeaconBlock_BlindedFulu {
var blindedBlock structs.SignedBlindedBeaconBlockFulu
if err := json.Unmarshal([]byte(rpctesting.BlindedFuluBlock), &blindedBlock); err != nil {
panic(err)
}
genericBlock, err := blindedBlock.ToGeneric()
if err != nil {
panic(err)
}
return &ethpb.GenericSignedBeaconBlock_BlindedFulu{
BlindedFulu: genericBlock.GetBlindedFulu(),
}
}

View File

@@ -114,6 +114,22 @@ func (mr *MockJsonRestHandlerMockRecorder) Post(ctx, endpoint, headers, data, re
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Post", reflect.TypeOf((*MockJsonRestHandler)(nil).Post), ctx, endpoint, headers, data, resp)
}
// Post mocks base method.
func (m *MockJsonRestHandler) PostSSZ(ctx context.Context, endpoint string, headers map[string]string, data *bytes.Buffer) ([]byte, http.Header, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "PostSSZ", ctx, endpoint, headers, data)
ret0, _ := ret[0].([]byte)
ret1, _ := ret[1].(http.Header)
ret2, _ := ret[2].(error)
return ret0,ret1,ret2
}
// Post indicates an expected call of Post.
func (mr *MockJsonRestHandlerMockRecorder) PostSSZ(ctx, endpoint, headers, data any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PostSSZ", reflect.TypeOf((*MockJsonRestHandler)(nil).PostSSZ), ctx, endpoint, headers, data)
}
// SetHost mocks base method.
func (m *MockJsonRestHandler) SetHost(host string) {
m.ctrl.T.Helper()

View File

@@ -15,8 +15,10 @@ import (
type blockProcessingResult struct {
consensusVersion string
beaconBlockRoot [32]byte
marshalledJSON []byte
marshalledSSZ []byte
blinded bool
// Function to marshal JSON on demand
marshalJSON func() ([]byte, error)
}
func (c *beaconApiValidatorClient) proposeBeaconBlock(ctx context.Context, in *ethpb.GenericSignedBeaconBlock) (*ethpb.ProposeResponse, error) {
@@ -62,17 +64,54 @@ func (c *beaconApiValidatorClient) proposeBeaconBlock(ctx context.Context, in *e
}
headers := map[string]string{"Eth-Consensus-Version": res.consensusVersion}
err = c.jsonRestHandler.Post(ctx, endpoint, headers, bytes.NewBuffer(res.marshalledJSON), nil)
errJson := &httputil.DefaultJsonError{}
if err != nil {
if !errors.As(err, &errJson) {
return nil, err
// Try PostSSZ first with SSZ data
if res.marshalledSSZ != nil {
_, _, err = c.jsonRestHandler.PostSSZ(ctx, endpoint, headers, bytes.NewBuffer(res.marshalledSSZ))
if err != nil {
errJson := &httputil.DefaultJsonError{}
// If PostSSZ fails with 406 (Not Acceptable), fall back to JSON
if !errors.As(err, &errJson) {
return nil, err
}
if errJson.Code == http.StatusNotAcceptable && res.marshalJSON != nil {
log.WithError(err).Warn("Failed to submit block ssz, falling back to JSON")
jsonData, jsonErr := res.marshalJSON()
if jsonErr != nil {
return nil, errors.Wrap(jsonErr, "failed to marshal JSON")
}
// Reset headers for JSON
err = c.jsonRestHandler.Post(ctx, endpoint, headers, bytes.NewBuffer(jsonData), nil)
// If JSON also fails, return that error
if err != nil {
return nil, errors.Wrap(err, "failed to submit block via JSON fallback")
}
} else {
// For non-406 errors or when no JSON fallback is available, return the SSZ error
return nil, errors.Wrap(errJson, "failed to submit block ssz")
}
}
// Error 202 means that the block was successfully broadcast, but validation failed
if errJson.Code == http.StatusAccepted {
return nil, errors.New("block was successfully broadcast but failed validation")
} else if res.marshalJSON == nil {
return nil, errors.New("no marshalling functions available")
} else {
// No SSZ data available, marshal and use JSON
jsonData, jsonErr := res.marshalJSON()
if jsonErr != nil {
return nil, errors.Wrap(jsonErr, "failed to marshal JSON")
}
// Reset headers for JSON
err = c.jsonRestHandler.Post(ctx, endpoint, headers, bytes.NewBuffer(jsonData), nil)
errJson := &httputil.DefaultJsonError{}
if err != nil {
if !errors.As(err, &errJson) {
return nil, err
}
// Error 202 means that the block was successfully broadcast, but validation failed
if errJson.Code == http.StatusAccepted {
return nil, errors.New("block was successfully broadcast but failed validation")
}
return nil, errJson
}
return nil, errJson
}
return &ethpb.ProposeResponse{BlockRoot: res.beaconBlockRoot[:]}, nil
@@ -89,11 +128,19 @@ func handlePhase0Block(block *ethpb.GenericSignedBeaconBlock_Phase0) (*blockProc
}
res.beaconBlockRoot = beaconBlockRoot
signedBlock := structs.SignedBeaconBlockPhase0FromConsensus(block.Phase0)
res.marshalledJSON, err = json.Marshal(signedBlock)
// Marshal SSZ
ssz, err := block.Phase0.MarshalSSZ()
if err != nil {
return nil, errors.Wrap(err, "failed to marshall phase0 beacon block to json")
return nil, errors.Wrap(err, "failed to serialize block for phase0 beacon block")
}
res.marshalledSSZ = ssz
// Set up JSON marshalling function for fallback
res.marshalJSON = func() ([]byte, error) {
signedBlock := structs.SignedBeaconBlockPhase0FromConsensus(block.Phase0)
return json.Marshal(signedBlock)
}
return &res, nil
}
@@ -108,11 +155,19 @@ func handleAltairBlock(block *ethpb.GenericSignedBeaconBlock_Altair) (*blockProc
}
res.beaconBlockRoot = beaconBlockRoot
signedBlock := structs.SignedBeaconBlockAltairFromConsensus(block.Altair)
res.marshalledJSON, err = json.Marshal(signedBlock)
// Marshal SSZ
ssz, err := block.Altair.MarshalSSZ()
if err != nil {
return nil, errors.Wrap(err, "failed to marshall altair beacon block to json")
return nil, errors.Wrap(err, "failed to serialize block for altair beacon block")
}
res.marshalledSSZ = ssz
// Set up JSON marshalling function for fallback
res.marshalJSON = func() ([]byte, error) {
signedBlock := structs.SignedBeaconBlockAltairFromConsensus(block.Altair)
return json.Marshal(signedBlock)
}
return &res, nil
}
@@ -127,13 +182,20 @@ func handleBellatrixBlock(block *ethpb.GenericSignedBeaconBlock_Bellatrix) (*blo
}
res.beaconBlockRoot = beaconBlockRoot
signedBlock, err := structs.SignedBeaconBlockBellatrixFromConsensus(block.Bellatrix)
// Marshal SSZ
ssz, err := block.Bellatrix.MarshalSSZ()
if err != nil {
return nil, errors.Wrap(err, "failed to marshall bellatrix beacon block")
return nil, errors.Wrap(err, "failed to serialize block for bellatrix beacon block")
}
res.marshalledJSON, err = json.Marshal(signedBlock)
if err != nil {
return nil, errors.Wrap(err, "failed to marshall bellatrix beacon block to json")
res.marshalledSSZ = ssz
// Set up JSON marshalling function for fallback
res.marshalJSON = func() ([]byte, error) {
signedBlock, err := structs.SignedBeaconBlockBellatrixFromConsensus(block.Bellatrix)
if err != nil {
return nil, errors.Wrap(err, "failed to convert bellatrix beacon block")
}
return json.Marshal(signedBlock)
}
return &res, nil
@@ -150,13 +212,20 @@ func handleBlindedBellatrixBlock(block *ethpb.GenericSignedBeaconBlock_BlindedBe
}
res.beaconBlockRoot = beaconBlockRoot
signedBlock, err := structs.SignedBlindedBeaconBlockBellatrixFromConsensus(block.BlindedBellatrix)
// Marshal SSZ
ssz, err := block.BlindedBellatrix.MarshalSSZ()
if err != nil {
return nil, errors.Wrap(err, "failed to marshall blinded bellatrix beacon block")
return nil, errors.Wrap(err, "failed to serialize block for bellatrix beacon block")
}
res.marshalledJSON, err = json.Marshal(signedBlock)
if err != nil {
return nil, errors.Wrap(err, "failed to marshall blinded bellatrix beacon block to json")
res.marshalledSSZ = ssz
// Set up JSON marshalling function for fallback
res.marshalJSON = func() ([]byte, error) {
signedBlock, err := structs.SignedBlindedBeaconBlockBellatrixFromConsensus(block.BlindedBellatrix)
if err != nil {
return nil, errors.Wrap(err, "failed to convert blinded bellatrix beacon block")
}
return json.Marshal(signedBlock)
}
return &res, nil
@@ -173,13 +242,20 @@ func handleCapellaBlock(block *ethpb.GenericSignedBeaconBlock_Capella) (*blockPr
}
res.beaconBlockRoot = beaconBlockRoot
signedBlock, err := structs.SignedBeaconBlockCapellaFromConsensus(block.Capella)
// Marshal SSZ
ssz, err := block.Capella.MarshalSSZ()
if err != nil {
return nil, errors.Wrap(err, "failed to marshall capella beacon block")
return nil, errors.Wrap(err, "failed to serialize capella beacon block")
}
res.marshalledJSON, err = json.Marshal(signedBlock)
if err != nil {
return nil, errors.Wrap(err, "failed to marshall capella beacon block to json")
res.marshalledSSZ = ssz
// Set up JSON marshalling function for fallback
res.marshalJSON = func() ([]byte, error) {
signedBlock, err := structs.SignedBeaconBlockCapellaFromConsensus(block.Capella)
if err != nil {
return nil, errors.Wrap(err, "failed to convert capella beacon block")
}
return json.Marshal(signedBlock)
}
return &res, nil
@@ -196,13 +272,20 @@ func handleBlindedCapellaBlock(block *ethpb.GenericSignedBeaconBlock_BlindedCape
}
res.beaconBlockRoot = beaconBlockRoot
signedBlock, err := structs.SignedBlindedBeaconBlockCapellaFromConsensus(block.BlindedCapella)
// Marshal SSZ
ssz, err := block.BlindedCapella.MarshalSSZ()
if err != nil {
return nil, errors.Wrap(err, "failed to marshall blinded capella beacon block")
return nil, errors.Wrap(err, "failed to serialize blinded capella beacon block")
}
res.marshalledJSON, err = json.Marshal(signedBlock)
if err != nil {
return nil, errors.Wrap(err, "failed to marshall blinded capella beacon block to json")
res.marshalledSSZ = ssz
// Set up JSON marshalling function for fallback
res.marshalJSON = func() ([]byte, error) {
signedBlock, err := structs.SignedBlindedBeaconBlockCapellaFromConsensus(block.BlindedCapella)
if err != nil {
return nil, errors.Wrap(err, "failed to convert blinded capella beacon block")
}
return json.Marshal(signedBlock)
}
return &res, nil
@@ -219,13 +302,20 @@ func handleDenebBlockContents(block *ethpb.GenericSignedBeaconBlock_Deneb) (*blo
}
res.beaconBlockRoot = beaconBlockRoot
signedBlock, err := structs.SignedBeaconBlockContentsDenebFromConsensus(block.Deneb)
// Marshal SSZ
ssz, err := block.Deneb.MarshalSSZ()
if err != nil {
return nil, errors.Wrap(err, "failed to marshall deneb beacon block contents")
return nil, errors.Wrap(err, "failed to serialize deneb beacon block")
}
res.marshalledJSON, err = json.Marshal(signedBlock)
if err != nil {
return nil, errors.Wrap(err, "failed to marshall deneb beacon block contents to json")
res.marshalledSSZ = ssz
// Set up JSON marshalling function for fallback
res.marshalJSON = func() ([]byte, error) {
signedBlock, err := structs.SignedBeaconBlockContentsDenebFromConsensus(block.Deneb)
if err != nil {
return nil, errors.Wrap(err, "failed to convert deneb beacon block contents")
}
return json.Marshal(signedBlock)
}
return &res, nil
@@ -242,13 +332,20 @@ func handleBlindedDenebBlock(block *ethpb.GenericSignedBeaconBlock_BlindedDeneb)
}
res.beaconBlockRoot = beaconBlockRoot
signedBlock, err := structs.SignedBlindedBeaconBlockDenebFromConsensus(block.BlindedDeneb)
// Marshal SSZ
ssz, err := block.BlindedDeneb.MarshalSSZ()
if err != nil {
return nil, errors.Wrap(err, "failed to marshall deneb blinded beacon block ")
return nil, errors.Wrap(err, "failed to serialize blinded deneb beacon block")
}
res.marshalledJSON, err = json.Marshal(signedBlock)
if err != nil {
return nil, errors.Wrap(err, "failed to marshall deneb blinded beacon block to json")
res.marshalledSSZ = ssz
// Set up JSON marshalling function for fallback
res.marshalJSON = func() ([]byte, error) {
signedBlock, err := structs.SignedBlindedBeaconBlockDenebFromConsensus(block.BlindedDeneb)
if err != nil {
return nil, errors.Wrap(err, "failed to convert deneb blinded beacon block")
}
return json.Marshal(signedBlock)
}
return &res, nil
@@ -265,13 +362,20 @@ func handleElectraBlockContents(block *ethpb.GenericSignedBeaconBlock_Electra) (
}
res.beaconBlockRoot = beaconBlockRoot
signedBlock, err := structs.SignedBeaconBlockContentsElectraFromConsensus(block.Electra)
// Marshal SSZ
ssz, err := block.Electra.MarshalSSZ()
if err != nil {
return nil, errors.Wrap(err, "failed to marshall electra beacon block contents")
return nil, errors.Wrap(err, "failed to serialize electra beacon block")
}
res.marshalledJSON, err = json.Marshal(signedBlock)
if err != nil {
return nil, errors.Wrap(err, "failed to marshall electra beacon block contents to json")
res.marshalledSSZ = ssz
// Set up JSON marshalling function for fallback
res.marshalJSON = func() ([]byte, error) {
signedBlock, err := structs.SignedBeaconBlockContentsElectraFromConsensus(block.Electra)
if err != nil {
return nil, errors.Wrap(err, "failed to convert electra beacon block contents")
}
return json.Marshal(signedBlock)
}
return &res, nil
@@ -288,13 +392,20 @@ func handleBlindedElectraBlock(block *ethpb.GenericSignedBeaconBlock_BlindedElec
}
res.beaconBlockRoot = beaconBlockRoot
signedBlock, err := structs.SignedBlindedBeaconBlockElectraFromConsensus(block.BlindedElectra)
// Marshal SSZ
ssz, err := block.BlindedElectra.MarshalSSZ()
if err != nil {
return nil, errors.Wrap(err, "failed to marshall electra blinded beacon block")
return nil, errors.Wrap(err, "failed to serialize blinded electra beacon block")
}
res.marshalledJSON, err = json.Marshal(signedBlock)
if err != nil {
return nil, errors.Wrap(err, "failed to marshall electra blinded beacon block to json")
res.marshalledSSZ = ssz
// Set up JSON marshalling function for fallback
res.marshalJSON = func() ([]byte, error) {
signedBlock, err := structs.SignedBlindedBeaconBlockElectraFromConsensus(block.BlindedElectra)
if err != nil {
return nil, errors.Wrap(err, "failed to convert electra blinded beacon block")
}
return json.Marshal(signedBlock)
}
return &res, nil
@@ -311,13 +422,20 @@ func handleFuluBlockContents(block *ethpb.GenericSignedBeaconBlock_Fulu) (*block
}
res.beaconBlockRoot = beaconBlockRoot
signedBlock, err := structs.SignedBeaconBlockContentsFuluFromConsensus(block.Fulu)
// Marshal SSZ
ssz, err := block.Fulu.MarshalSSZ()
if err != nil {
return nil, errors.Wrap(err, "failed to marshall fulu beacon block contents")
return nil, errors.Wrap(err, "failed to serialize fulu beacon block")
}
res.marshalledJSON, err = json.Marshal(signedBlock)
if err != nil {
return nil, errors.Wrap(err, "failed to marshall fulu beacon block contents to json")
res.marshalledSSZ = ssz
// Set up JSON marshalling function for fallback
res.marshalJSON = func() ([]byte, error) {
signedBlock, err := structs.SignedBeaconBlockContentsFuluFromConsensus(block.Fulu)
if err != nil {
return nil, errors.Wrap(err, "failed to convert fulu beacon block contents")
}
return json.Marshal(signedBlock)
}
return &res, nil
@@ -334,13 +452,20 @@ func handleBlindedFuluBlock(block *ethpb.GenericSignedBeaconBlock_BlindedFulu) (
}
res.beaconBlockRoot = beaconBlockRoot
signedBlock, err := structs.SignedBlindedBeaconBlockFuluFromConsensus(block.BlindedFulu)
// Marshal SSZ
ssz, err := block.BlindedFulu.MarshalSSZ()
if err != nil {
return nil, errors.Wrap(err, "failed to marshall fulu blinded beacon block")
return nil, errors.Wrap(err, "failed to serialize blinded fulu beacon block")
}
res.marshalledJSON, err = json.Marshal(signedBlock)
if err != nil {
return nil, errors.Wrap(err, "failed to marshall fulu blinded beacon block to json")
res.marshalledSSZ = ssz
// Set up JSON marshalling function for fallback
res.marshalJSON = func() ([]byte, error) {
signedBlock, err := structs.SignedBlindedBeaconBlockFuluFromConsensus(block.BlindedFulu)
if err != nil {
return nil, errors.Wrap(err, "failed to convert fulu blinded beacon block")
}
return json.Marshal(signedBlock)
}
return &res, nil

View File

@@ -1,63 +0,0 @@
package beacon_api
import (
"bytes"
"encoding/json"
"testing"
"github.com/OffchainLabs/prysm/v6/api/server/structs"
ethpb "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/validator/client/beacon-api/mock"
testhelpers "github.com/OffchainLabs/prysm/v6/validator/client/beacon-api/test-helpers"
"go.uber.org/mock/gomock"
)
func TestProposeBeaconBlock_Altair(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
altairBlock := generateSignedAltairBlock()
genericSignedBlock := &ethpb.GenericSignedBeaconBlock{}
genericSignedBlock.Block = altairBlock
jsonAltairBlock := structs.SignedBeaconBlockAltairFromConsensus(altairBlock.Altair)
marshalledBlock, err := json.Marshal(jsonAltairBlock)
require.NoError(t, err)
ctx := t.Context()
// Make sure that what we send in the POST body is the marshalled version of the protobuf block
headers := map[string]string{"Eth-Consensus-Version": "altair"}
jsonRestHandler.EXPECT().Post(
gomock.Any(),
"/eth/v2/beacon/blocks",
headers,
bytes.NewBuffer(marshalledBlock),
nil,
)
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
proposeResponse, err := validatorClient.proposeBeaconBlock(ctx, genericSignedBlock)
assert.NoError(t, err)
require.NotNil(t, proposeResponse)
expectedBlockRoot, err := altairBlock.Altair.Block.HashTreeRoot()
require.NoError(t, err)
// Make sure that the block root is set
assert.DeepEqual(t, expectedBlockRoot[:], proposeResponse.BlockRoot)
}
func generateSignedAltairBlock() *ethpb.GenericSignedBeaconBlock_Altair {
return &ethpb.GenericSignedBeaconBlock_Altair{
Altair: &ethpb.SignedBeaconBlockAltair{
Block: testhelpers.GenerateProtoAltairBeaconBlock(),
Signature: testhelpers.FillByteSlice(96, 112),
},
}
}

View File

@@ -1,64 +0,0 @@
package beacon_api
import (
"bytes"
"encoding/json"
"testing"
"github.com/OffchainLabs/prysm/v6/api/server/structs"
ethpb "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/validator/client/beacon-api/mock"
testhelpers "github.com/OffchainLabs/prysm/v6/validator/client/beacon-api/test-helpers"
"go.uber.org/mock/gomock"
)
func TestProposeBeaconBlock_Bellatrix(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
bellatrixBlock := generateSignedBellatrixBlock()
genericSignedBlock := &ethpb.GenericSignedBeaconBlock{}
genericSignedBlock.Block = bellatrixBlock
jsonBellatrixBlock, err := structs.SignedBeaconBlockBellatrixFromConsensus(bellatrixBlock.Bellatrix)
require.NoError(t, err)
marshalledBlock, err := json.Marshal(jsonBellatrixBlock)
require.NoError(t, err)
ctx := t.Context()
// Make sure that what we send in the POST body is the marshalled version of the protobuf block
headers := map[string]string{"Eth-Consensus-Version": "bellatrix"}
jsonRestHandler.EXPECT().Post(
gomock.Any(),
"/eth/v2/beacon/blocks",
headers,
bytes.NewBuffer(marshalledBlock),
nil,
)
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
proposeResponse, err := validatorClient.proposeBeaconBlock(ctx, genericSignedBlock)
assert.NoError(t, err)
require.NotNil(t, proposeResponse)
expectedBlockRoot, err := bellatrixBlock.Bellatrix.Block.HashTreeRoot()
require.NoError(t, err)
// Make sure that the block root is set
assert.DeepEqual(t, expectedBlockRoot[:], proposeResponse.BlockRoot)
}
func generateSignedBellatrixBlock() *ethpb.GenericSignedBeaconBlock_Bellatrix {
return &ethpb.GenericSignedBeaconBlock_Bellatrix{
Bellatrix: &ethpb.SignedBeaconBlockBellatrix{
Block: testhelpers.GenerateProtoBellatrixBeaconBlock(),
Signature: testhelpers.FillByteSlice(96, 127),
},
}
}

View File

@@ -1,291 +0,0 @@
package beacon_api
import (
"bytes"
"encoding/json"
"testing"
"github.com/OffchainLabs/prysm/v6/api/server/structs"
enginev1 "github.com/OffchainLabs/prysm/v6/proto/engine/v1"
ethpb "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/validator/client/beacon-api/mock"
testhelpers "github.com/OffchainLabs/prysm/v6/validator/client/beacon-api/test-helpers"
"go.uber.org/mock/gomock"
)
func TestProposeBeaconBlock_BlindedBellatrix(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
blindedBellatrixBlock := generateSignedBlindedBellatrixBlock()
genericSignedBlock := &ethpb.GenericSignedBeaconBlock{}
genericSignedBlock.Block = blindedBellatrixBlock
jsonBlindedBellatrixBlock, err := structs.SignedBlindedBeaconBlockBellatrixFromConsensus(blindedBellatrixBlock.BlindedBellatrix)
require.NoError(t, err)
marshalledBlock, err := json.Marshal(jsonBlindedBellatrixBlock)
require.NoError(t, err)
ctx := t.Context()
// Make sure that what we send in the POST body is the marshalled version of the protobuf block
headers := map[string]string{"Eth-Consensus-Version": "bellatrix"}
jsonRestHandler.EXPECT().Post(
gomock.Any(),
"/eth/v2/beacon/blinded_blocks",
headers,
bytes.NewBuffer(marshalledBlock),
nil,
)
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
proposeResponse, err := validatorClient.proposeBeaconBlock(ctx, genericSignedBlock)
assert.NoError(t, err)
require.NotNil(t, proposeResponse)
expectedBlockRoot, err := blindedBellatrixBlock.BlindedBellatrix.Block.HashTreeRoot()
require.NoError(t, err)
// Make sure that the block root is set
assert.DeepEqual(t, expectedBlockRoot[:], proposeResponse.BlockRoot)
}
func generateSignedBlindedBellatrixBlock() *ethpb.GenericSignedBeaconBlock_BlindedBellatrix {
return &ethpb.GenericSignedBeaconBlock_BlindedBellatrix{
BlindedBellatrix: &ethpb.SignedBlindedBeaconBlockBellatrix{
Block: &ethpb.BlindedBeaconBlockBellatrix{
Slot: 1,
ProposerIndex: 2,
ParentRoot: testhelpers.FillByteSlice(32, 3),
StateRoot: testhelpers.FillByteSlice(32, 4),
Body: &ethpb.BlindedBeaconBlockBodyBellatrix{
RandaoReveal: testhelpers.FillByteSlice(96, 5),
Eth1Data: &ethpb.Eth1Data{
DepositRoot: testhelpers.FillByteSlice(32, 6),
DepositCount: 7,
BlockHash: testhelpers.FillByteSlice(32, 8),
},
Graffiti: testhelpers.FillByteSlice(32, 9),
ProposerSlashings: []*ethpb.ProposerSlashing{
{
Header_1: &ethpb.SignedBeaconBlockHeader{
Header: &ethpb.BeaconBlockHeader{
Slot: 10,
ProposerIndex: 11,
ParentRoot: testhelpers.FillByteSlice(32, 12),
StateRoot: testhelpers.FillByteSlice(32, 13),
BodyRoot: testhelpers.FillByteSlice(32, 14),
},
Signature: testhelpers.FillByteSlice(96, 15),
},
Header_2: &ethpb.SignedBeaconBlockHeader{
Header: &ethpb.BeaconBlockHeader{
Slot: 16,
ProposerIndex: 17,
ParentRoot: testhelpers.FillByteSlice(32, 18),
StateRoot: testhelpers.FillByteSlice(32, 19),
BodyRoot: testhelpers.FillByteSlice(32, 20),
},
Signature: testhelpers.FillByteSlice(96, 21),
},
},
{
Header_1: &ethpb.SignedBeaconBlockHeader{
Header: &ethpb.BeaconBlockHeader{
Slot: 22,
ProposerIndex: 23,
ParentRoot: testhelpers.FillByteSlice(32, 24),
StateRoot: testhelpers.FillByteSlice(32, 25),
BodyRoot: testhelpers.FillByteSlice(32, 26),
},
Signature: testhelpers.FillByteSlice(96, 27),
},
Header_2: &ethpb.SignedBeaconBlockHeader{
Header: &ethpb.BeaconBlockHeader{
Slot: 28,
ProposerIndex: 29,
ParentRoot: testhelpers.FillByteSlice(32, 30),
StateRoot: testhelpers.FillByteSlice(32, 31),
BodyRoot: testhelpers.FillByteSlice(32, 32),
},
Signature: testhelpers.FillByteSlice(96, 33),
},
},
},
AttesterSlashings: []*ethpb.AttesterSlashing{
{
Attestation_1: &ethpb.IndexedAttestation{
AttestingIndices: []uint64{34, 35},
Data: &ethpb.AttestationData{
Slot: 36,
CommitteeIndex: 37,
BeaconBlockRoot: testhelpers.FillByteSlice(32, 38),
Source: &ethpb.Checkpoint{
Epoch: 39,
Root: testhelpers.FillByteSlice(32, 40),
},
Target: &ethpb.Checkpoint{
Epoch: 41,
Root: testhelpers.FillByteSlice(32, 42),
},
},
Signature: testhelpers.FillByteSlice(96, 43),
},
Attestation_2: &ethpb.IndexedAttestation{
AttestingIndices: []uint64{44, 45},
Data: &ethpb.AttestationData{
Slot: 46,
CommitteeIndex: 47,
BeaconBlockRoot: testhelpers.FillByteSlice(32, 38),
Source: &ethpb.Checkpoint{
Epoch: 49,
Root: testhelpers.FillByteSlice(32, 50),
},
Target: &ethpb.Checkpoint{
Epoch: 51,
Root: testhelpers.FillByteSlice(32, 52),
},
},
Signature: testhelpers.FillByteSlice(96, 53),
},
},
{
Attestation_1: &ethpb.IndexedAttestation{
AttestingIndices: []uint64{54, 55},
Data: &ethpb.AttestationData{
Slot: 56,
CommitteeIndex: 57,
BeaconBlockRoot: testhelpers.FillByteSlice(32, 38),
Source: &ethpb.Checkpoint{
Epoch: 59,
Root: testhelpers.FillByteSlice(32, 60),
},
Target: &ethpb.Checkpoint{
Epoch: 61,
Root: testhelpers.FillByteSlice(32, 62),
},
},
Signature: testhelpers.FillByteSlice(96, 63),
},
Attestation_2: &ethpb.IndexedAttestation{
AttestingIndices: []uint64{64, 65},
Data: &ethpb.AttestationData{
Slot: 66,
CommitteeIndex: 67,
BeaconBlockRoot: testhelpers.FillByteSlice(32, 38),
Source: &ethpb.Checkpoint{
Epoch: 69,
Root: testhelpers.FillByteSlice(32, 70),
},
Target: &ethpb.Checkpoint{
Epoch: 71,
Root: testhelpers.FillByteSlice(32, 72),
},
},
Signature: testhelpers.FillByteSlice(96, 73),
},
},
},
Attestations: []*ethpb.Attestation{
{
AggregationBits: testhelpers.FillByteSlice(4, 74),
Data: &ethpb.AttestationData{
Slot: 75,
CommitteeIndex: 76,
BeaconBlockRoot: testhelpers.FillByteSlice(32, 38),
Source: &ethpb.Checkpoint{
Epoch: 78,
Root: testhelpers.FillByteSlice(32, 79),
},
Target: &ethpb.Checkpoint{
Epoch: 80,
Root: testhelpers.FillByteSlice(32, 81),
},
},
Signature: testhelpers.FillByteSlice(96, 82),
},
{
AggregationBits: testhelpers.FillByteSlice(4, 83),
Data: &ethpb.AttestationData{
Slot: 84,
CommitteeIndex: 85,
BeaconBlockRoot: testhelpers.FillByteSlice(32, 38),
Source: &ethpb.Checkpoint{
Epoch: 87,
Root: testhelpers.FillByteSlice(32, 88),
},
Target: &ethpb.Checkpoint{
Epoch: 89,
Root: testhelpers.FillByteSlice(32, 90),
},
},
Signature: testhelpers.FillByteSlice(96, 91),
},
},
Deposits: []*ethpb.Deposit{
{
Proof: testhelpers.FillByteArraySlice(33, testhelpers.FillByteSlice(32, 92)),
Data: &ethpb.Deposit_Data{
PublicKey: testhelpers.FillByteSlice(48, 94),
WithdrawalCredentials: testhelpers.FillByteSlice(32, 95),
Amount: 96,
Signature: testhelpers.FillByteSlice(96, 97),
},
},
{
Proof: testhelpers.FillByteArraySlice(33, testhelpers.FillByteSlice(32, 98)),
Data: &ethpb.Deposit_Data{
PublicKey: testhelpers.FillByteSlice(48, 100),
WithdrawalCredentials: testhelpers.FillByteSlice(32, 101),
Amount: 102,
Signature: testhelpers.FillByteSlice(96, 103),
},
},
},
VoluntaryExits: []*ethpb.SignedVoluntaryExit{
{
Exit: &ethpb.VoluntaryExit{
Epoch: 104,
ValidatorIndex: 105,
},
Signature: testhelpers.FillByteSlice(96, 106),
},
{
Exit: &ethpb.VoluntaryExit{
Epoch: 107,
ValidatorIndex: 108,
},
Signature: testhelpers.FillByteSlice(96, 109),
},
},
SyncAggregate: &ethpb.SyncAggregate{
SyncCommitteeBits: testhelpers.FillByteSlice(64, 110),
SyncCommitteeSignature: testhelpers.FillByteSlice(96, 111),
},
ExecutionPayloadHeader: &enginev1.ExecutionPayloadHeader{
ParentHash: testhelpers.FillByteSlice(32, 112),
FeeRecipient: testhelpers.FillByteSlice(20, 113),
StateRoot: testhelpers.FillByteSlice(32, 114),
ReceiptsRoot: testhelpers.FillByteSlice(32, 115),
LogsBloom: testhelpers.FillByteSlice(256, 116),
PrevRandao: testhelpers.FillByteSlice(32, 117),
BlockNumber: 118,
GasLimit: 119,
GasUsed: 120,
Timestamp: 121,
ExtraData: testhelpers.FillByteSlice(32, 122),
BaseFeePerGas: testhelpers.FillByteSlice(32, 123),
BlockHash: testhelpers.FillByteSlice(32, 124),
TransactionsRoot: testhelpers.FillByteSlice(32, 125),
},
},
},
Signature: testhelpers.FillByteSlice(96, 126),
},
}
}

View File

@@ -1,310 +0,0 @@
package beacon_api
import (
"bytes"
"encoding/json"
"testing"
"github.com/OffchainLabs/prysm/v6/api/server/structs"
enginev1 "github.com/OffchainLabs/prysm/v6/proto/engine/v1"
ethpb "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/validator/client/beacon-api/mock"
testhelpers "github.com/OffchainLabs/prysm/v6/validator/client/beacon-api/test-helpers"
"go.uber.org/mock/gomock"
)
func TestProposeBeaconBlock_BlindedCapella(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
blindedCapellaBlock := generateSignedBlindedCapellaBlock()
genericSignedBlock := &ethpb.GenericSignedBeaconBlock{}
genericSignedBlock.Block = blindedCapellaBlock
jsonBlindedCapellaBlock, err := structs.SignedBlindedBeaconBlockCapellaFromConsensus(blindedCapellaBlock.BlindedCapella)
require.NoError(t, err)
marshalledBlock, err := json.Marshal(jsonBlindedCapellaBlock)
require.NoError(t, err)
ctx := t.Context()
// Make sure that what we send in the POST body is the marshalled version of the protobuf block
headers := map[string]string{"Eth-Consensus-Version": "capella"}
jsonRestHandler.EXPECT().Post(
gomock.Any(),
"/eth/v2/beacon/blinded_blocks",
headers,
bytes.NewBuffer(marshalledBlock),
nil,
)
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
proposeResponse, err := validatorClient.proposeBeaconBlock(ctx, genericSignedBlock)
assert.NoError(t, err)
require.NotNil(t, proposeResponse)
expectedBlockRoot, err := blindedCapellaBlock.BlindedCapella.Block.HashTreeRoot()
require.NoError(t, err)
// Make sure that the block root is set
assert.DeepEqual(t, expectedBlockRoot[:], proposeResponse.BlockRoot)
}
func generateSignedBlindedCapellaBlock() *ethpb.GenericSignedBeaconBlock_BlindedCapella {
return &ethpb.GenericSignedBeaconBlock_BlindedCapella{
BlindedCapella: &ethpb.SignedBlindedBeaconBlockCapella{
Block: &ethpb.BlindedBeaconBlockCapella{
Slot: 1,
ProposerIndex: 2,
ParentRoot: testhelpers.FillByteSlice(32, 3),
StateRoot: testhelpers.FillByteSlice(32, 4),
Body: &ethpb.BlindedBeaconBlockBodyCapella{
RandaoReveal: testhelpers.FillByteSlice(96, 5),
Eth1Data: &ethpb.Eth1Data{
DepositRoot: testhelpers.FillByteSlice(32, 6),
DepositCount: 7,
BlockHash: testhelpers.FillByteSlice(32, 8),
},
Graffiti: testhelpers.FillByteSlice(32, 9),
ProposerSlashings: []*ethpb.ProposerSlashing{
{
Header_1: &ethpb.SignedBeaconBlockHeader{
Header: &ethpb.BeaconBlockHeader{
Slot: 10,
ProposerIndex: 11,
ParentRoot: testhelpers.FillByteSlice(32, 12),
StateRoot: testhelpers.FillByteSlice(32, 13),
BodyRoot: testhelpers.FillByteSlice(32, 14),
},
Signature: testhelpers.FillByteSlice(96, 15),
},
Header_2: &ethpb.SignedBeaconBlockHeader{
Header: &ethpb.BeaconBlockHeader{
Slot: 16,
ProposerIndex: 17,
ParentRoot: testhelpers.FillByteSlice(32, 18),
StateRoot: testhelpers.FillByteSlice(32, 19),
BodyRoot: testhelpers.FillByteSlice(32, 20),
},
Signature: testhelpers.FillByteSlice(96, 21),
},
},
{
Header_1: &ethpb.SignedBeaconBlockHeader{
Header: &ethpb.BeaconBlockHeader{
Slot: 22,
ProposerIndex: 23,
ParentRoot: testhelpers.FillByteSlice(32, 24),
StateRoot: testhelpers.FillByteSlice(32, 25),
BodyRoot: testhelpers.FillByteSlice(32, 26),
},
Signature: testhelpers.FillByteSlice(96, 27),
},
Header_2: &ethpb.SignedBeaconBlockHeader{
Header: &ethpb.BeaconBlockHeader{
Slot: 28,
ProposerIndex: 29,
ParentRoot: testhelpers.FillByteSlice(32, 30),
StateRoot: testhelpers.FillByteSlice(32, 31),
BodyRoot: testhelpers.FillByteSlice(32, 32),
},
Signature: testhelpers.FillByteSlice(96, 33),
},
},
},
AttesterSlashings: []*ethpb.AttesterSlashing{
{
Attestation_1: &ethpb.IndexedAttestation{
AttestingIndices: []uint64{34, 35},
Data: &ethpb.AttestationData{
Slot: 36,
CommitteeIndex: 37,
BeaconBlockRoot: testhelpers.FillByteSlice(32, 38),
Source: &ethpb.Checkpoint{
Epoch: 39,
Root: testhelpers.FillByteSlice(32, 40),
},
Target: &ethpb.Checkpoint{
Epoch: 41,
Root: testhelpers.FillByteSlice(32, 42),
},
},
Signature: testhelpers.FillByteSlice(96, 43),
},
Attestation_2: &ethpb.IndexedAttestation{
AttestingIndices: []uint64{44, 45},
Data: &ethpb.AttestationData{
Slot: 46,
CommitteeIndex: 47,
BeaconBlockRoot: testhelpers.FillByteSlice(32, 38),
Source: &ethpb.Checkpoint{
Epoch: 49,
Root: testhelpers.FillByteSlice(32, 50),
},
Target: &ethpb.Checkpoint{
Epoch: 51,
Root: testhelpers.FillByteSlice(32, 52),
},
},
Signature: testhelpers.FillByteSlice(96, 53),
},
},
{
Attestation_1: &ethpb.IndexedAttestation{
AttestingIndices: []uint64{54, 55},
Data: &ethpb.AttestationData{
Slot: 56,
CommitteeIndex: 57,
BeaconBlockRoot: testhelpers.FillByteSlice(32, 38),
Source: &ethpb.Checkpoint{
Epoch: 59,
Root: testhelpers.FillByteSlice(32, 60),
},
Target: &ethpb.Checkpoint{
Epoch: 61,
Root: testhelpers.FillByteSlice(32, 62),
},
},
Signature: testhelpers.FillByteSlice(96, 63),
},
Attestation_2: &ethpb.IndexedAttestation{
AttestingIndices: []uint64{64, 65},
Data: &ethpb.AttestationData{
Slot: 66,
CommitteeIndex: 67,
BeaconBlockRoot: testhelpers.FillByteSlice(32, 38),
Source: &ethpb.Checkpoint{
Epoch: 69,
Root: testhelpers.FillByteSlice(32, 70),
},
Target: &ethpb.Checkpoint{
Epoch: 71,
Root: testhelpers.FillByteSlice(32, 72),
},
},
Signature: testhelpers.FillByteSlice(96, 73),
},
},
},
Attestations: []*ethpb.Attestation{
{
AggregationBits: testhelpers.FillByteSlice(4, 74),
Data: &ethpb.AttestationData{
Slot: 75,
CommitteeIndex: 76,
BeaconBlockRoot: testhelpers.FillByteSlice(32, 38),
Source: &ethpb.Checkpoint{
Epoch: 78,
Root: testhelpers.FillByteSlice(32, 79),
},
Target: &ethpb.Checkpoint{
Epoch: 80,
Root: testhelpers.FillByteSlice(32, 81),
},
},
Signature: testhelpers.FillByteSlice(96, 82),
},
{
AggregationBits: testhelpers.FillByteSlice(4, 83),
Data: &ethpb.AttestationData{
Slot: 84,
CommitteeIndex: 85,
BeaconBlockRoot: testhelpers.FillByteSlice(32, 38),
Source: &ethpb.Checkpoint{
Epoch: 87,
Root: testhelpers.FillByteSlice(32, 88),
},
Target: &ethpb.Checkpoint{
Epoch: 89,
Root: testhelpers.FillByteSlice(32, 90),
},
},
Signature: testhelpers.FillByteSlice(96, 91),
},
},
Deposits: []*ethpb.Deposit{
{
Proof: testhelpers.FillByteArraySlice(33, testhelpers.FillByteSlice(32, 92)),
Data: &ethpb.Deposit_Data{
PublicKey: testhelpers.FillByteSlice(48, 94),
WithdrawalCredentials: testhelpers.FillByteSlice(32, 95),
Amount: 96,
Signature: testhelpers.FillByteSlice(96, 97),
},
},
{
Proof: testhelpers.FillByteArraySlice(33, testhelpers.FillByteSlice(32, 98)),
Data: &ethpb.Deposit_Data{
PublicKey: testhelpers.FillByteSlice(48, 100),
WithdrawalCredentials: testhelpers.FillByteSlice(32, 101),
Amount: 102,
Signature: testhelpers.FillByteSlice(96, 103),
},
},
},
VoluntaryExits: []*ethpb.SignedVoluntaryExit{
{
Exit: &ethpb.VoluntaryExit{
Epoch: 104,
ValidatorIndex: 105,
},
Signature: testhelpers.FillByteSlice(96, 106),
},
{
Exit: &ethpb.VoluntaryExit{
Epoch: 107,
ValidatorIndex: 108,
},
Signature: testhelpers.FillByteSlice(96, 109),
},
},
SyncAggregate: &ethpb.SyncAggregate{
SyncCommitteeBits: testhelpers.FillByteSlice(64, 110),
SyncCommitteeSignature: testhelpers.FillByteSlice(96, 111),
},
ExecutionPayloadHeader: &enginev1.ExecutionPayloadHeaderCapella{
ParentHash: testhelpers.FillByteSlice(32, 112),
FeeRecipient: testhelpers.FillByteSlice(20, 113),
StateRoot: testhelpers.FillByteSlice(32, 114),
ReceiptsRoot: testhelpers.FillByteSlice(32, 115),
LogsBloom: testhelpers.FillByteSlice(256, 116),
PrevRandao: testhelpers.FillByteSlice(32, 117),
BlockNumber: 118,
GasLimit: 119,
GasUsed: 120,
Timestamp: 121,
ExtraData: testhelpers.FillByteSlice(32, 122),
BaseFeePerGas: testhelpers.FillByteSlice(32, 123),
BlockHash: testhelpers.FillByteSlice(32, 124),
TransactionsRoot: testhelpers.FillByteSlice(32, 125),
WithdrawalsRoot: testhelpers.FillByteSlice(32, 126),
},
BlsToExecutionChanges: []*ethpb.SignedBLSToExecutionChange{
{
Message: &ethpb.BLSToExecutionChange{
ValidatorIndex: 127,
FromBlsPubkey: testhelpers.FillByteSlice(48, 128),
ToExecutionAddress: testhelpers.FillByteSlice(20, 129),
},
Signature: testhelpers.FillByteSlice(96, 130),
},
{
Message: &ethpb.BLSToExecutionChange{
ValidatorIndex: 131,
FromBlsPubkey: testhelpers.FillByteSlice(48, 132),
ToExecutionAddress: testhelpers.FillByteSlice(20, 133),
},
Signature: testhelpers.FillByteSlice(96, 134),
},
},
},
},
Signature: testhelpers.FillByteSlice(96, 135),
},
}
}

View File

@@ -1,49 +0,0 @@
package beacon_api
import (
"bytes"
"encoding/json"
"testing"
"github.com/OffchainLabs/prysm/v6/api/server/structs"
rpctesting "github.com/OffchainLabs/prysm/v6/beacon-chain/rpc/eth/shared/testing"
"github.com/OffchainLabs/prysm/v6/testing/assert"
"github.com/OffchainLabs/prysm/v6/testing/require"
"github.com/OffchainLabs/prysm/v6/validator/client/beacon-api/mock"
"go.uber.org/mock/gomock"
)
func TestProposeBeaconBlock_BlindedDeneb(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
var block structs.SignedBlindedBeaconBlockDeneb
err := json.Unmarshal([]byte(rpctesting.BlindedDenebBlock), &block)
require.NoError(t, err)
genericSignedBlock, err := block.ToGeneric()
require.NoError(t, err)
denebBytes, err := json.Marshal(block)
require.NoError(t, err)
// Make sure that what we send in the POST body is the marshalled version of the protobuf block
headers := map[string]string{"Eth-Consensus-Version": "deneb"}
jsonRestHandler.EXPECT().Post(
gomock.Any(),
"/eth/v2/beacon/blinded_blocks",
headers,
bytes.NewBuffer(denebBytes),
nil,
)
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
proposeResponse, err := validatorClient.proposeBeaconBlock(t.Context(), genericSignedBlock)
assert.NoError(t, err)
require.NotNil(t, proposeResponse)
expectedBlockRoot, err := genericSignedBlock.GetBlindedDeneb().HashTreeRoot()
require.NoError(t, err)
// Make sure that the block root is set
assert.DeepEqual(t, expectedBlockRoot[:], proposeResponse.BlockRoot)
}

View File

@@ -1,49 +0,0 @@
package beacon_api
import (
"bytes"
"encoding/json"
"testing"
"github.com/OffchainLabs/prysm/v6/api/server/structs"
rpctesting "github.com/OffchainLabs/prysm/v6/beacon-chain/rpc/eth/shared/testing"
"github.com/OffchainLabs/prysm/v6/testing/assert"
"github.com/OffchainLabs/prysm/v6/testing/require"
"github.com/OffchainLabs/prysm/v6/validator/client/beacon-api/mock"
"go.uber.org/mock/gomock"
)
func TestProposeBeaconBlock_BlindedElectra(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
var block structs.SignedBlindedBeaconBlockElectra
err := json.Unmarshal([]byte(rpctesting.BlindedElectraBlock), &block)
require.NoError(t, err)
genericSignedBlock, err := block.ToGeneric()
require.NoError(t, err)
electraBytes, err := json.Marshal(block)
require.NoError(t, err)
// Make sure that what we send in the POST body is the marshalled version of the protobuf block
headers := map[string]string{"Eth-Consensus-Version": "electra"}
jsonRestHandler.EXPECT().Post(
gomock.Any(),
"/eth/v2/beacon/blinded_blocks",
headers,
bytes.NewBuffer(electraBytes),
nil,
)
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
proposeResponse, err := validatorClient.proposeBeaconBlock(t.Context(), genericSignedBlock)
assert.NoError(t, err)
require.NotNil(t, proposeResponse)
expectedBlockRoot, err := genericSignedBlock.GetBlindedElectra().HashTreeRoot()
require.NoError(t, err)
// Make sure that the block root is set
assert.DeepEqual(t, expectedBlockRoot[:], proposeResponse.BlockRoot)
}

View File

@@ -1,49 +0,0 @@
package beacon_api
import (
"bytes"
"encoding/json"
"testing"
"github.com/OffchainLabs/prysm/v6/api/server/structs"
rpctesting "github.com/OffchainLabs/prysm/v6/beacon-chain/rpc/eth/shared/testing"
"github.com/OffchainLabs/prysm/v6/testing/assert"
"github.com/OffchainLabs/prysm/v6/testing/require"
"github.com/OffchainLabs/prysm/v6/validator/client/beacon-api/mock"
"go.uber.org/mock/gomock"
)
func TestProposeBeaconBlock_BlindedFulu(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
var block structs.SignedBlindedBeaconBlockFulu
err := json.Unmarshal([]byte(rpctesting.BlindedFuluBlock), &block)
require.NoError(t, err)
genericSignedBlock, err := block.ToGeneric()
require.NoError(t, err)
fuluBytes, err := json.Marshal(block)
require.NoError(t, err)
// Make sure that what we send in the POST body is the marshalled version of the protobuf block
headers := map[string]string{"Eth-Consensus-Version": "fulu"}
jsonRestHandler.EXPECT().Post(
gomock.Any(),
"/eth/v2/beacon/blinded_blocks",
headers,
bytes.NewBuffer(fuluBytes),
nil,
)
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
proposeResponse, err := validatorClient.proposeBeaconBlock(t.Context(), genericSignedBlock)
assert.NoError(t, err)
require.NotNil(t, proposeResponse)
expectedBlockRoot, err := genericSignedBlock.GetBlindedFulu().HashTreeRoot()
require.NoError(t, err)
// Make sure that the block root is set
assert.DeepEqual(t, expectedBlockRoot[:], proposeResponse.BlockRoot)
}

View File

@@ -1,62 +0,0 @@
package beacon_api
import (
"bytes"
"encoding/json"
"testing"
"github.com/OffchainLabs/prysm/v6/api/server/structs"
ethpb "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/validator/client/beacon-api/mock"
testhelpers "github.com/OffchainLabs/prysm/v6/validator/client/beacon-api/test-helpers"
"go.uber.org/mock/gomock"
)
func TestProposeBeaconBlock_Capella(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
capellaBlock := generateSignedCapellaBlock()
genericSignedBlock := &ethpb.GenericSignedBeaconBlock{}
genericSignedBlock.Block = capellaBlock
jsonCapellaBlock, err := structs.SignedBeaconBlockCapellaFromConsensus(capellaBlock.Capella)
require.NoError(t, err)
marshalledBlock, err := json.Marshal(jsonCapellaBlock)
require.NoError(t, err)
// Make sure that what we send in the POST body is the marshalled version of the protobuf block
headers := map[string]string{"Eth-Consensus-Version": "capella"}
jsonRestHandler.EXPECT().Post(
gomock.Any(),
"/eth/v2/beacon/blocks",
headers,
bytes.NewBuffer(marshalledBlock),
nil,
)
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
proposeResponse, err := validatorClient.proposeBeaconBlock(t.Context(), genericSignedBlock)
assert.NoError(t, err)
require.NotNil(t, proposeResponse)
expectedBlockRoot, err := capellaBlock.Capella.Block.HashTreeRoot()
require.NoError(t, err)
// Make sure that the block root is set
assert.DeepEqual(t, expectedBlockRoot[:], proposeResponse.BlockRoot)
}
func generateSignedCapellaBlock() *ethpb.GenericSignedBeaconBlock_Capella {
return &ethpb.GenericSignedBeaconBlock_Capella{
Capella: &ethpb.SignedBeaconBlockCapella{
Block: testhelpers.GenerateProtoCapellaBeaconBlock(),
Signature: testhelpers.FillByteSlice(96, 127),
},
}
}

View File

@@ -1,49 +0,0 @@
package beacon_api
import (
"bytes"
"encoding/json"
"testing"
"github.com/OffchainLabs/prysm/v6/api/server/structs"
rpctesting "github.com/OffchainLabs/prysm/v6/beacon-chain/rpc/eth/shared/testing"
"github.com/OffchainLabs/prysm/v6/testing/assert"
"github.com/OffchainLabs/prysm/v6/testing/require"
"github.com/OffchainLabs/prysm/v6/validator/client/beacon-api/mock"
"go.uber.org/mock/gomock"
)
func TestProposeBeaconBlock_Deneb(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
var blockContents structs.SignedBeaconBlockContentsDeneb
err := json.Unmarshal([]byte(rpctesting.DenebBlockContents), &blockContents)
require.NoError(t, err)
genericSignedBlock, err := blockContents.ToGeneric()
require.NoError(t, err)
denebBytes, err := json.Marshal(blockContents)
require.NoError(t, err)
// Make sure that what we send in the POST body is the marshalled version of the protobuf block
headers := map[string]string{"Eth-Consensus-Version": "deneb"}
jsonRestHandler.EXPECT().Post(
gomock.Any(),
"/eth/v2/beacon/blocks",
headers,
bytes.NewBuffer(denebBytes),
nil,
)
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
proposeResponse, err := validatorClient.proposeBeaconBlock(t.Context(), genericSignedBlock)
assert.NoError(t, err)
require.NotNil(t, proposeResponse)
expectedBlockRoot, err := genericSignedBlock.GetDeneb().Block.HashTreeRoot()
require.NoError(t, err)
// Make sure that the block root is set
assert.DeepEqual(t, expectedBlockRoot[:], proposeResponse.BlockRoot)
}

View File

@@ -1,49 +0,0 @@
package beacon_api
import (
"bytes"
"encoding/json"
"testing"
"github.com/OffchainLabs/prysm/v6/api/server/structs"
rpctesting "github.com/OffchainLabs/prysm/v6/beacon-chain/rpc/eth/shared/testing"
"github.com/OffchainLabs/prysm/v6/testing/assert"
"github.com/OffchainLabs/prysm/v6/testing/require"
"github.com/OffchainLabs/prysm/v6/validator/client/beacon-api/mock"
"go.uber.org/mock/gomock"
)
func TestProposeBeaconBlock_Electra(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
var blockContents structs.SignedBeaconBlockContentsElectra
err := json.Unmarshal([]byte(rpctesting.ElectraBlockContents), &blockContents)
require.NoError(t, err)
genericSignedBlock, err := blockContents.ToGeneric()
require.NoError(t, err)
electraBytes, err := json.Marshal(blockContents)
require.NoError(t, err)
// Make sure that what we send in the POST body is the marshalled version of the protobuf block
headers := map[string]string{"Eth-Consensus-Version": "electra"}
jsonRestHandler.EXPECT().Post(
gomock.Any(),
"/eth/v2/beacon/blocks",
headers,
bytes.NewBuffer(electraBytes),
nil,
)
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
proposeResponse, err := validatorClient.proposeBeaconBlock(t.Context(), genericSignedBlock)
assert.NoError(t, err)
require.NotNil(t, proposeResponse)
expectedBlockRoot, err := genericSignedBlock.GetElectra().Block.HashTreeRoot()
require.NoError(t, err)
// Make sure that the block root is set
assert.DeepEqual(t, expectedBlockRoot[:], proposeResponse.BlockRoot)
}

View File

@@ -1,49 +0,0 @@
package beacon_api
import (
"bytes"
"encoding/json"
"testing"
"github.com/OffchainLabs/prysm/v6/api/server/structs"
rpctesting "github.com/OffchainLabs/prysm/v6/beacon-chain/rpc/eth/shared/testing"
"github.com/OffchainLabs/prysm/v6/testing/assert"
"github.com/OffchainLabs/prysm/v6/testing/require"
"github.com/OffchainLabs/prysm/v6/validator/client/beacon-api/mock"
"go.uber.org/mock/gomock"
)
func TestProposeBeaconBlock_Fulu(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
var blockContents structs.SignedBeaconBlockContentsFulu
err := json.Unmarshal([]byte(rpctesting.FuluBlockContents), &blockContents)
require.NoError(t, err)
genericSignedBlock, err := blockContents.ToGeneric()
require.NoError(t, err)
fuluBytes, err := json.Marshal(blockContents)
require.NoError(t, err)
// Make sure that what we send in the POST body is the marshalled version of the protobuf block
headers := map[string]string{"Eth-Consensus-Version": "fulu"}
jsonRestHandler.EXPECT().Post(
gomock.Any(),
"/eth/v2/beacon/blocks",
headers,
bytes.NewBuffer(fuluBytes),
nil,
)
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
proposeResponse, err := validatorClient.proposeBeaconBlock(t.Context(), genericSignedBlock)
assert.NoError(t, err)
require.NotNil(t, proposeResponse)
expectedBlockRoot, err := genericSignedBlock.GetFulu().Block.HashTreeRoot()
require.NoError(t, err)
// Make sure that the block root is set
assert.DeepEqual(t, expectedBlockRoot[:], proposeResponse.BlockRoot)
}

View File

@@ -1,63 +0,0 @@
package beacon_api
import (
"bytes"
"encoding/json"
"testing"
"github.com/OffchainLabs/prysm/v6/api/server/structs"
ethpb "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/validator/client/beacon-api/mock"
testhelpers "github.com/OffchainLabs/prysm/v6/validator/client/beacon-api/test-helpers"
"go.uber.org/mock/gomock"
)
func TestProposeBeaconBlock_Phase0(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
phase0Block := generateSignedPhase0Block()
genericSignedBlock := &ethpb.GenericSignedBeaconBlock{}
genericSignedBlock.Block = phase0Block
jsonPhase0Block := structs.SignedBeaconBlockPhase0FromConsensus(phase0Block.Phase0)
marshalledBlock, err := json.Marshal(jsonPhase0Block)
require.NoError(t, err)
ctx := t.Context()
// Make sure that what we send in the POST body is the marshalled version of the protobuf block
headers := map[string]string{"Eth-Consensus-Version": "phase0"}
jsonRestHandler.EXPECT().Post(
gomock.Any(),
"/eth/v2/beacon/blocks",
headers,
bytes.NewBuffer(marshalledBlock),
nil,
)
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
proposeResponse, err := validatorClient.proposeBeaconBlock(ctx, genericSignedBlock)
assert.NoError(t, err)
require.NotNil(t, proposeResponse)
expectedBlockRoot, err := phase0Block.Phase0.Block.HashTreeRoot()
require.NoError(t, err)
// Make sure that the block root is set
assert.DeepEqual(t, expectedBlockRoot[:], proposeResponse.BlockRoot)
}
func generateSignedPhase0Block() *ethpb.GenericSignedBeaconBlock_Phase0 {
return &ethpb.GenericSignedBeaconBlock_Phase0{
Phase0: &ethpb.SignedBeaconBlock{
Block: testhelpers.GenerateProtoPhase0BeaconBlock(),
Signature: testhelpers.FillByteSlice(96, 110),
},
}
}

View File

@@ -1,43 +1,42 @@
package beacon_api
import (
"bytes"
"encoding/json"
"errors"
"net/http"
"testing"
"github.com/OffchainLabs/prysm/v6/api/server/structs"
rpctesting "github.com/OffchainLabs/prysm/v6/beacon-chain/rpc/eth/shared/testing"
"github.com/OffchainLabs/prysm/v6/network/httputil"
engine "github.com/OffchainLabs/prysm/v6/proto/engine/v1"
ethpb "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/validator/client/beacon-api/mock"
testhelpers "github.com/OffchainLabs/prysm/v6/validator/client/beacon-api/test-helpers"
"go.uber.org/mock/gomock"
)
func TestProposeBeaconBlock_Error(t *testing.T) {
func TestProposeBeaconBlock_SSZ_Error(t *testing.T) {
testSuites := []struct {
name string
returnedError error
expectedErrorMessage string
}{
{
name: "error 202",
expectedErrorMessage: "block was successfully broadcast but failed validation",
returnedError: &httputil.DefaultJsonError{
Code: http.StatusAccepted,
Message: "202 error",
},
},
{
name: "error 500",
expectedErrorMessage: "HTTP request unsuccessful (500: foo error)",
expectedErrorMessage: "failed to submit block ssz",
returnedError: &httputil.DefaultJsonError{
Code: http.StatusInternalServerError,
Message: "foo error",
Message: "failed to submit block ssz",
},
},
{
name: "other error",
expectedErrorMessage: "foo error",
returnedError: errors.New("foo error"),
expectedErrorMessage: "failed to submit block ssz",
returnedError: errors.New("failed to submit block ssz"),
},
}
@@ -106,17 +105,21 @@ func TestProposeBeaconBlock_Error(t *testing.T) {
ctx := t.Context()
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
headers := map[string]string{"Eth-Consensus-Version": testCase.consensusVersion}
jsonRestHandler.EXPECT().Post(
// Expect PostSSZ to be called first with SSZ data
headers := map[string]string{
"Eth-Consensus-Version": testCase.consensusVersion,
}
jsonRestHandler.EXPECT().PostSSZ(
gomock.Any(),
testCase.endpoint,
headers,
gomock.Any(),
nil,
).Return(
testSuite.returnedError,
nil, nil, testSuite.returnedError,
).Times(1)
// No JSON fallback expected for non-406 errors
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
_, err := validatorClient.proposeBeaconBlock(ctx, testCase.block)
assert.ErrorContains(t, testSuite.expectedErrorMessage, err)
@@ -130,3 +133,546 @@ func TestProposeBeaconBlock_UnsupportedBlockType(t *testing.T) {
_, err := validatorClient.proposeBeaconBlock(t.Context(), &ethpb.GenericSignedBeaconBlock{})
assert.ErrorContains(t, "unsupported block type", err)
}
func TestProposeBeaconBlock_SSZSuccess_NoFallback(t *testing.T) {
testCases := []struct {
name string
consensusVersion string
endpoint string
block *ethpb.GenericSignedBeaconBlock
}{
{
name: "phase0",
consensusVersion: "phase0",
endpoint: "/eth/v2/beacon/blocks",
block: &ethpb.GenericSignedBeaconBlock{
Block: generateSignedPhase0Block(),
},
},
{
name: "altair",
consensusVersion: "altair",
endpoint: "/eth/v2/beacon/blocks",
block: &ethpb.GenericSignedBeaconBlock{
Block: generateSignedAltairBlock(),
},
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
ctx := t.Context()
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
// Expect PostSSZ to be called and succeed
headers := map[string]string{
"Eth-Consensus-Version": testCase.consensusVersion,
}
jsonRestHandler.EXPECT().PostSSZ(
gomock.Any(),
testCase.endpoint,
headers,
gomock.Any(),
).Return(
nil, nil, nil,
).Times(1)
// Post should NOT be called when PostSSZ succeeds
jsonRestHandler.EXPECT().Post(
gomock.Any(),
gomock.Any(),
gomock.Any(),
gomock.Any(),
gomock.Any(),
).Times(0)
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
_, err := validatorClient.proposeBeaconBlock(ctx, testCase.block)
assert.NoError(t, err)
})
}
}
func TestProposeBeaconBlock_NewerTypes_SSZMarshal(t *testing.T) {
t.Run("deneb", func(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
var blockContents structs.SignedBeaconBlockContentsDeneb
err := json.Unmarshal([]byte(rpctesting.DenebBlockContents), &blockContents)
require.NoError(t, err)
genericSignedBlock, err := blockContents.ToGeneric()
require.NoError(t, err)
denebBytes, err := genericSignedBlock.GetDeneb().MarshalSSZ()
require.NoError(t, err)
jsonRestHandler.EXPECT().PostSSZ(
gomock.Any(),
"/eth/v2/beacon/blocks",
gomock.Any(),
bytes.NewBuffer(denebBytes),
)
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
proposeResponse, err := validatorClient.proposeBeaconBlock(t.Context(), genericSignedBlock)
assert.NoError(t, err)
require.NotNil(t, proposeResponse)
expectedBlockRoot, err := genericSignedBlock.GetDeneb().Block.HashTreeRoot()
require.NoError(t, err)
assert.DeepEqual(t, expectedBlockRoot[:], proposeResponse.BlockRoot)
})
t.Run("blinded_deneb", func(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
var blindedBlock structs.SignedBlindedBeaconBlockDeneb
err := json.Unmarshal([]byte(rpctesting.BlindedDenebBlock), &blindedBlock)
require.NoError(t, err)
genericSignedBlock, err := blindedBlock.ToGeneric()
require.NoError(t, err)
blindedDenebBytes, err := genericSignedBlock.GetBlindedDeneb().MarshalSSZ()
require.NoError(t, err)
jsonRestHandler.EXPECT().PostSSZ(
gomock.Any(),
"/eth/v2/beacon/blinded_blocks",
gomock.Any(),
bytes.NewBuffer(blindedDenebBytes),
)
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
proposeResponse, err := validatorClient.proposeBeaconBlock(t.Context(), genericSignedBlock)
assert.NoError(t, err)
require.NotNil(t, proposeResponse)
expectedBlockRoot, err := genericSignedBlock.GetBlindedDeneb().HashTreeRoot()
require.NoError(t, err)
assert.DeepEqual(t, expectedBlockRoot[:], proposeResponse.BlockRoot)
})
t.Run("electra", func(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
var blockContents structs.SignedBeaconBlockContentsElectra
err := json.Unmarshal([]byte(rpctesting.ElectraBlockContents), &blockContents)
require.NoError(t, err)
genericSignedBlock, err := blockContents.ToGeneric()
require.NoError(t, err)
electraBytes, err := genericSignedBlock.GetElectra().MarshalSSZ()
require.NoError(t, err)
jsonRestHandler.EXPECT().PostSSZ(
gomock.Any(),
"/eth/v2/beacon/blocks",
gomock.Any(),
bytes.NewBuffer(electraBytes),
)
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
proposeResponse, err := validatorClient.proposeBeaconBlock(t.Context(), genericSignedBlock)
assert.NoError(t, err)
require.NotNil(t, proposeResponse)
expectedBlockRoot, err := genericSignedBlock.GetElectra().Block.HashTreeRoot()
require.NoError(t, err)
assert.DeepEqual(t, expectedBlockRoot[:], proposeResponse.BlockRoot)
})
t.Run("blinded_electra", func(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
var blindedBlock structs.SignedBlindedBeaconBlockElectra
err := json.Unmarshal([]byte(rpctesting.BlindedElectraBlock), &blindedBlock)
require.NoError(t, err)
genericSignedBlock, err := blindedBlock.ToGeneric()
require.NoError(t, err)
blindedElectraBytes, err := genericSignedBlock.GetBlindedElectra().MarshalSSZ()
require.NoError(t, err)
jsonRestHandler.EXPECT().PostSSZ(
gomock.Any(),
"/eth/v2/beacon/blinded_blocks",
gomock.Any(),
bytes.NewBuffer(blindedElectraBytes),
)
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
proposeResponse, err := validatorClient.proposeBeaconBlock(t.Context(), genericSignedBlock)
assert.NoError(t, err)
require.NotNil(t, proposeResponse)
expectedBlockRoot, err := genericSignedBlock.GetBlindedElectra().HashTreeRoot()
require.NoError(t, err)
assert.DeepEqual(t, expectedBlockRoot[:], proposeResponse.BlockRoot)
})
t.Run("fulu", func(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
var blockContents structs.SignedBeaconBlockContentsFulu
err := json.Unmarshal([]byte(rpctesting.FuluBlockContents), &blockContents)
require.NoError(t, err)
genericSignedBlock, err := blockContents.ToGeneric()
require.NoError(t, err)
fuluBytes, err := genericSignedBlock.GetFulu().MarshalSSZ()
require.NoError(t, err)
jsonRestHandler.EXPECT().PostSSZ(
gomock.Any(),
"/eth/v2/beacon/blocks",
gomock.Any(),
bytes.NewBuffer(fuluBytes),
)
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
proposeResponse, err := validatorClient.proposeBeaconBlock(t.Context(), genericSignedBlock)
assert.NoError(t, err)
require.NotNil(t, proposeResponse)
expectedBlockRoot, err := genericSignedBlock.GetFulu().Block.HashTreeRoot()
require.NoError(t, err)
assert.DeepEqual(t, expectedBlockRoot[:], proposeResponse.BlockRoot)
})
t.Run("blinded_fulu", func(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
var blindedBlock structs.SignedBlindedBeaconBlockFulu
err := json.Unmarshal([]byte(rpctesting.BlindedFuluBlock), &blindedBlock)
require.NoError(t, err)
genericSignedBlock, err := blindedBlock.ToGeneric()
require.NoError(t, err)
blindedFuluBytes, err := genericSignedBlock.GetBlindedFulu().MarshalSSZ()
require.NoError(t, err)
jsonRestHandler.EXPECT().PostSSZ(
gomock.Any(),
"/eth/v2/beacon/blinded_blocks",
gomock.Any(),
bytes.NewBuffer(blindedFuluBytes),
)
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
proposeResponse, err := validatorClient.proposeBeaconBlock(t.Context(), genericSignedBlock)
assert.NoError(t, err)
require.NotNil(t, proposeResponse)
expectedBlockRoot, err := genericSignedBlock.GetBlindedFulu().HashTreeRoot()
require.NoError(t, err)
assert.DeepEqual(t, expectedBlockRoot[:], proposeResponse.BlockRoot)
})
}
// Generator functions for test blocks
func generateSignedPhase0Block() *ethpb.GenericSignedBeaconBlock_Phase0 {
return &ethpb.GenericSignedBeaconBlock_Phase0{
Phase0: &ethpb.SignedBeaconBlock{
Block: testhelpers.GenerateProtoPhase0BeaconBlock(),
Signature: testhelpers.FillByteSlice(96, 110),
},
}
}
func generateSignedAltairBlock() *ethpb.GenericSignedBeaconBlock_Altair {
return &ethpb.GenericSignedBeaconBlock_Altair{
Altair: &ethpb.SignedBeaconBlockAltair{
Block: testhelpers.GenerateProtoAltairBeaconBlock(),
Signature: testhelpers.FillByteSlice(96, 112),
},
}
}
func generateSignedBellatrixBlock() *ethpb.GenericSignedBeaconBlock_Bellatrix {
return &ethpb.GenericSignedBeaconBlock_Bellatrix{
Bellatrix: &ethpb.SignedBeaconBlockBellatrix{
Block: testhelpers.GenerateProtoBellatrixBeaconBlock(),
Signature: testhelpers.FillByteSlice(96, 127),
},
}
}
func generateSignedBlindedBellatrixBlock() *ethpb.GenericSignedBeaconBlock_BlindedBellatrix {
return &ethpb.GenericSignedBeaconBlock_BlindedBellatrix{
BlindedBellatrix: &ethpb.SignedBlindedBeaconBlockBellatrix{
Block: &ethpb.BlindedBeaconBlockBellatrix{
Slot: 1,
ProposerIndex: 2,
ParentRoot: testhelpers.FillByteSlice(32, 3),
StateRoot: testhelpers.FillByteSlice(32, 4),
Body: &ethpb.BlindedBeaconBlockBodyBellatrix{
RandaoReveal: testhelpers.FillByteSlice(96, 5),
Eth1Data: &ethpb.Eth1Data{
DepositRoot: testhelpers.FillByteSlice(32, 6),
DepositCount: 7,
BlockHash: testhelpers.FillByteSlice(32, 8),
},
Graffiti: testhelpers.FillByteSlice(32, 9),
ProposerSlashings: []*ethpb.ProposerSlashing{
{
Header_1: &ethpb.SignedBeaconBlockHeader{
Header: &ethpb.BeaconBlockHeader{
Slot: 10,
ProposerIndex: 11,
ParentRoot: testhelpers.FillByteSlice(32, 12),
StateRoot: testhelpers.FillByteSlice(32, 13),
BodyRoot: testhelpers.FillByteSlice(32, 14),
},
Signature: testhelpers.FillByteSlice(96, 15),
},
Header_2: &ethpb.SignedBeaconBlockHeader{
Header: &ethpb.BeaconBlockHeader{
Slot: 16,
ProposerIndex: 17,
ParentRoot: testhelpers.FillByteSlice(32, 18),
StateRoot: testhelpers.FillByteSlice(32, 19),
BodyRoot: testhelpers.FillByteSlice(32, 20),
},
Signature: testhelpers.FillByteSlice(96, 21),
},
},
},
AttesterSlashings: []*ethpb.AttesterSlashing{},
Attestations: []*ethpb.Attestation{},
Deposits: []*ethpb.Deposit{},
VoluntaryExits: []*ethpb.SignedVoluntaryExit{},
SyncAggregate: &ethpb.SyncAggregate{
SyncCommitteeBits: testhelpers.FillByteSlice(64, 100),
SyncCommitteeSignature: testhelpers.FillByteSlice(96, 101),
},
ExecutionPayloadHeader: &engine.ExecutionPayloadHeader{
ParentHash: testhelpers.FillByteSlice(32, 102),
FeeRecipient: testhelpers.FillByteSlice(20, 103),
StateRoot: testhelpers.FillByteSlice(32, 104),
ReceiptsRoot: testhelpers.FillByteSlice(32, 105),
LogsBloom: testhelpers.FillByteSlice(256, 106),
PrevRandao: testhelpers.FillByteSlice(32, 107),
BlockNumber: 108,
GasLimit: 109,
GasUsed: 110,
Timestamp: 111,
ExtraData: testhelpers.FillByteSlice(32, 112),
BaseFeePerGas: testhelpers.FillByteSlice(32, 113),
BlockHash: testhelpers.FillByteSlice(32, 114),
TransactionsRoot: testhelpers.FillByteSlice(32, 115),
},
},
},
Signature: testhelpers.FillByteSlice(96, 116),
},
}
}
func generateSignedCapellaBlock() *ethpb.GenericSignedBeaconBlock_Capella {
return &ethpb.GenericSignedBeaconBlock_Capella{
Capella: &ethpb.SignedBeaconBlockCapella{
Block: testhelpers.GenerateProtoCapellaBeaconBlock(),
Signature: testhelpers.FillByteSlice(96, 127),
},
}
}
func generateSignedBlindedCapellaBlock() *ethpb.GenericSignedBeaconBlock_BlindedCapella {
return &ethpb.GenericSignedBeaconBlock_BlindedCapella{
BlindedCapella: &ethpb.SignedBlindedBeaconBlockCapella{
Block: &ethpb.BlindedBeaconBlockCapella{
Slot: 1,
ProposerIndex: 2,
ParentRoot: testhelpers.FillByteSlice(32, 3),
StateRoot: testhelpers.FillByteSlice(32, 4),
Body: &ethpb.BlindedBeaconBlockBodyCapella{
RandaoReveal: testhelpers.FillByteSlice(96, 5),
Eth1Data: &ethpb.Eth1Data{
DepositRoot: testhelpers.FillByteSlice(32, 6),
DepositCount: 7,
BlockHash: testhelpers.FillByteSlice(32, 8),
},
Graffiti: testhelpers.FillByteSlice(32, 9),
ProposerSlashings: []*ethpb.ProposerSlashing{
{
Header_1: &ethpb.SignedBeaconBlockHeader{
Header: &ethpb.BeaconBlockHeader{
Slot: 10,
ProposerIndex: 11,
ParentRoot: testhelpers.FillByteSlice(32, 12),
StateRoot: testhelpers.FillByteSlice(32, 13),
BodyRoot: testhelpers.FillByteSlice(32, 14),
},
Signature: testhelpers.FillByteSlice(96, 15),
},
Header_2: &ethpb.SignedBeaconBlockHeader{
Header: &ethpb.BeaconBlockHeader{
Slot: 16,
ProposerIndex: 17,
ParentRoot: testhelpers.FillByteSlice(32, 18),
StateRoot: testhelpers.FillByteSlice(32, 19),
BodyRoot: testhelpers.FillByteSlice(32, 20),
},
Signature: testhelpers.FillByteSlice(96, 21),
},
},
},
AttesterSlashings: []*ethpb.AttesterSlashing{},
Attestations: []*ethpb.Attestation{},
Deposits: []*ethpb.Deposit{},
VoluntaryExits: []*ethpb.SignedVoluntaryExit{},
SyncAggregate: &ethpb.SyncAggregate{
SyncCommitteeBits: testhelpers.FillByteSlice(64, 37),
SyncCommitteeSignature: testhelpers.FillByteSlice(96, 38),
},
ExecutionPayloadHeader: &engine.ExecutionPayloadHeaderCapella{
ParentHash: testhelpers.FillByteSlice(32, 39),
FeeRecipient: testhelpers.FillByteSlice(20, 40),
StateRoot: testhelpers.FillByteSlice(32, 41),
ReceiptsRoot: testhelpers.FillByteSlice(32, 42),
LogsBloom: testhelpers.FillByteSlice(256, 43),
PrevRandao: testhelpers.FillByteSlice(32, 44),
BlockNumber: 45,
GasLimit: 46,
GasUsed: 47,
Timestamp: 48,
ExtraData: testhelpers.FillByteSlice(32, 49),
BaseFeePerGas: testhelpers.FillByteSlice(32, 50),
BlockHash: testhelpers.FillByteSlice(32, 51),
TransactionsRoot: testhelpers.FillByteSlice(32, 52),
WithdrawalsRoot: testhelpers.FillByteSlice(32, 53),
},
BlsToExecutionChanges: []*ethpb.SignedBLSToExecutionChange{},
},
},
Signature: testhelpers.FillByteSlice(96, 54),
},
}
}
func TestProposeBeaconBlock_SSZFails_406_FallbackToJSON(t *testing.T) {
testCases := []struct {
name string
consensusVersion string
endpoint string
block *ethpb.GenericSignedBeaconBlock
}{
{
name: "phase0",
consensusVersion: "phase0",
endpoint: "/eth/v2/beacon/blocks",
block: &ethpb.GenericSignedBeaconBlock{
Block: generateSignedPhase0Block(),
},
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
ctx := t.Context()
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
// Expect PostSSZ to be called first and fail
jsonRestHandler.EXPECT().PostSSZ(
gomock.Any(),
testCase.endpoint,
gomock.Any(),
gomock.Any(),
).Return(
nil, nil, &httputil.DefaultJsonError{
Code: http.StatusNotAcceptable,
Message: "SSZ not supported",
},
).Times(1)
jsonRestHandler.EXPECT().Post(
gomock.Any(),
testCase.endpoint,
gomock.Any(),
gomock.Any(),
nil,
).Return(
nil,
).Times(1)
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
_, err := validatorClient.proposeBeaconBlock(ctx, testCase.block)
assert.NoError(t, err)
})
}
}
func TestProposeBeaconBlock_SSZFails_Non406_NoFallback(t *testing.T) {
testCases := []struct {
name string
consensusVersion string
endpoint string
block *ethpb.GenericSignedBeaconBlock
}{
{
name: "phase0",
consensusVersion: "phase0",
endpoint: "/eth/v2/beacon/blocks",
block: &ethpb.GenericSignedBeaconBlock{
Block: generateSignedPhase0Block(),
},
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()
ctx := t.Context()
jsonRestHandler := mock.NewMockJsonRestHandler(ctrl)
// Expect PostSSZ to be called first and fail with non-406 error
sszHeaders := map[string]string{
"Eth-Consensus-Version": testCase.consensusVersion,
}
jsonRestHandler.EXPECT().PostSSZ(
gomock.Any(),
testCase.endpoint,
sszHeaders,
gomock.Any(),
).Return(
nil, nil, &httputil.DefaultJsonError{
Code: http.StatusInternalServerError,
Message: "Internal server error",
},
).Times(1)
// Post should NOT be called for non-406 errors
jsonRestHandler.EXPECT().Post(
gomock.Any(),
gomock.Any(),
gomock.Any(),
gomock.Any(),
gomock.Any(),
).Times(0)
validatorClient := &beaconApiValidatorClient{jsonRestHandler: jsonRestHandler}
_, err := validatorClient.proposeBeaconBlock(ctx, testCase.block)
require.ErrorContains(t, "Internal server error", err)
})
}
}

View File

@@ -25,6 +25,7 @@ type RestHandler interface {
Get(ctx context.Context, endpoint string, resp interface{}) error
GetSSZ(ctx context.Context, endpoint string) ([]byte, http.Header, error)
Post(ctx context.Context, endpoint string, headers map[string]string, data *bytes.Buffer, resp interface{}) error
PostSSZ(ctx context.Context, endpoint string, headers map[string]string, data *bytes.Buffer) ([]byte, http.Header, error)
HttpClient() *http.Client
Host() string
SetHost(host string)
@@ -179,6 +180,75 @@ func (c *BeaconApiRestHandler) Post(
return decodeResp(httpResp, resp)
}
// PostSSZ sends a POST request and prefers an SSZ (application/octet-stream) response body.
func (c *BeaconApiRestHandler) PostSSZ(
ctx context.Context,
apiEndpoint string,
headers map[string]string,
data *bytes.Buffer,
) ([]byte, http.Header, error) {
if data == nil {
return nil, nil, errors.New("data is nil")
}
url := c.host + apiEndpoint
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, data)
if err != nil {
return nil, nil, errors.Wrapf(err, "failed to create request for endpoint %s", url)
}
// Accept header: prefer octet-stream (SSZ), fall back to JSON
primaryAcceptType := fmt.Sprintf("%s;q=%s", api.OctetStreamMediaType, "0.95")
secondaryAcceptType := fmt.Sprintf("%s;q=%s", api.JsonMediaType, "0.9")
acceptHeaderString := fmt.Sprintf("%s,%s", primaryAcceptType, secondaryAcceptType)
req.Header.Set("Accept", acceptHeaderString)
// User-supplied headers
for headerKey, headerValue := range headers {
req.Header.Set(headerKey, headerValue)
}
for _, o := range c.reqOverrides {
o(req)
}
req.Header.Set("Content-Type", api.OctetStreamMediaType)
req.Header.Set("User-Agent", version.BuildData())
httpResp, err := c.client.Do(req)
if err != nil {
return nil, nil, errors.Wrapf(err, "failed to perform request for endpoint %s", url)
}
defer func() {
if err := httpResp.Body.Close(); err != nil {
return
}
}()
accept := req.Header.Get("Accept")
contentType := httpResp.Header.Get("Content-Type")
body, err := io.ReadAll(httpResp.Body)
if err != nil {
return nil, nil, errors.Wrapf(err, "failed to read response body for %s", httpResp.Request.URL)
}
if !apiutil.PrimaryAcceptMatches(accept, contentType) {
log.WithFields(logrus.Fields{
"Accept": accept,
"Content-Type": contentType,
}).Debug("Server responded with non primary accept type")
}
// non-2XX codes are a failure
if !strings.HasPrefix(httpResp.Status, "2") {
decoder := json.NewDecoder(bytes.NewBuffer(body))
errorJson := &httputil.DefaultJsonError{}
if err = decoder.Decode(errorJson); err != nil {
return nil, nil, errors.Wrapf(err, "failed to decode response body into error json for %s", httpResp.Request.URL)
}
return nil, nil, errorJson
}
return body, httpResp.Header, nil
}
func decodeResp(httpResp *http.Response, resp interface{}) error {
body, err := io.ReadAll(httpResp.Body)
if err != nil {