Compare commits

..

21 Commits

Author SHA1 Message Date
terence tsao
f26f197c02 Log atts bit indices 2025-03-02 16:27:21 -08:00
Potuz
a9dc6a1dbb missing save 2025-03-02 16:48:38 -03:00
Nishant Das
713fd33eb5 Fix Bugs In Sync From Head (#15006)
* Fix Bugs

* Remove log
2025-03-02 16:48:16 -03:00
nisdas
5f56507bee Merge branch 'sync_from_head' of https://github.com/prysmaticlabs/geth-sharding into hackSync 2025-03-01 07:39:52 +08:00
rkapka
07f0d5ee72 delete on error 2025-02-28 18:16:30 +01:00
rkapka
23bbf2380f Ignore errors from hasSeenBit and don't pack unaggregated attestations 2025-02-28 18:11:47 +01:00
Potuz
9e96de033b Add feature flag to start from any beacon block in db
The new feature flag called --sync-from takes a string that can take
values:

- `head` or
- a 0x-prefixed hex encoded beacon block root.

The beacon block root or the head block root has to be known in db and
has to be a descendant of the current justified checkpoint.
2025-02-28 10:47:43 -03:00
nisdas
fd41691178 Revert Fallback Sync 2025-02-27 12:11:12 +08:00
nisdas
5916c6e625 Fix Attester Slashing Validation In Electra 2025-02-26 15:09:59 +08:00
terence tsao
0312cb223a Move blockRoot == badHoleskyRoot earlier 2025-02-25 13:53:24 -08:00
terence tsao
68cf7a59f2 Update BUILD.bazel 2025-02-25 12:46:40 -08:00
Kasey Kirkham
537ddb1a24 reject bad root in receive block(batch) 2025-02-25 14:44:18 -06:00
Kasey Kirkham
7afaa6994b hardcode root to remove cache eviction possibility 2025-02-25 14:37:50 -06:00
terence tsao
defb3ab87b Revert terence's downscore change 2025-02-25 11:45:56 -08:00
terence tsao
81dce25c98 Gazelle 2025-02-25 11:41:28 -08:00
Kasey Kirkham
5c409b90bc hack to downscore peers with bad holesky root 2025-02-25 13:40:33 -06:00
terence tsao
aa47661602 Downscore 2025-02-25 11:29:53 -08:00
potuz
7259a2b983 blacklist on batches too 2025-02-25 16:16:53 -03:00
terence tsao
e9f511ac00 Update bootnodes 2025-02-25 08:39:17 -08:00
potuz
34a68715b8 blacklist bad block 2025-02-25 11:40:35 -03:00
nisdas
c33e0575ab do not resync 2025-02-25 17:32:16 +08:00
113 changed files with 828 additions and 2049 deletions

View File

@@ -1,4 +1,4 @@
FROM golang:1.24-alpine
FROM golang:1.23-alpine
COPY entrypoint.sh /entrypoint.sh

View File

@@ -28,15 +28,15 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Go 1.24
- name: Set up Go 1.23
uses: actions/setup-go@v4
with:
go-version: '1.24.0'
go-version: '1.23.5'
- name: Run Gosec Security Scanner
run: | # https://github.com/securego/gosec/issues/469
export PATH=$PATH:$(go env GOPATH)/bin
go install github.com/securego/gosec/v2/cmd/gosec@v2.22.1
gosec -exclude-generated -exclude=G307,G115 -exclude-dir=crypto/bls/herumi ./...
go install github.com/securego/gosec/v2/cmd/gosec@v2.19.0
gosec -exclude-generated -exclude=G307 -exclude-dir=crypto/bls/herumi ./...
lint:
name: Lint
@@ -45,16 +45,16 @@ jobs:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Go 1.24
- name: Set up Go 1.23
uses: actions/setup-go@v4
with:
go-version: '1.24.0'
go-version: '1.23.5'
id: go
- name: Golangci-lint
uses: golangci/golangci-lint-action@v5
with:
version: v1.64.5
version: v1.63.4
args: --config=.golangci.yml --out-${NO_FUTURE}format colored-line-number
build:
@@ -64,7 +64,7 @@ jobs:
- name: Set up Go 1.x
uses: actions/setup-go@v4
with:
go-version: '1.24.0'
go-version: '1.23.5'
id: go
- name: Check out code into the Go module directory

View File

@@ -75,7 +75,6 @@ linters:
- tagliatelle
- thelper
- unparam
- usetesting
- varnamelen
- wrapcheck
- wsl

View File

@@ -165,7 +165,7 @@ STATICCHECK_ANALYZERS = [
"sa6006",
"sa9001",
"sa9002",
"sa9003",
#"sa9003", # Doesn't build. See https://github.com/dominikh/go-tools/pull/1483
"sa9004",
"sa9005",
"sa9006",

View File

@@ -160,15 +160,15 @@ oci_register_toolchains(
http_archive(
name = "io_bazel_rules_go",
integrity = "sha256-JD8o94crTb2DFiJJR8nMAGdBAW95zIENB4cbI+JnrI4=",
patch_args = ["-p1"],
patches = [
# Expose internals of go_test for custom build transitions.
"//third_party:io_bazel_rules_go_test.patch",
],
strip_prefix = "rules_go-cf3c3af34bd869b864f5f2b98e2f41c2b220d6c9",
sha256 = "b2038e2de2cace18f032249cb4bb0048abf583a36369fa98f687af1b3f880b26",
urls = [
"https://github.com/bazel-contrib/rules_go/archive/cf3c3af34bd869b864f5f2b98e2f41c2b220d6c9.tar.gz",
"https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/v0.48.1/rules_go-v0.48.1.zip",
"https://github.com/bazelbuild/rules_go/releases/download/v0.48.1/rules_go-v0.48.1.zip",
],
)
@@ -210,7 +210,7 @@ load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_depe
go_rules_dependencies()
go_register_toolchains(
go_version = "1.24.0",
go_version = "1.23.5",
nogo = "@//:nogo",
)
@@ -431,7 +431,7 @@ gometalinter_dependencies()
load("@bazel_gazelle//:deps.bzl", "gazelle_dependencies")
gazelle_dependencies(go_sdk = "go_sdk")
gazelle_dependencies()
load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps")

View File

@@ -54,5 +54,4 @@ type ForkChoiceNodeExtraData struct {
Balance string `json:"balance"`
ExecutionOptimistic bool `json:"execution_optimistic"`
TimeStamp string `json:"timestamp"`
Target string `json:"target"`
}

View File

@@ -12,6 +12,7 @@ go_library(
"forkchoice_update_execution.go",
"head.go",
"head_sync_committee_info.go",
"holeskyhack.go",
"init_sync_process_block.go",
"log.go",
"merge_ascii_art.go",
@@ -97,7 +98,6 @@ go_library(
"@com_github_pkg_errors//:go_default_library",
"@com_github_prometheus_client_golang//prometheus:go_default_library",
"@com_github_prometheus_client_golang//prometheus/promauto:go_default_library",
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@org_golang_x_sync//errgroup:go_default_library",
],
@@ -128,7 +128,6 @@ go_test(
"receive_block_test.go",
"service_norace_test.go",
"service_test.go",
"setup_forkchoice_test.go",
"setup_test.go",
"weak_subjectivity_checks_test.go",
],
@@ -157,7 +156,6 @@ go_test(
"//beacon-chain/forkchoice/doubly-linked-tree:go_default_library",
"//beacon-chain/forkchoice/types:go_default_library",
"//beacon-chain/operations/attestations:go_default_library",
"//beacon-chain/operations/attestations/kv:go_default_library",
"//beacon-chain/operations/blstoexec:go_default_library",
"//beacon-chain/operations/slashings:go_default_library",
"//beacon-chain/operations/voluntaryexits:go_default_library",
@@ -189,7 +187,6 @@ go_test(
"@com_github_ethereum_go_ethereum//core/types:go_default_library",
"@com_github_holiman_uint256//:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@com_github_sirupsen_logrus//hooks/test:go_default_library",
"@org_golang_google_protobuf//proto:go_default_library",

View File

@@ -582,7 +582,6 @@ func TestService_IsOptimisticForRoot_StateSummaryRecovered(t *testing.T) {
br, err := b.Block.HashTreeRoot()
require.NoError(t, err)
util.SaveBlock(t, context.Background(), beaconDB, b)
require.NoError(t, beaconDB.SaveGenesisBlockRoot(ctx, [32]byte{}))
_, err = c.IsOptimisticForRoot(ctx, br)
assert.NoError(t, err)
summ, err := beaconDB.StateSummary(ctx, br)

View File

@@ -72,6 +72,7 @@ func (s *Service) notifyForkchoiceUpdate(ctx context.Context, arg *fcuConfig) (*
if arg.attributes == nil {
arg.attributes = payloadattribute.EmptyWithVersion(headBlk.Version())
}
go firePayloadAttributesEvent(ctx, s.cfg.StateNotifier.StateFeed(), arg)
payloadID, lastValidHash, err := s.cfg.ExecutionEngineCaller.ForkchoiceUpdated(ctx, fcs, arg.attributes)
if err != nil {
switch {
@@ -158,7 +159,6 @@ func (s *Service) notifyForkchoiceUpdate(ctx context.Context, arg *fcuConfig) (*
log.WithFields(logrus.Fields{
"blockRoot": fmt.Sprintf("%#x", bytesutil.Trunc(arg.headRoot[:])),
"headSlot": headBlk.Slot(),
"nextSlot": nextSlot,
"payloadID": fmt.Sprintf("%#x", bytesutil.Trunc(payloadID[:])),
}).Info("Forkchoice updated with payload attributes for proposal")
s.cfg.PayloadIDCache.Set(nextSlot, arg.headRoot, pId)
@@ -166,19 +166,40 @@ func (s *Service) notifyForkchoiceUpdate(ctx context.Context, arg *fcuConfig) (*
log.WithFields(logrus.Fields{
"blockHash": fmt.Sprintf("%#x", headPayload.BlockHash()),
"slot": headBlk.Slot(),
"nextSlot": nextSlot,
}).Error("Received nil payload ID on VALID engine response")
}
return payloadID, nil
}
func firePayloadAttributesEvent(_ context.Context, f event.SubscriberSender, nextSlot primitives.Slot) {
// the fcu args have differing amounts of completeness based on the code path,
// and there is work we only want to do if a client is actually listening to the events beacon api endpoint.
// temporary solution: just fire a blank event and fill in the details in the api handler.
func firePayloadAttributesEvent(ctx context.Context, f event.SubscriberSender, cfg *fcuConfig) {
pidx, err := helpers.BeaconProposerIndex(ctx, cfg.headState)
if err != nil {
log.WithError(err).
WithField("head_root", cfg.headRoot[:]).
Error("Could not get proposer index for PayloadAttributes event")
return
}
evd := payloadattribute.EventData{
ProposerIndex: pidx,
ProposalSlot: cfg.headState.Slot(),
ParentBlockRoot: cfg.headRoot[:],
Attributer: cfg.attributes,
HeadRoot: cfg.headRoot,
HeadState: cfg.headState,
HeadBlock: cfg.headBlock,
}
if cfg.headBlock != nil && !cfg.headBlock.IsNil() {
headPayload, err := cfg.headBlock.Block().Body().Execution()
if err != nil {
log.WithError(err).Error("Could not get execution payload for head block")
return
}
evd.ParentBlockHash = headPayload.BlockHash()
evd.ParentBlockNumber = headPayload.BlockNumber()
}
f.Send(&feed.Event{
Type: statefeed.PayloadAttributes,
Data: payloadattribute.EventData{ProposalSlot: nextSlot},
Data: evd,
})
}

View File

@@ -102,10 +102,8 @@ func (s *Service) forkchoiceUpdateWithExecution(ctx context.Context, args *fcuCo
log.WithError(err).Error("could not save head")
}
go firePayloadAttributesEvent(ctx, s.cfg.StateNotifier.StateFeed(), s.CurrentSlot()+1)
// Only need to prune attestations from pool if the head has changed.
if err := s.pruneAttsFromPool(s.ctx, args.headState, args.headBlock); err != nil {
if err := s.pruneAttsFromPool(args.headBlock); err != nil {
log.WithError(err).Error("could not prune attestations from pool")
}
return nil

View File

@@ -0,0 +1,21 @@
package blockchain
import (
"encoding/hex"
"github.com/pkg/errors"
)
var errHoleskyForbiddenRoot = errors.New("refusing to process forbidden holesky block")
// hack to prevent bad holesky block importation
var badHoleskyRoot [32]byte
func init() {
hexStr := "2db899881ed8546476d0b92c6aa9110bea9a4cd0dbeb5519eb0ea69575f1f359"
bytes, err := hex.DecodeString(hexStr)
if err != nil {
panic(err)
}
badHoleskyRoot = [32]byte(bytes)
}

View File

@@ -3,7 +3,6 @@ package blockchain
import (
"testing"
mock "github.com/prysmaticlabs/prysm/v5/beacon-chain/blockchain/testing"
testDB "github.com/prysmaticlabs/prysm/v5/beacon-chain/db/testing"
doublylinkedtree "github.com/prysmaticlabs/prysm/v5/beacon-chain/forkchoice/doubly-linked-tree"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/startup"
@@ -19,7 +18,6 @@ func testServiceOptsWithDB(t *testing.T) []Option {
WithStateGen(stategen.New(beaconDB, fcs)),
WithForkChoiceStore(fcs),
WithClockSynchronizer(cs),
WithStateNotifier(&mock.MockStateNotifier{RecordEvents: true}),
}
}

View File

@@ -213,10 +213,3 @@ func WithSyncChecker(checker Checker) Option {
return nil
}
}
func WithSlasherEnabled(enabled bool) Option {
return func(s *Service) error {
s.slasherEnabled = enabled
return nil
}
}

View File

@@ -80,7 +80,7 @@ func (s *Service) OnAttestation(ctx context.Context, a ethpb.Att, disparity time
}
// Use the target state to verify attesting indices are valid.
committees, err := helpers.AttestationCommitteesFromState(ctx, baseState, a)
committees, err := helpers.AttestationCommittees(ctx, baseState, a)
if err != nil {
return err
}

View File

@@ -6,7 +6,6 @@ import (
"time"
"github.com/pkg/errors"
"github.com/prysmaticlabs/go-bitfield"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers"
coreTime "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/time"
@@ -16,7 +15,6 @@ import (
forkchoicetypes "github.com/prysmaticlabs/prysm/v5/beacon-chain/forkchoice/types"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v5/config/features"
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
"github.com/prysmaticlabs/prysm/v5/config/params"
consensusblocks "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
@@ -175,6 +173,9 @@ func (s *Service) onBlockBatch(ctx context.Context, blks []consensusblocks.ROBlo
var set *bls.SignatureBatch
boundaries := make(map[[32]byte]state.BeaconState)
for i, b := range blks {
if b.Root() == badHoleskyRoot {
return errHoleskyForbiddenRoot
}
v, h, err := getStateVersionAndPayload(preState)
if err != nil {
return err
@@ -370,7 +371,7 @@ func (s *Service) handleEpochBoundary(ctx context.Context, slot primitives.Slot,
func (s *Service) handleBlockAttestations(ctx context.Context, blk interfaces.ReadOnlyBeaconBlock, st state.BeaconState) error {
// Feed in block's attestations to fork choice store.
for _, a := range blk.Body().Attestations() {
committees, err := helpers.AttestationCommitteesFromState(ctx, st, a)
committees, err := helpers.AttestationCommittees(ctx, st, a)
if err != nil {
return err
}
@@ -421,99 +422,24 @@ func (s *Service) savePostStateInfo(ctx context.Context, r [32]byte, b interface
return nil
}
// pruneAttsFromPool removes these attestations from the attestation pool
// which are covered by attestations from the received block.
func (s *Service) pruneAttsFromPool(ctx context.Context, headState state.BeaconState, headBlock interfaces.ReadOnlySignedBeaconBlock) error {
for _, att := range headBlock.Block().Body().Attestations() {
if err := s.pruneCoveredAttsFromPool(ctx, headState, att); err != nil {
log.WithError(err).Warn("Could not prune attestations covered by a received block's attestation")
}
}
return nil
}
func (s *Service) pruneCoveredAttsFromPool(ctx context.Context, headState state.BeaconState, att ethpb.Att) error {
switch {
case !att.IsAggregated():
return s.cfg.AttPool.DeleteUnaggregatedAttestation(att)
case att.Version() == version.Phase0:
// This removes the attestations in block `b` from the attestation mem pool.
func (s *Service) pruneAttsFromPool(headBlock interfaces.ReadOnlySignedBeaconBlock) error {
atts := headBlock.Block().Body().Attestations()
for _, att := range atts {
if features.Get().EnableExperimentalAttestationPool {
return errors.Wrap(s.cfg.AttestationCache.DeleteCovered(att), "could not delete covered attestation")
}
return errors.Wrap(s.cfg.AttPool.DeleteAggregatedAttestation(att), "could not delete aggregated attestation")
default:
return s.pruneCoveredElectraAttsFromPool(ctx, headState, att)
}
}
// pruneCoveredElectraAttsFromPool handles removing aggregated Electra attestations from the pool after receiving a block.
// Because in Electra block attestations can combine aggregates for multiple committees, comparing attestation bits
// of a block attestation with attestations bits of an aggregate can cause unexpected results, leading to covered
// aggregates not being removed from the pool.
//
// To make sure aggregates are removed, we decompose the block attestation into dummy aggregates, with each
// aggregate accounting for one committee. This allows us to compare aggregates in the same way it's done for
// Phase0. Even though we can't provide a valid signature for the dummy aggregate, it does not matter because
// signatures play no part in pruning attestations.
func (s *Service) pruneCoveredElectraAttsFromPool(ctx context.Context, headState state.BeaconState, att ethpb.Att) error {
if att.Version() == version.Phase0 {
log.Error("Called pruneCoveredElectraAttsFromPool with a Phase0 attestation")
return nil
}
// We don't want to recompute committees. If they are not cached already,
// we allow attestations to stay in the pool. If these attestations are
// included in a later block, they will be redundant. But given that
// they were not cached in the first place, it's unlikely that they
// will be chosen into a block.
ok, committees, err := helpers.AttestationCommitteesFromCache(ctx, headState, att)
if err != nil {
return errors.Wrap(err, "could not get attestation committees")
}
if !ok {
log.Debug("Attestation committees are not cached. Skipping attestation pruning.")
return nil
}
committeeIndices := att.CommitteeBitsVal().BitIndices()
offset := uint64(0)
// Sanity check as this should never happen
if len(committeeIndices) != len(committees) {
return errors.New("committee indices and committees have different lengths")
}
for i, c := range committees {
ab := bitfield.NewBitlist(uint64(len(c)))
for j := uint64(0); j < uint64(len(c)); j++ {
ab.SetBitAt(j, att.GetAggregationBits().BitAt(j+offset))
}
cb := primitives.NewAttestationCommitteeBits()
cb.SetBitAt(uint64(committeeIndices[i]), true)
a := &ethpb.AttestationElectra{
AggregationBits: ab,
Data: att.GetData(),
CommitteeBits: cb,
Signature: make([]byte, fieldparams.BLSSignatureLength),
}
if features.Get().EnableExperimentalAttestationPool {
if err = s.cfg.AttestationCache.DeleteCovered(a); err != nil {
return errors.Wrap(err, "could not delete covered attestation")
if err := s.cfg.AttestationCache.DeleteCovered(att); err != nil {
return errors.Wrap(err, "could not delete attestation")
}
} else if !a.IsAggregated() {
if err = s.cfg.AttPool.DeleteUnaggregatedAttestation(a); err != nil {
return errors.Wrap(err, "could not delete unaggregated attestation")
} else if att.IsAggregated() {
if err := s.cfg.AttPool.DeleteAggregatedAttestation(att); err != nil {
return err
}
} else {
if err := s.cfg.AttPool.DeleteUnaggregatedAttestation(att); err != nil {
return err
}
} else if err = s.cfg.AttPool.DeleteAggregatedAttestation(a); err != nil {
return errors.Wrap(err, "could not delete aggregated attestation")
}
offset += uint64(len(c))
}
return nil
}
@@ -727,9 +653,13 @@ func (s *Service) lateBlockTasks(ctx context.Context) {
attribute := s.getPayloadAttribute(ctx, headState, s.CurrentSlot()+1, headRoot[:])
// return early if we are not proposing next slot
if attribute.IsEmpty() {
// notifyForkchoiceUpdate fires the payload attribute event. But in this case, we won't
// call notifyForkchoiceUpdate, so the event is fired here.
go firePayloadAttributesEvent(ctx, s.cfg.StateNotifier.StateFeed(), s.CurrentSlot()+1)
fcuArgs := &fcuConfig{
headState: headState,
headRoot: headRoot,
headBlock: nil,
attributes: attribute,
}
go firePayloadAttributesEvent(ctx, s.cfg.StateNotifier.StateFeed(), fcuArgs)
return
}

View File

@@ -12,10 +12,8 @@ import (
"github.com/ethereum/go-ethereum/common"
gethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/pkg/errors"
"github.com/prysmaticlabs/go-bitfield"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/cache"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers"
lightClient "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/light-client"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/signing"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/transition"
@@ -27,7 +25,6 @@ import (
mockExecution "github.com/prysmaticlabs/prysm/v5/beacon-chain/execution/testing"
doublylinkedtree "github.com/prysmaticlabs/prysm/v5/beacon-chain/forkchoice/doubly-linked-tree"
forkchoicetypes "github.com/prysmaticlabs/prysm/v5/beacon-chain/forkchoice/types"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/operations/attestations/kv"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v5/config/features"
fieldparams "github.com/prysmaticlabs/prysm/v5/config/fieldparams"
@@ -48,95 +45,6 @@ import (
logTest "github.com/sirupsen/logrus/hooks/test"
)
func Test_pruneAttsFromPool_Electra(t *testing.T) {
ctx := context.Background()
logHook := logTest.NewGlobal()
params.SetupTestConfigCleanup(t)
cfg := params.BeaconConfig().Copy()
cfg.TargetCommitteeSize = 8
params.OverrideBeaconConfig(cfg)
s := Service{
cfg: &config{
AttPool: kv.NewAttCaches(),
},
}
data := &ethpb.AttestationData{
BeaconBlockRoot: make([]byte, 32),
Source: &ethpb.Checkpoint{Root: make([]byte, 32)},
Target: &ethpb.Checkpoint{Root: make([]byte, 32)},
}
cb := primitives.NewAttestationCommitteeBits()
cb.SetBitAt(0, true)
att1 := &ethpb.AttestationElectra{
AggregationBits: bitfield.Bitlist{0b10000000, 0b00000001},
Data: data,
Signature: make([]byte, 96),
CommitteeBits: cb,
}
cb = primitives.NewAttestationCommitteeBits()
cb.SetBitAt(1, true)
att2 := &ethpb.AttestationElectra{
AggregationBits: bitfield.Bitlist{0b11110111, 0b00000001},
Data: data,
Signature: make([]byte, 96),
CommitteeBits: cb,
}
cb = primitives.NewAttestationCommitteeBits()
cb.SetBitAt(3, true)
att3 := &ethpb.AttestationElectra{
AggregationBits: bitfield.Bitlist{0b11110111, 0b00000001},
Data: data,
Signature: make([]byte, 96),
CommitteeBits: cb,
}
require.NoError(t, s.cfg.AttPool.SaveUnaggregatedAttestation(att1))
require.NoError(t, s.cfg.AttPool.SaveAggregatedAttestation(att2))
require.NoError(t, s.cfg.AttPool.SaveAggregatedAttestation(att3))
cb = primitives.NewAttestationCommitteeBits()
cb.SetBitAt(0, true)
cb.SetBitAt(1, true)
onChainAtt := &ethpb.AttestationElectra{
AggregationBits: bitfield.Bitlist{0b10000000, 0b11110111, 0b00000001},
Data: data,
Signature: make([]byte, 96),
CommitteeBits: cb,
}
bl := &ethpb.SignedBeaconBlockElectra{
Block: &ethpb.BeaconBlockElectra{
Body: &ethpb.BeaconBlockBodyElectra{
Attestations: []*ethpb.AttestationElectra{onChainAtt},
},
},
Signature: make([]byte, 96),
}
rob, err := consensusblocks.NewSignedBeaconBlock(bl)
require.NoError(t, err)
st, _ := util.DeterministicGenesisStateElectra(t, 1024)
committees, err := helpers.BeaconCommittees(ctx, st, 0)
require.NoError(t, err)
// Sanity check to make sure the on-chain att will be decomposed
// into the correct number of aggregates.
require.Equal(t, 4, len(committees))
require.NoError(t, s.pruneAttsFromPool(ctx, st, rob))
require.LogsDoNotContain(t, logHook, "Could not prune attestations")
attsInPool, err := s.cfg.AttPool.UnaggregatedAttestations()
require.NoError(t, err)
assert.Equal(t, 0, len(attsInPool))
attsInPool = s.cfg.AttPool.AggregatedAttestations()
require.Equal(t, 1, len(attsInPool))
assert.DeepEqual(t, att3, attsInPool[0])
}
func TestStore_OnBlockBatch(t *testing.T) {
service, tr := minimalTestService(t)
ctx := tr.ctx
@@ -913,8 +821,6 @@ func TestInsertFinalizedDeposits_MultipleFinalizedRoutines(t *testing.T) {
}
func TestRemoveBlockAttestationsInPool(t *testing.T) {
logHook := logTest.NewGlobal()
genesis, keys := util.DeterministicGenesisState(t, 64)
b, err := util.GenerateFullBlock(genesis, keys, util.DefaultBlockGenConfig(), 1)
assert.NoError(t, err)
@@ -934,8 +840,7 @@ func TestRemoveBlockAttestationsInPool(t *testing.T) {
require.NoError(t, service.cfg.AttPool.SaveAggregatedAttestations(atts))
wsb, err := consensusblocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
require.NoError(t, service.pruneAttsFromPool(context.Background(), nil /* state not needed pre-Electra */, wsb))
require.LogsDoNotContain(t, logHook, "Could not prune attestations")
require.NoError(t, service.pruneAttsFromPool(wsb))
require.Equal(t, 0, service.cfg.AttPool.AggregatedAttestationCount())
}
@@ -1991,7 +1896,6 @@ func TestNoViableHead_Reboot(t *testing.T) {
require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, genesisState, genesisRoot), "Could not save genesis state")
require.NoError(t, service.cfg.BeaconDB.SaveHeadBlockRoot(ctx, genesisRoot), "Could not save genesis state")
require.NoError(t, service.cfg.BeaconDB.SaveGenesisBlockRoot(ctx, genesisRoot), "Could not save genesis state")
for i := 1; i < 6; i++ {
driftGenesisTime(service, int64(i), 0)
@@ -2126,7 +2030,6 @@ func TestNoViableHead_Reboot(t *testing.T) {
require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, genesisState, jroot))
service.cfg.ForkChoiceStore.SetBalancesByRooter(service.cfg.StateGen.ActiveNonSlashedBalancesByRoot)
require.NoError(t, service.StartFromSavedState(genesisState))
require.NoError(t, service.cfg.BeaconDB.SaveGenesisBlockRoot(ctx, genesisRoot))
// Forkchoice has the genesisRoot loaded at startup
require.Equal(t, genesisRoot, service.ensureRootNotZeros(service.cfg.ForkChoiceStore.CachedHeadRoot()))
@@ -2136,7 +2039,7 @@ func TestNoViableHead_Reboot(t *testing.T) {
require.Equal(t, genesisRoot, bytesutil.ToBytes32(headRoot))
optimistic, err := service.IsOptimistic(ctx)
require.NoError(t, err)
require.Equal(t, false, optimistic)
require.Equal(t, true, optimistic)
// Check that the node's justified checkpoint does not agree with the
// last valid state's justified checkpoint

View File

@@ -16,6 +16,7 @@ import (
"github.com/prysmaticlabs/prysm/v5/beacon-chain/das"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/slasher/types"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v5/config/features"
"github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
@@ -64,6 +65,11 @@ type SlashingReceiver interface {
func (s *Service) ReceiveBlock(ctx context.Context, block interfaces.ReadOnlySignedBeaconBlock, blockRoot [32]byte, avs das.AvailabilityStore) error {
ctx, span := trace.StartSpan(ctx, "blockChain.ReceiveBlock")
defer span.End()
if blockRoot == badHoleskyRoot {
return errHoleskyForbiddenRoot
}
// Return early if the block has been synced
if s.InForkchoice(blockRoot) {
log.WithField("blockRoot", fmt.Sprintf("%#x", blockRoot)).Debug("Ignoring already synced block")
@@ -120,7 +126,7 @@ func (s *Service) ReceiveBlock(ctx context.Context, block interfaces.ReadOnlySig
return err
}
// If slasher is configured, forward the attestations in the block via an event feed for processing.
if s.slasherEnabled {
if features.Get().EnableSlasher {
go s.sendBlockAttestationsToSlasher(blockCopy, preState)
}
@@ -547,7 +553,7 @@ func (s *Service) sendBlockAttestationsToSlasher(signed interfaces.ReadOnlySigne
// is done in the background to avoid adding more load to this critical code path.
ctx := context.TODO()
for _, att := range signed.Block().Body().Attestations() {
committees, err := helpers.AttestationCommitteesFromState(ctx, preState, att)
committees, err := helpers.AttestationCommittees(ctx, preState, att)
if err != nil {
log.WithError(err).Error("Could not get attestation committees")
return

View File

@@ -63,7 +63,6 @@ type Service struct {
blobNotifiers *blobNotifierMap
blockBeingSynced *currentlySyncingBlock
blobStorage *filesystem.BlobStorage
slasherEnabled bool
}
// config options for the service.
@@ -340,14 +339,17 @@ func (s *Service) initializeHead(ctx context.Context, st state.BeaconState) erro
return errors.Wrap(err, "could not get head state")
}
}
if err := s.setHead(&head{root, blk, st, blk.Block().Slot(), false}); err != nil {
return errors.Wrap(err, "could not set head")
}
log.WithFields(logrus.Fields{
"root": fmt.Sprintf("%#x", root),
"slot": blk.Block().Slot(),
}).Info("Initialized head block from DB")
return nil
return errors.Wrap(s.setHead(&head{
root,
blk,
st,
blk.Block().Slot(),
false,
}), "could not set head")
}
func (s *Service) startFromExecutionChain() error {

View File

@@ -59,6 +59,20 @@ func (s *Service) setupForkchoiceTree(st state.BeaconState) error {
if err := s.setupForkchoiceRoot(st); err != nil {
return errors.Wrap(err, "could not set up forkchoice root")
}
fBlk, err := s.cfg.BeaconDB.Block(s.ctx, fRoot)
if err != nil {
return errors.Wrap(err, "could not get finalized block")
}
if err := s.setHead(&head{
fRoot,
fBlk,
st,
fBlk.Block().Slot(),
false,
}); err != nil {
return errors.Wrap(err, "could not set head")
}
if headRoot == fRoot {
return nil
}
@@ -96,9 +110,7 @@ func (s *Service) buildForkchoiceChain(ctx context.Context, head interfaces.Read
return nil, err
}
// This chain sets the justified checkpoint for every block, including some that are older than jp.
// This should be however safe for forkchoice at startup. An alternative would be to hook during the
// block processing pipeline when setting the head state, to compute the right states for the justified
// checkpoint.
// This should be however safe for forkchoice at startup.
chain = append(chain, &forkchoicetypes.BlockAndCheckpoints{Block: roblock, JustifiedCheckpoint: jp, FinalizedCheckpoint: cp})
root = head.Block().ParentRoot()
if root == fRoot {

View File

@@ -1,128 +0,0 @@
package blockchain
import (
"testing"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/blocks"
"github.com/prysmaticlabs/prysm/v5/config/features"
"github.com/prysmaticlabs/prysm/v5/config/params"
consensusblocks "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/testing/require"
"github.com/prysmaticlabs/prysm/v5/testing/util"
logTest "github.com/sirupsen/logrus/hooks/test"
)
func Test_startupHeadRoot(t *testing.T) {
service, tr := minimalTestService(t)
ctx := tr.ctx
hook := logTest.NewGlobal()
cp := service.FinalizedCheckpt()
require.DeepEqual(t, cp.Root, params.BeaconConfig().ZeroHash[:])
gr := [32]byte{'r', 'o', 'o', 't'}
service.originBlockRoot = gr
require.NoError(t, service.cfg.BeaconDB.SaveGenesisBlockRoot(ctx, gr))
t.Run("start from finalized", func(t *testing.T) {
require.Equal(t, service.startupHeadRoot(), gr)
})
t.Run("head requested, error path", func(t *testing.T) {
resetCfg := features.InitWithReset(&features.Flags{
ForceHead: "head",
})
defer resetCfg()
require.Equal(t, service.startupHeadRoot(), gr)
require.LogsContain(t, hook, "could not get head block root, starting with finalized block as head")
})
st, _ := util.DeterministicGenesisState(t, 64)
hr := [32]byte{'h', 'e', 'a', 'd'}
require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, st, hr), "Could not save genesis state")
require.NoError(t, service.cfg.BeaconDB.SaveHeadBlockRoot(ctx, hr), "Could not save genesis state")
require.NoError(t, service.cfg.BeaconDB.SaveHeadBlockRoot(ctx, hr))
t.Run("start from head", func(t *testing.T) {
resetCfg := features.InitWithReset(&features.Flags{
ForceHead: "head",
})
defer resetCfg()
require.Equal(t, service.startupHeadRoot(), hr)
})
}
func Test_setupForkchoiceTree_Finalized(t *testing.T) {
service, tr := minimalTestService(t)
ctx := tr.ctx
st, _ := util.DeterministicGenesisState(t, 64)
stateRoot, err := st.HashTreeRoot(ctx)
require.NoError(t, err, "Could not hash genesis state")
require.NoError(t, service.saveGenesisData(ctx, st))
genesis := blocks.NewGenesisBlock(stateRoot[:])
wsb, err := consensusblocks.NewSignedBeaconBlock(genesis)
require.NoError(t, err)
require.NoError(t, service.cfg.BeaconDB.SaveBlock(ctx, wsb), "Could not save genesis block")
parentRoot, err := genesis.Block.HashTreeRoot()
require.NoError(t, err, "Could not get signing root")
require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, st, parentRoot), "Could not save genesis state")
require.NoError(t, service.cfg.BeaconDB.SaveHeadBlockRoot(ctx, parentRoot), "Could not save genesis state")
require.NoError(t, service.cfg.BeaconDB.SaveJustifiedCheckpoint(ctx, &ethpb.Checkpoint{Root: parentRoot[:]}))
require.NoError(t, service.cfg.BeaconDB.SaveFinalizedCheckpoint(ctx, &ethpb.Checkpoint{Root: parentRoot[:]}))
require.NoError(t, service.setupForkchoiceTree(st))
require.Equal(t, 1, service.cfg.ForkChoiceStore.NodeCount())
}
func Test_setupForkchoiceTree_Head(t *testing.T) {
service, tr := minimalTestService(t)
ctx := tr.ctx
resetCfg := features.InitWithReset(&features.Flags{
ForceHead: "head",
})
defer resetCfg()
genesisState, keys := util.DeterministicGenesisState(t, 64)
stateRoot, err := genesisState.HashTreeRoot(ctx)
require.NoError(t, err, "Could not hash genesis state")
genesis := blocks.NewGenesisBlock(stateRoot[:])
wsb, err := consensusblocks.NewSignedBeaconBlock(genesis)
require.NoError(t, err)
genesisRoot, err := genesis.Block.HashTreeRoot()
require.NoError(t, err, "Could not get signing root")
require.NoError(t, service.cfg.BeaconDB.SaveBlock(ctx, wsb), "Could not save genesis block")
require.NoError(t, service.saveGenesisData(ctx, genesisState))
require.NoError(t, service.cfg.BeaconDB.SaveState(ctx, genesisState, genesisRoot), "Could not save genesis state")
require.NoError(t, service.cfg.BeaconDB.SaveHeadBlockRoot(ctx, genesisRoot), "Could not save genesis state")
st, err := service.HeadState(ctx)
require.NoError(t, err)
b, err := util.GenerateFullBlock(st, keys, util.DefaultBlockGenConfig(), primitives.Slot(1))
require.NoError(t, err)
wsb, err = consensusblocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
root, err := b.Block.HashTreeRoot()
require.NoError(t, err)
preState, err := service.getBlockPreState(ctx, wsb.Block())
require.NoError(t, err)
postState, err := service.validateStateTransition(ctx, preState, wsb)
require.NoError(t, err)
require.NoError(t, service.savePostStateInfo(ctx, root, wsb, postState))
b, err = util.GenerateFullBlock(postState, keys, util.DefaultBlockGenConfig(), primitives.Slot(2))
require.NoError(t, err)
wsb, err = consensusblocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
root, err = b.Block.HashTreeRoot()
require.NoError(t, err)
require.NoError(t, service.savePostStateInfo(ctx, root, wsb, preState))
require.NoError(t, service.cfg.BeaconDB.SaveHeadBlockRoot(ctx, root))
cp := service.FinalizedCheckpt()
fRoot := service.ensureRootNotZeros([32]byte(cp.Root))
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())
}

View File

@@ -66,7 +66,7 @@ func ProcessAttestationNoVerifySignature(
if err != nil {
return nil, err
}
committees, err := helpers.AttestationCommitteesFromState(ctx, beaconState, att)
committees, err := helpers.AttestationCommittees(ctx, beaconState, att)
if err != nil {
return nil, err
}

View File

@@ -192,7 +192,7 @@ func createAttestationSignatureBatch(
descs := make([]string, len(atts))
for i, a := range atts {
sigs[i] = a.GetSignature()
committees, err := helpers.AttestationCommitteesFromState(ctx, beaconState, a)
committees, err := helpers.AttestationCommittees(ctx, beaconState, a)
if err != nil {
return nil, err
}

View File

@@ -30,13 +30,6 @@ var (
proposerIndicesCache = cache.NewProposerIndicesCache()
)
type beaconCommitteeFunc = func(
ctx context.Context,
state state.ReadOnlyBeaconState,
slot primitives.Slot,
committeeIndex primitives.CommitteeIndex,
) ([]primitives.ValidatorIndex, error)
// SlotCommitteeCount returns the number of beacon committees of a slot. The
// active validator count is provided as an argument rather than an imported implementation
// from the spec definition. Having the active validator count as an argument allows for
@@ -66,48 +59,21 @@ func SlotCommitteeCount(activeValidatorCount uint64) uint64 {
return committeesPerSlot
}
// AttestationCommitteesFromState returns beacon state committees that reflect attestation's committee indices.
func AttestationCommitteesFromState(ctx context.Context, st state.ReadOnlyBeaconState, att ethpb.Att) ([][]primitives.ValidatorIndex, error) {
return attestationCommittees(ctx, st, att, BeaconCommitteeFromState)
}
// AttestationCommitteesFromCache has the same functionality as AttestationCommitteesFromState, but only returns a value
// when all attestation committees are already cached.
func AttestationCommitteesFromCache(ctx context.Context, st state.ReadOnlyBeaconState, att ethpb.Att) (bool, [][]primitives.ValidatorIndex, error) {
committees, err := attestationCommittees(ctx, st, att, BeaconCommitteeFromCache)
if err != nil {
return false, nil, err
}
if len(committees) == 0 {
return false, nil, nil
}
for _, c := range committees {
if len(c) == 0 {
return false, nil, nil
}
}
return true, committees, nil
}
func attestationCommittees(
ctx context.Context,
st state.ReadOnlyBeaconState,
att ethpb.Att,
committeeFunc beaconCommitteeFunc,
) ([][]primitives.ValidatorIndex, error) {
// AttestationCommittees returns beacon state committees that reflect attestation's committee indices.
func AttestationCommittees(ctx context.Context, st state.ReadOnlyBeaconState, att ethpb.Att) ([][]primitives.ValidatorIndex, error) {
var committees [][]primitives.ValidatorIndex
if att.Version() >= version.Electra {
committeeIndices := att.CommitteeBitsVal().BitIndices()
committees = make([][]primitives.ValidatorIndex, len(committeeIndices))
for i, ci := range committeeIndices {
committee, err := committeeFunc(ctx, st, att.GetData().Slot, primitives.CommitteeIndex(ci))
committee, err := BeaconCommitteeFromState(ctx, st, att.GetData().Slot, primitives.CommitteeIndex(ci))
if err != nil {
return nil, err
}
committees[i] = committee
}
} else {
committee, err := committeeFunc(ctx, st, att.GetData().Slot, att.GetData().CommitteeIndex)
committee, err := BeaconCommitteeFromState(ctx, st, att.GetData().Slot, att.GetData().CommitteeIndex)
if err != nil {
return nil, err
}
@@ -198,27 +164,6 @@ func BeaconCommitteeFromState(ctx context.Context, state state.ReadOnlyBeaconSta
return BeaconCommittee(ctx, activeIndices, seed, slot, committeeIndex)
}
// BeaconCommitteeFromCache has the same functionality as BeaconCommitteeFromState, but only returns a value
// when the committee is already cached.
func BeaconCommitteeFromCache(
ctx context.Context,
state state.ReadOnlyBeaconState,
slot primitives.Slot,
committeeIndex primitives.CommitteeIndex,
) ([]primitives.ValidatorIndex, error) {
epoch := slots.ToEpoch(slot)
seed, err := Seed(state, epoch, params.BeaconConfig().DomainBeaconAttester)
if err != nil {
return nil, errors.Wrap(err, "could not get seed")
}
committee, err := committeeCache.Committee(ctx, slot, seed, committeeIndex)
if err != nil {
return nil, errors.Wrap(err, "could not interface with committee cache")
}
return committee, nil
}
// BeaconCommittee returns the beacon committee of a given slot and committee index. The
// validator indices and seed are provided as an argument rather than an imported implementation
// from the spec definition. Having them as an argument allows for cheaper computation run time.

View File

@@ -729,9 +729,7 @@ func TestCommitteeIndices(t *testing.T) {
assert.DeepEqual(t, []primitives.CommitteeIndex{0, 1, 3}, indices)
}
func TestAttestationCommitteesFromState(t *testing.T) {
ctx := context.Background()
func TestAttestationCommittees(t *testing.T) {
validators := make([]*ethpb.Validator, params.BeaconConfig().SlotsPerEpoch.Mul(params.BeaconConfig().TargetCommitteeSize))
for i := 0; i < len(validators); i++ {
validators[i] = &ethpb.Validator{
@@ -747,7 +745,7 @@ func TestAttestationCommitteesFromState(t *testing.T) {
t.Run("pre-Electra", func(t *testing.T) {
att := &ethpb.Attestation{Data: &ethpb.AttestationData{CommitteeIndex: 0}}
committees, err := helpers.AttestationCommitteesFromState(ctx, state, att)
committees, err := helpers.AttestationCommittees(context.Background(), state, att)
require.NoError(t, err)
require.Equal(t, 1, len(committees))
assert.Equal(t, params.BeaconConfig().TargetCommitteeSize, uint64(len(committees[0])))
@@ -757,7 +755,7 @@ func TestAttestationCommitteesFromState(t *testing.T) {
bits.SetBitAt(0, true)
bits.SetBitAt(1, true)
att := &ethpb.AttestationElectra{CommitteeBits: bits, Data: &ethpb.AttestationData{}}
committees, err := helpers.AttestationCommitteesFromState(ctx, state, att)
committees, err := helpers.AttestationCommittees(context.Background(), state, att)
require.NoError(t, err)
require.Equal(t, 2, len(committees))
assert.Equal(t, params.BeaconConfig().TargetCommitteeSize, uint64(len(committees[0])))
@@ -765,58 +763,9 @@ func TestAttestationCommitteesFromState(t *testing.T) {
})
}
func TestAttestationCommitteesFromCache(t *testing.T) {
ctx := context.Background()
validators := make([]*ethpb.Validator, params.BeaconConfig().SlotsPerEpoch.Mul(params.BeaconConfig().TargetCommitteeSize))
for i := 0; i < len(validators); i++ {
validators[i] = &ethpb.Validator{
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
}
}
state, err := state_native.InitializeFromProtoPhase0(&ethpb.BeaconState{
Validators: validators,
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
})
require.NoError(t, err)
t.Run("pre-Electra", func(t *testing.T) {
helpers.ClearCache()
att := &ethpb.Attestation{Data: &ethpb.AttestationData{CommitteeIndex: 0}}
ok, _, err := helpers.AttestationCommitteesFromCache(ctx, state, att)
require.NoError(t, err)
require.Equal(t, false, ok)
require.NoError(t, helpers.UpdateCommitteeCache(ctx, state, 0))
ok, committees, err := helpers.AttestationCommitteesFromCache(ctx, state, att)
require.NoError(t, err)
require.Equal(t, true, ok)
require.Equal(t, 1, len(committees))
assert.Equal(t, params.BeaconConfig().TargetCommitteeSize, uint64(len(committees[0])))
})
t.Run("post-Electra", func(t *testing.T) {
helpers.ClearCache()
bits := primitives.NewAttestationCommitteeBits()
bits.SetBitAt(0, true)
bits.SetBitAt(1, true)
att := &ethpb.AttestationElectra{CommitteeBits: bits, Data: &ethpb.AttestationData{}}
ok, _, err := helpers.AttestationCommitteesFromCache(ctx, state, att)
require.NoError(t, err)
require.Equal(t, false, ok)
require.NoError(t, helpers.UpdateCommitteeCache(ctx, state, 0))
ok, committees, err := helpers.AttestationCommitteesFromCache(ctx, state, att)
require.NoError(t, err)
require.Equal(t, true, ok)
require.Equal(t, 2, len(committees))
assert.Equal(t, params.BeaconConfig().TargetCommitteeSize, uint64(len(committees[0])))
assert.Equal(t, params.BeaconConfig().TargetCommitteeSize, uint64(len(committees[1])))
})
}
func TestBeaconCommitteesFromState(t *testing.T) {
ctx := context.Background()
params.SetupTestConfigCleanup(t)
func TestBeaconCommittees(t *testing.T) {
prevConfig := params.BeaconConfig().Copy()
defer params.OverrideBeaconConfig(prevConfig)
c := params.BeaconConfig().Copy()
c.MinGenesisActiveValidatorCount = 128
c.SlotsPerEpoch = 4
@@ -825,49 +774,15 @@ func TestBeaconCommitteesFromState(t *testing.T) {
state, _ := util.DeterministicGenesisState(t, 256)
activeCount, err := helpers.ActiveValidatorCount(ctx, state, 0)
activeCount, err := helpers.ActiveValidatorCount(context.Background(), state, 0)
require.NoError(t, err)
committeesPerSlot := helpers.SlotCommitteeCount(activeCount)
committees, err := helpers.BeaconCommittees(ctx, state, 0)
committees, err := helpers.BeaconCommittees(context.Background(), state, 0)
require.NoError(t, err)
require.Equal(t, committeesPerSlot, uint64(len(committees)))
for idx := primitives.CommitteeIndex(0); idx < primitives.CommitteeIndex(len(committees)); idx++ {
committee, err := helpers.BeaconCommitteeFromState(ctx, state, 0, idx)
committee, err := helpers.BeaconCommitteeFromState(context.Background(), state, 0, idx)
require.NoError(t, err)
assert.DeepEqual(t, committees[idx], committee)
}
}
func TestBeaconCommitteesFromCache(t *testing.T) {
ctx := context.Background()
params.SetupTestConfigCleanup(t)
c := params.BeaconConfig().Copy()
c.MinGenesisActiveValidatorCount = 128
c.SlotsPerEpoch = 4
c.TargetCommitteeSize = 16
params.OverrideBeaconConfig(c)
state, _ := util.DeterministicGenesisState(t, 256)
activeCount, err := helpers.ActiveValidatorCount(ctx, state, 0)
require.NoError(t, err)
committeesPerSlot := helpers.SlotCommitteeCount(activeCount)
committees, err := helpers.BeaconCommittees(ctx, state, 0)
require.NoError(t, err)
require.Equal(t, committeesPerSlot, uint64(len(committees)))
helpers.ClearCache()
for idx := primitives.CommitteeIndex(0); idx < primitives.CommitteeIndex(len(committees)); idx++ {
committee, err := helpers.BeaconCommitteeFromCache(ctx, state, 0, idx)
require.NoError(t, err)
assert.Equal(t, 0, len(committee))
}
require.NoError(t, helpers.UpdateCommitteeCache(ctx, state, 0))
for idx := primitives.CommitteeIndex(0); idx < primitives.CommitteeIndex(len(committees)); idx++ {
committee, err := helpers.BeaconCommitteeFromCache(ctx, state, 0, idx)
require.NoError(t, err)
assert.DeepEqual(t, committees[idx], committee)
require.DeepEqual(t, committees[idx], committee)
}
}

View File

@@ -101,7 +101,7 @@ type NoHeadAccessDatabase interface {
SaveLightClientBootstrap(ctx context.Context, blockRoot []byte, bootstrap interfaces.LightClientBootstrap) error
CleanUpDirtyStates(ctx context.Context, slotsPerArchivedPoint primitives.Slot) error
DeleteHistoricalDataBeforeSlot(ctx context.Context, slot primitives.Slot, batchSize int) (int, error)
DeleteHistoricalDataBeforeSlot(ctx context.Context, slot primitives.Slot) error
}
// HeadAccessDatabase defines a struct with access to reading chain head data.

View File

@@ -30,32 +30,22 @@ var errInvalidSlotRange = errors.New("invalid end slot and start slot provided")
func (s *Store) Block(ctx context.Context, blockRoot [32]byte) (interfaces.ReadOnlySignedBeaconBlock, error) {
ctx, span := trace.StartSpan(ctx, "BeaconDB.Block")
defer span.End()
blk, err := s.getBlock(ctx, blockRoot, nil)
if errors.Is(err, ErrNotFound) {
return nil, nil
}
return blk, err
}
func (s *Store) getBlock(ctx context.Context, blockRoot [32]byte, tx *bolt.Tx) (interfaces.ReadOnlySignedBeaconBlock, error) {
// Return block from cache if it exists.
if v, ok := s.blockCache.Get(string(blockRoot[:])); v != nil && ok {
return v.(interfaces.ReadOnlySignedBeaconBlock), nil
}
// This method allows the caller to pass in its tx if one is already open.
// Or if a nil value is used, a transaction will be managed intenally.
if tx == nil {
var err error
tx, err = s.db.Begin(false)
if err != nil {
return nil, err
var blk interfaces.ReadOnlySignedBeaconBlock
err := s.db.View(func(tx *bolt.Tx) error {
bkt := tx.Bucket(blocksBucket)
enc := bkt.Get(blockRoot[:])
if enc == nil {
return nil
}
defer func() {
if err := tx.Rollback(); err != nil {
log.WithError(err).Error("could not rollback read-only getBlock transaction")
}
}()
}
return unmarshalBlock(ctx, tx.Bucket(blocksBucket).Get(blockRoot[:]))
var err error
blk, err = unmarshalBlock(ctx, enc)
return err
})
return blk, err
}
// OriginCheckpointBlockRoot returns the value written to the db in SaveOriginCheckpointBlockRoot
@@ -86,7 +76,7 @@ func (s *Store) HeadBlockRoot() ([32]byte, error) {
err := s.db.View(func(tx *bolt.Tx) error {
bkt := tx.Bucket(blocksBucket)
headRoot := bkt.Get(headBlockRootKey)
if len(headRoot) == 0 {
if headRoot == nil {
return errors.New("no head block root found")
}
copy(root[:], headRoot)
@@ -252,21 +242,6 @@ func (s *Store) DeleteBlock(ctx context.Context, root [32]byte) error {
return ErrDeleteJustifiedAndFinalized
}
// Look up the block to find its slot; needed to remove the slot index entry.
blk, err := s.getBlock(ctx, root, tx)
if err != nil {
// getBlock can return ErrNotFound, in which case we won't even try to delete it.
if errors.Is(err, ErrNotFound) {
return nil
}
return err
}
if err := s.deleteSlotIndexEntry(tx, blk.Block().Slot(), root); err != nil {
return err
}
if err := s.deleteMatchingParentIndex(tx, blk.Block().ParentRoot(), root); err != nil {
return err
}
if err := s.deleteBlock(tx, root[:]); err != nil {
return err
}
@@ -285,82 +260,77 @@ func (s *Store) DeleteBlock(ctx context.Context, root [32]byte) error {
// - blockRootValidatorHashesBucket
// - blockSlotIndicesBucket
// - stateSlotIndicesBucket
func (s *Store) DeleteHistoricalDataBeforeSlot(ctx context.Context, cutoffSlot primitives.Slot, batchSize int) (int, error) {
func (s *Store) DeleteHistoricalDataBeforeSlot(ctx context.Context, cutoffSlot primitives.Slot) error {
ctx, span := trace.StartSpan(ctx, "BeaconDB.DeleteHistoricalDataBeforeSlot")
defer span.End()
// Collect slot/root pairs to perform deletions in a separate read only transaction.
slotRoots, err := s.slotRootsInRange(ctx, primitives.Slot(0), cutoffSlot, batchSize)
var (
roots [][]byte
slts []primitives.Slot
)
err := s.db.View(func(tx *bolt.Tx) error {
var err error
roots, slts, err = blockRootsBySlotRange(ctx, tx.Bucket(blockSlotIndicesBucket), primitives.Slot(0), cutoffSlot, nil, nil, nil)
if err != nil {
return errors.Wrap(err, "could not retrieve block roots")
}
return nil
})
if err != nil {
return 0, err
}
// Return early if there's nothing to delete.
if len(slotRoots) == 0 {
return 0, nil
return errors.Wrap(err, "could not retrieve block roots and slots")
}
// Perform all deletions in a single transaction for atomicity
var numSlotsDeleted int
err = s.db.Update(func(tx *bolt.Tx) error {
for _, sr := range slotRoots {
// Return if context is cancelled or deadline is exceeded.
if ctx.Err() != nil {
//nolint:nilerr
return nil
}
return s.db.Update(func(tx *bolt.Tx) error {
for _, root := range roots {
// Delete block
if err = s.deleteBlock(tx, sr.root[:]); err != nil {
if err = s.deleteBlock(tx, root); err != nil {
return err
}
// Delete finalized block roots index
if err = tx.Bucket(finalizedBlockRootsIndexBucket).Delete(sr.root[:]); err != nil {
if err = tx.Bucket(finalizedBlockRootsIndexBucket).Delete(root); err != nil {
return errors.Wrap(err, "could not delete finalized block root index")
}
// Delete state
if err = tx.Bucket(stateBucket).Delete(sr.root[:]); err != nil {
if err = tx.Bucket(stateBucket).Delete(root); err != nil {
return errors.Wrap(err, "could not delete state")
}
// Delete state summary
if err = tx.Bucket(stateSummaryBucket).Delete(sr.root[:]); err != nil {
if err = tx.Bucket(stateSummaryBucket).Delete(root); err != nil {
return errors.Wrap(err, "could not delete state summary")
}
// Delete validator entries
if err = s.deleteValidatorHashes(tx, sr.root[:]); err != nil {
if err = s.deleteValidatorHashes(tx, root); err != nil {
return errors.Wrap(err, "could not delete validators")
}
numSlotsDeleted++
}
for _, sr := range slotRoots {
for _, slot := range slts {
// Delete slot indices
if err = tx.Bucket(blockSlotIndicesBucket).Delete(bytesutil.SlotToBytesBigEndian(sr.slot)); err != nil {
if err = tx.Bucket(blockSlotIndicesBucket).Delete(bytesutil.SlotToBytesBigEndian(slot)); err != nil {
return errors.Wrap(err, "could not delete block slot index")
}
if err = tx.Bucket(stateSlotIndicesBucket).Delete(bytesutil.SlotToBytesBigEndian(sr.slot)); err != nil {
if err = tx.Bucket(stateSlotIndicesBucket).Delete(bytesutil.SlotToBytesBigEndian(slot)); err != nil {
return errors.Wrap(err, "could not delete state slot index")
}
}
// Delete all caches after we have deleted everything from buckets.
// This is done after the buckets are deleted to avoid any issues in case of transaction rollback.
for _, sr := range slotRoots {
for _, root := range roots {
// Delete block from cache
s.blockCache.Del(string(sr.root[:]))
s.blockCache.Del(string(root))
// Delete state summary from cache
s.stateSummaryCache.delete(sr.root)
s.stateSummaryCache.delete([32]byte(root))
}
return nil
})
return numSlotsDeleted, err
}
// SaveBlock to the db.
@@ -381,7 +351,7 @@ func (s *Store) SaveBlock(ctx context.Context, signed interfaces.ReadOnlySignedB
// if a `saveBlindedBeaconBlocks` key exists in the database. Otherwise, we check if the last
// blocked stored to check if it is blinded, and then write that `saveBlindedBeaconBlocks` key
// to the DB for future checks.
func (s *Store) shouldSaveBlinded() (bool, error) {
func (s *Store) shouldSaveBlinded(ctx context.Context) (bool, error) {
var saveBlinded bool
if err := s.db.View(func(tx *bolt.Tx) error {
metadataBkt := tx.Bucket(chainMetadataBucket)
@@ -443,7 +413,7 @@ func prepareBlockBatch(blks []blocks.ROBlock, shouldBlind bool) ([]blockBatchEnt
}
func (s *Store) SaveROBlocks(ctx context.Context, blks []blocks.ROBlock, cache bool) error {
shouldBlind, err := s.shouldSaveBlinded()
shouldBlind, err := s.shouldSaveBlinded(ctx)
if err != nil {
return err
}
@@ -714,49 +684,6 @@ func (s *Store) SaveRegistrationsByValidatorIDs(ctx context.Context, ids []primi
})
}
type slotRoot struct {
slot primitives.Slot
root [32]byte
}
// slotRootsInRange returns slot and block root pairs of length min(batchSize, end-slot)
func (s *Store) slotRootsInRange(ctx context.Context, start, end primitives.Slot, batchSize int) ([]slotRoot, error) {
_, span := trace.StartSpan(ctx, "BeaconDB.slotRootsInRange")
defer span.End()
if end < start {
return nil, errInvalidSlotRange
}
var pairs []slotRoot
key := bytesutil.SlotToBytesBigEndian(end)
err := s.db.View(func(tx *bolt.Tx) error {
bkt := tx.Bucket(blockSlotIndicesBucket)
c := bkt.Cursor()
for k, v := c.Seek(key); k != nil; k, v = c.Prev() {
slot := bytesutil.BytesToSlotBigEndian(k)
if slot > end {
continue // Seek will seek to the next key *after* the given one if not present
}
if slot < start {
return nil
}
roots, err := splitRoots(v)
if err != nil {
return errors.Wrapf(err, "corrupt value %v in block slot index for slot=%d", v, slot)
}
for _, r := range roots {
pairs = append(pairs, slotRoot{slot: slot, root: r})
}
if len(pairs) >= batchSize {
return nil // allows code to easily cap the number of items pruned
}
}
return nil
})
return pairs, err
}
// blockRootsByFilter retrieves the block roots given the filter criteria.
func blockRootsByFilter(ctx context.Context, tx *bolt.Tx, f *filters.QueryFilter) ([][]byte, error) {
ctx, span := trace.StartSpan(ctx, "BeaconDB.blockRootsByFilter")
@@ -777,7 +704,7 @@ func blockRootsByFilter(ctx context.Context, tx *bolt.Tx, f *filters.QueryFilter
// We retrieve block roots that match a filter criteria of slot ranges, if specified.
filtersMap := f.Filters()
rootsBySlotRange, err := blockRootsBySlotRange(
rootsBySlotRange, _, err := blockRootsBySlotRange(
ctx,
tx.Bucket(blockSlotIndicesBucket),
filtersMap[filters.StartSlot],
@@ -822,13 +749,13 @@ func blockRootsBySlotRange(
ctx context.Context,
bkt *bolt.Bucket,
startSlotEncoded, endSlotEncoded, startEpochEncoded, endEpochEncoded, slotStepEncoded interface{},
) ([][]byte, error) {
) ([][]byte, []primitives.Slot, error) {
_, span := trace.StartSpan(ctx, "BeaconDB.blockRootsBySlotRange")
defer span.End()
// Return nothing when all slot parameters are missing
if startSlotEncoded == nil && endSlotEncoded == nil && startEpochEncoded == nil && endEpochEncoded == nil {
return [][]byte{}, nil
return [][]byte{}, nil, nil
}
var startSlot, endSlot primitives.Slot
@@ -849,11 +776,11 @@ func blockRootsBySlotRange(
if startEpochOk && endEpochOk {
startSlot, err = slots.EpochStart(startEpoch)
if err != nil {
return nil, err
return nil, nil, err
}
endSlot, err = slots.EpochStart(endEpoch)
if err != nil {
return nil, err
return nil, nil, err
}
endSlot = endSlot + params.BeaconConfig().SlotsPerEpoch - 1
}
@@ -864,10 +791,11 @@ func blockRootsBySlotRange(
return key != nil && bytes.Compare(key, max) <= 0
}
if endSlot < startSlot {
return nil, errInvalidSlotRange
return nil, nil, errInvalidSlotRange
}
rootsRange := endSlot.SubSlot(startSlot).Div(step)
roots := make([][]byte, 0, rootsRange)
var slts []primitives.Slot
c := bkt.Cursor()
for k, v := c.Seek(min); conditional(k, max); k, v = c.Next() {
slot := bytesutil.BytesToSlotBigEndian(k)
@@ -882,8 +810,9 @@ func blockRootsBySlotRange(
splitRoots = append(splitRoots, v[i:i+32])
}
roots = append(roots, splitRoots...)
slts = append(slts, slot)
}
return roots, nil
return roots, slts, nil
}
// blockRootsBySlot retrieves the block roots by slot
@@ -939,9 +868,6 @@ func createBlockIndicesFromFilters(ctx context.Context, f *filters.QueryFilter)
// unmarshal block from marshaled proto beacon block bytes to versioned beacon block struct type.
func unmarshalBlock(_ context.Context, enc []byte) (interfaces.ReadOnlySignedBeaconBlock, error) {
if len(enc) == 0 {
return nil, errors.Wrap(ErrNotFound, "empty block bytes in db")
}
var err error
enc, err = snappy.Decode(nil, enc)
if err != nil {
@@ -1093,47 +1019,6 @@ func (s *Store) deleteBlock(tx *bolt.Tx, root []byte) error {
return nil
}
func (s *Store) deleteMatchingParentIndex(tx *bolt.Tx, parent, child [32]byte) error {
bkt := tx.Bucket(blockParentRootIndicesBucket)
if err := deleteRootIndexEntry(bkt, parent[:], child); err != nil {
return errors.Wrap(err, "could not delete parent root index entry")
}
return nil
}
func (s *Store) deleteSlotIndexEntry(tx *bolt.Tx, slot primitives.Slot, root [32]byte) error {
key := bytesutil.SlotToBytesBigEndian(slot)
bkt := tx.Bucket(blockSlotIndicesBucket)
if err := deleteRootIndexEntry(bkt, key, root); err != nil {
return errors.Wrap(err, "could not delete slot index entry")
}
return nil
}
func deleteRootIndexEntry(bkt *bolt.Bucket, key []byte, root [32]byte) error {
packed := bkt.Get(key)
if len(packed) == 0 {
return nil
}
updated, err := removeRoot(packed, root)
if err != nil {
return err
}
// Don't update the value if the root was not found.
if bytes.Equal(updated, packed) {
return nil
}
// If there are no other roots in the key, just delete it.
if len(updated) == 0 {
if err := bkt.Delete(key); err != nil {
return err
}
return nil
}
// Update the key with the root removed.
return bkt.Put(key, updated)
}
func (s *Store) deleteValidatorHashes(tx *bolt.Tx, root []byte) error {
ok, err := s.isStateValidatorMigrationOver()
if err != nil {

View File

@@ -196,13 +196,9 @@ func TestStore_BlocksCRUD(t *testing.T) {
blockRoot, err := blk.Block().HashTreeRoot()
require.NoError(t, err)
_, err = db.getBlock(ctx, blockRoot, nil)
require.ErrorIs(t, err, ErrNotFound)
retrievedBlock, err := db.Block(ctx, blockRoot)
require.NoError(t, err)
assert.DeepEqual(t, nil, retrievedBlock, "Expected nil block")
_, err = db.getBlock(ctx, blockRoot, nil)
require.ErrorIs(t, err, ErrNotFound)
require.NoError(t, db.SaveBlock(ctx, blk))
assert.Equal(t, true, db.HasBlock(ctx, blockRoot), "Expected block to exist in the db")
@@ -218,34 +214,10 @@ func TestStore_BlocksCRUD(t *testing.T) {
retrievedPb, err := retrievedBlock.Proto()
require.NoError(t, err)
assert.Equal(t, true, proto.Equal(wantedPb, retrievedPb), "Wanted: %v, received: %v", wanted, retrievedBlock)
// Check that the block is in the slot->block index
found, roots, err := db.BlockRootsBySlot(ctx, blk.Block().Slot())
require.NoError(t, err)
require.Equal(t, true, found)
require.Equal(t, 1, len(roots))
require.Equal(t, blockRoot, roots[0])
// Delete the block, then check that it is no longer in the index.
parent := blk.Block().ParentRoot()
testCheckParentIndices(t, db.db, parent, true)
require.NoError(t, db.DeleteBlock(ctx, blockRoot))
require.NoError(t, err)
testCheckParentIndices(t, db.db, parent, false)
found, roots, err = db.BlockRootsBySlot(ctx, blk.Block().Slot())
require.NoError(t, err)
require.Equal(t, false, found)
require.Equal(t, 0, len(roots))
})
}
}
func testCheckParentIndices(t *testing.T, db *bolt.DB, parent [32]byte, expected bool) {
require.NoError(t, db.View(func(tx *bolt.Tx) error {
require.Equal(t, expected, tx.Bucket(blockParentRootIndicesBucket).Get(parent[:]) != nil)
return nil
}))
}
func TestStore_BlocksHandleZeroCase(t *testing.T) {
for _, tt := range blockTests {
t.Run(tt.name, func(t *testing.T) {
@@ -387,221 +359,184 @@ func TestStore_DeleteFinalizedBlock(t *testing.T) {
func TestStore_HistoricalDataBeforeSlot(t *testing.T) {
slotsPerEpoch := uint64(params.BeaconConfig().SlotsPerEpoch)
db := setupDB(t)
ctx := context.Background()
tests := []struct {
name string
batchSize int
numOfEpochs uint64
deleteBeforeSlot uint64
}{
{
name: "batchSize less than delete range",
batchSize: 10,
numOfEpochs: 4,
deleteBeforeSlot: 25,
},
{
name: "batchSize greater than delete range",
batchSize: 30,
numOfEpochs: 4,
deleteBeforeSlot: 15,
},
}
// Save genesis block root
require.NoError(t, db.SaveGenesisBlockRoot(ctx, genesisBlockRoot))
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
db := setupDB(t)
// Save genesis block root
require.NoError(t, db.SaveGenesisBlockRoot(ctx, genesisBlockRoot))
// Create and save blocks for 4 epochs
blks := makeBlocks(t, 0, slotsPerEpoch*4, genesisBlockRoot)
require.NoError(t, db.SaveBlocks(ctx, blks))
// Create and save blocks for given epochs
blks := makeBlocks(t, 0, slotsPerEpoch*tt.numOfEpochs, genesisBlockRoot)
require.NoError(t, db.SaveBlocks(ctx, blks))
// Mark state validator migration as complete
err := db.db.Update(func(tx *bolt.Tx) error {
return tx.Bucket(migrationsBucket).Put(migrationStateValidatorsKey, migrationCompleted)
})
require.NoError(t, err)
// Mark state validator migration as complete
err := db.db.Update(func(tx *bolt.Tx) error {
return tx.Bucket(migrationsBucket).Put(migrationStateValidatorsKey, migrationCompleted)
})
require.NoError(t, err)
migrated, err := db.isStateValidatorMigrationOver()
require.NoError(t, err)
require.Equal(t, true, migrated)
migrated, err := db.isStateValidatorMigrationOver()
require.NoError(t, err)
require.Equal(t, true, migrated)
// Create state summaries and states for each block
ss := make([]*ethpb.StateSummary, len(blks))
states := make([]state.BeaconState, len(blks))
// Create state summaries and states for each block
ss := make([]*ethpb.StateSummary, len(blks))
states := make([]state.BeaconState, len(blks))
for i, blk := range blks {
slot := blk.Block().Slot()
r, err := blk.Block().HashTreeRoot()
require.NoError(t, err)
for i, blk := range blks {
slot := blk.Block().Slot()
r, err := blk.Block().HashTreeRoot()
require.NoError(t, err)
// Create and save state summary
ss[i] = &ethpb.StateSummary{
Slot: slot,
Root: r[:],
}
// Create and save state summary
ss[i] = &ethpb.StateSummary{
Slot: slot,
Root: r[:],
}
// Create and save state with validator entries
vals := make([]*ethpb.Validator, 2)
for j := range vals {
vals[j] = &ethpb.Validator{
PublicKey: bytesutil.PadTo([]byte{byte(i*j + 1)}, 48),
WithdrawalCredentials: bytesutil.PadTo([]byte{byte(i*j + 2)}, 32),
}
}
st, err := util.NewBeaconState(func(state *ethpb.BeaconState) error {
state.Validators = vals
state.Slot = slot
return nil
})
require.NoError(t, err)
require.NoError(t, db.SaveState(ctx, st, r))
states[i] = st
// Verify validator entries are saved to db
valsActual, err := db.validatorEntries(ctx, r)
require.NoError(t, err)
for j, val := range valsActual {
require.DeepEqual(t, vals[j], val)
}
// Create and save state with validator entries
vals := make([]*ethpb.Validator, 2)
for j := range vals {
vals[j] = &ethpb.Validator{
PublicKey: bytesutil.PadTo([]byte{byte(i*j + 1)}, 48),
WithdrawalCredentials: bytesutil.PadTo([]byte{byte(i*j + 2)}, 32),
}
require.NoError(t, db.SaveStateSummaries(ctx, ss))
}
// Verify slot indices exist before deletion
err = db.db.View(func(tx *bolt.Tx) error {
blockSlotBkt := tx.Bucket(blockSlotIndicesBucket)
stateSlotBkt := tx.Bucket(stateSlotIndicesBucket)
for i := uint64(0); i < uint64(tt.deleteBeforeSlot); i++ {
slot := bytesutil.SlotToBytesBigEndian(primitives.Slot(i + 1))
assert.NotNil(t, blockSlotBkt.Get(slot), "Expected block slot index to exist")
assert.NotNil(t, stateSlotBkt.Get(slot), "Expected state slot index to exist", i)
}
return nil
})
require.NoError(t, err)
// Delete data before slot
slotsDeleted, err := db.DeleteHistoricalDataBeforeSlot(ctx, primitives.Slot(tt.deleteBeforeSlot), tt.batchSize)
require.NoError(t, err)
var startSlotDeleted, endSlotDeleted uint64
if tt.batchSize >= int(tt.deleteBeforeSlot) {
startSlotDeleted = 1
endSlotDeleted = tt.deleteBeforeSlot
} else {
startSlotDeleted = tt.deleteBeforeSlot - uint64(tt.batchSize) + 1
endSlotDeleted = tt.deleteBeforeSlot
}
require.Equal(t, endSlotDeleted-startSlotDeleted+1, uint64(slotsDeleted))
// Verify blocks before given slot/batch are deleted
for i := startSlotDeleted; i < endSlotDeleted; i++ {
root, err := blks[i].Block().HashTreeRoot()
require.NoError(t, err)
// Check block is deleted
retrievedBlocks, err := db.BlocksBySlot(ctx, primitives.Slot(i))
require.NoError(t, err)
assert.Equal(t, 0, len(retrievedBlocks), fmt.Sprintf("Expected %d blocks, got %d for slot %d", 0, len(retrievedBlocks), i))
// Verify block does not exist
assert.Equal(t, false, db.HasBlock(ctx, root), fmt.Sprintf("Expected block index to not exist for slot %d", i))
// Verify block parent root does not exist
err = db.db.View(func(tx *bolt.Tx) error {
require.Equal(t, 0, len(tx.Bucket(blockParentRootIndicesBucket).Get(root[:])))
return nil
})
require.NoError(t, err)
// Verify state is deleted
hasState := db.HasState(ctx, root)
assert.Equal(t, false, hasState)
// Verify state summary is deleted
hasSummary := db.HasStateSummary(ctx, root)
assert.Equal(t, false, hasSummary)
// Verify validator hashes for block roots are deleted
err = db.db.View(func(tx *bolt.Tx) error {
assert.Equal(t, 0, len(tx.Bucket(blockRootValidatorHashesBucket).Get(root[:])))
return nil
})
require.NoError(t, err)
}
// Verify slot indices are deleted
err = db.db.View(func(tx *bolt.Tx) error {
blockSlotBkt := tx.Bucket(blockSlotIndicesBucket)
stateSlotBkt := tx.Bucket(stateSlotIndicesBucket)
for i := startSlotDeleted; i < endSlotDeleted; i++ {
slot := bytesutil.SlotToBytesBigEndian(primitives.Slot(i + 1))
assert.Equal(t, 0, len(blockSlotBkt.Get(slot)), fmt.Sprintf("Expected block slot index to be deleted, slot: %d", slot))
assert.Equal(t, 0, len(stateSlotBkt.Get(slot)), fmt.Sprintf("Expected state slot index to be deleted, slot: %d", slot))
}
return nil
})
require.NoError(t, err)
// Verify blocks from expectedLastDeletedSlot till numEpochs still exist
for i := endSlotDeleted; i < slotsPerEpoch*tt.numOfEpochs; i++ {
root, err := blks[i].Block().HashTreeRoot()
require.NoError(t, err)
// Verify block exists
assert.Equal(t, true, db.HasBlock(ctx, root))
// Verify remaining block parent root exists, except last slot since we store parent roots of each block.
if i < slotsPerEpoch*tt.numOfEpochs-1 {
err = db.db.View(func(tx *bolt.Tx) error {
require.NotNil(t, tx.Bucket(blockParentRootIndicesBucket).Get(root[:]), fmt.Sprintf("Expected block parent index to be deleted, slot: %d", i))
return nil
})
require.NoError(t, err)
}
// Verify state exists
hasState := db.HasState(ctx, root)
assert.Equal(t, true, hasState)
// Verify state summary exists
hasSummary := db.HasStateSummary(ctx, root)
assert.Equal(t, true, hasSummary)
// Verify slot indices still exist
err = db.db.View(func(tx *bolt.Tx) error {
blockSlotBkt := tx.Bucket(blockSlotIndicesBucket)
stateSlotBkt := tx.Bucket(stateSlotIndicesBucket)
slot := bytesutil.SlotToBytesBigEndian(primitives.Slot(i + 1))
assert.NotNil(t, blockSlotBkt.Get(slot), "Expected block slot index to exist")
assert.NotNil(t, stateSlotBkt.Get(slot), "Expected state slot index to exist")
return nil
})
require.NoError(t, err)
// Verify validator entries still exist
valsActual, err := db.validatorEntries(ctx, root)
require.NoError(t, err)
assert.NotNil(t, valsActual)
// Verify remaining validator hashes for block roots exists
err = db.db.View(func(tx *bolt.Tx) error {
assert.NotNil(t, tx.Bucket(blockRootValidatorHashesBucket).Get(root[:]))
return nil
})
require.NoError(t, err)
}
st, err := util.NewBeaconState(func(state *ethpb.BeaconState) error {
state.Validators = vals
state.Slot = slot
return nil
})
require.NoError(t, err)
require.NoError(t, db.SaveState(ctx, st, r))
states[i] = st
// Verify validator entries are saved to db
valsActual, err := db.validatorEntries(ctx, r)
require.NoError(t, err)
for j, val := range valsActual {
require.DeepEqual(t, vals[j], val)
}
}
require.NoError(t, db.SaveStateSummaries(ctx, ss))
// Verify slot indices exist before deletion
err = db.db.View(func(tx *bolt.Tx) error {
blockSlotBkt := tx.Bucket(blockSlotIndicesBucket)
stateSlotBkt := tx.Bucket(stateSlotIndicesBucket)
for i := uint64(0); i < slotsPerEpoch; i++ {
slot := bytesutil.SlotToBytesBigEndian(primitives.Slot(i + 1))
assert.NotNil(t, blockSlotBkt.Get(slot), "Expected block slot index to exist")
assert.NotNil(t, stateSlotBkt.Get(slot), "Expected state slot index to exist", i)
}
return nil
})
require.NoError(t, err)
// Delete data before slot at epoch 1
require.NoError(t, db.DeleteHistoricalDataBeforeSlot(ctx, primitives.Slot(slotsPerEpoch)))
// Verify blocks from epoch 0 are deleted
for i := uint64(0); i < slotsPerEpoch; i++ {
root, err := blks[i].Block().HashTreeRoot()
require.NoError(t, err)
// Check block is deleted
retrievedBlocks, err := db.BlocksBySlot(ctx, primitives.Slot(i))
require.NoError(t, err)
assert.Equal(t, 0, len(retrievedBlocks))
// Verify block does not exist
assert.Equal(t, false, db.HasBlock(ctx, root))
// Verify block parent root does not exist
err = db.db.View(func(tx *bolt.Tx) error {
require.Equal(t, 0, len(tx.Bucket(blockParentRootIndicesBucket).Get(root[:])))
return nil
})
require.NoError(t, err)
// Verify state is deleted
hasState := db.HasState(ctx, root)
assert.Equal(t, false, hasState)
// Verify state summary is deleted
hasSummary := db.HasStateSummary(ctx, root)
assert.Equal(t, false, hasSummary)
// Verify validator hashes for block roots are deleted
err = db.db.View(func(tx *bolt.Tx) error {
assert.Equal(t, 0, len(tx.Bucket(blockRootValidatorHashesBucket).Get(root[:])))
return nil
})
require.NoError(t, err)
}
// Verify slot indices are deleted
err = db.db.View(func(tx *bolt.Tx) error {
blockSlotBkt := tx.Bucket(blockSlotIndicesBucket)
stateSlotBkt := tx.Bucket(stateSlotIndicesBucket)
for i := uint64(0); i < slotsPerEpoch; i++ {
slot := bytesutil.SlotToBytesBigEndian(primitives.Slot(i + 1))
assert.Equal(t, 0, len(blockSlotBkt.Get(slot)), fmt.Sprintf("Expected block slot index to be deleted, slot: %d", slot))
assert.Equal(t, 0, len(stateSlotBkt.Get(slot)), fmt.Sprintf("Expected state slot index to be deleted, slot: %d", slot))
}
return nil
})
require.NoError(t, err)
// Verify blocks from epochs 1-3 still exist
for i := slotsPerEpoch; i < slotsPerEpoch*4; i++ {
root, err := blks[i].Block().HashTreeRoot()
require.NoError(t, err)
// Verify block exists
assert.Equal(t, true, db.HasBlock(ctx, root))
// Verify remaining block parent root exists, except last slot since we store parent roots of each block.
if i < slotsPerEpoch*4-1 {
err = db.db.View(func(tx *bolt.Tx) error {
require.NotNil(t, tx.Bucket(blockParentRootIndicesBucket).Get(root[:]), fmt.Sprintf("Expected block parent index to be deleted, slot: %d", i))
return nil
})
require.NoError(t, err)
}
// Verify state exists
hasState := db.HasState(ctx, root)
assert.Equal(t, true, hasState)
// Verify state summary exists
hasSummary := db.HasStateSummary(ctx, root)
assert.Equal(t, true, hasSummary)
// Verify slot indices still exist
err = db.db.View(func(tx *bolt.Tx) error {
blockSlotBkt := tx.Bucket(blockSlotIndicesBucket)
stateSlotBkt := tx.Bucket(stateSlotIndicesBucket)
slot := bytesutil.SlotToBytesBigEndian(primitives.Slot(i + 1))
assert.NotNil(t, blockSlotBkt.Get(slot), "Expected block slot index to exist")
assert.NotNil(t, stateSlotBkt.Get(slot), "Expected state slot index to exist")
return nil
})
require.NoError(t, err)
// Verify validator entries still exist
valsActual, err := db.validatorEntries(ctx, root)
require.NoError(t, err)
assert.NotNil(t, valsActual)
// Verify remaining validator hashes for block roots exists
err = db.db.View(func(tx *bolt.Tx) error {
assert.NotNil(t, tx.Bucket(blockRootValidatorHashesBucket).Get(root[:]))
return nil
})
require.NoError(t, err)
}
}
func TestStore_GenesisBlock(t *testing.T) {

View File

@@ -820,25 +820,30 @@ func (s *Store) slotByBlockRoot(ctx context.Context, tx *bolt.Tx, blockRoot []by
// no need to construct the validator entries as it is not used here.
s, err := s.unmarshalState(ctx, enc, nil)
if err != nil {
return 0, errors.Wrap(err, "could not unmarshal state")
return 0, err
}
if s == nil || s.IsNil() {
return 0, errors.New("state can't be nil")
}
return s.Slot(), nil
}
b, err := unmarshalBlock(ctx, enc)
b := &ethpb.SignedBeaconBlock{}
err := decode(ctx, enc, b)
if err != nil {
return 0, errors.Wrap(err, "could not unmarshal block")
}
if err := blocks.BeaconBlockIsNil(b); err != nil {
return 0, err
}
return b.Block().Slot(), nil
wsb, err := blocks.NewSignedBeaconBlock(b)
if err != nil {
return 0, err
}
if err := blocks.BeaconBlockIsNil(wsb); err != nil {
return 0, err
}
return b.Block.Slot, nil
}
stateSummary := &ethpb.StateSummary{}
if err := decode(ctx, enc, stateSummary); err != nil {
return 0, errors.Wrap(err, "could not unmarshal state summary")
return 0, err
}
return stateSummary.Slot, nil
}

View File

@@ -5,6 +5,7 @@ import (
"crypto/rand"
"encoding/binary"
mathRand "math/rand"
"strconv"
"testing"
"time"
@@ -1069,31 +1070,6 @@ func TestBellatrixState_CanDelete(t *testing.T) {
require.Equal(t, state.ReadOnlyBeaconState(nil), savedS, "Unsaved state should've been nil")
}
func TestBellatrixState_CanDeleteWithBlock(t *testing.T) {
db := setupDB(t)
b := util.NewBeaconBlockBellatrix()
b.Block.Slot = 100
r, err := b.Block.HashTreeRoot()
require.NoError(t, err)
wsb, err := blocks.NewSignedBeaconBlock(b)
require.NoError(t, err)
require.NoError(t, db.SaveBlock(context.Background(), wsb))
require.Equal(t, false, db.HasState(context.Background(), r))
st, _ := util.DeterministicGenesisStateBellatrix(t, 1)
require.NoError(t, st.SetSlot(100))
require.NoError(t, db.SaveState(context.Background(), st, r))
require.Equal(t, true, db.HasState(context.Background(), r))
require.NoError(t, db.DeleteState(context.Background(), r))
savedS, err := db.State(context.Background(), r)
require.NoError(t, err)
require.Equal(t, state.ReadOnlyBeaconState(nil), savedS, "Unsaved state should've been nil")
}
func TestDenebState_CanSaveRetrieve(t *testing.T) {
db := setupDB(t)

View File

@@ -114,27 +114,3 @@ func splitRoots(b []byte) ([][32]byte, error) {
}
return rl, nil
}
func removeRoot(roots []byte, root [32]byte) ([]byte, error) {
if len(roots) == 0 {
return []byte{}, nil
}
if len(roots) == 32 && bytes.Equal(roots, root[:]) {
return []byte{}, nil
}
if len(roots)%32 != 0 {
return nil, errors.Wrapf(errMisalignedRootList, "root list len=%d", len(roots))
}
search := root[:]
for i := 0; i <= len(roots)-32; i += 32 {
if bytes.Equal(roots[i:i+32], search) {
result := make([]byte, len(roots)-32)
copy(result, roots[:i])
copy(result[i:], roots[i+32:])
return result, nil
}
}
return roots, nil
}

View File

@@ -1,7 +1,6 @@
package kv
import (
"bytes"
"context"
"crypto/rand"
"testing"
@@ -196,85 +195,3 @@ func TestSplitRoots(t *testing.T) {
})
}
}
func tPad(p ...[]byte) []byte {
r := make([]byte, 32*len(p))
for i, b := range p {
copy(r[i*32:], b)
}
return r
}
func TestRemoveRoot(t *testing.T) {
cases := []struct {
name string
roots []byte
root [32]byte
expect []byte
err error
}{
{
name: "empty",
roots: []byte{},
root: [32]byte{0xde, 0xad, 0xbe, 0xef},
expect: []byte{},
},
{
name: "single",
roots: tPad([]byte{0xde, 0xad, 0xbe, 0xef}),
root: [32]byte{0xde, 0xad, 0xbe, 0xef},
expect: []byte{},
},
{
name: "single, different",
roots: tPad([]byte{0xde, 0xad, 0xbe, 0xef}),
root: [32]byte{0xde, 0xad, 0xbe, 0xee},
expect: tPad([]byte{0xde, 0xad, 0xbe, 0xef}),
},
{
name: "multi",
roots: tPad([]byte{0xde, 0xad, 0xbe, 0xef}, []byte{0xac, 0x1d, 0xfa, 0xce}),
root: [32]byte{0xac, 0x1d, 0xfa, 0xce},
expect: tPad([]byte{0xde, 0xad, 0xbe, 0xef}),
},
{
name: "multi, reordered",
roots: tPad([]byte{0xac, 0x1d, 0xfa, 0xce}, []byte{0xde, 0xad, 0xbe, 0xef}),
root: [32]byte{0xac, 0x1d, 0xfa, 0xce},
expect: tPad([]byte{0xde, 0xad, 0xbe, 0xef}),
},
{
name: "multi, 3",
roots: tPad([]byte{0xac, 0x1d, 0xfa, 0xce}, []byte{0xbe, 0xef, 0xca, 0xb5}, []byte{0xde, 0xad, 0xbe, 0xef}),
root: [32]byte{0xac, 0x1d, 0xfa, 0xce},
expect: tPad([]byte{0xbe, 0xef, 0xca, 0xb5}, []byte{0xde, 0xad, 0xbe, 0xef}),
},
{
name: "multi, different",
roots: tPad([]byte{0xde, 0xad, 0xbe, 0xef}, []byte{0xac, 0x1d, 0xfa, 0xce}),
root: [32]byte{0xac, 0x1d, 0xbe, 0xa7},
expect: tPad([]byte{0xde, 0xad, 0xbe, 0xef}, []byte{0xac, 0x1d, 0xfa, 0xce}),
},
{
name: "misaligned",
roots: make([]byte, 61),
root: [32]byte{0xac, 0x1d, 0xbe, 0xa7},
err: errMisalignedRootList,
},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
before := make([]byte, len(c.roots))
copy(before, c.roots)
r, err := removeRoot(c.roots, c.root)
if c.err != nil {
require.ErrorIs(t, err, c.err)
return
}
require.NoError(t, err)
require.Equal(t, len(c.expect), len(r))
require.Equal(t, true, bytes.Equal(c.expect, r))
require.Equal(t, true, bytes.Equal(before, c.roots))
})
}
}

View File

@@ -1,10 +1,8 @@
package kv
import (
"bytes"
"context"
"github.com/prysmaticlabs/prysm/v5/config/params"
"github.com/prysmaticlabs/prysm/v5/monitoring/tracing/trace"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
bolt "go.etcd.io/bbolt"
@@ -21,17 +19,7 @@ func (s *Store) LastValidatedCheckpoint(ctx context.Context) (*ethpb.Checkpoint,
if enc == nil {
var finErr error
checkpoint, finErr = s.FinalizedCheckpoint(ctx)
if finErr != nil {
return finErr
}
if bytes.Equal(checkpoint.Root, params.BeaconConfig().ZeroHash[:]) {
bkt = tx.Bucket(blocksBucket)
r := bkt.Get(genesisBlockRootKey)
if r != nil {
checkpoint.Root = r
}
}
return nil
return finErr
}
checkpoint = &ethpb.Checkpoint{}
return decode(ctx, enc, checkpoint)

View File

@@ -16,15 +16,6 @@ import (
var log = logrus.WithField("prefix", "db-pruner")
const (
// defaultPrunableBatchSize is the number of slots that can be pruned at once.
defaultPrunableBatchSize = 32
// defaultPruningWindow is the duration of one pruning window.
defaultPruningWindow = time.Second * 3
// defaultNumBatchesToPrune is the number of batches to prune in one pruning window.
defaultNumBatchesToPrune = 15
)
type ServiceOption func(*Service)
// WithRetentionPeriod allows the user to specify a different data retention period than the spec default.
@@ -152,17 +143,14 @@ func (p *Service) prune(slot primitives.Slot) error {
}).Debug("Pruning chain data")
tt := time.Now()
numBatches, err := p.pruneBatches(pruneUpto)
if err != nil {
return errors.Wrap(err, "failed to prune batches")
if err := p.db.DeleteHistoricalDataBeforeSlot(p.ctx, pruneUpto); err != nil {
return errors.Wrapf(err, "could not delete upto slot %d", pruneUpto)
}
log.WithFields(logrus.Fields{
"prunedUpto": pruneUpto,
"duration": time.Since(tt),
"currentSlot": slot,
"batchSize": defaultPrunableBatchSize,
"numBatches": numBatches,
}).Debug("Successfully pruned chain data")
// Update pruning checkpoint.
@@ -171,33 +159,6 @@ func (p *Service) prune(slot primitives.Slot) error {
return nil
}
func (p *Service) pruneBatches(pruneUpto primitives.Slot) (int, error) {
ctx, cancel := context.WithTimeout(p.ctx, defaultPruningWindow)
defer cancel()
numBatches := 0
for {
select {
case <-ctx.Done():
return numBatches, nil
default:
for i := 0; i < defaultNumBatchesToPrune; i++ {
slotsDeleted, err := p.db.DeleteHistoricalDataBeforeSlot(ctx, pruneUpto, defaultPrunableBatchSize)
if err != nil {
return 0, errors.Wrapf(err, "could not delete upto slot %d", pruneUpto)
}
// Return if there's nothing to delete.
if slotsDeleted == 0 {
return numBatches, nil
}
numBatches++
}
}
}
}
// pruneStartSlotFunc returns the function to determine the start slot to start pruning.
func pruneStartSlotFunc(retentionEpochs primitives.Epoch) func(primitives.Slot) primitives.Slot {
return func(current primitives.Slot) primitives.Slot {

View File

@@ -156,10 +156,6 @@ func (n *Node) nodeTreeDump(ctx context.Context, nodes []*forkchoice2.Node) ([]*
if n.parent != nil {
parentRoot = n.parent.root
}
target := [32]byte{}
if n.target != nil {
target = n.target.root
}
thisNode := &forkchoice2.Node{
Slot: n.slot,
BlockRoot: n.root[:],
@@ -173,7 +169,6 @@ func (n *Node) nodeTreeDump(ctx context.Context, nodes []*forkchoice2.Node) ([]*
ExecutionOptimistic: n.optimistic,
ExecutionBlockHash: n.payloadHash[:],
Timestamp: n.timestamp,
Target: target[:],
}
if n.optimistic {
thisNode.Validity = forkchoice2.Optimistic

View File

@@ -122,7 +122,6 @@ type BeaconNode struct {
BlobStorageOptions []filesystem.BlobStorageOption
verifyInitWaiter *verification.InitializerWaiter
syncChecker *initialsync.SyncChecker
slasherEnabled bool
}
// New creates a new node instance, sets up configuration options, and registers
@@ -160,7 +159,6 @@ func New(cliCtx *cli.Context, cancel context.CancelFunc, opts ...Option) (*Beaco
serviceFlagOpts: &serviceFlagOpts{},
initialSyncComplete: make(chan struct{}),
syncChecker: &initialsync.SyncChecker{},
slasherEnabled: cliCtx.Bool(flags.SlasherFlag.Name),
}
for _, opt := range opts {
@@ -344,7 +342,7 @@ func registerServices(cliCtx *cli.Context, beacon *BeaconNode, synchronizer *sta
return errors.Wrap(err, "could not register slashing pool service")
}
log.WithField("enabled", beacon.slasherEnabled).Debugln("Registering Slasher Service")
log.Debugln("Registering Slasher Service")
if err := beacon.registerSlasherService(); err != nil {
return errors.Wrap(err, "could not register slasher service")
}
@@ -589,7 +587,7 @@ func (b *BeaconNode) startDB(cliCtx *cli.Context, depositAddress string) error {
}
func (b *BeaconNode) startSlasherDB(cliCtx *cli.Context) error {
if !b.slasherEnabled {
if !features.Get().EnableSlasher {
return nil
}
baseDir := cliCtx.String(cmd.DataDirFlag.Name)
@@ -777,7 +775,6 @@ func (b *BeaconNode) registerBlockchainService(fc forkchoice.ForkChoicer, gs *st
blockchain.WithTrackedValidatorsCache(b.trackedValidatorsCache),
blockchain.WithPayloadIDCache(b.payloadIDCache),
blockchain.WithSyncChecker(b.syncChecker),
blockchain.WithSlasherEnabled(b.slasherEnabled),
)
blockchainService, err := blockchain.NewService(b.ctx, opts...)
@@ -862,7 +859,6 @@ func (b *BeaconNode) registerSyncService(initialSyncComplete chan struct{}, bFil
regularsync.WithBlobStorage(b.BlobStorage),
regularsync.WithVerifierWaiter(b.verifyInitWaiter),
regularsync.WithAvailableBlocker(bFillStore),
regularsync.WithSlasherEnabled(b.slasherEnabled),
)
return b.services.RegisterService(rs)
}
@@ -891,7 +887,7 @@ func (b *BeaconNode) registerInitialSyncService(complete chan struct{}) error {
}
func (b *BeaconNode) registerSlasherService() error {
if !b.slasherEnabled {
if !features.Get().EnableSlasher {
return nil
}
var chainService *blockchain.Service
@@ -938,7 +934,7 @@ func (b *BeaconNode) registerRPCService(router *http.ServeMux) error {
}
var slasherService *slasher.Service
if b.slasherEnabled {
if features.Get().EnableSlasher {
if err := b.services.FetchService(&slasherService); err != nil {
return err
}

View File

@@ -6,6 +6,7 @@ import (
"github.com/prysmaticlabs/go-bitfield"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1/attestation"
log "github.com/sirupsen/logrus"
)
func (c *AttCaches) insertSeenBit(att ethpb.Att) error {
@@ -54,6 +55,10 @@ func (c *AttCaches) hasSeenBit(att ethpb.Att) (bool, error) {
}
for _, bit := range seenBits {
if c, err := bit.Contains(att.GetAggregationBits()); err != nil {
log.WithFields(log.Fields{
"bitLength": bit.Len(),
"incomingBitLength": att.GetAggregationBits().Len(),
}).Error("could not check if bit contains aggregation bits")
return false, err
} else if c {
return true, nil

View File

@@ -61,7 +61,12 @@ func (c *AttCaches) UnaggregatedAttestations() ([]ethpb.Att, error) {
for _, att := range unAggregatedAtts {
seen, err := c.hasSeenBit(att)
if err != nil {
log.WithError(err).Debug("Could not check if unaggregated attestation's bit has been seen. Attestation will not be returned")
log.WithFields(log.Fields{
"slot": att.GetData().Slot,
"index": att.GetData().CommitteeIndex,
"version": att.Version(),
"aggBits": att.GetAggregationBits(),
}).WithError(err).Error("could not check if attestation has been seen")
continue
}
if !seen {
@@ -139,7 +144,7 @@ func (c *AttCaches) DeleteUnaggregatedAttestation(att ethpb.Att) error {
}
if err := c.insertSeenBit(att); err != nil {
log.WithError(err).Debug("Could not insert seen bit of unaggregated attestation. Attestation will be deleted")
return err
}
id, err := attestation.NewId(att, attestation.Full)
@@ -167,8 +172,9 @@ func (c *AttCaches) DeleteSeenUnaggregatedAttestations() (int, error) {
}
seen, err := c.hasSeenBit(att)
if err != nil {
log.WithError(err).Debug("Could not check if unaggregated attestation's bit has been seen. Attestation will be deleted")
seen = true
log.WithError(err).Debug("Could not check if attestations bits have been seen")
delete(c.unAggregatedAtt, r)
count++
}
if seen {
delete(c.unAggregatedAtt, r)

View File

@@ -17,24 +17,6 @@ import (
"github.com/prysmaticlabs/prysm/v5/testing/util"
)
func TestKV_Unaggregated_UnaggregatedAttestations(t *testing.T) {
t.Run("not returned when hasSeenBit fails", func(t *testing.T) {
att := util.HydrateAttestation(&ethpb.Attestation{Data: &ethpb.AttestationData{Slot: 1}, AggregationBits: bitfield.Bitlist{0b101}})
id, err := attestation.NewId(att, attestation.Data)
require.NoError(t, err)
cache := NewAttCaches()
require.NoError(t, cache.SaveUnaggregatedAttestation(att))
cache.seenAtt.Delete(id.String())
// cache a bitlist whose length is different from the attestation bitlist's length
cache.seenAtt.Set(id.String(), []bitfield.Bitlist{{0b1001}}, c.DefaultExpiration)
atts, err := cache.UnaggregatedAttestations()
require.NoError(t, err)
assert.Equal(t, 0, len(atts))
})
}
func TestKV_Unaggregated_SaveUnaggregatedAttestation(t *testing.T) {
tests := []struct {
name string
@@ -173,21 +155,6 @@ func TestKV_Unaggregated_DeleteUnaggregatedAttestation(t *testing.T) {
require.NoError(t, err)
assert.DeepEqual(t, []ethpb.Att{}, returned)
})
t.Run("deleted when insertSeenBit fails", func(t *testing.T) {
att := util.HydrateAttestation(&ethpb.Attestation{Data: &ethpb.AttestationData{Slot: 1}, AggregationBits: bitfield.Bitlist{0b101}})
id, err := attestation.NewId(att, attestation.Data)
require.NoError(t, err)
cache := NewAttCaches()
require.NoError(t, cache.SaveUnaggregatedAttestation(att))
cache.seenAtt.Delete(id.String())
// cache a bitlist whose length is different from the attestation bitlist's length
cache.seenAtt.Set(id.String(), []bitfield.Bitlist{{0b1001}}, c.DefaultExpiration)
require.NoError(t, cache.DeleteUnaggregatedAttestation(att))
assert.Equal(t, 0, len(cache.unAggregatedAtt), "Attestation was not deleted")
})
}
func TestKV_Unaggregated_DeleteSeenUnaggregatedAttestations(t *testing.T) {
@@ -265,23 +232,6 @@ func TestKV_Unaggregated_DeleteSeenUnaggregatedAttestations(t *testing.T) {
require.NoError(t, err)
assert.DeepEqual(t, []ethpb.Att{}, returned)
})
t.Run("deleted when hasSeenBit fails", func(t *testing.T) {
att := util.HydrateAttestation(&ethpb.Attestation{Data: &ethpb.AttestationData{Slot: 1}, AggregationBits: bitfield.Bitlist{0b101}})
id, err := attestation.NewId(att, attestation.Data)
require.NoError(t, err)
cache := NewAttCaches()
require.NoError(t, cache.SaveUnaggregatedAttestation(att))
cache.seenAtt.Delete(id.String())
// cache a bitlist whose length is different from the attestation bitlist's length
cache.seenAtt.Set(id.String(), []bitfield.Bitlist{{0b1001}}, c.DefaultExpiration)
count, err := cache.DeleteSeenUnaggregatedAttestations()
require.NoError(t, err)
assert.Equal(t, 1, count)
assert.Equal(t, 0, len(cache.unAggregatedAtt), "Attestation was not deleted")
})
}
func TestKV_Unaggregated_UnaggregatedAttestationsBySlotIndex(t *testing.T) {

View File

@@ -36,6 +36,11 @@ func (s *Service) prepareForkChoiceAtts() {
select {
case slotInterval := <-ticker.C():
t := time.Now()
_, err := s.cfg.Pool.UnaggregatedAttestations()
if err != nil {
log.WithError(err).Error("Could not get unaggregated attestations")
continue
}
if err := s.batchForkChoiceAtts(s.ctx); err != nil {
log.WithError(err).Error("Could not prepare attestations for fork choice")
}

View File

@@ -633,7 +633,7 @@ func (s *Server) SubmitBLSToExecutionChanges(w http.ResponseWriter, r *http.Requ
toBroadcast = append(toBroadcast, sbls)
}
}
go s.broadcastBLSChanges(context.Background(), toBroadcast)
go s.broadcastBLSChanges(ctx, toBroadcast)
if len(failures) > 0 {
failuresErr := &server.IndexedVerificationFailureError{
Code: http.StatusBadRequest,

View File

@@ -160,8 +160,6 @@ func TestGetSpec(t *testing.T) {
config.MaxTransactionsPerPayload = 99
config.FieldElementsPerBlob = 100
config.KzgCommitmentInclusionProofDepth = 101
config.BlobsidecarSubnetCount = 102
config.BlobsidecarSubnetCountElectra = 103
var dbp [4]byte
copy(dbp[:], []byte{'0', '0', '0', '1'})
@@ -200,7 +198,7 @@ func TestGetSpec(t *testing.T) {
data, ok := resp.Data.(map[string]interface{})
require.Equal(t, true, ok)
assert.Equal(t, 170, len(data))
assert.Equal(t, 168, len(data))
for k, v := range data {
t.Run(k, func(t *testing.T) {
switch k {
@@ -561,10 +559,6 @@ func TestGetSpec(t *testing.T) {
assert.Equal(t, "100", v)
case "KZG_COMMITMENT_INCLUSION_PROOF_DEPTH":
assert.Equal(t, "101", v)
case "BLOB_SIDECAR_SUBNET_COUNT":
assert.Equal(t, "102", v)
case "BLOB_SIDECAR_SUBNET_COUNT_ELECTRA":
assert.Equal(t, "103", v)
default:
t.Errorf("Incorrect key: %s", k)
}

View File

@@ -190,7 +190,6 @@ func (s *Server) GetForkChoice(w http.ResponseWriter, r *http.Request) {
Balance: fmt.Sprintf("%d", n.Balance),
ExecutionOptimistic: n.ExecutionOptimistic,
TimeStamp: fmt.Sprintf("%d", n.Timestamp),
Target: fmt.Sprintf("%#x", n.Target),
},
}
}

View File

@@ -18,8 +18,7 @@ go_library(
"//beacon-chain/core/feed/operation:go_default_library",
"//beacon-chain/core/feed/state:go_default_library",
"//beacon-chain/core/helpers:go_default_library",
"//beacon-chain/core/transition:go_default_library",
"//beacon-chain/state:go_default_library",
"//beacon-chain/core/time:go_default_library",
"//config/params:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//consensus-types/payload-attribute:go_default_library",
@@ -59,7 +58,6 @@ go_test(
"//consensus-types/interfaces:go_default_library",
"//consensus-types/payload-attribute:go_default_library",
"//consensus-types/primitives:go_default_library",
"//proto/engine/v1:go_default_library",
"//proto/eth/v1:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//testing/require:go_default_library",

View File

@@ -7,7 +7,6 @@ import (
"fmt"
"io"
"net/http"
"runtime/debug"
"strconv"
"time"
@@ -21,8 +20,7 @@ import (
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/feed/operation"
statefeed "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/feed/state"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/transition"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
chaintime "github.com/prysmaticlabs/prysm/v5/beacon-chain/core/time"
"github.com/prysmaticlabs/prysm/v5/config/params"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
payloadattribute "github.com/prysmaticlabs/prysm/v5/consensus-types/payload-attribute"
@@ -354,18 +352,9 @@ func writeLazyReaderWithRecover(w *streamingResponseWriterController, lr lazyRea
if r := recover(); r != nil {
log.WithField("panic", r).Error("Recovered from panic while writing event to client.")
err = errWriterUnusable
debug.PrintStack()
}
}()
if lr == nil {
log.Warn("Event stream skipping a nil lazy event reader callback")
return nil
}
r := lr()
if r == nil {
log.Warn("Event stream skipping a nil event reader")
return nil
}
out, err := io.ReadAll(r)
if err != nil {
return err
@@ -611,14 +600,27 @@ func (s *Server) lazyReaderForEvent(ctx context.Context, event *feed.Event, topi
var errUnsupportedPayloadAttribute = errors.New("cannot compute payload attributes pre-Bellatrix")
func (s *Server) computePayloadAttributes(ctx context.Context, st state.ReadOnlyBeaconState, root [32]byte, proposer primitives.ValidatorIndex, timestamp uint64, randao []byte) (payloadattribute.Attributer, error) {
v := st.Version()
func (s *Server) computePayloadAttributes(ctx context.Context, ev payloadattribute.EventData) (payloadattribute.Attributer, error) {
v := ev.HeadState.Version()
if v < version.Bellatrix {
return nil, errors.Wrapf(errUnsupportedPayloadAttribute, "%s is not supported", version.String(v))
}
t, err := slots.ToTime(ev.HeadState.GenesisTime(), ev.HeadState.Slot())
if err != nil {
return nil, errors.Wrap(err, "could not get head state slot time")
}
timestamp := uint64(t.Unix())
prevRando, err := helpers.RandaoMix(ev.HeadState, chaintime.CurrentEpoch(ev.HeadState))
if err != nil {
return nil, errors.Wrap(err, "could not get head state randao mix")
}
proposerIndex, err := helpers.BeaconProposerIndex(ctx, ev.HeadState)
if err != nil {
return nil, errors.Wrap(err, "could not get head state proposer index")
}
feeRecpt := params.BeaconConfig().DefaultFeeRecipient.Bytes()
tValidator, exists := s.TrackedValidatorsCache.Validator(proposer)
tValidator, exists := s.TrackedValidatorsCache.Validator(proposerIndex)
if exists {
feeRecpt = tValidator.FeeRecipient[:]
}
@@ -626,30 +628,34 @@ func (s *Server) computePayloadAttributes(ctx context.Context, st state.ReadOnly
if v == version.Bellatrix {
return payloadattribute.New(&engine.PayloadAttributes{
Timestamp: timestamp,
PrevRandao: randao,
PrevRandao: prevRando,
SuggestedFeeRecipient: feeRecpt,
})
}
w, _, err := st.ExpectedWithdrawals()
w, _, err := ev.HeadState.ExpectedWithdrawals()
if err != nil {
return nil, errors.Wrap(err, "could not get withdrawals from head state")
}
if v == version.Capella {
return payloadattribute.New(&engine.PayloadAttributesV2{
Timestamp: timestamp,
PrevRandao: randao,
PrevRandao: prevRando,
SuggestedFeeRecipient: feeRecpt,
Withdrawals: w,
})
}
pr, err := ev.HeadBlock.Block().HashTreeRoot()
if err != nil {
return nil, errors.Wrap(err, "could not compute head block root")
}
return payloadattribute.New(&engine.PayloadAttributesV3{
Timestamp: timestamp,
PrevRandao: randao,
PrevRandao: prevRando,
SuggestedFeeRecipient: feeRecpt,
Withdrawals: w,
ParentBeaconBlockRoot: root[:],
ParentBeaconBlockRoot: pr[:],
})
}
@@ -659,75 +665,37 @@ type asyncPayloadAttrData struct {
err error
}
var zeroRoot [32]byte
// needsFill allows tests to provide filled EventData values. An ordinary event data value fired by the blockchain package will have
// all of the checked fields empty, so the logical short circuit should hit immediately.
func needsFill(ev payloadattribute.EventData) bool {
return ev.HeadState == nil || ev.HeadState.IsNil() || ev.HeadState.LatestBlockHeader() == nil ||
ev.HeadBlock == nil || ev.HeadBlock.IsNil() ||
ev.HeadRoot == zeroRoot || len(ev.ParentBlockRoot) == 0 || len(ev.ParentBlockHash) == 0 ||
ev.Attributer == nil || ev.Attributer.IsEmpty()
}
func (s *Server) fillEventData(ctx context.Context, ev payloadattribute.EventData) (payloadattribute.EventData, error) {
var err error
if !needsFill(ev) {
return ev, nil
}
ev.HeadState, err = s.HeadFetcher.HeadState(ctx)
if err != nil {
return ev, errors.Wrap(err, "could not get head state")
}
ev.HeadBlock, err = s.HeadFetcher.HeadBlock(ctx)
if err != nil {
return ev, errors.Wrap(err, "could not look up head block")
}
ev.HeadRoot, err = ev.HeadBlock.Block().HashTreeRoot()
if err != nil {
return ev, errors.Wrap(err, "could not compute head block root")
}
pr := ev.HeadBlock.Block().ParentRoot()
ev.ParentBlockRoot = pr[:]
hsr, err := ev.HeadState.LatestBlockHeader().HashTreeRoot()
if err != nil {
return ev, errors.Wrap(err, "could not compute latest block header root")
}
pse := slots.ToEpoch(ev.ProposalSlot)
st := ev.HeadState
if slots.ToEpoch(st.Slot()) != pse {
st, err = transition.ProcessSlotsUsingNextSlotCache(ctx, st, hsr[:], ev.ProposalSlot)
if ev.HeadBlock == nil || ev.HeadBlock.IsNil() {
hb, err := s.HeadFetcher.HeadBlock(ctx)
if err != nil {
return ev, errors.Wrap(err, "could not run process blocks on head state into the proposal slot epoch")
return ev, errors.Wrap(err, "Could not look up head block")
}
}
ev.ProposerIndex, err = helpers.BeaconProposerIndexAtSlot(ctx, st, ev.ProposalSlot)
if err != nil {
return ev, errors.Wrap(err, "failed to compute proposer index")
}
randao, err := helpers.RandaoMix(st, pse)
if err != nil {
return ev, errors.Wrap(err, "could not get head state randado")
root, err := hb.Block().HashTreeRoot()
if err != nil {
return ev, errors.Wrap(err, "Could not compute head block root")
}
if ev.HeadRoot != root {
return ev, errors.Wrap(err, "head root changed before payload attribute event handler execution")
}
ev.HeadBlock = hb
payload, err := hb.Block().Body().Execution()
if err != nil {
return ev, errors.Wrap(err, "Could not get execution payload for head block")
}
ev.ParentBlockHash = payload.BlockHash()
ev.ParentBlockNumber = payload.BlockNumber()
}
payload, err := ev.HeadBlock.Block().Body().Execution()
if err != nil {
return ev, errors.Wrap(err, "could not get execution payload for head block")
attr := ev.Attributer
if attr == nil || attr.IsEmpty() {
attr, err := s.computePayloadAttributes(ctx, ev)
if err != nil {
return ev, errors.Wrap(err, "Could not compute payload attributes")
}
ev.Attributer = attr
}
ev.ParentBlockHash = payload.BlockHash()
ev.ParentBlockNumber = payload.BlockNumber()
t, err := slots.ToTime(st.GenesisTime(), ev.ProposalSlot)
if err != nil {
return ev, errors.Wrap(err, "could not get head state slot time")
}
ev.Attributer, err = s.computePayloadAttributes(ctx, st, hsr, ev.ProposerIndex, uint64(t.Unix()), randao)
return ev, err
return ev, nil
}
// This event stream is intended to be used by builders and relays.
@@ -736,7 +704,10 @@ func (s *Server) payloadAttributesReader(ctx context.Context, ev payloadattribut
ctx, cancel := context.WithTimeout(ctx, payloadAttributeTimeout)
edc := make(chan asyncPayloadAttrData)
go func() {
d := asyncPayloadAttrData{}
d := asyncPayloadAttrData{
version: version.String(ev.HeadState.Version()),
}
defer func() {
edc <- d
}()
@@ -745,7 +716,6 @@ func (s *Server) payloadAttributesReader(ctx context.Context, ev payloadattribut
d.err = errors.Wrap(err, "Could not fill event data")
return
}
d.version = version.String(ev.HeadBlock.Version())
attributesBytes, err := marshalAttributes(ev.Attributer)
if err != nil {
d.err = errors.Wrap(err, "errors marshaling payload attributes to json")

View File

@@ -2,7 +2,6 @@ package events
import (
"context"
"encoding/binary"
"fmt"
"io"
"math"
@@ -25,7 +24,6 @@ import (
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
payloadattribute "github.com/prysmaticlabs/prysm/v5/consensus-types/payload-attribute"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
enginev1 "github.com/prysmaticlabs/prysm/v5/proto/engine/v1"
ethpb "github.com/prysmaticlabs/prysm/v5/proto/eth/v1"
eth "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v5/testing/require"
@@ -559,110 +557,6 @@ func TestStreamEvents_OperationsEvents(t *testing.T) {
})
}
func TestFillEventData(t *testing.T) {
ctx := context.Background()
t.Run("AlreadyFilledData_ShouldShortCircuitWithoutError", func(t *testing.T) {
st, err := util.NewBeaconStateBellatrix()
require.NoError(t, err)
b, err := blocks.NewSignedBeaconBlock(util.HydrateSignedBeaconBlockBellatrix(&eth.SignedBeaconBlockBellatrix{}))
require.NoError(t, err)
attributor, err := payloadattribute.New(&enginev1.PayloadAttributes{
Timestamp: uint64(time.Now().Unix()),
})
require.NoError(t, err)
alreadyFilled := payloadattribute.EventData{
HeadState: st,
HeadBlock: b,
HeadRoot: [32]byte{1, 2, 3},
Attributer: attributor,
ParentBlockRoot: []byte{1, 2, 3},
ParentBlockHash: []byte{4, 5, 6},
}
srv := &Server{} // No real HeadFetcher needed here since it won't be called.
result, err := srv.fillEventData(ctx, alreadyFilled)
require.NoError(t, err)
require.DeepEqual(t, alreadyFilled, result)
})
t.Run("Electra PartialData_ShouldFetchHeadStateAndBlock", func(t *testing.T) {
st, err := util.NewBeaconStateElectra()
require.NoError(t, err)
valCount := 10
setActiveValidators(t, st, valCount)
inactivityScores := make([]uint64, valCount)
for i := range inactivityScores {
inactivityScores[i] = 10
}
require.NoError(t, st.SetInactivityScores(inactivityScores))
b, err := blocks.NewSignedBeaconBlock(util.HydrateSignedBeaconBlockElectra(&eth.SignedBeaconBlockElectra{}))
require.NoError(t, err)
attributor, err := payloadattribute.New(&enginev1.PayloadAttributes{
Timestamp: uint64(time.Now().Unix()),
})
require.NoError(t, err)
// Create an event data object missing certain fields:
partial := payloadattribute.EventData{
// The presence of a nil HeadState, nil HeadBlock, zeroed HeadRoot, etc.
// will cause fillEventData to try to fill the values.
ProposalSlot: 42, // different epoch from current slot
Attributer: attributor, // Must be Bellatrix or later
}
currentSlot := primitives.Slot(0)
// to avoid slot processing
require.NoError(t, st.SetSlot(currentSlot+1))
mockChainService := &mockChain.ChainService{
Root: make([]byte, 32),
State: st,
Block: b,
Slot: &currentSlot,
}
stn := mockChain.NewEventFeedWrapper()
opn := mockChain.NewEventFeedWrapper()
srv := &Server{
StateNotifier: &mockChain.SimpleNotifier{Feed: stn},
OperationNotifier: &mockChain.SimpleNotifier{Feed: opn},
HeadFetcher: mockChainService,
ChainInfoFetcher: mockChainService,
TrackedValidatorsCache: cache.NewTrackedValidatorsCache(),
EventWriteTimeout: testEventWriteTimeout,
}
filled, err := srv.fillEventData(ctx, partial)
require.NoError(t, err, "expected successful fill of partial event data")
// Verify that fields have been updated from the mock data:
require.NotNil(t, filled.HeadState, "HeadState should be assigned")
require.NotNil(t, filled.HeadBlock, "HeadBlock should be assigned")
require.NotEqual(t, [32]byte{}, filled.HeadRoot, "HeadRoot should no longer be zero")
require.NotEmpty(t, filled.ParentBlockRoot, "ParentBlockRoot should be filled")
require.NotEmpty(t, filled.ParentBlockHash, "ParentBlockHash should be filled")
require.Equal(t, uint64(0), filled.ParentBlockNumber, "ParentBlockNumber must match mock block")
// Check that a valid Attributer was set:
require.NotNil(t, filled.Attributer, "Should have a valid payload attributes object")
require.Equal(t, false, filled.Attributer.IsEmpty(), "Attributer should not be empty after fill")
})
}
func setActiveValidators(t *testing.T, st state.BeaconState, count int) {
balances := make([]uint64, count)
validators := make([]*eth.Validator, 0, count)
for i := 0; i < count; i++ {
pubKey := make([]byte, params.BeaconConfig().BLSPubkeyLength)
binary.LittleEndian.PutUint64(pubKey, uint64(i))
balances[i] = uint64(i)
validators = append(validators, &eth.Validator{
PublicKey: pubKey,
ActivationEpoch: 0,
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
WithdrawalCredentials: make([]byte, 32),
})
}
require.NoError(t, st.SetValidators(validators))
require.NoError(t, st.SetBalances(balances))
}
func TestStuckReaderScenarios(t *testing.T) {
cases := []struct {
name string

View File

@@ -636,16 +636,6 @@ func (s *Server) ProduceSyncCommitteeContribution(w http.ResponseWriter, r *http
ctx, span := trace.StartSpan(r.Context(), "validator.ProduceSyncCommitteeContribution")
defer span.End()
isOptimistic, err := s.OptimisticModeFetcher.IsOptimistic(ctx)
if err != nil {
httputil.HandleError(w, err.Error(), http.StatusInternalServerError)
return
}
if isOptimistic {
httputil.HandleError(w, "Beacon node is currently syncing and not serving request on that endpoint", http.StatusServiceUnavailable)
return
}
_, index, ok := shared.UintFromQuery(w, r, "subcommittee_index", true)
if !ok {
return

View File

@@ -1584,8 +1584,7 @@ func TestProduceSyncCommitteeContribution(t *testing.T) {
SyncCommitteeIndices: []primitives.CommitteeIndex{0},
},
},
SyncCommitteePool: syncCommitteePool,
OptimisticModeFetcher: &mockChain.ChainService{},
SyncCommitteePool: syncCommitteePool,
}
t.Run("ok", func(t *testing.T) {
url := "http://example.com?slot=1&subcommittee_index=1&beacon_block_root=0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
@@ -1673,8 +1672,7 @@ func TestProduceSyncCommitteeContribution(t *testing.T) {
SyncCommitteeIndices: []primitives.CommitteeIndex{0},
},
},
SyncCommitteePool: syncCommitteePool,
OptimisticModeFetcher: &mockChain.ChainService{},
SyncCommitteePool: syncCommitteePool,
}
server.ProduceSyncCommitteeContribution(writer, request)
assert.Equal(t, http.StatusNotFound, writer.Code)
@@ -1682,26 +1680,6 @@ func TestProduceSyncCommitteeContribution(t *testing.T) {
require.NoError(t, json.Unmarshal(writer.Body.Bytes(), resp2))
require.ErrorContains(t, "No subcommittee messages found", errors.New(writer.Body.String()))
})
t.Run("Optimistic returns 503", func(t *testing.T) {
url := "http://example.com?slot=1&subcommittee_index=1&beacon_block_root=0xcf8e0d4e9587369b2301d0790347320302cc0943d5a1884560367e8208d920f2"
request := httptest.NewRequest(http.MethodGet, url, nil)
writer := httptest.NewRecorder()
writer.Body = &bytes.Buffer{}
syncCommitteePool = synccommittee.NewStore()
server = Server{
CoreService: &core.Service{
HeadFetcher: &mockChain.ChainService{
SyncCommitteeIndices: []primitives.CommitteeIndex{0},
},
},
SyncCommitteePool: syncCommitteePool,
OptimisticModeFetcher: &mockChain.ChainService{
Optimistic: true,
},
}
server.ProduceSyncCommitteeContribution(writer, request)
assert.Equal(t, http.StatusServiceUnavailable, writer.Code)
})
}
func TestServer_RegisterValidator(t *testing.T) {

View File

@@ -39,13 +39,6 @@ func (vs *Server) packAttestations(ctx context.Context, latestState state.Beacon
} else {
atts = vs.AttPool.AggregatedAttestations()
atts = vs.validateAndDeleteAttsInPool(ctx, latestState, atts)
uAtts, err := vs.AttPool.UnaggregatedAttestations()
if err != nil {
return nil, errors.Wrap(err, "could not get unaggregated attestations")
}
uAtts = vs.validateAndDeleteAttsInPool(ctx, latestState, uAtts)
atts = append(atts, uAtts...)
}
// Checking the state's version here will give the wrong result if the last slot of Deneb is missed.

View File

@@ -121,10 +121,9 @@ func (s *State) Resume(ctx context.Context, fState state.BeaconState) (state.Bea
return nil, err
}
fRoot := bytesutil.ToBytes32(c.Root)
st := fState
// Resume as genesis state if last finalized root is zero hashes.
if fRoot == params.BeaconConfig().ZeroHash {
st, err = s.beaconDB.GenesisState(ctx)
st, err := s.beaconDB.GenesisState(ctx)
if err != nil {
return nil, errors.Wrap(err, "could not get genesis state")
}
@@ -133,13 +132,10 @@ func (s *State) Resume(ctx context.Context, fState state.BeaconState) (state.Bea
if err != nil {
return nil, stderrors.Join(ErrNoGenesisBlock, err)
}
fRoot = gbr
if err := s.SaveState(ctx, gbr, st); err != nil {
return nil, errors.Wrap(err, "could not save genesis state")
}
return st, s.SaveState(ctx, gbr, st)
}
if st == nil || st.IsNil() {
if fState == nil || fState.IsNil() {
return nil, errors.New("finalized state is nil")
}
@@ -149,22 +145,20 @@ func (s *State) Resume(ctx context.Context, fState state.BeaconState) (state.Bea
}
}()
s.finalizedInfo = &finalizedInfo{slot: st.Slot(), root: fRoot, state: st.Copy()}
populatePubkeyCache(ctx, st)
return st, nil
}
s.finalizedInfo = &finalizedInfo{slot: fState.Slot(), root: fRoot, state: fState.Copy()}
fEpoch := slots.ToEpoch(fState.Slot())
func populatePubkeyCache(ctx context.Context, st state.BeaconState) {
epoch := slots.ToEpoch(st.Slot())
// Pre-populate the pubkey cache with the validator public keys from the finalized state.
// This process takes about 30 seconds on mainnet with 450,000 validators.
go populatePubkeyCacheOnce.Do(func() {
log.Debug("Populating pubkey cache")
start := time.Now()
if err := st.ReadFromEveryValidator(func(_ int, val state.ReadOnlyValidator) error {
if err := fState.ReadFromEveryValidator(func(_ int, val state.ReadOnlyValidator) error {
if ctx.Err() != nil {
return ctx.Err()
}
// Do not cache for non-active validators.
if !helpers.IsActiveValidatorUsingTrie(val, epoch) {
if !helpers.IsActiveValidatorUsingTrie(val, fEpoch) {
return nil
}
pub := val.PublicKey()
@@ -175,6 +169,8 @@ func populatePubkeyCache(ctx context.Context, st state.BeaconState) {
}
log.WithField("duration", time.Since(start)).Debug("Done populating pubkey cache")
})
return fState, nil
}
// SaveFinalizedState saves the finalized slot, root and state into memory to be used by state gen service.

View File

@@ -38,6 +38,7 @@ go_library(
"//consensus-types/primitives:go_default_library",
"//container/leaky-bucket:go_default_library",
"//crypto/rand:go_default_library",
"//encoding/bytesutil:go_default_library",
"//math:go_default_library",
"//monitoring/tracing/trace:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",

View File

@@ -2,6 +2,7 @@ package initialsync
import (
"context"
"encoding/hex"
"fmt"
"sort"
"strings"
@@ -24,6 +25,7 @@ import (
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
leakybucket "github.com/prysmaticlabs/prysm/v5/container/leaky-bucket"
"github.com/prysmaticlabs/prysm/v5/crypto/rand"
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/v5/math"
"github.com/prysmaticlabs/prysm/v5/monitoring/tracing/trace"
p2ppb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1"
@@ -67,6 +69,8 @@ var (
// Period to calculate expected limit for a single peer.
var blockLimiterPeriod = 30 * time.Second
type isBannedBlock func(root [32]byte) bool
// blocksFetcherConfig is a config to setup the block fetcher.
type blocksFetcherConfig struct {
clock *startup.Clock
@@ -101,6 +105,7 @@ type blocksFetcher struct {
capacityWeight float64 // how remaining capacity affects peer selection
mode syncMode // allows to use fetcher in different sync scenarios
quit chan struct{} // termination notifier
isBannedBlock isBannedBlock
}
// peerLock restricts fetcher actions on per peer basis. Currently, used for rate limiting.
@@ -126,6 +131,13 @@ type fetchRequestResponse struct {
err error
}
// set in init()
var holeskyBadRoot [32]byte
func isHoleskyBannedBlock(root [32]byte) bool {
return root == holeskyBadRoot
}
// newBlocksFetcher creates ready to use fetcher.
func newBlocksFetcher(ctx context.Context, cfg *blocksFetcherConfig) *blocksFetcher {
blockBatchLimit := maxBatchLimit()
@@ -160,6 +172,7 @@ func newBlocksFetcher(ctx context.Context, cfg *blocksFetcherConfig) *blocksFetc
capacityWeight: capacityWeight,
mode: cfg.mode,
quit: make(chan struct{}),
isBannedBlock: isHoleskyBannedBlock,
}
}
@@ -357,6 +370,13 @@ func (f *blocksFetcher) fetchBlocksFromPeer(
log.WithField("peer", p).WithError(err).Debug("invalid BeaconBlocksByRange response")
continue
}
if f.isBannedBlock != nil {
for _, b := range robs {
if f.isBannedBlock(b.Block.Root()) {
return nil, p, prysmsync.ErrInvalidFetchedData
}
}
}
return robs, p, err
}
return nil, "", errNoPeersAvailable
@@ -727,3 +747,11 @@ func dedupPeers(peers []peer.ID) []peer.ID {
}
return newPeerList
}
func init() {
bytes, err := hex.DecodeString("2db899881ed8546476d0b92c6aa9110bea9a4cd0dbeb5519eb0ea69575f1f359")
if err != nil {
panic(err)
}
holeskyBadRoot = bytesutil.ToBytes32(bytes)
}

View File

@@ -125,6 +125,7 @@ func (s *Service) syncToNonFinalizedEpoch(ctx context.Context, genesis time.Time
if err != nil {
return err
}
for data := range queue.fetchedData {
s.processFetchedDataRegSync(ctx, genesis, s.cfg.Chain.HeadSlot(), data)
}
@@ -169,6 +170,7 @@ func (s *Service) processFetchedDataRegSync(
"firstSlot": data.bwb[0].Block.Block().Slot(),
"firstUnprocessed": bwb[0].Block.Block().Slot(),
}
for _, b := range bwb {
if err := avs.Persist(s.clock.CurrentSlot(), b.Blobs...); err != nil {
log.WithError(err).WithFields(batchFields).WithFields(syncFields(b.Block)).Warn("Batch failure due to BlobSidecar issues")

View File

@@ -188,11 +188,3 @@ func WithAvailableBlocker(avb coverage.AvailableBlocker) Option {
return nil
}
}
// WithSlasherEnabled configures the sync package to support slashing detection.
func WithSlasherEnabled(enabled bool) Option {
return func(s *Service) error {
s.slasherEnabled = enabled
return nil
}
}

View File

@@ -140,7 +140,8 @@ func (s *Service) processUnaggregated(ctx context.Context, att ethpb.Att) {
data := att.GetData()
// This is an important validation before retrieving attestation pre state to defend against
// attestation's target intentionally referencing a checkpoint that's long ago.
// attestation's target intentionally reference checkpoint that's long ago.
// Verify current finalized checkpoint is an ancestor of the block defined by the attestation's beacon block root.
if !s.cfg.chain.InForkchoice(bytesutil.ToBytes32(data.BeaconBlockRoot)) {
log.WithError(blockchain.ErrNotDescendantOfFinalized).Debug("Could not verify finalized consistency")
return
@@ -168,57 +169,35 @@ func (s *Service) processUnaggregated(ctx context.Context, att ethpb.Att) {
return
}
// Decide if the attestation is an Electra SingleAttestation or a Phase0 unaggregated attestation
var (
attForValidation ethpb.Att
broadcastAtt ethpb.Att
eventType feed.EventType
eventData interface{}
)
var singleAtt *ethpb.SingleAttestation
if att.Version() >= version.Electra {
singleAtt, ok := att.(*ethpb.SingleAttestation)
var ok bool
singleAtt, ok = att.(*ethpb.SingleAttestation)
if !ok {
log.Debugf("Attestation has wrong type (expected %T, got %T)", &ethpb.SingleAttestation{}, att)
return
}
// Convert Electra SingleAttestation to unaggregated ElectraAttestation. This is needed because many parts of the codebase assume that attestations have a certain structure and SingleAttestation validates these assumptions.
attForValidation = singleAtt.ToAttestationElectra(committee)
broadcastAtt = singleAtt
eventType = operation.SingleAttReceived
eventData = &operation.SingleAttReceivedData{
Attestation: singleAtt,
}
} else {
// Phase0 attestation
attForValidation = att
broadcastAtt = att
eventType = operation.UnaggregatedAttReceived
eventData = &operation.UnAggregatedAttReceivedData{
Attestation: att,
}
att = singleAtt.ToAttestationElectra(committee)
}
valid, err = s.validateUnaggregatedAttWithState(ctx, attForValidation, preState)
valid, err = s.validateUnaggregatedAttWithState(ctx, att, preState)
if err != nil {
log.WithError(err).Debug("Pending unaggregated attestation failed validation")
return
}
if valid == pubsub.ValidationAccept {
if features.Get().EnableExperimentalAttestationPool {
if err = s.cfg.attestationCache.Add(attForValidation); err != nil {
if err = s.cfg.attestationCache.Add(att); err != nil {
log.WithError(err).Debug("Could not save unaggregated attestation")
return
}
} else {
if err := s.cfg.attPool.SaveUnaggregatedAttestation(attForValidation); err != nil {
if err := s.cfg.attPool.SaveUnaggregatedAttestation(att); err != nil {
log.WithError(err).Debug("Could not save unaggregated attestation")
return
}
}
s.setSeenCommitteeIndicesSlot(data.Slot, attForValidation.GetCommitteeIndex(), attForValidation.GetAggregationBits())
s.setSeenCommitteeIndicesSlot(data.Slot, data.CommitteeIndex, att.GetAggregationBits())
valCount, err := helpers.ActiveValidatorCount(ctx, preState, slots.ToEpoch(data.Slot))
if err != nil {
@@ -226,16 +205,34 @@ func (s *Service) processUnaggregated(ctx context.Context, att ethpb.Att) {
return
}
// Broadcast the final 'broadcastAtt' object
if err := s.cfg.p2p.BroadcastAttestation(ctx, helpers.ComputeSubnetForAttestation(valCount, broadcastAtt), broadcastAtt); err != nil {
// Broadcasting the signed attestation again once a node is able to process it.
var attToBroadcast ethpb.Att
if singleAtt != nil {
attToBroadcast = singleAtt
} else {
attToBroadcast = att
}
if err := s.cfg.p2p.BroadcastAttestation(ctx, helpers.ComputeSubnetForAttestation(valCount, attToBroadcast), attToBroadcast); err != nil {
log.WithError(err).Debug("Could not broadcast")
}
// Feed event notification for other services
s.cfg.attestationNotifier.OperationFeed().Send(&feed.Event{
Type: eventType,
Data: eventData,
})
// Broadcast the unaggregated attestation on a feed to notify other services in the beacon node
// of a received unaggregated attestation.
if singleAtt != nil {
s.cfg.attestationNotifier.OperationFeed().Send(&feed.Event{
Type: operation.SingleAttReceived,
Data: &operation.SingleAttReceivedData{
Attestation: singleAtt,
},
})
} else {
s.cfg.attestationNotifier.OperationFeed().Send(&feed.Event{
Type: operation.UnaggregatedAttReceived,
Data: &operation.UnAggregatedAttReceivedData{
Attestation: att,
},
})
}
}
}

View File

@@ -706,41 +706,3 @@ func Test_attsAreEqual_Committee(t *testing.T) {
assert.Equal(t, false, attsAreEqual(att1, att2))
})
}
func Test_SeenCommitteeIndicesSlot(t *testing.T) {
t.Run("phase 0 success", func(t *testing.T) {
s := &Service{
seenUnAggregatedAttestationCache: lruwrpr.New(1),
}
data := &ethpb.AttestationData{Slot: 1, CommitteeIndex: 44}
att := &ethpb.Attestation{
AggregationBits: bitfield.Bitlist{0x01},
Data: data,
}
s.setSeenCommitteeIndicesSlot(data.Slot, att.GetCommitteeIndex(), att.GetAggregationBits())
b := append(bytesutil.Bytes32(uint64(1)), bytesutil.Bytes32(uint64(44))...)
b = append(b, bytesutil.SafeCopyBytes(att.GetAggregationBits())...)
_, ok := s.seenUnAggregatedAttestationCache.Get(string(b))
require.Equal(t, true, ok)
})
t.Run("electra success", func(t *testing.T) {
s := &Service{
seenUnAggregatedAttestationCache: lruwrpr.New(1),
}
// committee index is 0 post electra for attestation electra
data := &ethpb.AttestationData{Slot: 1, CommitteeIndex: 0}
cb := primitives.NewAttestationCommitteeBits()
cb.SetBitAt(uint64(63), true)
att := &ethpb.AttestationElectra{
AggregationBits: bitfield.Bitlist{0x01},
Data: data,
CommitteeBits: cb,
}
ci := att.GetCommitteeIndex()
s.setSeenCommitteeIndicesSlot(data.Slot, ci, att.GetAggregationBits())
b := append(bytesutil.Bytes32(uint64(1)), bytesutil.Bytes32(uint64(63))...)
b = append(b, bytesutil.SafeCopyBytes(att.GetAggregationBits())...)
_, ok := s.seenUnAggregatedAttestationCache.Get(string(b))
require.Equal(t, true, ok)
})
}

View File

@@ -6,6 +6,7 @@ package sync
import (
"context"
"encoding/hex"
"sync"
"time"
@@ -49,6 +50,9 @@ import (
"github.com/prysmaticlabs/prysm/v5/time/slots"
)
// hack to prevent bad holesky block importation
var badHoleskyRoot [32]byte
var _ runtime.Service = (*Service)(nil)
const (
@@ -164,7 +168,6 @@ type Service struct {
newBlobVerifier verification.NewBlobVerifier
availableBlocker coverage.AvailableBlocker
ctxMap ContextByteVersions
slasherEnabled bool
}
// NewService initializes new regular sync service.
@@ -383,3 +386,13 @@ type Checker interface {
Status() error
Resync() error
}
func init() {
hexStr := "2db899881ed8546476d0b92c6aa9110bea9a4cd0dbeb5519eb0ea69575f1f359"
bytes, err := hex.DecodeString(hexStr)
if err != nil {
log.WithError(err).Error("Could not decode hex string")
return
}
badHoleskyRoot = [32]byte(bytes)
}

View File

@@ -17,6 +17,7 @@ import (
"github.com/prysmaticlabs/prysm/v5/beacon-chain/p2p"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/slasher/types"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v5/config/features"
"github.com/prysmaticlabs/prysm/v5/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
"github.com/prysmaticlabs/prysm/v5/monitoring/tracing"
@@ -33,11 +34,7 @@ import (
// - The attestation is unaggregated -- that is, it has exactly one participating validator (len(get_attesting_indices(state, attestation.data, attestation.aggregation_bits)) == 1).
// - attestation.data.slot is within the last ATTESTATION_PROPAGATION_SLOT_RANGE slots (attestation.data.slot + ATTESTATION_PROPAGATION_SLOT_RANGE >= current_slot >= attestation.data.slot).
// - The signature of attestation is valid.
func (s *Service) validateCommitteeIndexBeaconAttestation(
ctx context.Context,
pid peer.ID,
msg *pubsub.Message,
) (pubsub.ValidationResult, error) {
func (s *Service) validateCommitteeIndexBeaconAttestation(ctx context.Context, pid peer.ID, msg *pubsub.Message) (pubsub.ValidationResult, error) {
if pid == s.cfg.p2p.PeerID() {
return pubsub.ValidationAccept, nil
}
@@ -67,7 +64,6 @@ func (s *Service) validateCommitteeIndexBeaconAttestation(
if err := helpers.ValidateNilAttestation(att); err != nil {
return pubsub.ValidationReject, err
}
data := att.GetData()
// Do not process slot 0 attestations.
@@ -77,7 +73,8 @@ func (s *Service) validateCommitteeIndexBeaconAttestation(
// Attestation's slot is within ATTESTATION_PROPAGATION_SLOT_RANGE and early attestation
// processing tolerance.
if err := helpers.ValidateAttestationTime(data.Slot, s.cfg.clock.GenesisTime(), earlyAttestationProcessingTolerance); err != nil {
if err := helpers.ValidateAttestationTime(data.Slot, s.cfg.clock.GenesisTime(),
earlyAttestationProcessingTolerance); err != nil {
tracing.AnnotateError(span, err)
return pubsub.ValidationIgnore, err
}
@@ -87,11 +84,12 @@ func (s *Service) validateCommitteeIndexBeaconAttestation(
committeeIndex := att.GetCommitteeIndex()
if !s.slasherEnabled {
if !features.Get().EnableSlasher {
// Verify this the first attestation received for the participating validator for the slot.
if s.hasSeenCommitteeIndicesSlot(data.Slot, committeeIndex, att.GetAggregationBits()) {
return pubsub.ValidationIgnore, nil
}
// Reject an attestation if it references an invalid block.
if s.hasBadBlock(bytesutil.ToBytes32(data.BeaconBlockRoot)) ||
s.hasBadBlock(bytesutil.ToBytes32(data.Target.Root)) ||
@@ -101,12 +99,15 @@ func (s *Service) validateCommitteeIndexBeaconAttestation(
}
}
var validationRes pubsub.ValidationResult
// Verify the block being voted and the processed state is in beaconDB and the block has passed validation if it's in the beaconDB.
blockRoot := bytesutil.ToBytes32(data.BeaconBlockRoot)
if !s.hasBlockAndState(ctx, blockRoot) {
return s.saveToPendingAttPool(att)
}
if !s.cfg.chain.InForkchoice(blockRoot) {
if !s.cfg.chain.InForkchoice(bytesutil.ToBytes32(data.BeaconBlockRoot)) {
tracing.AnnotateError(span, blockchain.ErrNotDescendantOfFinalized)
return pubsub.ValidationIgnore, blockchain.ErrNotDescendantOfFinalized
}
@@ -122,12 +123,12 @@ func (s *Service) validateCommitteeIndexBeaconAttestation(
return pubsub.ValidationIgnore, err
}
validationRes, err := s.validateUnaggregatedAttTopic(ctx, att, preState, *msg.Topic)
validationRes, err = s.validateUnaggregatedAttTopic(ctx, att, preState, *msg.Topic)
if validationRes != pubsub.ValidationAccept {
return validationRes, err
}
committee, err := helpers.BeaconCommitteeFromState(ctx, preState, data.Slot, committeeIndex)
committee, err := helpers.BeaconCommitteeFromState(ctx, preState, att.GetData().Slot, committeeIndex)
if err != nil {
tracing.AnnotateError(span, err)
return pubsub.ValidationIgnore, err
@@ -138,42 +139,21 @@ func (s *Service) validateCommitteeIndexBeaconAttestation(
return validationRes, err
}
// Consolidated handling of Electra SingleAttestation vs Phase0 unaggregated attestation
var (
attForValidation eth.Att // what we'll pass to further validation
eventType feed.EventType
eventData interface{}
)
var singleAtt *eth.SingleAttestation
if att.Version() >= version.Electra {
singleAtt, ok := att.(*eth.SingleAttestation)
singleAtt, ok = att.(*eth.SingleAttestation)
if !ok {
return pubsub.ValidationIgnore, fmt.Errorf(
"attestation has wrong type (expected %T, got %T)",
&eth.SingleAttestation{}, att,
)
}
// Convert Electra SingleAttestation to unaggregated ElectraAttestation. This is needed because many parts of the codebase assume that attestations have a certain structure and SingleAttestation validates these assumptions.
attForValidation = singleAtt.ToAttestationElectra(committee)
eventType = operation.SingleAttReceived
eventData = &operation.SingleAttReceivedData{
Attestation: singleAtt,
}
} else {
// Phase0 unaggregated attestation
attForValidation = att
eventType = operation.UnaggregatedAttReceived
eventData = &operation.UnAggregatedAttReceivedData{
Attestation: att,
return pubsub.ValidationIgnore, fmt.Errorf("attestation has wrong type (expected %T, got %T)", &eth.SingleAttestation{}, att)
}
att = singleAtt.ToAttestationElectra(committee)
}
validationRes, err = s.validateUnaggregatedAttWithState(ctx, attForValidation, preState)
validationRes, err = s.validateUnaggregatedAttWithState(ctx, att, preState)
if validationRes != pubsub.ValidationAccept {
return validationRes, err
}
if s.slasherEnabled {
if features.Get().EnableSlasher {
// Feed the indexed attestation to slasher if enabled. This action
// is done in the background to avoid adding more load to this critical code path.
go func() {
@@ -192,7 +172,7 @@ func (s *Service) validateCommitteeIndexBeaconAttestation(
tracing.AnnotateError(span, err)
return
}
indexedAtt, err := attestation.ConvertToIndexed(ctx, attForValidation, committee)
indexedAtt, err := attestation.ConvertToIndexed(ctx, att, committee)
if err != nil {
log.WithError(err).Error("Could not convert to indexed attestation")
tracing.AnnotateError(span, err)
@@ -202,16 +182,27 @@ func (s *Service) validateCommitteeIndexBeaconAttestation(
}()
}
// Notify other services in the beacon node
s.cfg.attestationNotifier.OperationFeed().Send(&feed.Event{
Type: eventType,
Data: eventData,
})
// Broadcast the unaggregated attestation on a feed to notify other services in the beacon node
// of a received unaggregated attestation.
if singleAtt != nil {
s.cfg.attestationNotifier.OperationFeed().Send(&feed.Event{
Type: operation.SingleAttReceived,
Data: &operation.SingleAttReceivedData{
Attestation: singleAtt,
},
})
} else {
s.cfg.attestationNotifier.OperationFeed().Send(&feed.Event{
Type: operation.UnaggregatedAttReceived,
Data: &operation.UnAggregatedAttReceivedData{
Attestation: att,
},
})
}
s.setSeenCommitteeIndicesSlot(data.Slot, committeeIndex, attForValidation.GetAggregationBits())
s.setSeenCommitteeIndicesSlot(data.Slot, committeeIndex, att.GetAggregationBits())
// Attach final validated attestation to the message for further pipeline use
msg.ValidatorData = attForValidation
msg.ValidatorData = att
return pubsub.ValidationAccept, nil
}

View File

@@ -15,6 +15,7 @@ import (
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/core/transition"
"github.com/prysmaticlabs/prysm/v5/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v5/config/features"
"github.com/prysmaticlabs/prysm/v5/config/params"
consensusblocks "github.com/prysmaticlabs/prysm/v5/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v5/consensus-types/interfaces"
@@ -79,7 +80,7 @@ func (s *Service) validateBeaconBlockPubSub(ctx context.Context, pid peer.ID, ms
},
})
if s.slasherEnabled {
if features.Get().EnableSlasher {
// Feed the block header to slasher if enabled. This action
// is done in the background to avoid adding more load to this critical code path.
go func() {
@@ -399,6 +400,9 @@ func (s *Service) setSeenBlockIndexSlot(slot primitives.Slot, proposerIdx primit
// Returns true if the block is marked as a bad block.
func (s *Service) hasBadBlock(root [32]byte) bool {
if root == badHoleskyRoot {
return true
}
s.badBlockLock.RLock()
defer s.badBlockLock.RUnlock()
_, seen := s.badBlockCache.Get(string(root[:]))

View File

@@ -92,11 +92,11 @@ func (s *Service) validateBlob(ctx context.Context, pid peer.ID, msg *pubsub.Mes
return pubsub.ValidationIgnore, err
}
if err := vf.SidecarParentValid(s.hasBadBlock); err != nil {
if err := vf.ValidProposerSignature(ctx); err != nil {
return pubsub.ValidationReject, err
}
if err := vf.ValidProposerSignature(ctx); err != nil {
if err := vf.SidecarParentValid(s.hasBadBlock); err != nil {
return pubsub.ValidationReject, err
}

View File

@@ -1,3 +0,0 @@
### Fixed
- Fixed pruner to not block while pruning large database by introducing batchSize

View File

@@ -1,3 +0,0 @@
### Fixed
- /eth/v1/validator/sync_committee_contribution should check for optimistic status and return a 503 if it's optimistic.

View File

@@ -1,4 +0,0 @@
### Ignored
- Cleanup single attestation code for readability.

View File

@@ -1,3 +0,0 @@
### Fixed
- fixed /eth/v1/config/spec displays BLOB_SIDECAR_SUBNET_COUNT,BLOB_SIDECAR_SUBNET_COUNT_ELECTRA

View File

@@ -1,4 +0,0 @@
### Fixed
- Fixes printing superfluous response.WriteHeader call from error in builder.
- Fixes e2e run with builder having wrong gaslimit header due to not being set on eth1 nodes.

View File

@@ -1,3 +0,0 @@
### Fixed
- cosmetic fix for post electra validator logs displaying attestation committee information correctly.

View File

@@ -1,3 +0,0 @@
### Fixed
- fix inserting the wrong committee index into the seen cache for electra attestations

View File

@@ -1,3 +0,0 @@
### Changed
- Updated default gas limit from 30M to 36M

View File

@@ -1,2 +0,0 @@
### Fixed
- Ensure that deleting a block from the database clears its entry in the slot->root db index.

View File

@@ -1,2 +0,0 @@
### Fixed
- Fixed a bug in the event stream handler when processing payload attribute events where the timestamp and slot of the event would be based on the head rather than the current slot.

View File

@@ -1,3 +0,0 @@
## Changed
- `--validators-registration-batch-size`: Change default value from `0` to `200`.

View File

@@ -1,3 +0,0 @@
### Fixed
- Allow any block type to be unmarshaled rather than only phase0 blocks in `slotByBlockRoot`.

View File

@@ -1,3 +0,0 @@
### Ignored
- Add target root to forkchoice dump

View File

@@ -1,3 +0,0 @@
### Ignored
- When starting a node, check that the last validated checkpoint has zero as root and return the genesis block root

View File

@@ -1,3 +0,0 @@
### Ignored
- Populate pubkey cache at genesis.

View File

@@ -1,2 +0,0 @@
### Changed
- Reorganized beacon chain flags in `--help` text into logical sections.

View File

@@ -1,3 +0,0 @@
### Fixed
- Broadcasting BLS to execution changes should not use the request context in a go routine. Use context.Background() for the broadcasting go routine.

View File

@@ -1,11 +0,0 @@
### Changed
- Updated go to go1.24.0.
- Updated gosec to v2.22.1 and golangci to v1.64.5.
- Updated github.com/trailofbits/go-mutexasserts.
- Updated rules_go to cf3c3af34bd869b864f5f2b98e2f41c2b220d6c9 to support go1.24.0.
### Fixed
- Fixed use of deprecated rand.Seed.
- Fixed build issue with SszGen where the go binary was not present in the $PATH.

View File

@@ -1,3 +0,0 @@
### Fixed
- Decompose Electra block attestations to prevent redundant packing.

View File

@@ -1,3 +0,0 @@
### Changed
- Ignore errors from `hasSeenBit` and don't pack unaggregated attestations.

View File

@@ -1,3 +0,0 @@
### Fixed
- Handle unaggregated attestations when decomposing Electra block attestations.

View File

@@ -1,3 +0,0 @@
### Added
- Add acceptable address types for static peers

View File

@@ -1,3 +0,0 @@
### Changed
- Validate blob sidecar re-order signature and bad parent block.

View File

@@ -296,11 +296,6 @@ var (
Usage: "Directory for the slasher database",
Value: cmd.DefaultDataDir(),
}
// SlasherFlag defines a flag to enable the beacon chain slasher.
SlasherFlag = &cli.BoolFlag{
Name: "slasher",
Usage: "Enables a slasher in the beacon node for detecting slashable offenses.",
}
// BeaconDBPruning enables the pruning of beacon db.
BeaconDBPruning = &cli.BoolFlag{
Name: "beacon-db-pruning",

View File

@@ -142,7 +142,6 @@ var appFlags = []cli.Flag{
genesis.StatePath,
genesis.BeaconAPIURL,
flags.SlasherDirFlag,
flags.SlasherFlag,
flags.JwtId,
storage.BlobStoragePathFlag,
storage.BlobRetentionEpochFlag,

View File

@@ -45,188 +45,154 @@ type flagGroup struct {
}
var appHelpFlagGroups = []flagGroup{
{ // Flags relevant to running the process.
{
Name: "cmd",
Flags: []cli.Flag{
cmd.AcceptTosFlag,
cmd.ConfigFileFlag,
},
},
{ // Flags relevant to configuring the beacon chain and APIs.
Name: "beacon-chain",
Flags: []cli.Flag{
cmd.ApiTimeoutFlag,
cmd.ChainConfigFileFlag,
cmd.E2EConfigFlag,
cmd.GrpcMaxCallRecvMsgSizeFlag,
cmd.MinimalConfigFlag,
cmd.E2EConfigFlag,
cmd.RPCMaxPageSizeFlag,
flags.CertFlag,
flags.ChainID,
flags.DisableDebugRPCEndpoints,
flags.HTTPModules,
flags.HTTPServerCorsDomain,
flags.HTTPServerHost,
flags.HTTPServerPort,
flags.KeyFlag,
flags.NetworkID,
flags.RPCHost,
flags.RPCPort,
cmd.NoDiscovery,
cmd.BootstrapNode,
cmd.RelayNode,
cmd.P2PUDPPort,
cmd.P2PQUICPort,
cmd.P2PTCPPort,
cmd.DataDirFlag,
cmd.VerbosityFlag,
cmd.EnableTracingFlag,
cmd.TracingProcessNameFlag,
cmd.TracingEndpointFlag,
cmd.TraceSampleFractionFlag,
cmd.MonitoringHostFlag,
flags.MonitoringPortFlag,
cmd.DisableMonitoringFlag,
cmd.MaxGoroutines,
cmd.ForceClearDB,
cmd.ClearDB,
cmd.ConfigFileFlag,
cmd.ChainConfigFileFlag,
cmd.GrpcMaxCallRecvMsgSizeFlag,
cmd.AcceptTosFlag,
cmd.RestoreSourceFileFlag,
cmd.RestoreTargetDirFlag,
cmd.ValidatorMonitorIndicesFlag,
cmd.ApiTimeoutFlag,
},
},
{
// p2p flags configure the p2p side of beacon-chain.
Name: "p2p",
Name: "debug",
Flags: []cli.Flag{
cmd.BootstrapNode,
cmd.EnableUPnPFlag,
cmd.NoDiscovery,
cmd.P2PAllowList,
cmd.P2PDenyList,
cmd.P2PHost,
cmd.P2PHostDNS,
cmd.P2PIP,
cmd.P2PMaxPeers,
cmd.P2PMetadata,
cmd.P2PPrivKey,
cmd.P2PQUICPort,
cmd.P2PStaticID,
cmd.P2PTCPPort,
cmd.P2PUDPPort,
cmd.PubsubQueueSize,
cmd.RelayNode,
cmd.StaticPeers,
flags.BlobBatchLimit,
flags.BlobBatchLimitBurstFactor,
flags.BlockBatchLimit,
flags.BlockBatchLimitBurstFactor,
flags.MaxConcurrentDials,
flags.MinPeersPerSubnet,
flags.MinSyncPeers,
flags.SubscribeToAllSubnets,
debug.PProfFlag,
debug.PProfAddrFlag,
debug.PProfPortFlag,
debug.MemProfileRateFlag,
debug.CPUProfileFlag,
debug.TraceFlag,
debug.BlockProfileRateFlag,
debug.MutexProfileFractionFlag,
},
},
{ // Flags relevant to storing data on disk and configuring the beacon chain database.
Name: "db",
{
Name: "beacon-chain",
Flags: []cli.Flag{
backfill.BackfillBatchSize,
backfill.BackfillOldestSlot,
backfill.BackfillWorkerCount,
backfill.EnableExperimentalBackfill,
cmd.ClearDB,
cmd.DataDirFlag,
cmd.ForceClearDB,
cmd.RestoreSourceFileFlag,
cmd.RestoreTargetDirFlag,
flags.BeaconDBPruning,
flags.PrunerRetentionEpochs,
flags.SlotsPerArchivedPoint,
storage.BlobRetentionEpochFlag,
storage.BlobStorageLayout,
storage.BlobStoragePathFlag,
},
},
{ // Flags relevant to configuring local block production or external builders such as mev-boost.
Name: "builder",
Flags: []cli.Flag{
flags.LocalBlockValueBoost,
flags.MaxBuilderConsecutiveMissedSlots,
flags.MaxBuilderEpochMissedSlots,
flags.MevRelayEndpoint,
flags.MinBuilderBid,
flags.MinBuilderDiff,
flags.SuggestedFeeRecipient,
},
},
{ // Flags relevant to syncing the beacon chain.
Name: "sync",
Flags: []cli.Flag{
checkpoint.BlockPath,
checkpoint.RemoteURL,
checkpoint.StatePath,
flags.WeakSubjectivityCheckpoint,
genesis.BeaconAPIURL,
genesis.StatePath,
},
},
{ // Flags relevant to interacting with the execution layer.
Name: "execution layer",
Flags: []cli.Flag{
flags.ContractDeploymentBlock,
flags.InteropMockEth1DataVotesFlag,
flags.DepositContractFlag,
flags.EngineEndpointTimeoutSeconds,
flags.Eth1HeaderReqLimit,
flags.ContractDeploymentBlock,
flags.RPCHost,
flags.RPCPort,
flags.CertFlag,
flags.KeyFlag,
flags.HTTPModules,
flags.HTTPServerHost,
flags.HTTPServerPort,
flags.HTTPServerCorsDomain,
flags.ExecutionEngineEndpoint,
flags.ExecutionEngineHeaders,
flags.ExecutionJWTSecretFlag,
flags.JwtId,
flags.InteropMockEth1DataVotesFlag,
},
},
{ // Flags relevant to configuring beacon chain monitoring.
Name: "monitoring",
Flags: []cli.Flag{
cmd.DisableMonitoringFlag,
cmd.EnableTracingFlag,
cmd.MonitoringHostFlag,
cmd.TraceSampleFractionFlag,
cmd.TracingEndpointFlag,
cmd.TracingProcessNameFlag,
cmd.ValidatorMonitorIndicesFlag,
flags.MonitoringPortFlag,
},
},
{ // Flags relevant to slasher operation.
Name: "slasher",
Flags: []cli.Flag{
flags.SetGCPercent,
flags.SlotsPerArchivedPoint,
flags.BlockBatchLimit,
flags.BlockBatchLimitBurstFactor,
flags.BlobBatchLimit,
flags.BlobBatchLimitBurstFactor,
flags.DisableDebugRPCEndpoints,
flags.SubscribeToAllSubnets,
flags.HistoricalSlasherNode,
flags.ChainID,
flags.NetworkID,
flags.WeakSubjectivityCheckpoint,
flags.Eth1HeaderReqLimit,
flags.MinPeersPerSubnet,
flags.MaxConcurrentDials,
flags.MevRelayEndpoint,
flags.MaxBuilderEpochMissedSlots,
flags.MaxBuilderConsecutiveMissedSlots,
flags.EngineEndpointTimeoutSeconds,
flags.SlasherDirFlag,
flags.SlasherFlag,
flags.LocalBlockValueBoost,
flags.MinBuilderBid,
flags.MinBuilderDiff,
flags.JwtId,
flags.BeaconDBPruning,
flags.PrunerRetentionEpochs,
checkpoint.BlockPath,
checkpoint.StatePath,
checkpoint.RemoteURL,
genesis.StatePath,
genesis.BeaconAPIURL,
storage.BlobStoragePathFlag,
storage.BlobRetentionEpochFlag,
storage.BlobStorageLayout,
backfill.EnableExperimentalBackfill,
backfill.BackfillWorkerCount,
backfill.BackfillBatchSize,
backfill.BackfillOldestSlot,
},
},
{
// Flags in the "log" section control how Prysm handles logging.
Name: "log",
Flags: []cli.Flag{
cmd.LogFormat,
cmd.LogFileName,
cmd.VerbosityFlag,
},
},
{ // Feature flags.
Name: "features",
Flags: features.ActiveFlags(features.BeaconChainFlags),
},
{ // Flags required to configure the merge.
Name: "merge",
Flags: []cli.Flag{
flags.SuggestedFeeRecipient,
flags.TerminalTotalDifficultyOverride,
flags.TerminalBlockHashOverride,
flags.TerminalBlockHashActivationEpochOverride,
},
},
{ // The deprecated section represents beacon flags that still have use, but should not be used
// as they are expected to be deleted in a feature release.
{
Name: "p2p",
Flags: []cli.Flag{
cmd.P2PIP,
cmd.P2PHost,
cmd.P2PHostDNS,
cmd.P2PMaxPeers,
cmd.P2PPrivKey,
cmd.P2PStaticID,
cmd.P2PMetadata,
cmd.P2PAllowList,
cmd.P2PDenyList,
cmd.PubsubQueueSize,
cmd.StaticPeers,
cmd.EnableUPnPFlag,
flags.MinSyncPeers,
},
},
{
Name: "log",
Flags: []cli.Flag{
cmd.LogFormat,
cmd.LogFileName,
},
},
{
Name: "features",
Flags: features.ActiveFlags(features.BeaconChainFlags),
},
{
Name: "deprecated",
Flags: []cli.Flag{
cmd.BackupWebhookOutputDir,
},
},
{ // Flags used in debugging Prysm. These are flags not usually run by end users.
Name: "debug",
Flags: []cli.Flag{
cmd.MaxGoroutines,
debug.BlockProfileRateFlag,
debug.CPUProfileFlag,
debug.MemProfileRateFlag,
debug.MutexProfileFractionFlag,
debug.PProfAddrFlag,
debug.PProfFlag,
debug.PProfPortFlag,
debug.TraceFlag,
flags.SetGCPercent,
},
},
}
func init() {

View File

@@ -93,20 +93,15 @@ var (
Name: "no-discovery",
Usage: "Enable only local network p2p and do not connect to cloud bootstrap nodes",
}
// StaticPeers specifies a set of peers to connect to explicitly, accepting following format of addresses:
// enode, multiaddr, enr.
// StaticPeers specifies a set of peers to connect to explicitly.
StaticPeers = &cli.StringSliceFlag{
Name: "peer",
Usage: "Connect with this peer, this flag may be used multiple times. " +
"This peer is recognized as a trusted peer." +
"Accepts enode, multiaddr, and enr formats.",
Name: "peer",
Usage: "Connect with this peer, this flag may be used multiple times. This peer is recognized as a trusted peer.",
}
// BootstrapNode tells the beacon node which bootstrap node to connect to
BootstrapNode = &cli.StringSliceFlag{
Name: "bootstrap-node",
Usage: "The enr/enode address of bootstrap node. Beacon node will connect for peer discovery via DHT. " +
"Multiple nodes can be passed by using the flag multiple times but not comma-separated. " +
"You can also pass YAML files containing multiple nodes.",
Name: "bootstrap-node",
Usage: "The address of bootstrap node. Beacon node will connect for peer discovery via DHT. Multiple nodes can be passed by using the flag multiple times but not comma-separated. You can also pass YAML files containing multiple nodes.",
Value: cli.NewStringSlice(params.BeaconNetworkConfig().BootstrapNodes...),
}
// RelayNode tells the beacon node which relay node to connect to.

View File

@@ -381,7 +381,7 @@ var (
ValidatorsRegistrationBatchSizeFlag = &cli.IntFlag{
Name: "validators-registration-batch-size",
Usage: "Sets the maximum size for one batch of validator registrations. Use a non-positive value to disable batching.",
Value: 200,
Value: 0,
}
// EnableDistributed enables the usage of prysm validator client in a Distributed Validator Cluster.
EnableDistributed = &cli.BoolFlag{

View File

@@ -60,6 +60,7 @@ type Flags struct {
// Bug fixes related flags.
AttestTimely bool // AttestTimely fixes #8185. It is gated behind a flag to ensure beacon node's fix can safely roll out first. We'll invert this in v1.1.0.
EnableSlasher bool // Enable slasher in the beacon node runtime.
EnableSlashingProtectionPruning bool // Enable slashing protection pruning for the validator client.
EnableMinimalSlashingProtection bool // Enable minimal slashing protection database for the validator client.
@@ -213,6 +214,10 @@ func ConfigureBeaconChain(ctx *cli.Context) error {
logDisabled(disableBroadcastSlashingFlag)
cfg.DisableBroadcastSlashings = true
}
if ctx.Bool(enableSlasherFlag.Name) {
log.WithField(enableSlasherFlag.Name, enableSlasherFlag.Usage).Warn(enabledFeatureFlag)
cfg.EnableSlasher = true
}
if ctx.Bool(enableHistoricalSpaceRepresentation.Name) {
log.WithField(enableHistoricalSpaceRepresentation.Name, enableHistoricalSpaceRepresentation.Usage).Warn(enabledFeatureFlag)
cfg.EnableHistoricalSpaceRepresentation = true

View File

@@ -12,39 +12,39 @@ import (
func TestInitFeatureConfig(t *testing.T) {
defer Init(&Flags{})
cfg := &Flags{
EnableDoppelGanger: true,
EnableSlasher: true,
}
Init(cfg)
c := Get()
assert.Equal(t, true, c.EnableDoppelGanger)
assert.Equal(t, true, c.EnableSlasher)
}
func TestInitWithReset(t *testing.T) {
defer Init(&Flags{})
Init(&Flags{
EnableDoppelGanger: true,
EnableSlasher: true,
})
assert.Equal(t, true, Get().EnableDoppelGanger)
assert.Equal(t, true, Get().EnableSlasher)
// Overwrite previously set value (value that didn't come by default).
resetCfg := InitWithReset(&Flags{
EnableDoppelGanger: false,
EnableSlasher: false,
})
assert.Equal(t, false, Get().EnableDoppelGanger)
assert.Equal(t, false, Get().EnableSlasher)
// Reset must get to previously set configuration (not to default config values).
resetCfg()
assert.Equal(t, true, Get().EnableDoppelGanger)
assert.Equal(t, true, Get().EnableSlasher)
}
func TestConfigureBeaconConfig(t *testing.T) {
app := cli.App{}
set := flag.NewFlagSet("test", 0)
set.Bool(saveInvalidBlockTempFlag.Name, true, "test")
set.Bool(enableSlasherFlag.Name, true, "test")
context := cli.NewContext(&app, set, nil)
require.NoError(t, ConfigureBeaconChain(context))
c := Get()
assert.Equal(t, true, c.SaveInvalidBlock)
assert.Equal(t, true, c.EnableSlasher)
}
func TestValidateNetworkFlags(t *testing.T) {

View File

@@ -89,6 +89,10 @@ var (
Name: "attest-timely",
Usage: "Fixes validator can attest timely after current block processes. See #8185 for more details.",
}
enableSlasherFlag = &cli.BoolFlag{
Name: "slasher",
Usage: "Enables a slasher in the beacon node for detecting slashable offenses.",
}
enableSlashingProtectionPruning = &cli.BoolFlag{
Name: "enable-slashing-protection-history-pruning",
Usage: "Enables the pruning of the validator client's slashing protection database.",
@@ -219,6 +223,7 @@ var BeaconChainFlags = combinedFlags([]cli.Flag{
Mainnet,
disablePeerScorer,
disableBroadcastSlashingFlag,
enableSlasherFlag,
disableStakinContractCheck,
SaveFullExecutionPayloads,
enableStartupOptimistic,

View File

@@ -235,8 +235,8 @@ type BeaconChainConfig struct {
ExecutionEngineTimeoutValue uint64 // ExecutionEngineTimeoutValue defines the seconds to wait before timing out engine endpoints with execution payload execution semantics (newPayload, forkchoiceUpdated).
// Subnet value
BlobsidecarSubnetCount uint64 `yaml:"BLOB_SIDECAR_SUBNET_COUNT" spec:"true"` // BlobsidecarSubnetCount is the number of blobsidecar subnets used in the gossipsub protocol.
BlobsidecarSubnetCountElectra uint64 `yaml:"BLOB_SIDECAR_SUBNET_COUNT_ELECTRA" spec:"true"` // BlobsidecarSubnetCountElectra is the number of blobsidecar subnets used in the gossipsub protocol post Electra hard fork.
BlobsidecarSubnetCount uint64 `yaml:"BLOB_SIDECAR_SUBNET_COUNT"` // BlobsidecarSubnetCount is the number of blobsidecar subnets used in the gossipsub protocol.
BlobsidecarSubnetCountElectra uint64 `yaml:"BLOB_SIDECAR_SUBNET_COUNT_ELECTRA"` // BlobsidecarSubnetCountElectra is the number of blobsidecar subnets used in the gossipsub protocol post Electra hard fork.
// Values introduced in Deneb hard fork
MaxPerEpochActivationChurnLimit uint64 `yaml:"MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT" spec:"true"` // MaxPerEpochActivationChurnLimit is the maximum amount of churn allotted for validator activation.

View File

@@ -267,7 +267,7 @@ var mainnetBeaconConfig = &BeaconChainConfig{
BytesPerLogsBloom: 256,
MaxExtraDataBytes: 32,
EthBurnAddressHex: "0x0000000000000000000000000000000000000000",
DefaultBuilderGasLimit: uint64(36000000),
DefaultBuilderGasLimit: uint64(30000000),
// Mevboost circuit breaker
MaxBuilderConsecutiveMissedSlots: 3,

View File

@@ -7,15 +7,10 @@ func UseHoleskyNetworkConfig() {
cfg := BeaconNetworkConfig().Copy()
cfg.ContractDeploymentBlock = 0
cfg.BootstrapNodes = []string{
// EF
"enr:-Ku4QFo-9q73SspYI8cac_4kTX7yF800VXqJW4Lj3HkIkb5CMqFLxciNHePmMt4XdJzHvhrCC5ADI4D_GkAsxGJRLnQBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpAhnTT-AQFwAP__________gmlkgnY0gmlwhLKAiOmJc2VjcDI1NmsxoQORcM6e19T1T9gi7jxEZjk_sjVLGFscUNqAY9obgZaxbIN1ZHCCIyk",
"enr:-Ku4QPG7F72mbKx3gEQEx07wpYYusGDh-ni6SNkLvOS-hhN-BxIggN7tKlmalb0L5JPoAfqD-akTZ-gX06hFeBEz4WoBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpAhnTT-AQFwAP__________gmlkgnY0gmlwhJK-DYCJc2VjcDI1NmsxoQKLVXFOhp2uX6jeT0DvvDpPcU8FWMjQdR4wMuORMhpX24N1ZHCCIyk",
"enr:-LK4QPxe-mDiSOtEB_Y82ozvxn9aQM07Ui8A-vQHNgYGMMthfsfOabaaTHhhJHFCBQQVRjBww_A5bM1rf8MlkJU_l68Eh2F0dG5ldHOIAADAAAAAAACEZXRoMpBpt9l0BAFwAAABAAAAAAAAgmlkgnY0gmlwhLKAiOmJc2VjcDI1NmsxoQJu6T9pclPObAzEVQ53DpVQqjadmVxdTLL-J3h9NFoCeIN0Y3CCIyiDdWRwgiMo",
"enr:-Ly4QGbOw4xNel5EhmDsJJ-QhC9XycWtsetnWoZ0uRy381GHdHsNHJiCwDTOkb3S1Ade0SFQkWJX_pgb3g8Jfh93rvMBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpBpt9l0BAFwAAABAAAAAAAAgmlkgnY0gmlwhJK-DYCJc2VjcDI1NmsxoQOxKv9sv3zKF8GDewgFGGHKP5HCZZpPpTrwl9eXKAWGxIhzeW5jbmV0cwCDdGNwgiMog3VkcIIjKA",
// Teku
"enr:-LS4QG0uV4qvcpJ-HFDJRGBmnlD3TJo7yc4jwK8iP7iKaTlfQ5kZvIDspLMJhk7j9KapuL9yyHaZmwTEZqr10k9XumyCEcmHYXR0bmV0c4gAAAAABgAAAIRldGgykGm32XQEAXAAAAEAAAAAAACCaWSCdjSCaXCErK4j-YlzZWNwMjU2azGhAgfWRBEJlb7gAhXIB5ePmjj2b8io0UpEenq1Kl9cxStJg3RjcIIjKIN1ZHCCIyg",
// Sigma Prime
"enr:-Le4QLoE1wFHSlGcm48a9ZESb_MRLqPPu6G0vHqu4MaUcQNDHS69tsy-zkN0K6pglyzX8m24mkb-LtBcbjAYdP1uxm4BhGV0aDKQabfZdAQBcAAAAQAAAAAAAIJpZIJ2NIJpcIQ5gR6Wg2lwNpAgAUHQBwEQAAAAAAAAADR-iXNlY3AyNTZrMaEDPMSNdcL92uNIyCsS177Z6KTXlbZakQqxv3aQcWawNXeDdWRwgiMohHVkcDaCI4I",
"enr:-Oa4QOuWj-_JX6iJWjdS_67qQkkVoomo3YztvdOLVF4lVwc3NzFR3D8y-8CmGjmQA16DJBOZWwbc3XMOw_w_ScU6-qCCAgSHYXR0bmV0c4gAAAMAAAAAAIZjbGllbnTYikxpZ2h0aG91c2WMNy4wLjAtYmV0YS4whGV0aDKQAZ4hrQYBcAD__________4JpZIJ2NIJpcISyE90mhHF1aWOCfLuJc2VjcDI1NmsxoQK-fTVglh3wyHIcyauubuyvLeFmn6QpUwog7Aio2OSucYhzeW5jbmV0cwCDdGNwgny6g3VkcIJ8ug",
"enr:-PW4QAOnzqnCuwuNNrUEXebSD3MFMOe-9NApsb8UkAQK-MquYtUhj35Ksz4EWcmdB0Cmj43bGBJJEpt9fYMAg1vOHXobh2F0dG5ldHOIAAAYAAAAAACGY2xpZW502IpMaWdodGhvdXNljDcuMC4wLWJldGEuMIRldGgykAGeIa0GAXAA__________-CaWSCdjSCaXCEff1tSYRxdWljgiMphXF1aWM2giMpiXNlY3AyNTZrMaECUiAFSBathSIPGhDHbZjQS5gTqaPcRkAe4HECCk-vt6KIc3luY25ldHMPg3RjcIIjKIR0Y3A2giMog3VkcIIjKA",
"enr:-QESuEA2tFgFDu5LX9T6j1_bayowdRzrtdQcjwmTq_zOVjwe1WQOsM7-Q4qRcgc7AjpAQOcdb2F3wyPDBkbP-vxW2dLgXYdhdHRuZXRziAADAAAAAAAAhmNsaWVudNiKTGlnaHRob3VzZYw3LjAuMC1iZXRhLjCEZXRoMpABniGtBgFwAP__________gmlkgnY0gmlwhIe1ME2DaXA2kCoBBPkwgDCeAAAAAAAAAAKEcXVpY4IjKYVxdWljNoIjg4lzZWNwMjU2azGhA4oHjOmlWOfLizFFIQSI_dzn4rzvDvMG8h7zmxhmOVzXiHN5bmNuZXRzD4N0Y3CCIyiEdGNwNoIjgoN1ZHCCIyiEdWRwNoIjgg",
"enr:-KG4QCvyykb0pA1T-EZJUPkl2P1PKYk8-4El8TqwWdKwtoI6NtIBGMJVDgGZKVy2eMszI0_ermORtQ340lj1dTHzGVVPhGV0aDKQAZ4hrQYBcAD__________4JpZIJ2NIJpcIQ5gNI3iXNlY3AyNTZrMaEDMQYffETNbuGwVzWEJSgCpA50LTxHUWU1A0TDfleEa5mDdGNwgjvFg3VkcII7xQ",
}
OverrideBeaconNetworkConfig(cfg)
}

View File

@@ -4,7 +4,7 @@
"fee_recipient": "0x50155530FCE8a85ec7055A5F8b2bE214B3DaeFd3",
"builder": {
"enabled": true,
"gas_limit": "36000000"
"gas_limit": "30000000"
}
},
"0xb057816155ad77931185101128655c0191bd0214c201ca48ed887f6c4c6adf334070efcd75140eada5ac83a92506dd7b": {

View File

@@ -9,4 +9,4 @@ default_config:
fee_recipient: '0x6e35733c5af9B61374A128e6F85f553aF09ff89A'
builder:
enabled: false
gas_limit: '36000000'
gas_limit: '30000000'

View File

@@ -51,5 +51,4 @@ type Node struct {
BlockRoot []byte
ParentRoot []byte
ExecutionBlockHash []byte
Target []byte
}

View File

@@ -146,7 +146,7 @@ func EncryptKey(key *Key, password string, scryptN, scryptP int) ([]byte, error)
return nil, errors.New("reading from crypto/rand failed: " + err.Error())
}
cipherText, err := aesCTRXOR(encryptKey, keyBytes, iv) // #nosec G407
cipherText, err := aesCTRXOR(encryptKey, keyBytes, iv)
if err != nil {
return nil, err
}

View File

@@ -37,8 +37,8 @@ def prysm_deps():
go_repository(
name = "co_honnef_go_tools",
importpath = "honnef.co/go/tools",
sum = "h1:TAODvD3knlq75WCp2nyGJtT4LeRV/o7NN9nYPeVJXf8=",
version = "v0.6.0",
sum = "h1:4bH5o3b5ZULQ4UrBmP+63W9r7qIkqJClEA9ko5YKx+I=",
version = "v0.5.1",
)
go_repository(
name = "com_github_aclements_go_moremath",
@@ -3363,8 +3363,8 @@ def prysm_deps():
go_repository(
name = "com_github_trailofbits_go_mutexasserts",
importpath = "github.com/trailofbits/go-mutexasserts",
sum = "h1:EBoYk5zHOfuHDBqLFx4eSPRVcbnW+L3aFJzoCi8zRnk=",
version = "v0.0.0-20250212181730-4c2b8e9e784b",
sum = "h1:+LynomhWB+14Plp/bOONEAZCtvCZk4leRbTvNzNVkL0=",
version = "v0.0.0-20230328101604-8cdbc5f3d279",
)
go_repository(
name = "com_github_tyler_smith_go_bip39",
@@ -4739,8 +4739,8 @@ def prysm_deps():
go_repository(
name = "org_golang_x_crypto",
importpath = "golang.org/x/crypto",
sum = "h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=",
version = "v0.33.0",
sum = "h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=",
version = "v0.32.0",
)
go_repository(
name = "org_golang_x_exp",
@@ -4775,14 +4775,14 @@ def prysm_deps():
go_repository(
name = "org_golang_x_mod",
importpath = "golang.org/x/mod",
sum = "h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=",
version = "v0.23.0",
sum = "h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=",
version = "v0.22.0",
)
go_repository(
name = "org_golang_x_net",
importpath = "golang.org/x/net",
sum = "h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=",
version = "v0.35.0",
sum = "h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=",
version = "v0.34.0",
)
go_repository(
name = "org_golang_x_oauth2",
@@ -4799,14 +4799,14 @@ def prysm_deps():
go_repository(
name = "org_golang_x_sync",
importpath = "golang.org/x/sync",
sum = "h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=",
version = "v0.11.0",
sum = "h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=",
version = "v0.10.0",
)
go_repository(
name = "org_golang_x_sys",
importpath = "golang.org/x/sys",
sum = "h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=",
version = "v0.30.0",
sum = "h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=",
version = "v0.29.0",
)
go_repository(
name = "org_golang_x_telemetry",
@@ -4817,14 +4817,14 @@ def prysm_deps():
go_repository(
name = "org_golang_x_term",
importpath = "golang.org/x/term",
sum = "h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=",
version = "v0.29.0",
sum = "h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=",
version = "v0.28.0",
)
go_repository(
name = "org_golang_x_text",
importpath = "golang.org/x/text",
sum = "h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=",
version = "v0.22.0",
sum = "h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=",
version = "v0.21.0",
)
go_repository(
name = "org_golang_x_time",
@@ -4835,8 +4835,8 @@ def prysm_deps():
go_repository(
name = "org_golang_x_tools",
importpath = "golang.org/x/tools",
sum = "h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY=",
version = "v0.30.0",
sum = "h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE=",
version = "v0.29.0",
)
go_repository(
name = "org_golang_x_xerrors",

View File

@@ -22,17 +22,10 @@ func IsHex(b []byte) bool {
// DecodeHexWithLength takes a string and a length in bytes,
// and validates whether the string is a hex and has the correct length.
func DecodeHexWithLength(s string, length int) ([]byte, error) {
if len(s) > 2*length+2 {
return nil, fmt.Errorf("%s is greather than length %d bytes", s, length)
if len(s) != 2*length+2 {
return nil, fmt.Errorf("%s is not length %d bytes", s, length)
}
bytes, err := hexutil.Decode(s)
if err != nil {
return nil, errors.Wrap(err, fmt.Sprintf("%s is not a valid hex", s))
}
if len(bytes) != length {
return nil, fmt.Errorf("length of %s is not %d bytes", s, length)
}
return bytes, nil
return hexutil.Decode(s)
}
// DecodeHexWithMaxLength takes a string and a length in bytes,

22
go.mod
View File

@@ -1,6 +1,6 @@
module github.com/prysmaticlabs/prysm/v5
go 1.24.0
go 1.23.5
require (
github.com/MariusVanDerWijden/FuzzyVM v0.0.0-20240516070431-7828990cad7d
@@ -71,7 +71,7 @@ require (
github.com/stretchr/testify v1.10.0
github.com/supranational/blst v0.3.14
github.com/thomaso-mirodin/intmath v0.0.0-20160323211736-5dc6d854e46e
github.com/trailofbits/go-mutexasserts v0.0.0-20250212181730-4c2b8e9e784b
github.com/trailofbits/go-mutexasserts v0.0.0-20230328101604-8cdbc5f3d279
github.com/tyler-smith/go-bip39 v1.1.0
github.com/urfave/cli/v2 v2.27.1
github.com/uudashr/gocognit v1.0.5
@@ -87,17 +87,17 @@ require (
go.opentelemetry.io/otel/trace v1.34.0
go.uber.org/automaxprocs v1.5.2
go.uber.org/mock v0.4.0
golang.org/x/crypto v0.33.0
golang.org/x/crypto v0.32.0
golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa
golang.org/x/sync v0.11.0
golang.org/x/tools v0.30.0
golang.org/x/sync v0.10.0
golang.org/x/tools v0.29.0
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1
google.golang.org/grpc v1.69.4
google.golang.org/protobuf v1.36.3
gopkg.in/d4l3k/messagediff.v1 v1.2.1
gopkg.in/yaml.v2 v2.4.0
gopkg.in/yaml.v3 v3.0.1
honnef.co/go/tools v0.6.0
honnef.co/go/tools v0.5.1
k8s.io/apimachinery v0.30.4
k8s.io/client-go v0.30.4
)
@@ -259,11 +259,11 @@ require (
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
golang.org/x/mod v0.23.0 // indirect
golang.org/x/net v0.35.0 // indirect
golang.org/x/mod v0.22.0 // indirect
golang.org/x/net v0.34.0 // indirect
golang.org/x/oauth2 v0.24.0 // indirect
golang.org/x/term v0.29.0 // indirect
golang.org/x/text v0.22.0 // indirect
golang.org/x/term v0.28.0 // indirect
golang.org/x/text v0.21.0 // indirect
golang.org/x/time v0.5.0 // indirect
gopkg.in/cenkalti/backoff.v1 v1.1.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
@@ -284,7 +284,7 @@ require (
github.com/go-playground/validator/v10 v10.13.0
github.com/peterh/liner v1.2.0 // indirect
github.com/prysmaticlabs/gohashtree v0.0.4-beta.0.20240624100937-73632381301b
golang.org/x/sys v0.30.0 // indirect
golang.org/x/sys v0.29.0 // indirect
k8s.io/klog/v2 v2.120.1 // indirect
k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect
)

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