mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-02-02 17:15:21 -05:00
Compare commits
6 Commits
gloas-ptc-
...
synced-new
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6fa5515cea | ||
|
|
981ea8ef3b | ||
|
|
5b513fe3be | ||
|
|
571c6f39aa | ||
|
|
55fe85c887 | ||
|
|
31f77567dd |
@@ -540,12 +540,6 @@ type PayloadAttestation struct {
|
||||
Signature string `json:"signature"`
|
||||
}
|
||||
|
||||
type PayloadAttestationMessage struct {
|
||||
ValidatorIndex string `json:"validator_index"`
|
||||
Data *PayloadAttestationData `json:"data"`
|
||||
Signature string `json:"signature"`
|
||||
}
|
||||
|
||||
type BeaconBlockBodyGloas struct {
|
||||
RandaoReveal string `json:"randao_reveal"`
|
||||
Eth1Data *Eth1Data `json:"eth1_data"`
|
||||
|
||||
@@ -3263,34 +3263,3 @@ func (d *PayloadAttestationData) ToConsensus() (*eth.PayloadAttestationData, err
|
||||
BlobDataAvailable: d.BlobDataAvailable,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func PayloadAttestationMessageFromConsensus(msg *eth.PayloadAttestationMessage) *PayloadAttestationMessage {
|
||||
return &PayloadAttestationMessage{
|
||||
ValidatorIndex: fmt.Sprintf("%d", msg.ValidatorIndex),
|
||||
Data: PayloadAttestationDataFromConsensus(msg.Data),
|
||||
Signature: hexutil.Encode(msg.Signature),
|
||||
}
|
||||
}
|
||||
|
||||
func (p *PayloadAttestationMessage) ToConsensus() (*eth.PayloadAttestationMessage, error) {
|
||||
if p == nil {
|
||||
return nil, errNilValue
|
||||
}
|
||||
validatorIndex, err := strconv.ParseUint(p.ValidatorIndex, 10, 64)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "ValidatorIndex")
|
||||
}
|
||||
data, err := p.Data.ToConsensus()
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "Data")
|
||||
}
|
||||
sig, err := bytesutil.DecodeHexWithLength(p.Signature, fieldparams.BLSSignatureLength)
|
||||
if err != nil {
|
||||
return nil, server.NewDecodeError(err, "Signature")
|
||||
}
|
||||
return ð.PayloadAttestationMessage{
|
||||
ValidatorIndex: primitives.ValidatorIndex(validatorIndex),
|
||||
Data: data,
|
||||
Signature: sig,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -188,11 +188,6 @@ type BLSToExecutionChangesPoolResponse struct {
|
||||
Data []*SignedBLSToExecutionChange `json:"data"`
|
||||
}
|
||||
|
||||
type GetPoolPayloadAttestationsResponse struct {
|
||||
Version string `json:"version"`
|
||||
Data []*PayloadAttestation `json:"data"`
|
||||
}
|
||||
|
||||
type GetAttesterSlashingsResponse struct {
|
||||
Version string `json:"version,omitempty"`
|
||||
Data json.RawMessage `json:"data"` // Accepts both `[]*AttesterSlashing` and `[]*AttesterSlashingElectra` types
|
||||
|
||||
@@ -31,11 +31,6 @@ type GetAttestationDataResponse struct {
|
||||
Data *AttestationData `json:"data"`
|
||||
}
|
||||
|
||||
type GetPayloadAttestationDataResponse struct {
|
||||
Version string `json:"version"`
|
||||
Data *PayloadAttestationData `json:"data"`
|
||||
}
|
||||
|
||||
type ProduceSyncCommitteeContributionResponse struct {
|
||||
Data *SyncCommitteeContribution `json:"data"`
|
||||
}
|
||||
|
||||
@@ -87,36 +87,32 @@ func logStateTransitionData(b interfaces.ReadOnlyBeaconBlock) error {
|
||||
func logBlockSyncStatus(block interfaces.ReadOnlyBeaconBlock, blockRoot [32]byte, justified, finalized *ethpb.Checkpoint, receivedTime time.Time, genesis time.Time, daWaitedTime time.Duration) error {
|
||||
startTime, err := slots.StartTime(genesis, block.Slot())
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("start time: %w", err)
|
||||
}
|
||||
level := log.Logger.GetLevel()
|
||||
if level >= logrus.DebugLevel {
|
||||
|
||||
log := log.WithFields(logrus.Fields{
|
||||
"slot": block.Slot(),
|
||||
"block": fmt.Sprintf("0x%s...", hex.EncodeToString(blockRoot[:])[:8]),
|
||||
"finalizedEpoch": finalized.Epoch,
|
||||
"finalizedRoot": fmt.Sprintf("0x%s...", hex.EncodeToString(finalized.Root)[:8]),
|
||||
"epoch": slots.ToEpoch(block.Slot()),
|
||||
"slotInEpoch": block.Slot() % params.BeaconConfig().SlotsPerEpoch,
|
||||
"sinceSlotStartTime": prysmTime.Now().Sub(startTime),
|
||||
})
|
||||
|
||||
if log.Logger.GetLevel() >= logrus.DebugLevel {
|
||||
parentRoot := block.ParentRoot()
|
||||
lf := logrus.Fields{
|
||||
"slot": block.Slot(),
|
||||
"slotInEpoch": block.Slot() % params.BeaconConfig().SlotsPerEpoch,
|
||||
"block": fmt.Sprintf("0x%s...", hex.EncodeToString(blockRoot[:])[:8]),
|
||||
"epoch": slots.ToEpoch(block.Slot()),
|
||||
log = log.WithFields(logrus.Fields{
|
||||
"justifiedEpoch": justified.Epoch,
|
||||
"justifiedRoot": fmt.Sprintf("0x%s...", hex.EncodeToString(justified.Root)[:8]),
|
||||
"finalizedEpoch": finalized.Epoch,
|
||||
"finalizedRoot": fmt.Sprintf("0x%s...", hex.EncodeToString(finalized.Root)[:8]),
|
||||
"parentRoot": fmt.Sprintf("0x%s...", hex.EncodeToString(parentRoot[:])[:8]),
|
||||
"version": version.String(block.Version()),
|
||||
"sinceSlotStartTime": prysmTime.Now().Sub(startTime),
|
||||
"chainServiceProcessedTime": prysmTime.Now().Sub(receivedTime) - daWaitedTime,
|
||||
"dataAvailabilityWaitedTime": daWaitedTime,
|
||||
}
|
||||
log.WithFields(lf).Debug("Synced new block")
|
||||
} else {
|
||||
log.WithFields(logrus.Fields{
|
||||
"slot": block.Slot(),
|
||||
"block": fmt.Sprintf("0x%s...", hex.EncodeToString(blockRoot[:])[:8]),
|
||||
"finalizedEpoch": finalized.Epoch,
|
||||
"finalizedRoot": fmt.Sprintf("0x%s...", hex.EncodeToString(finalized.Root)[:8]),
|
||||
"epoch": slots.ToEpoch(block.Slot()),
|
||||
}).Info("Synced new block")
|
||||
})
|
||||
}
|
||||
|
||||
log.Info("Synced new block")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,6 @@ go_library(
|
||||
"//beacon-chain/node/registration:go_default_library",
|
||||
"//beacon-chain/operations/attestations:go_default_library",
|
||||
"//beacon-chain/operations/blstoexec:go_default_library",
|
||||
"//beacon-chain/operations/payloadattestation:go_default_library",
|
||||
"//beacon-chain/operations/slashings:go_default_library",
|
||||
"//beacon-chain/operations/synccommittee:go_default_library",
|
||||
"//beacon-chain/operations/voluntaryexits:go_default_library",
|
||||
|
||||
@@ -40,7 +40,6 @@ import (
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/node/registration"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/operations/attestations"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/operations/blstoexec"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/operations/payloadattestation"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/operations/slashings"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/operations/synccommittee"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/operations/voluntaryexits"
|
||||
@@ -102,7 +101,6 @@ type BeaconNode struct {
|
||||
slashingsPool slashings.PoolManager
|
||||
syncCommitteePool synccommittee.Pool
|
||||
blsToExecPool blstoexec.PoolManager
|
||||
payloadAttestationPool payloadattestation.PoolManager
|
||||
depositCache cache.DepositCache
|
||||
trackedValidatorsCache *cache.TrackedValidatorsCache
|
||||
payloadIDCache *cache.PayloadIDCache
|
||||
@@ -143,21 +141,20 @@ func New(cliCtx *cli.Context, cancel context.CancelFunc, opts ...Option) (*Beaco
|
||||
ctx := cliCtx.Context
|
||||
|
||||
beacon := &BeaconNode{
|
||||
cliCtx: cliCtx,
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
services: runtime.NewServiceRegistry(),
|
||||
stop: make(chan struct{}),
|
||||
stateFeed: new(event.Feed),
|
||||
blockFeed: new(event.Feed),
|
||||
opFeed: new(event.Feed),
|
||||
attestationCache: cache.NewAttestationCache(),
|
||||
attestationPool: attestations.NewPool(),
|
||||
exitPool: voluntaryexits.NewPool(),
|
||||
slashingsPool: slashings.NewPool(),
|
||||
syncCommitteePool: synccommittee.NewPool(),
|
||||
blsToExecPool: blstoexec.NewPool(),
|
||||
// TODO payloadAttestationPool: set once a PoolManager implementation exists.
|
||||
cliCtx: cliCtx,
|
||||
ctx: ctx,
|
||||
cancel: cancel,
|
||||
services: runtime.NewServiceRegistry(),
|
||||
stop: make(chan struct{}),
|
||||
stateFeed: new(event.Feed),
|
||||
blockFeed: new(event.Feed),
|
||||
opFeed: new(event.Feed),
|
||||
attestationCache: cache.NewAttestationCache(),
|
||||
attestationPool: attestations.NewPool(),
|
||||
exitPool: voluntaryexits.NewPool(),
|
||||
slashingsPool: slashings.NewPool(),
|
||||
syncCommitteePool: synccommittee.NewPool(),
|
||||
blsToExecPool: blstoexec.NewPool(),
|
||||
trackedValidatorsCache: cache.NewTrackedValidatorsCache(),
|
||||
payloadIDCache: cache.NewPayloadIDCache(),
|
||||
slasherBlockHeadersFeed: new(event.Feed),
|
||||
@@ -975,7 +972,6 @@ func (b *BeaconNode) registerRPCService(router *http.ServeMux) error {
|
||||
SlashingsPool: b.slashingsPool,
|
||||
BLSChangesPool: b.blsToExecPool,
|
||||
SyncCommitteeObjectPool: b.syncCommitteePool,
|
||||
PayloadAttestationPool: b.payloadAttestationPool,
|
||||
ExecutionChainService: web3Service,
|
||||
ExecutionChainInfoFetcher: web3Service,
|
||||
ChainStartFetcher: chainStartFetcher,
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
load("@prysm//tools/go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["pool.go"],
|
||||
importpath = "github.com/OffchainLabs/prysm/v7/beacon-chain/operations/payloadattestation",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//consensus-types/primitives:go_default_library",
|
||||
"//proto/prysm/v1alpha1:go_default_library",
|
||||
],
|
||||
)
|
||||
@@ -1,9 +0,0 @@
|
||||
load("@prysm//tools/go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["mock.go"],
|
||||
importpath = "github.com/OffchainLabs/prysm/v7/beacon-chain/operations/payloadattestation/mock",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = ["//proto/prysm/v1alpha1:go_default_library"],
|
||||
)
|
||||
@@ -1,28 +0,0 @@
|
||||
package mock
|
||||
|
||||
import (
|
||||
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
|
||||
)
|
||||
|
||||
// PoolMock is a fake implementation of PoolManager.
|
||||
type PoolMock struct {
|
||||
Attestations []*ethpb.PayloadAttestation
|
||||
}
|
||||
|
||||
// PendingPayloadAttestations --
|
||||
func (m *PoolMock) PendingPayloadAttestations() []*ethpb.PayloadAttestation {
|
||||
return m.Attestations
|
||||
}
|
||||
|
||||
// InsertPayloadAttestation --
|
||||
func (m *PoolMock) InsertPayloadAttestation(msg *ethpb.PayloadAttestationMessage) error {
|
||||
m.Attestations = append(m.Attestations, ðpb.PayloadAttestation{
|
||||
Data: msg.Data,
|
||||
Signature: msg.Signature,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarkIncluded --
|
||||
func (*PoolMock) MarkIncluded(_ *ethpb.PayloadAttestation) {
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package payloadattestation
|
||||
|
||||
import (
|
||||
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
|
||||
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
|
||||
)
|
||||
|
||||
// PoolManager maintains pending payload attestations.
|
||||
// This pool is used by proposers to insert payload attestations into new blocks.
|
||||
type PoolManager interface {
|
||||
PendingPayloadAttestations() []*ethpb.PayloadAttestation
|
||||
InsertPayloadAttestation(msg *ethpb.PayloadAttestationMessage) error
|
||||
MarkIncluded(att *ethpb.PayloadAttestation)
|
||||
}
|
||||
|
||||
// PayloadStatusFetcher determines the payload presence and blob data availability
|
||||
// for a given slot. This is used by PTC validators to produce PayloadAttestationData.
|
||||
type PayloadStatusFetcher interface {
|
||||
PayloadStatus(slot primitives.Slot) (payloadPresent bool, blobDataAvailable bool, err error)
|
||||
}
|
||||
@@ -26,7 +26,6 @@ go_library(
|
||||
"//beacon-chain/light-client:go_default_library",
|
||||
"//beacon-chain/operations/attestations:go_default_library",
|
||||
"//beacon-chain/operations/blstoexec:go_default_library",
|
||||
"//beacon-chain/operations/payloadattestation:go_default_library",
|
||||
"//beacon-chain/operations/slashings:go_default_library",
|
||||
"//beacon-chain/operations/synccommittee:go_default_library",
|
||||
"//beacon-chain/operations/voluntaryexits:go_default_library",
|
||||
|
||||
@@ -217,7 +217,6 @@ func (s *Service) validatorEndpoints(
|
||||
OperationNotifier: s.cfg.OperationNotifier,
|
||||
TrackedValidatorsCache: s.cfg.TrackedValidatorsCache,
|
||||
PayloadIDCache: s.cfg.PayloadIDCache,
|
||||
PayloadAttestationPool: s.cfg.PayloadAttestationPool,
|
||||
CoreService: coreService,
|
||||
BlockRewardFetcher: rewardFetcher,
|
||||
}
|
||||
@@ -391,16 +390,6 @@ func (s *Service) validatorEndpoints(
|
||||
handler: server.SyncCommitteeSelections,
|
||||
methods: []string{http.MethodPost},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/validator/payload_attestation_data/{slot}",
|
||||
name: namespace + ".GetPayloadAttestationData",
|
||||
middleware: []middleware.Middleware{
|
||||
middleware.AcceptHeaderHandler([]string{api.JsonMediaType, api.OctetStreamMediaType}),
|
||||
middleware.AcceptEncodingHeaderHandler(),
|
||||
},
|
||||
handler: server.GetPayloadAttestationData,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -523,7 +512,6 @@ func (s *Service) beaconEndpoints(
|
||||
SyncChecker: s.cfg.SyncService,
|
||||
ExecutionReconstructor: s.cfg.ExecutionReconstructor,
|
||||
BLSChangesPool: s.cfg.BLSChangesPool,
|
||||
PayloadAttestationPool: s.cfg.PayloadAttestationPool,
|
||||
FinalizationFetcher: s.cfg.FinalizationFetcher,
|
||||
ForkchoiceFetcher: s.cfg.ForkchoiceFetcher,
|
||||
CoreService: coreService,
|
||||
@@ -881,27 +869,6 @@ func (s *Service) beaconEndpoints(
|
||||
handler: server.GetProposerLookahead,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/beacon/pool/payload_attestations",
|
||||
name: namespace + ".ListPayloadAttestations",
|
||||
middleware: []middleware.Middleware{
|
||||
middleware.AcceptHeaderHandler([]string{api.JsonMediaType}),
|
||||
middleware.AcceptEncodingHeaderHandler(),
|
||||
},
|
||||
handler: server.ListPayloadAttestations,
|
||||
methods: []string{http.MethodGet},
|
||||
},
|
||||
{
|
||||
template: "/eth/v1/beacon/pool/payload_attestations",
|
||||
name: namespace + ".SubmitPayloadAttestations",
|
||||
middleware: []middleware.Middleware{
|
||||
middleware.ContentTypeHandler([]string{api.JsonMediaType}),
|
||||
middleware.AcceptHeaderHandler([]string{api.JsonMediaType}),
|
||||
middleware.AcceptEncodingHeaderHandler(),
|
||||
},
|
||||
handler: server.SubmitPayloadAttestations,
|
||||
methods: []string{http.MethodPost},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -48,7 +48,6 @@ func Test_endpoints(t *testing.T) {
|
||||
"/eth/v1/beacon/pool/sync_committees": {http.MethodPost},
|
||||
"/eth/v1/beacon/pool/voluntary_exits": {http.MethodGet, http.MethodPost},
|
||||
"/eth/v1/beacon/pool/bls_to_execution_changes": {http.MethodGet, http.MethodPost},
|
||||
"/eth/v1/beacon/pool/payload_attestations": {http.MethodGet, http.MethodPost},
|
||||
"/prysm/v1/beacon/individual_votes": {http.MethodPost},
|
||||
}
|
||||
|
||||
@@ -92,23 +91,22 @@ func Test_endpoints(t *testing.T) {
|
||||
}
|
||||
|
||||
validatorRoutes := map[string][]string{
|
||||
"/eth/v1/validator/duties/attester/{epoch}": {http.MethodPost},
|
||||
"/eth/v1/validator/duties/proposer/{epoch}": {http.MethodGet},
|
||||
"/eth/v1/validator/duties/sync/{epoch}": {http.MethodPost},
|
||||
"/eth/v3/validator/blocks/{slot}": {http.MethodGet},
|
||||
"/eth/v1/validator/attestation_data": {http.MethodGet},
|
||||
"/eth/v2/validator/aggregate_attestation": {http.MethodGet},
|
||||
"/eth/v2/validator/aggregate_and_proofs": {http.MethodPost},
|
||||
"/eth/v1/validator/beacon_committee_subscriptions": {http.MethodPost},
|
||||
"/eth/v1/validator/sync_committee_subscriptions": {http.MethodPost},
|
||||
"/eth/v1/validator/beacon_committee_selections": {http.MethodPost},
|
||||
"/eth/v1/validator/sync_committee_selections": {http.MethodPost},
|
||||
"/eth/v1/validator/sync_committee_contribution": {http.MethodGet},
|
||||
"/eth/v1/validator/contribution_and_proofs": {http.MethodPost},
|
||||
"/eth/v1/validator/prepare_beacon_proposer": {http.MethodPost},
|
||||
"/eth/v1/validator/register_validator": {http.MethodPost},
|
||||
"/eth/v1/validator/liveness/{epoch}": {http.MethodPost},
|
||||
"/eth/v1/validator/payload_attestation_data/{slot}": {http.MethodGet},
|
||||
"/eth/v1/validator/duties/attester/{epoch}": {http.MethodPost},
|
||||
"/eth/v1/validator/duties/proposer/{epoch}": {http.MethodGet},
|
||||
"/eth/v1/validator/duties/sync/{epoch}": {http.MethodPost},
|
||||
"/eth/v3/validator/blocks/{slot}": {http.MethodGet},
|
||||
"/eth/v1/validator/attestation_data": {http.MethodGet},
|
||||
"/eth/v2/validator/aggregate_attestation": {http.MethodGet},
|
||||
"/eth/v2/validator/aggregate_and_proofs": {http.MethodPost},
|
||||
"/eth/v1/validator/beacon_committee_subscriptions": {http.MethodPost},
|
||||
"/eth/v1/validator/sync_committee_subscriptions": {http.MethodPost},
|
||||
"/eth/v1/validator/beacon_committee_selections": {http.MethodPost},
|
||||
"/eth/v1/validator/sync_committee_selections": {http.MethodPost},
|
||||
"/eth/v1/validator/sync_committee_contribution": {http.MethodGet},
|
||||
"/eth/v1/validator/contribution_and_proofs": {http.MethodPost},
|
||||
"/eth/v1/validator/prepare_beacon_proposer": {http.MethodPost},
|
||||
"/eth/v1/validator/register_validator": {http.MethodPost},
|
||||
"/eth/v1/validator/liveness/{epoch}": {http.MethodPost},
|
||||
}
|
||||
|
||||
prysmBeaconRoutes := map[string][]string{
|
||||
|
||||
@@ -32,7 +32,6 @@ go_library(
|
||||
"//beacon-chain/execution:go_default_library",
|
||||
"//beacon-chain/operations/attestations:go_default_library",
|
||||
"//beacon-chain/operations/blstoexec:go_default_library",
|
||||
"//beacon-chain/operations/payloadattestation:go_default_library",
|
||||
"//beacon-chain/operations/slashings:go_default_library",
|
||||
"//beacon-chain/operations/voluntaryexits:go_default_library",
|
||||
"//beacon-chain/p2p:go_default_library",
|
||||
@@ -95,7 +94,6 @@ go_test(
|
||||
"//beacon-chain/operations/attestations:go_default_library",
|
||||
"//beacon-chain/operations/blstoexec:go_default_library",
|
||||
"//beacon-chain/operations/blstoexec/mock:go_default_library",
|
||||
"//beacon-chain/operations/payloadattestation/mock:go_default_library",
|
||||
"//beacon-chain/operations/slashings/mock:go_default_library",
|
||||
"//beacon-chain/operations/synccommittee:go_default_library",
|
||||
"//beacon-chain/operations/voluntaryexits/mock:go_default_library",
|
||||
|
||||
@@ -893,99 +893,3 @@ func (s *Server) SubmitProposerSlashing(w http.ResponseWriter, r *http.Request)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SubmitPayloadAttestations submits payload attestation messages to the node's pool.
|
||||
func (s *Server) SubmitPayloadAttestations(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, span := trace.StartSpan(r.Context(), "beacon.SubmitPayloadAttestations")
|
||||
defer span.End()
|
||||
|
||||
currentEpoch := slots.ToEpoch(s.TimeFetcher.CurrentSlot())
|
||||
if currentEpoch < params.BeaconConfig().GloasForkEpoch {
|
||||
httputil.HandleError(w, fmt.Sprintf("payload attestations require the Gloas fork, current epoch %d, Gloas epoch %d", currentEpoch, params.BeaconConfig().GloasForkEpoch), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if shared.IsSyncing(ctx, w, s.SyncChecker, s.HeadFetcher, s.TimeFetcher, s.OptimisticModeFetcher) {
|
||||
return
|
||||
}
|
||||
|
||||
versionHeader := r.Header.Get(api.VersionHeader)
|
||||
if versionHeader == "" {
|
||||
httputil.HandleError(w, api.VersionHeader+" header is required", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var msgs []*structs.PayloadAttestationMessage
|
||||
if err := json.NewDecoder(r.Body).Decode(&msgs); err != nil {
|
||||
httputil.HandleError(w, "Could not decode request body: "+err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
var failures []*server.IndexedError
|
||||
for i, msg := range msgs {
|
||||
consensusMsg, err := msg.ToConsensus()
|
||||
if err != nil {
|
||||
failures = append(failures, &server.IndexedError{
|
||||
Index: i,
|
||||
Message: "Could not convert message: " + err.Error(),
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
// TODO: Add full gossip validation (BLS signatures, PTC membership).
|
||||
if err := s.PayloadAttestationPool.InsertPayloadAttestation(consensusMsg); err != nil {
|
||||
failures = append(failures, &server.IndexedError{
|
||||
Index: i,
|
||||
Message: "Could not insert payload attestation: " + err.Error(),
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
if err := s.Broadcaster.Broadcast(ctx, consensusMsg); err != nil {
|
||||
log.WithError(err).Error("Could not broadcast payload attestation message")
|
||||
}
|
||||
}
|
||||
|
||||
if len(failures) > 0 {
|
||||
failuresErr := &server.IndexedErrorContainer{
|
||||
Code: http.StatusBadRequest,
|
||||
Message: server.ErrIndexedValidationFail,
|
||||
Failures: failures,
|
||||
}
|
||||
httputil.WriteError(w, failuresErr)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// ListPayloadAttestations retrieves payload attestations from the pool.
|
||||
func (s *Server) ListPayloadAttestations(w http.ResponseWriter, r *http.Request) {
|
||||
_, span := trace.StartSpan(r.Context(), "beacon.ListPayloadAttestations")
|
||||
defer span.End()
|
||||
|
||||
currentEpoch := slots.ToEpoch(s.TimeFetcher.CurrentSlot())
|
||||
if currentEpoch < params.BeaconConfig().GloasForkEpoch {
|
||||
httputil.HandleError(w, fmt.Sprintf("payload attestations require the Gloas fork, current epoch %d, Gloas epoch %d", currentEpoch, params.BeaconConfig().GloasForkEpoch), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
rawSlot, slot, ok := shared.UintFromQuery(w, r, "slot", false)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
allAtts := s.PayloadAttestationPool.PendingPayloadAttestations()
|
||||
|
||||
var data []*structs.PayloadAttestation
|
||||
for _, att := range allAtts {
|
||||
if rawSlot != "" && att.Data.Slot != primitives.Slot(slot) {
|
||||
continue
|
||||
}
|
||||
data = append(data, structs.PayloadAttestationFromConsensus(att))
|
||||
}
|
||||
|
||||
w.Header().Set(api.VersionHeader, version.String(version.Gloas))
|
||||
httputil.WriteJson(w, &structs.GetPoolPayloadAttestationsResponse{
|
||||
Version: version.String(version.Gloas),
|
||||
Data: data,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -21,14 +21,13 @@ import (
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/operations/attestations"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/operations/blstoexec"
|
||||
blstoexecmock "github.com/OffchainLabs/prysm/v7/beacon-chain/operations/blstoexec/mock"
|
||||
payloadattestationmock "github.com/OffchainLabs/prysm/v7/beacon-chain/operations/payloadattestation/mock"
|
||||
slashingsmock "github.com/OffchainLabs/prysm/v7/beacon-chain/operations/slashings/mock"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/operations/synccommittee"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/operations/voluntaryexits/mock"
|
||||
p2pMock "github.com/OffchainLabs/prysm/v7/beacon-chain/p2p/testing"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/rpc/core"
|
||||
state_native "github.com/OffchainLabs/prysm/v7/beacon-chain/state/state-native"
|
||||
mockSync "github.com/OffchainLabs/prysm/v7/beacon-chain/sync/initial-sync/testing"
|
||||
state_native "github.com/OffchainLabs/prysm/v7/beacon-chain/state/state-native"
|
||||
"github.com/OffchainLabs/prysm/v7/config/params"
|
||||
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
|
||||
"github.com/OffchainLabs/prysm/v7/crypto/bls"
|
||||
@@ -2548,253 +2547,3 @@ var (
|
||||
}
|
||||
}`
|
||||
)
|
||||
|
||||
func TestSubmitPayloadAttestations(t *testing.T) {
|
||||
t.Run("pre-gloas fork returns error", func(t *testing.T) {
|
||||
params.SetupTestConfigCleanup(t)
|
||||
cfg := params.BeaconConfig().Copy()
|
||||
cfg.GloasForkEpoch = 100
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
|
||||
slot := primitives.Slot(0)
|
||||
chainService := &blockchainmock.ChainService{Slot: &slot}
|
||||
s := &Server{
|
||||
TimeFetcher: chainService,
|
||||
}
|
||||
|
||||
request := httptest.NewRequest(http.MethodPost, "http://example.com", nil)
|
||||
writer := httptest.NewRecorder()
|
||||
writer.Body = &bytes.Buffer{}
|
||||
|
||||
s.SubmitPayloadAttestations(writer, request)
|
||||
assert.Equal(t, http.StatusBadRequest, writer.Code)
|
||||
assert.StringContains(t, "Gloas fork", writer.Body.String())
|
||||
})
|
||||
t.Run("no version header", func(t *testing.T) {
|
||||
params.SetupTestConfigCleanup(t)
|
||||
cfg := params.BeaconConfig().Copy()
|
||||
cfg.GloasForkEpoch = 0
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
|
||||
slot := primitives.Slot(0)
|
||||
chainService := &blockchainmock.ChainService{Slot: &slot}
|
||||
s := &Server{
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
HeadFetcher: chainService,
|
||||
TimeFetcher: chainService,
|
||||
OptimisticModeFetcher: chainService,
|
||||
}
|
||||
|
||||
request := httptest.NewRequest(http.MethodPost, "http://example.com", nil)
|
||||
writer := httptest.NewRecorder()
|
||||
writer.Body = &bytes.Buffer{}
|
||||
|
||||
s.SubmitPayloadAttestations(writer, request)
|
||||
assert.Equal(t, http.StatusBadRequest, writer.Code)
|
||||
assert.StringContains(t, "Eth-Consensus-Version", writer.Body.String())
|
||||
})
|
||||
t.Run("ok", func(t *testing.T) {
|
||||
params.SetupTestConfigCleanup(t)
|
||||
cfg := params.BeaconConfig().Copy()
|
||||
cfg.GloasForkEpoch = 0
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
|
||||
slot := primitives.Slot(1)
|
||||
chainService := &blockchainmock.ChainService{Slot: &slot}
|
||||
broadcaster := &p2pMock.MockBroadcaster{}
|
||||
pool := &payloadattestationmock.PoolMock{}
|
||||
s := &Server{
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
HeadFetcher: chainService,
|
||||
TimeFetcher: chainService,
|
||||
OptimisticModeFetcher: chainService,
|
||||
Broadcaster: broadcaster,
|
||||
PayloadAttestationPool: pool,
|
||||
}
|
||||
|
||||
body := `[{
|
||||
"validator_index": "1",
|
||||
"data": {
|
||||
"beacon_block_root": "0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2",
|
||||
"slot": "1",
|
||||
"payload_present": true,
|
||||
"blob_data_available": true
|
||||
},
|
||||
"signature": "0x1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505cc411d61252fb6cb3fa0017b679f8bb2305b26a285fa2737f175668d0dff91cc1b66ac1fb663c9bc59509846d6ec05345bd908eda73e670af888da41af171505"
|
||||
}]`
|
||||
request := httptest.NewRequest(http.MethodPost, "http://example.com", strings.NewReader(body))
|
||||
request.Header.Set(api.VersionHeader, "gloas")
|
||||
writer := httptest.NewRecorder()
|
||||
writer.Body = &bytes.Buffer{}
|
||||
|
||||
s.SubmitPayloadAttestations(writer, request)
|
||||
assert.Equal(t, http.StatusOK, writer.Code)
|
||||
assert.Equal(t, 1, len(pool.Attestations))
|
||||
assert.Equal(t, true, broadcaster.BroadcastCalled.Load())
|
||||
})
|
||||
t.Run("invalid body", func(t *testing.T) {
|
||||
params.SetupTestConfigCleanup(t)
|
||||
cfg := params.BeaconConfig().Copy()
|
||||
cfg.GloasForkEpoch = 0
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
|
||||
slot := primitives.Slot(1)
|
||||
chainService := &blockchainmock.ChainService{Slot: &slot}
|
||||
s := &Server{
|
||||
SyncChecker: &mockSync.Sync{IsSyncing: false},
|
||||
HeadFetcher: chainService,
|
||||
TimeFetcher: chainService,
|
||||
OptimisticModeFetcher: chainService,
|
||||
}
|
||||
|
||||
request := httptest.NewRequest(http.MethodPost, "http://example.com", strings.NewReader("invalid"))
|
||||
request.Header.Set(api.VersionHeader, "gloas")
|
||||
writer := httptest.NewRecorder()
|
||||
writer.Body = &bytes.Buffer{}
|
||||
|
||||
s.SubmitPayloadAttestations(writer, request)
|
||||
assert.Equal(t, http.StatusBadRequest, writer.Code)
|
||||
})
|
||||
}
|
||||
|
||||
func TestListPayloadAttestations(t *testing.T) {
|
||||
t.Run("pre-gloas fork returns error", func(t *testing.T) {
|
||||
params.SetupTestConfigCleanup(t)
|
||||
cfg := params.BeaconConfig().Copy()
|
||||
cfg.GloasForkEpoch = 100
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
|
||||
slot := primitives.Slot(0)
|
||||
chainService := &blockchainmock.ChainService{Slot: &slot}
|
||||
s := &Server{
|
||||
TimeFetcher: chainService,
|
||||
}
|
||||
|
||||
request := httptest.NewRequest(http.MethodGet, "http://example.com", nil)
|
||||
writer := httptest.NewRecorder()
|
||||
writer.Body = &bytes.Buffer{}
|
||||
|
||||
s.ListPayloadAttestations(writer, request)
|
||||
assert.Equal(t, http.StatusBadRequest, writer.Code)
|
||||
assert.StringContains(t, "Gloas fork", writer.Body.String())
|
||||
})
|
||||
t.Run("empty pool", func(t *testing.T) {
|
||||
params.SetupTestConfigCleanup(t)
|
||||
cfg := params.BeaconConfig().Copy()
|
||||
cfg.GloasForkEpoch = 0
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
|
||||
slot := primitives.Slot(0)
|
||||
chainService := &blockchainmock.ChainService{Slot: &slot}
|
||||
pool := &payloadattestationmock.PoolMock{}
|
||||
s := &Server{
|
||||
TimeFetcher: chainService,
|
||||
PayloadAttestationPool: pool,
|
||||
}
|
||||
|
||||
request := httptest.NewRequest(http.MethodGet, "http://example.com", nil)
|
||||
writer := httptest.NewRecorder()
|
||||
writer.Body = &bytes.Buffer{}
|
||||
|
||||
s.ListPayloadAttestations(writer, request)
|
||||
assert.Equal(t, http.StatusOK, writer.Code)
|
||||
|
||||
resp := &structs.GetPoolPayloadAttestationsResponse{}
|
||||
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
|
||||
assert.Equal(t, 0, len(resp.Data))
|
||||
})
|
||||
t.Run("returns attestations", func(t *testing.T) {
|
||||
params.SetupTestConfigCleanup(t)
|
||||
cfg := params.BeaconConfig().Copy()
|
||||
cfg.GloasForkEpoch = 0
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
|
||||
slot := primitives.Slot(0)
|
||||
chainService := &blockchainmock.ChainService{Slot: &slot}
|
||||
pool := &payloadattestationmock.PoolMock{
|
||||
Attestations: []*ethpbv1alpha1.PayloadAttestation{
|
||||
{
|
||||
Data: ðpbv1alpha1.PayloadAttestationData{
|
||||
BeaconBlockRoot: bytesutil.PadTo([]byte("root1"), 32),
|
||||
Slot: 1,
|
||||
PayloadPresent: true,
|
||||
BlobDataAvailable: true,
|
||||
},
|
||||
Signature: bytesutil.PadTo([]byte("sig1"), 96),
|
||||
},
|
||||
{
|
||||
Data: ðpbv1alpha1.PayloadAttestationData{
|
||||
BeaconBlockRoot: bytesutil.PadTo([]byte("root2"), 32),
|
||||
Slot: 2,
|
||||
PayloadPresent: false,
|
||||
BlobDataAvailable: false,
|
||||
},
|
||||
Signature: bytesutil.PadTo([]byte("sig2"), 96),
|
||||
},
|
||||
},
|
||||
}
|
||||
s := &Server{
|
||||
TimeFetcher: chainService,
|
||||
PayloadAttestationPool: pool,
|
||||
}
|
||||
|
||||
request := httptest.NewRequest(http.MethodGet, "http://example.com", nil)
|
||||
writer := httptest.NewRecorder()
|
||||
writer.Body = &bytes.Buffer{}
|
||||
|
||||
s.ListPayloadAttestations(writer, request)
|
||||
assert.Equal(t, http.StatusOK, writer.Code)
|
||||
|
||||
resp := &structs.GetPoolPayloadAttestationsResponse{}
|
||||
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
|
||||
assert.Equal(t, 2, len(resp.Data))
|
||||
assert.Equal(t, "gloas", resp.Version)
|
||||
})
|
||||
t.Run("filter by slot", func(t *testing.T) {
|
||||
params.SetupTestConfigCleanup(t)
|
||||
cfg := params.BeaconConfig().Copy()
|
||||
cfg.GloasForkEpoch = 0
|
||||
params.OverrideBeaconConfig(cfg)
|
||||
|
||||
slot := primitives.Slot(0)
|
||||
chainService := &blockchainmock.ChainService{Slot: &slot}
|
||||
pool := &payloadattestationmock.PoolMock{
|
||||
Attestations: []*ethpbv1alpha1.PayloadAttestation{
|
||||
{
|
||||
Data: ðpbv1alpha1.PayloadAttestationData{
|
||||
BeaconBlockRoot: bytesutil.PadTo([]byte("root1"), 32),
|
||||
Slot: 1,
|
||||
PayloadPresent: true,
|
||||
BlobDataAvailable: true,
|
||||
},
|
||||
Signature: bytesutil.PadTo([]byte("sig1"), 96),
|
||||
},
|
||||
{
|
||||
Data: ðpbv1alpha1.PayloadAttestationData{
|
||||
BeaconBlockRoot: bytesutil.PadTo([]byte("root2"), 32),
|
||||
Slot: 2,
|
||||
PayloadPresent: false,
|
||||
BlobDataAvailable: false,
|
||||
},
|
||||
Signature: bytesutil.PadTo([]byte("sig2"), 96),
|
||||
},
|
||||
},
|
||||
}
|
||||
s := &Server{
|
||||
TimeFetcher: chainService,
|
||||
PayloadAttestationPool: pool,
|
||||
}
|
||||
|
||||
request := httptest.NewRequest(http.MethodGet, "http://example.com?slot=1", nil)
|
||||
writer := httptest.NewRecorder()
|
||||
writer.Body = &bytes.Buffer{}
|
||||
|
||||
s.ListPayloadAttestations(writer, request)
|
||||
assert.Equal(t, http.StatusOK, writer.Code)
|
||||
|
||||
resp := &structs.GetPoolPayloadAttestationsResponse{}
|
||||
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp))
|
||||
assert.Equal(t, 1, len(resp.Data))
|
||||
assert.Equal(t, "1", resp.Data[0].Data.Slot)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ import (
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/execution"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/operations/attestations"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/operations/blstoexec"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/operations/payloadattestation"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/operations/slashings"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/operations/voluntaryexits"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/p2p"
|
||||
@@ -49,7 +48,6 @@ type Server struct {
|
||||
ExecutionReconstructor execution.Reconstructor
|
||||
FinalizationFetcher blockchain.FinalizationFetcher
|
||||
BLSChangesPool blstoexec.PoolManager
|
||||
PayloadAttestationPool payloadattestation.PoolManager
|
||||
ForkchoiceFetcher blockchain.ForkchoiceFetcher
|
||||
CoreService *core.Service
|
||||
AttestationStateFetcher blockchain.AttestationStateFetcher
|
||||
|
||||
@@ -21,7 +21,6 @@ go_library(
|
||||
"//beacon-chain/core/helpers:go_default_library",
|
||||
"//beacon-chain/db:go_default_library",
|
||||
"//beacon-chain/operations/attestations:go_default_library",
|
||||
"//beacon-chain/operations/payloadattestation:go_default_library",
|
||||
"//beacon-chain/operations/synccommittee:go_default_library",
|
||||
"//beacon-chain/p2p:go_default_library",
|
||||
"//beacon-chain/rpc/core:go_default_library",
|
||||
|
||||
@@ -1427,65 +1427,3 @@ func sortProposerDuties(duties []*structs.ProposerDuty) error {
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
// GetPayloadAttestationData produces payload attestation data for the requested slot.
|
||||
func (s *Server) GetPayloadAttestationData(w http.ResponseWriter, r *http.Request) {
|
||||
ctx, span := trace.StartSpan(r.Context(), "validator.GetPayloadAttestationData")
|
||||
defer span.End()
|
||||
|
||||
currentEpoch := slots.ToEpoch(s.TimeFetcher.CurrentSlot())
|
||||
if currentEpoch < params.BeaconConfig().GloasForkEpoch {
|
||||
httputil.HandleError(w, fmt.Sprintf("payload attestation data requires the Gloas fork, current epoch %d, Gloas epoch %d", currentEpoch, params.BeaconConfig().GloasForkEpoch), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if shared.IsSyncing(ctx, w, s.SyncChecker, s.HeadFetcher, s.TimeFetcher, s.OptimisticModeFetcher) {
|
||||
return
|
||||
}
|
||||
|
||||
_, slot, ok := shared.UintFromRoute(w, r, "slot")
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
headRoot, err := s.HeadFetcher.HeadRoot(ctx)
|
||||
if err != nil {
|
||||
httputil.HandleError(w, "Could not get head root: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
var payloadPresent, blobDataAvailable bool
|
||||
if s.PayloadStatusFetcher != nil {
|
||||
var err error
|
||||
payloadPresent, blobDataAvailable, err = s.PayloadStatusFetcher.PayloadStatus(primitives.Slot(slot))
|
||||
if err != nil {
|
||||
httputil.HandleError(w, "Could not get payload status: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
data := ðpbalpha.PayloadAttestationData{
|
||||
BeaconBlockRoot: headRoot,
|
||||
Slot: primitives.Slot(slot),
|
||||
PayloadPresent: payloadPresent,
|
||||
BlobDataAvailable: blobDataAvailable,
|
||||
}
|
||||
|
||||
if httputil.RespondWithSsz(r) {
|
||||
sszData, err := data.MarshalSSZ()
|
||||
if err != nil {
|
||||
httputil.HandleError(w, "Could not marshal payload attestation data: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.Header().Set(api.VersionHeader, version.String(version.Gloas))
|
||||
httputil.WriteSsz(w, sszData)
|
||||
return
|
||||
}
|
||||
|
||||
response := &structs.GetPayloadAttestationDataResponse{
|
||||
Version: version.String(version.Gloas),
|
||||
Data: structs.PayloadAttestationDataFromConsensus(data),
|
||||
}
|
||||
w.Header().Set(api.VersionHeader, version.String(version.Gloas))
|
||||
httputil.WriteJson(w, response)
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/feed/operation"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/db"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/operations/attestations"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/operations/payloadattestation"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/operations/synccommittee"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/p2p"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/rpc/core"
|
||||
@@ -39,6 +38,4 @@ type Server struct {
|
||||
BlockRewardFetcher rewards.BlockRewardsFetcher
|
||||
TrackedValidatorsCache *cache.TrackedValidatorsCache
|
||||
PayloadIDCache *cache.PayloadIDCache
|
||||
PayloadAttestationPool payloadattestation.PoolManager
|
||||
PayloadStatusFetcher payloadattestation.PayloadStatusFetcher
|
||||
}
|
||||
|
||||
@@ -22,7 +22,6 @@ import (
|
||||
lightClient "github.com/OffchainLabs/prysm/v7/beacon-chain/light-client"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/operations/attestations"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/operations/blstoexec"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/operations/payloadattestation"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/operations/slashings"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/operations/synccommittee"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/operations/voluntaryexits"
|
||||
@@ -104,7 +103,6 @@ type Config struct {
|
||||
SlashingsPool slashings.PoolManager
|
||||
SyncCommitteeObjectPool synccommittee.Pool
|
||||
BLSChangesPool blstoexec.PoolManager
|
||||
PayloadAttestationPool payloadattestation.PoolManager
|
||||
SyncService chainSync.Checker
|
||||
Broadcaster p2p.Broadcaster
|
||||
PeersFetcher p2p.PeersProvider
|
||||
|
||||
3
changelog/jtraglia-add-specrefs-readme.md
Normal file
3
changelog/jtraglia-add-specrefs-readme.md
Normal file
@@ -0,0 +1,3 @@
|
||||
### Added
|
||||
|
||||
- Added README for maintaining specrefs.
|
||||
3
changelog/jtraglia-nightly-reftests-with-run-id.md
Normal file
3
changelog/jtraglia-nightly-reftests-with-run-id.md
Normal file
@@ -0,0 +1,3 @@
|
||||
### Added
|
||||
|
||||
- The ability to download the nightly reference tests from a specific day.
|
||||
4
changelog/manu-synced-new-block.md
Normal file
4
changelog/manu-synced-new-block.md
Normal file
@@ -0,0 +1,4 @@
|
||||
### Changed
|
||||
|
||||
- Always use `INFO` for the "Synced new block" log, but add additional data if the verbosity is set to `debug` or higher.
|
||||
- Add `slotInEpoch` and `sinceSlotStartTime` fields to the "Synced new block" INFO log.
|
||||
3
changelog/syjn99_docs-ssz-ql.md
Normal file
3
changelog/syjn99_docs-ssz-ql.md
Normal file
@@ -0,0 +1,3 @@
|
||||
### Ignored
|
||||
|
||||
- Add handy documentation for SSZ Query package (`encoding/ssz/query`).
|
||||
190
encoding/ssz/query/doc.md
Normal file
190
encoding/ssz/query/doc.md
Normal file
@@ -0,0 +1,190 @@
|
||||
# SSZ Query Package
|
||||
|
||||
The `encoding/ssz/query` package provides a system for analyzing and querying SSZ ([Simple Serialize](https://github.com/ethereum/consensus-specs/blob/master/ssz/simple-serialize.md)) data structures, as well as generating Merkle proofs from them. It enables runtime analysis of SSZ-serialized Go objects with reflection, path-based queries through nested structures, generalized index calculation, and Merkle proof generation.
|
||||
|
||||
This package is designed to be generic. It operates on arbitrary SSZ-serialized Go values at runtime, so the same query/proof machinery applies equally to any SSZ type, including the BeaconState/BeaconBlock.
|
||||
|
||||
## Usage Example
|
||||
|
||||
```go
|
||||
// 1. Analyze an SSZ object
|
||||
block := ðpb.BeaconBlock{...}
|
||||
info, err := query.AnalyzeObject(block)
|
||||
|
||||
// 2. Parse a path
|
||||
path, err := query.ParsePath(".body.attestations[0].data.slot")
|
||||
|
||||
// 3. Get the generalized index
|
||||
gindex, err := query.GetGeneralizedIndexFromPath(info, path)
|
||||
|
||||
// 4. Generate a Merkle proof
|
||||
proof, err := info.Prove(gindex)
|
||||
|
||||
// 5. Get offset and length to slice the SSZ-encoded bytes
|
||||
sszBytes, _ := block.MarshalSSZ()
|
||||
_, offset, length, err := query.CalculateOffsetAndLength(info, path)
|
||||
// slotBytes contains the SSZ-encoded value at the queried path
|
||||
slotBytes := sszBytes[offset : offset+length]
|
||||
```
|
||||
|
||||
## Exported API
|
||||
|
||||
The main exported API consists of:
|
||||
|
||||
```go
|
||||
// AnalyzeObject analyzes an SSZ object and returns its structural information
|
||||
func AnalyzeObject(obj SSZObject) (*SszInfo, error)
|
||||
|
||||
// ParsePath parses a path string like ".field1.field2[0].field3"
|
||||
func ParsePath(rawPath string) (Path, error)
|
||||
|
||||
// CalculateOffsetAndLength computes byte offset and length for a path within an SSZ object
|
||||
func CalculateOffsetAndLength(sszInfo *SszInfo, path Path) (*SszInfo, uint64, uint64, error)
|
||||
|
||||
// GetGeneralizedIndexFromPath calculates the generalized index for a given path
|
||||
func GetGeneralizedIndexFromPath(info *SszInfo, path Path) (uint64, error)
|
||||
|
||||
// Prove generates a Merkle proof for a target generalized index
|
||||
func (s *SszInfo) Prove(gindex uint64) (*fastssz.Proof, error)
|
||||
```
|
||||
|
||||
## Type System
|
||||
|
||||
### SSZ Types
|
||||
|
||||
The package now supports [all standard SSZ types](https://github.com/ethereum/consensus-specs/blob/master/ssz/simple-serialize.md#typing) except `ProgressiveList`, `ProgressiveContainer`, `ProgressiveBitlist`, `Union`, and `CompatibleUnion`.
|
||||
|
||||
### Core Data Structures
|
||||
|
||||
#### `SszInfo`
|
||||
|
||||
The `SszInfo` structure contains complete structural metadata for an SSZ type:
|
||||
|
||||
```go
|
||||
type SszInfo struct {
|
||||
sszType SszType // SSZ Type classification
|
||||
typ reflect.Type // Go reflect.Type
|
||||
source SSZObject // Original SSZObject reference. Mostly used for reusing SSZ methods like `HashTreeRoot`.
|
||||
isVariable bool // True if contains variable-size fields
|
||||
|
||||
// Composite types have corresponding metadata. Other fields would be nil except for the current type.
|
||||
containerInfo *containerInfo
|
||||
listInfo *listInfo
|
||||
vectorInfo *vectorInfo
|
||||
bitlistInfo *bitlistInfo
|
||||
bitvectorInfo *bitvectorInfo
|
||||
}
|
||||
```
|
||||
|
||||
#### `Path`
|
||||
|
||||
The `Path` structure represents navigation paths through SSZ structures. It supports accessing a field by field name, accessing an element by index (list/vector type), and finding the length of homogenous collection types. The `ParsePath` function parses a raw string into a `Path` instance, which is commonly used in other APIs like `CalculateOffsetAndLength` and `GetGeneralizedIndexFromPath`.
|
||||
|
||||
```go
|
||||
type Path struct {
|
||||
Length bool // Flag for length queries (e.g., len(.field))
|
||||
Elements []PathElement // Sequence of field accesses and indices
|
||||
}
|
||||
|
||||
type PathElement struct {
|
||||
Name string // Field name
|
||||
Index *uint64 // list/vector index (nil if not an index access)
|
||||
}
|
||||
```
|
||||
|
||||
## Implementation Details
|
||||
|
||||
### Type Analysis (`analyzer.go`)
|
||||
|
||||
The `AnalyzeObject` function performs recursive type introspection using Go reflection:
|
||||
|
||||
1. **Type Inspection** - Examines Go `reflect.Value` to determine SSZ type
|
||||
- Basic types (`uint8`, `uint16`, `uint32`, `uint64`, `bool`): `SSZType` constants
|
||||
- Slices: Determined from struct tags (`ssz-size` for vectors, `ssz-max` for lists). There is a related [write-up](https://hackmd.io/@junsong/H101DKnwxl) regarding struct tags.
|
||||
- Structs: Analyzed as Containers with field ordering from JSON tags
|
||||
- Pointers: Dereferenced automatically
|
||||
|
||||
2. **Variable-Length Population** - Determines actual sizes at runtime
|
||||
- For lists: Iterates elements, caches sizes for variable-element lists
|
||||
- For containers: Recursively populates variable fields, adjusts offsets
|
||||
- For bitlists: Decodes bit length from bitvector
|
||||
|
||||
3. **Offset Calculation** - Computes byte positions within serialized data
|
||||
- Fixed-size fields: Offset = sum of preceding field sizes
|
||||
- Variable-size fields: Offset stored as 4-byte pointer entries
|
||||
|
||||
### Path Parsing (`path.go`)
|
||||
|
||||
The `ParsePath` function parses path strings with the following rules:
|
||||
|
||||
- **Dot notation**: `.field1.field2` for field access
|
||||
- **Array indexing**: `[0]`, `[42]` for element access
|
||||
- **Length queries**: `len(.field)` for list/vector lengths
|
||||
- **Character set**: Only `[A-Za-z0-9._\[\]\(\)]` allowed
|
||||
|
||||
Example:
|
||||
```go
|
||||
path, _ := ParsePath(".nested.array_field[5].inner_field")
|
||||
// Returns: Path{
|
||||
// Elements: [
|
||||
// PathElement{Name: "nested"},
|
||||
// PathElement{Name: "array_field", Index: <Pointer to uint64(5)>},
|
||||
// PathElement{Name: "inner_field"}
|
||||
// ]
|
||||
// }
|
||||
```
|
||||
|
||||
### Generalized Index Calculation (`generalized_index.go`)
|
||||
|
||||
The generalized index is a tree position identifier. This package follows the [Ethereum consensus-specs](https://github.com/ethereum/consensus-specs/blob/master/ssz/merkle-proofs.md#generalized-merkle-tree-index) to calculate the generalized index.
|
||||
|
||||
### Merkle Proof Generation (`merkle_proof.go`, `proof_collector.go`)
|
||||
|
||||
The `Prove` method generates Merkle proofs using a single-sweep merkleization algorithm:
|
||||
|
||||
#### Algorithm Overview
|
||||
|
||||
**Key Terms:**
|
||||
|
||||
- **Target gindex** (generalized index): The position of the SSZ element you want to prove, expressed as a generalized Merkle tree index. Stored in `Proof.Index`.
|
||||
- Note: The generalized index for root is 1.
|
||||
- **Registered gindices**: The set of tree positions whose node hashes must be captured during merkleization in order to later assemble the proof.
|
||||
- **Sibling node**: The node that shares the same parent as another node.
|
||||
- **Leaf value**: The 32-byte hash of the target node (the node being proven). Stored in `Proof.Leaf`.
|
||||
|
||||
**Phases:**
|
||||
|
||||
1. **Registration Phase** (`addTarget`)
|
||||
> Goal: determine exactly which sibling hashes are needed for the proof.
|
||||
|
||||
- Record the target gindex as the proof target.
|
||||
- Starting from the target node, walk the Merkle tree from the leaf (target gindex) to the root (gindex = 1).
|
||||
- At each step:
|
||||
- Compute and register the sibling gindex (`i XOR 1`) as “must collect”.
|
||||
- Move to the parent (`i = i/2`).
|
||||
- This produces the full set of registered gindices (the sibling nodes on the target-to-root path).
|
||||
|
||||
2. **Merkleization Phase** (`merkleize`)
|
||||
> Goal: recursively merkleize the tree and capture the needed hashes.
|
||||
|
||||
- Recursively traverse the SSZ structure and compute Merkle tree node hashes from leaves to root.
|
||||
- Whenever the traversal computes a node whose gindex is in registered gindices, store that node’s hash for later proof construction.
|
||||
|
||||
3. **Proof Assembly Phase** (`toProof`)
|
||||
> Goal: create the final `fastssz.Proof` object in the correct format and order.
|
||||
|
||||
```go
|
||||
// Proof represents a merkle proof against a general index.
|
||||
type Proof struct {
|
||||
Index int
|
||||
Leaf []byte
|
||||
Hashes [][]byte
|
||||
}
|
||||
```
|
||||
|
||||
- Set `Proof.Index` to the target gindex.
|
||||
- Set `Proof.Leaf` to the 32-byte hash of the target node.
|
||||
- Build `Proof.Hashes` by walking from the target node up to (but not including) the root:
|
||||
- At node `i`, append the stored hash for the sibling (`i XOR 1`).
|
||||
- Move to the parent (`i = i/2`).
|
||||
- The resulting `Proof.Hashes` is ordered from the target level upward, containing one sibling hash per tree level on the path to the root.
|
||||
35
specrefs/README.md
Normal file
35
specrefs/README.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Specification References
|
||||
|
||||
This directory contains specification reference tracking files managed by
|
||||
[ethspecify](https://github.com/jtraglia/ethspecify).
|
||||
|
||||
## Installation
|
||||
|
||||
Install `ethspecify` with the following command:
|
||||
|
||||
```bash
|
||||
pipx install ethspecify
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> You can run `ethspecify <cmd>` in the `specrefs` directory or
|
||||
> `ethspecify <cmd> --path=specrefs` from the project's root directory.
|
||||
|
||||
## Maintenance
|
||||
|
||||
When adding support for a new specification version, follow these steps:
|
||||
|
||||
0. Change directory into the `specrefs` directory.
|
||||
1. Update the version in `.ethspecify.yml` configuration.
|
||||
2. Run `ethspecify process` to update/populate specrefs.
|
||||
3. Run `ethspecify check` to check specrefs.
|
||||
4. If there are errors, use the error message as a guide to fix the issue. If
|
||||
there are new specrefs with empty sources, implement/locate each item and
|
||||
update each specref source list. If you choose not to implement an item,
|
||||
add an exception to the appropriate section the the `.ethspecify.yml`
|
||||
configuration.
|
||||
5. Repeat steps 3 and 4 until `ethspecify check` passes.
|
||||
6. Run `git diff` to view updated specrefs. If an object/function/etc has
|
||||
changed, make the necessary updates to the implementation.
|
||||
7. Lastly, in the project's root directory, run `act -j check-specrefs` to
|
||||
ensure everything is correct.
|
||||
@@ -21,10 +21,14 @@ There are tests for mainnet and minimal config, so for each config we will add a
|
||||
|
||||
## Running nightly spectests
|
||||
|
||||
Since [PR 15312](https://github.com/OffchainLabs/prysm/pull/15312), Prysm has support to download "nightly" spectests from github via a starlark rule configuration by environment variable.
|
||||
Set `--repo_env=CONSENSUS_SPEC_TESTS_VERSION=nightly` when running spectest to download the "nightly" spectests.
|
||||
Note: A GITHUB_TOKEN environment variable is required to be set. The github token must be a [fine grained token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-fine-grained-personal-access-token).
|
||||
Since [PR 15312](https://github.com/OffchainLabs/prysm/pull/15312), Prysm has support to download "nightly" spectests from github via a starlark rule configuration by environment variable.
|
||||
Set `--repo_env=CONSENSUS_SPEC_TESTS_VERSION=nightly` or `--repo_env=CONSENSUS_SPEC_TESTS_VERSION=nightly-<run_id>` when running spectest to download the "nightly" spectests.
|
||||
Note: A GITHUB_TOKEN environment variable is required to be set. The github token does not need to be associated with your main account; it can be from a "burner account". And the token does not need to be a fine-grained token; it can be a classic token.
|
||||
|
||||
```
|
||||
bazel test //... --test_tag_filters=spectest --repo_env=CONSENSUS_SPEC_TESTS_VERSION=nightly
|
||||
```
|
||||
|
||||
```
|
||||
bazel test //... --test_tag_filters=spectest --repo_env=CONSENSUS_SPEC_TESTS_VERSION=nightly-21422848633
|
||||
```
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# bazel build @consensus_spec_tests//:test_data
|
||||
# bazel build @consensus_spec_tests//:test_data --repo_env=CONSENSUS_SPEC_TESTS_VERSION=nightly
|
||||
# bazel build @consensus_spec_tests//:test_data --repo_env=CONSENSUS_SPEC_TESTS_VERSION=nightly-<run_id>
|
||||
|
||||
def _get_redirected_url(repository_ctx, url, headers):
|
||||
if not repository_ctx.which("curl"):
|
||||
@@ -24,7 +25,7 @@ def _impl(repository_ctx):
|
||||
version = repository_ctx.getenv("CONSENSUS_SPEC_TESTS_VERSION") or repository_ctx.attr.version
|
||||
token = repository_ctx.getenv("GITHUB_TOKEN") or ""
|
||||
|
||||
if version == "nightly":
|
||||
if version == "nightly" or version.startswith("nightly-"):
|
||||
print("Downloading nightly tests")
|
||||
if not token:
|
||||
fail("Error GITHUB_TOKEN is not set")
|
||||
@@ -34,16 +35,22 @@ def _impl(repository_ctx):
|
||||
"Accept": "application/vnd.github+json",
|
||||
}
|
||||
|
||||
repository_ctx.download(
|
||||
"https://api.github.com/repos/%s/actions/workflows/%s/runs?branch=%s&status=success&per_page=1"
|
||||
% (repository_ctx.attr.repo, repository_ctx.attr.workflow, repository_ctx.attr.branch),
|
||||
headers = headers,
|
||||
output = "runs.json"
|
||||
)
|
||||
if version.startswith("nightly-"):
|
||||
run_id = version.split("nightly-", 1)[1]
|
||||
if not run_id:
|
||||
fail("Error invalid run id")
|
||||
else:
|
||||
repository_ctx.download(
|
||||
"https://api.github.com/repos/%s/actions/workflows/%s/runs?branch=%s&status=success&per_page=1"
|
||||
% (repository_ctx.attr.repo, repository_ctx.attr.workflow, repository_ctx.attr.branch),
|
||||
headers = headers,
|
||||
output = "runs.json"
|
||||
)
|
||||
|
||||
run_id = json.decode(repository_ctx.read("runs.json"))["workflow_runs"][0]["id"]
|
||||
repository_ctx.delete("runs.json")
|
||||
run_id = json.decode(repository_ctx.read("runs.json"))["workflow_runs"][0]["id"]
|
||||
repository_ctx.delete("runs.json")
|
||||
|
||||
print("Run id:", run_id)
|
||||
repository_ctx.download(
|
||||
"https://api.github.com/repos/%s/actions/runs/%s/artifacts"
|
||||
% (repository_ctx.attr.repo, run_id),
|
||||
@@ -108,8 +115,8 @@ consensus_spec_tests = repository_rule(
|
||||
"version": attr.string(mandatory = True),
|
||||
"flavors": attr.string_dict(mandatory = True),
|
||||
"repo": attr.string(default = "ethereum/consensus-specs"),
|
||||
"workflow": attr.string(default = "generate_vectors.yml"),
|
||||
"branch": attr.string(default = "dev"),
|
||||
"workflow": attr.string(default = "nightly-reftests.yml"),
|
||||
"branch": attr.string(default = "master"),
|
||||
"release_url_template": attr.string(default = "https://github.com/ethereum/consensus-specs/releases/download/%s"),
|
||||
},
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user