Compare commits

...

20 Commits

Author SHA1 Message Date
james-prysm
1d063d57b8 adding debounce to remote key manager 2025-11-25 16:34:12 -06:00
Forostovec
2d242a8d09 execution: avoid redundant WithHttpEndpoint when JWT is provided (#16032)
This change ensures FlagOptions in cmd/beacon-chain/execution/options.go
appends only one endpoint option depending on whether a JWT secret is
present. Previously the code always appended WithHttpEndpoint and then
conditionally appended WithHttpEndpointAndJWTSecret which overwrote the
first option, adding unnecessary allocations and cognitive overhead.
Since WithHttpEndpointAndJWTSecret fully configures the endpoint,
including URL and Bearer auth needed by the Engine API, the initial
WithHttpEndpoint is redundant when a JWT is supplied. The refactor
preserves behavior while simplifying option composition and avoiding
redundant state churn.

---------

Co-authored-by: james-prysm <90280386+james-prysm@users.noreply.github.com>
2025-11-25 17:43:38 +00:00
Radosław Kapka
6be1541e57 Initialize the ExecutionRequests field in gossip block map (#16047)
**What type of PR is this?**

Other

**What does this PR do? Why is it needed?**

When unmarshaling a block with fastssz, if the target block's
`ExecutionRequests` field is nil, it will not get populated
```
if b.ExecutionRequests == nil {
	b.ExecutionRequests = new(v1.ExecutionRequests)
}
if err = b.ExecutionRequests.UnmarshalSSZ(buf); err != nil {
	return err
}
```
This is true for other fields and that's why we initialize them in our
gossip data maps. There is no bug at the moment because even if
execution requests are nil, we initialize them in
`consensus-types/blocks/proto.go`
```
er := pb.ExecutionRequests
if er == nil {
	er = &enginev1.ExecutionRequests{}
}
```
However, since we initialize other fields in the data map, it's safer to
do it for execution requests too, to avoid a bug in case the code in
`consensus-types/blocks/proto.go` changes in the future.

**Acknowledgements**

- [x] I have read
[CONTRIBUTING.md](https://github.com/prysmaticlabs/prysm/blob/develop/CONTRIBUTING.md).
- [x] I have included a uniquely named [changelog fragment
file](https://github.com/prysmaticlabs/prysm/blob/develop/CONTRIBUTING.md#maintaining-changelogmd).
- [x] I have added a description to this PR with sufficient context for
reviewers to understand this PR.
2025-11-25 16:17:44 +00:00
Bastin
b845222ce7 Integrate state-diff into HasState() (#16045)
**What type of PR is this?**
Feature

**What does this PR do? Why is it needed?**
This PR adds integrates state-diff into `HasState()`. 

One thing to note: we are assuming that, for a given block root, that
has either a state summary or a block in db, and also falls in the state
diff tree, then there must exist a state. This function could return
true, even when there is no actual state saved due to any error.
But this is fine, because we have that assumption throughout the whole
state diff feature.
2025-11-25 13:22:57 +00:00
terence
5bbdebee22 Add Gloas consensus type block package (#15618)
Co-authored-by: Bastin <43618253+Inspector-Butters@users.noreply.github.com>
2025-11-25 09:21:19 +00:00
Bastin
26100e074d Refactor slot by blockroot (#16040)
**Review after #16033 is merged**

**What type of PR is this?**
Other

**What does this PR do? Why is it needed?**
This PR refactors the code to find the corresponding slot of a given
block root using state summary or the block itself, into its own
function `SlotByBlockRoot(ctx, blockroot)`.

Note that there exists a function `slotByBlockRoot(ctx context.Context,
tx *bolt.Tx, blockRoot []byte)` immediately below the new function. Also
note that this function has two drawbacks, which led to creation of the
new function:
- the old function requires a boltdb tx, which is not necessarily
available to the caller.
- the old function does NOT make use of the state summary cache. 

edit: 
- the old function also uses the state bucket to retrieve the state and
it's slot. this is not something we want in the state diff feature,
since there is no state bucket.
2025-11-24 19:59:13 +00:00
satushh
768fa0e5a1 Revert "Metrics for eas" (#16043)
Reverts OffchainLabs/prysm#16008
2025-11-24 16:12:30 +00:00
Bastin
11bb8542a4 Integrate state-diff into State() (#16033)
**What type of PR is this?**
Feature

**What does this PR do? Why is it needed?**
This PR integrates the state diff path into the `State()` function from
`db/kv`, which allows reading of states using the state diff db, when
the `EnableStateDiff` flag is enabled.

**Notes for reviewers:**
Files `kv/state_diff_test.go` and `config/features/config.go` only
contain renamings:
- `kv/state_diff_test.go`: rename `setDefaultExponents()` to
`setDefaultStateDiffExponents()` to be less vague.
- `config/features/config.go`: rename `enableStateDiff` to
`EnableStateDiff` to make it public.
2025-11-24 12:42:43 +00:00
Bharath Vedartham
b78c2c354b add a metric to measure the attestation gossip validator (#15785)
This PR adds a Summary metric to measure the p2p topic validation time
for attestations arriving from the network. It times the
`validateCommitteeIndexBeaconAttestation` method.

The metric is called `gossip_attestation_verification_milliseconds`

**Acknowledgements**

- [x] I have read
[CONTRIBUTING.md](https://github.com/prysmaticlabs/prysm/blob/develop/CONTRIBUTING.md).
- [x] I have included a uniquely named [changelog fragment
file](https://github.com/prysmaticlabs/prysm/blob/develop/CONTRIBUTING.md#maintaining-changelogmd).
- [x] I have added a description to this PR with sufficient context for
reviewers to understand this PR.

---------

Co-authored-by: Kasey Kirkham <kasey@users.noreply.github.com>
2025-11-21 21:57:19 +00:00
Muzry
55e2001a0b Check the JWT secret length (#15939)
<!-- Thanks for sending a PR! Before submitting:

1. If this is your first PR, check out our contribution guide here
https://docs.prylabs.network/docs/contribute/contribution-guidelines
You will then need to sign our Contributor License Agreement (CLA),
which will show up as a comment from a bot in this pull request after
you open it. We cannot review code without a signed CLA.
2. Please file an associated tracking issue if this pull request is
non-trivial and requires context for our team to understand. All
features and most bug fixes should have
an associated issue with a design discussed and decided upon. Small bug
   fixes and documentation improvements don't need issues.
3. New features and bug fixes must have tests. Documentation may need to
be updated. If you're unsure what to update, send the PR, and we'll
discuss
   in review.
4. Note that PRs updating dependencies and new Go versions are not
accepted.
   Please file an issue instead.
5. A changelog entry is required for user facing issues.
-->

**What type of PR is this?**
 Bug fix

**What does this PR do? Why is it needed?**

Previously, JWT secrets longer than 256 bits could cause client
compatibility issues. For example, Prysm would accept longer secrets
while Geth strictly requires exactly 32 bytes, causing Geth startup
failures when using the same secret file.

This change enforces the Engine API specification requirement that JWT
secrets must be exactly 256 bits (32 bytes), ensuring consistent
behavior across different client implementations.

**Which issues(s) does this PR fix?**

Fixes #

**Other notes for review**

**Acknowledgements**

- [x] I have read
[CONTRIBUTING.md](https://github.com/prysmaticlabs/prysm/blob/develop/CONTRIBUTING.md).
- [x] I have included a uniquely named [changelog fragment
file](https://github.com/prysmaticlabs/prysm/blob/develop/CONTRIBUTING.md#maintaining-changelogmd).
- [x] I have added a description to this PR with sufficient context for
reviewers to understand this PR.

---------

Co-authored-by: james-prysm <90280386+james-prysm@users.noreply.github.com>
2025-11-21 21:51:03 +00:00
sashaodessa
c093283b1b Replace fixed sleep delays with active polling in prometheus service test (#15828)
## **Description:**

**What type of PR is this?**

> Bug fix

**What does this PR do? Why is it needed?**

Replaces fixed `time.Sleep(time.Second)` delays in `TestLifecycle` with
active polling to wait for service readiness/shutdown. This improves
test reliability and reduces execution time by eliminating unnecessary
waits when services start/stop faster than expected.

**Which issues(s) does this PR fix?**

N/A - Minor test improvement

**Other notes for review**

- Uses 50ms polling interval with 3s timeout for both startup and
shutdown checks
- Maintains same test logic while making it more efficient and less
flaky
- No functional changes to the service itself

**Acknowledgements**

- [x] I have read
[CONTRIBUTING.md](https://github.com/prysmaticlabs/prysm/blob/develop/CONTRIBUTING.md).
- [ ] I have included a uniquely named [changelog fragment
file](https://github.com/prysmaticlabs/prysm/blob/develop/CONTRIBUTING.md#maintaining-changelogmd).
- [x] I have added a description to this PR with sufficient context for
reviewers to understand this PR.
2025-11-21 21:39:24 +00:00
terence
5449fd0352 p2p: wire stategen into service for last finalized state (#16034)
This PR removes the last production usage for: `LastArchivedRoot` by

- extend the P2P config to accept a `StateGen` dependency and wire it up
from the beacon node
- update gossip scoring to read the active validator count via stategen
using last finalized block root

note: i think the correct implementation should process last finalizes
state to current slot, but that's a bigger change i dont want to make in
this PR, i just want to remove usages for `LastArchivedRoot`
2025-11-21 16:13:00 +00:00
Bastin
3d7f7b588b Fix state diff repetitive anchor slot bug (#16037)
**What type of PR is this?**
 Bug fix

**What does this PR do? Why is it needed?**
This PR fixes a bug in the state diff `getBaseAndDiffChain()` method. In
the process of finding the diff chain indices, there could be a scenario
where multiple levels return the same diff slot number not equal to the
base (full snapshot) slot number.
This resulted in multiple diff items being added to the diff chain for
the same slot, but on different levels, which resulted in errors reading
the diff.

Fix: we keep a `lastSeenAnchorSlot` equal to the `BaseAnchorSlot` and
update it every time we see a new anchor slot. We ignore if the current
found anchor slot is equal to the `lastSeenAnchorSlot`.

Scenario example: 
exponents: [20, 14, 10, 7, 5]
offset: 0
slots saved: slot 2^11, and slot (2^11 + 2^5)
slot to read: slot (2^11 + 2^5)
resulting list of anchor slots (diff chain indices): [0, 0, 2^11, 2^11,
2^11 + 2^5]
2025-11-21 15:42:27 +00:00
terence
2f067c4164 Add supported / unsupported version for fork enum (#16030)
* gate unreleased forks

* Preston + Bastin's feedback

* Rename back to all versions

* Clean up, mark PR ready for review

* Changelog
2025-11-20 14:58:41 +00:00
Radosław Kapka
81266f60af Move BlockGossipReceived event to the end of gossip validation. (#16031)
* Move `BlockGossipReceived` event to the end of gossip validation.

* changelog <3

* tests
2025-11-19 22:34:02 +00:00
Bastin
207f36065a state-diff configs & kv functions (#15903)
* state-diff configs

* state diff kv functions

* potuz's comments

* Update config.go

* fix merge conflicts

* apply bazel's suggestion and fix some bugs

* preston's feedback
2025-11-19 19:27:56 +00:00
terence
eb9feabd6f stop emitting payload attribute events during late block handling (#16026)
* stop emitting payload attribute events during late block handling when we are not proposing the next slot

* Change the behavior to not even enter FCU if we are not proposing next slot
2025-11-19 16:51:46 +00:00
terence
bc0868e232 Add Gloas beacon state package (#15611)
* Add Gloas protobuf definitions with spec tests

Add Gloas state fields to beacon state implementation

* Remove shared field for pending payment

* Radek's feedback

* Potuz feedback

* use slice concat

* Fix comment

* Fix concat

* Fix comment

* Fix correct index
2025-11-18 15:08:31 +00:00
Chris Berry
35c1ab5e88 Downgrade log level for all validator indices. (#15998)
* Update logging behaviour for updated fee recipient.

* Updated changelog.

* Display validator indices only on TRACE

* Fix tests

---------

Co-authored-by: Manu NALEPA <enalepa@offchainlabs.com>
2025-11-17 16:44:03 +00:00
Radosław Kapka
21bb6f5258 Remove validator cross client from e2e (#16025) 2025-11-17 15:02:06 +00:00
108 changed files with 4569 additions and 380 deletions

View File

@@ -948,13 +948,6 @@ 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() {
headBlock, err := s.headBlock()
if err != nil {
log.WithError(err).WithField("head_root", headRoot).Error("Unable to retrieve head block to fire payload attributes event")
}
// notifyForkchoiceUpdate fires the payload attribute event. But in this case, we won't
// call notifyForkchoiceUpdate, so the event is fired here.
go s.firePayloadAttributesEvent(s.cfg.StateNotifier.StateFeed(), headBlock, headRoot, s.CurrentSlot()+1)
return
}

View File

@@ -2805,6 +2805,10 @@ func TestProcessLightClientUpdate(t *testing.T) {
require.NoError(t, s.cfg.BeaconDB.SaveHeadBlockRoot(ctx, [32]byte{1, 2}))
for _, testVersion := range version.All()[1:] {
if testVersion == version.Gloas {
// TODO(16027): Unskip light client tests for Gloas
continue
}
t.Run(version.String(testVersion), func(t *testing.T) {
l := util.NewTestLightClient(t, testVersion)

View File

@@ -90,6 +90,9 @@ func IsExecutionEnabled(st state.ReadOnlyBeaconState, body interfaces.ReadOnlyBe
if st == nil || body == nil {
return false, errors.New("nil state or block body")
}
if st.Version() >= version.Capella {
return true, nil
}
if IsPreBellatrixVersion(st.Version()) {
return false, nil
}

View File

@@ -260,11 +260,12 @@ func Test_IsExecutionBlockCapella(t *testing.T) {
func Test_IsExecutionEnabled(t *testing.T) {
tests := []struct {
name string
payload *enginev1.ExecutionPayload
header interfaces.ExecutionData
useAltairSt bool
want bool
name string
payload *enginev1.ExecutionPayload
header interfaces.ExecutionData
useAltairSt bool
useCapellaSt bool
want bool
}{
{
name: "use older than bellatrix state",
@@ -331,6 +332,17 @@ func Test_IsExecutionEnabled(t *testing.T) {
}(),
want: true,
},
{
name: "capella state always enabled",
payload: emptyPayload(),
header: func() interfaces.ExecutionData {
h, err := emptyPayloadHeader()
require.NoError(t, err)
return h
}(),
useCapellaSt: true,
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@@ -342,6 +354,8 @@ func Test_IsExecutionEnabled(t *testing.T) {
require.NoError(t, err)
if tt.useAltairSt {
st, _ = util.DeterministicGenesisStateAltair(t, 1)
} else if tt.useCapellaSt {
st, _ = util.DeterministicGenesisStateCapella(t, 1)
}
got, err := blocks.IsExecutionEnabled(st, body)
require.NoError(t, err)

View File

@@ -27,6 +27,9 @@ go_library(
"p2p.go",
"schema.go",
"state.go",
"state_diff.go",
"state_diff_cache.go",
"state_diff_helpers.go",
"state_summary.go",
"state_summary_cache.go",
"utils.go",
@@ -41,10 +44,12 @@ go_library(
"//beacon-chain/db/iface:go_default_library",
"//beacon-chain/state:go_default_library",
"//beacon-chain/state/state-native:go_default_library",
"//cmd/beacon-chain/flags:go_default_library",
"//config/features:go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//consensus-types/blocks:go_default_library",
"//consensus-types/hdiff:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//consensus-types/light-client:go_default_library",
"//consensus-types/primitives:go_default_library",
@@ -53,6 +58,7 @@ go_library(
"//encoding/ssz/detect:go_default_library",
"//genesis:go_default_library",
"//io/file:go_default_library",
"//math:go_default_library",
"//monitoring/progress:go_default_library",
"//monitoring/tracing:go_default_library",
"//monitoring/tracing/trace:go_default_library",
@@ -98,6 +104,7 @@ go_test(
"migration_block_slot_index_test.go",
"migration_state_validators_test.go",
"p2p_test.go",
"state_diff_test.go",
"state_summary_test.go",
"state_test.go",
"utils_test.go",
@@ -111,6 +118,7 @@ go_test(
"//beacon-chain/db/iface:go_default_library",
"//beacon-chain/state:go_default_library",
"//beacon-chain/state/state-native:go_default_library",
"//cmd/beacon-chain/flags:go_default_library",
"//config/features:go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
@@ -120,6 +128,7 @@ go_test(
"//consensus-types/primitives:go_default_library",
"//encoding/bytesutil:go_default_library",
"//genesis:go_default_library",
"//math:go_default_library",
"//proto/dbval:go_default_library",
"//proto/engine/v1:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
@@ -133,6 +142,7 @@ go_test(
"@com_github_golang_snappy//:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@com_github_sirupsen_logrus//hooks/test:go_default_library",
"@io_bazel_rules_go//go/tools/bazel:go_default_library",
"@io_etcd_go_bbolt//:go_default_library",
"@org_golang_google_protobuf//proto:go_default_library",

View File

@@ -2,6 +2,13 @@ package kv
import "bytes"
func hasPhase0Key(enc []byte) bool {
if len(phase0Key) >= len(enc) {
return false
}
return bytes.Equal(enc[:len(phase0Key)], phase0Key)
}
// In order for an encoding to be Altair compatible, it must be prefixed with altair key.
func hasAltairKey(enc []byte) bool {
if len(altairKey) >= len(enc) {

View File

@@ -91,6 +91,7 @@ type Store struct {
blockCache *ristretto.Cache[string, interfaces.ReadOnlySignedBeaconBlock]
validatorEntryCache *ristretto.Cache[[]byte, *ethpb.Validator]
stateSummaryCache *stateSummaryCache
stateDiffCache *stateDiffCache
ctx context.Context
}
@@ -112,6 +113,7 @@ var Buckets = [][]byte{
lightClientUpdatesBucket,
lightClientBootstrapBucket,
lightClientSyncCommitteeBucket,
stateDiffBucket,
// Indices buckets.
blockSlotIndicesBucket,
stateSlotIndicesBucket,
@@ -201,6 +203,14 @@ func NewKVStore(ctx context.Context, dirPath string, opts ...KVStoreOption) (*St
return nil, err
}
if features.Get().EnableStateDiff {
sdCache, err := newStateDiffCache(kv)
if err != nil {
return nil, err
}
kv.stateDiffCache = sdCache
}
return kv, nil
}

View File

@@ -216,6 +216,10 @@ func TestStore_LightClientUpdate_CanSaveRetrieve(t *testing.T) {
db := setupDB(t)
ctx := t.Context()
for _, testVersion := range version.All()[1:] {
if testVersion == version.Gloas {
// TODO(16027): Unskip light client tests for Gloas
continue
}
t.Run(version.String(testVersion), func(t *testing.T) {
update, err := createUpdate(t, testVersion)
require.NoError(t, err)
@@ -572,6 +576,10 @@ func TestStore_LightClientBootstrap_CanSaveRetrieve(t *testing.T) {
require.IsNil(t, retrievedBootstrap)
})
for _, testVersion := range version.All()[1:] {
if testVersion == version.Gloas {
// TODO(16027): Unskip light client tests for Gloas
continue
}
t.Run(version.String(testVersion), func(t *testing.T) {
bootstrap, err := createDefaultLightClientBootstrap(primitives.Slot(uint64(params.BeaconConfig().VersionToForkEpochMap()[testVersion]) * uint64(params.BeaconConfig().SlotsPerEpoch)))
require.NoError(t, err)

View File

@@ -16,6 +16,7 @@ var (
stateValidatorsBucket = []byte("state-validators")
feeRecipientBucket = []byte("fee-recipient")
registrationBucket = []byte("registration")
stateDiffBucket = []byte("state-diff")
// Light Client Updates Bucket
lightClientUpdatesBucket = []byte("light-client-updates")
@@ -46,6 +47,7 @@ var (
// Below keys are used to identify objects are to be fork compatible.
// Objects that are only compatible with specific forks should be prefixed with such keys.
phase0Key = []byte("phase0")
altairKey = []byte("altair")
bellatrixKey = []byte("merge")
bellatrixBlindKey = []byte("blind-bellatrix")

View File

@@ -8,7 +8,6 @@ import (
"github.com/OffchainLabs/prysm/v7/beacon-chain/state"
statenative "github.com/OffchainLabs/prysm/v7/beacon-chain/state/state-native"
"github.com/OffchainLabs/prysm/v7/config/features"
"github.com/OffchainLabs/prysm/v7/consensus-types/blocks"
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
"github.com/OffchainLabs/prysm/v7/encoding/bytesutil"
"github.com/OffchainLabs/prysm/v7/genesis"
@@ -28,6 +27,17 @@ func (s *Store) State(ctx context.Context, blockRoot [32]byte) (state.BeaconStat
ctx, span := trace.StartSpan(ctx, "BeaconDB.State")
defer span.End()
startTime := time.Now()
// If state diff is enabled, we get the state from the state-diff db.
if features.Get().EnableStateDiff {
st, err := s.getStateUsingStateDiff(ctx, blockRoot)
if err != nil {
return nil, err
}
stateReadingTime.Observe(float64(time.Since(startTime).Milliseconds()))
return st, nil
}
enc, err := s.stateBytes(ctx, blockRoot)
if err != nil {
return nil, err
@@ -417,6 +427,16 @@ func (s *Store) storeValidatorEntriesSeparately(ctx context.Context, tx *bolt.Tx
func (s *Store) HasState(ctx context.Context, blockRoot [32]byte) bool {
_, span := trace.StartSpan(ctx, "BeaconDB.HasState")
defer span.End()
if features.Get().EnableStateDiff {
hasState, err := s.hasStateUsingStateDiff(ctx, blockRoot)
if err != nil {
log.WithError(err).Error(fmt.Sprintf("error checking state existence using state-diff"))
return false
}
return hasState
}
hasState := false
err := s.db.View(func(tx *bolt.Tx) error {
bkt := tx.Bucket(stateBucket)
@@ -470,7 +490,7 @@ func (s *Store) DeleteState(ctx context.Context, blockRoot [32]byte) error {
return nil
}
slot, err := s.slotByBlockRoot(ctx, tx, blockRoot[:])
slot, err := s.SlotByBlockRoot(ctx, blockRoot)
if err != nil {
return err
}
@@ -812,50 +832,45 @@ func (s *Store) stateBytes(ctx context.Context, blockRoot [32]byte) ([]byte, err
return dst, err
}
// slotByBlockRoot retrieves the corresponding slot of the input block root.
func (s *Store) slotByBlockRoot(ctx context.Context, tx *bolt.Tx, blockRoot []byte) (primitives.Slot, error) {
ctx, span := trace.StartSpan(ctx, "BeaconDB.slotByBlockRoot")
// SlotByBlockRoot returns the slot of the input block root, based on state summary, block, or state.
// Check for state is only done if state diff feature is not enabled.
func (s *Store) SlotByBlockRoot(ctx context.Context, blockRoot [32]byte) (primitives.Slot, error) {
ctx, span := trace.StartSpan(ctx, "BeaconDB.SlotByBlockRoot")
defer span.End()
bkt := tx.Bucket(stateSummaryBucket)
enc := bkt.Get(blockRoot)
if enc == nil {
// Fall back to check the block.
bkt := tx.Bucket(blocksBucket)
enc := bkt.Get(blockRoot)
if enc == nil {
// Fallback and check the state.
bkt = tx.Bucket(stateBucket)
enc = bkt.Get(blockRoot)
if enc == nil {
return 0, errors.New("state enc can't be nil")
}
// 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")
}
if s == nil || s.IsNil() {
return 0, errors.New("state can't be nil")
}
return s.Slot(), nil
}
b, err := unmarshalBlock(ctx, enc)
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
// check state summary first
stateSummary, err := s.StateSummary(ctx, blockRoot)
if err != nil {
return 0, err
}
stateSummary := &ethpb.StateSummary{}
if err := decode(ctx, enc, stateSummary); err != nil {
return 0, errors.Wrap(err, "could not unmarshal state summary")
if stateSummary != nil {
return stateSummary.Slot, nil
}
return stateSummary.Slot, nil
// fall back to block if state summary is not found
blk, err := s.Block(ctx, blockRoot)
if err != nil {
return 0, err
}
if blk != nil && !blk.IsNil() {
return blk.Block().Slot(), nil
}
// fall back to state, only if state diff feature is not enabled
if features.Get().EnableStateDiff {
return 0, errors.New("neither state summary nor block found")
}
st, err := s.State(ctx, blockRoot)
if err != nil {
return 0, err
}
if st != nil && !st.IsNil() {
return st.Slot(), nil
}
// neither state summary, block nor state found
return 0, errors.New("neither state summary, block nor state found")
}
// HighestSlotStatesBelow returns the states with the highest slot below the input slot
@@ -1031,3 +1046,30 @@ func (s *Store) isStateValidatorMigrationOver() (bool, error) {
}
return returnFlag, nil
}
func (s *Store) getStateUsingStateDiff(ctx context.Context, blockRoot [32]byte) (state.BeaconState, error) {
slot, err := s.SlotByBlockRoot(ctx, blockRoot)
if err != nil {
return nil, err
}
st, err := s.stateByDiff(ctx, slot)
if err != nil {
return nil, err
}
if st == nil || st.IsNil() {
return nil, errors.New("state not found")
}
return st, nil
}
func (s *Store) hasStateUsingStateDiff(ctx context.Context, blockRoot [32]byte) (bool, error) {
slot, err := s.SlotByBlockRoot(ctx, blockRoot)
if err != nil {
return false, err
}
stateLvl := computeLevel(s.getOffset(), slot)
return stateLvl != -1, nil
}

View File

@@ -0,0 +1,232 @@
package kv
import (
"context"
"github.com/OffchainLabs/prysm/v7/beacon-chain/state"
"github.com/OffchainLabs/prysm/v7/cmd/beacon-chain/flags"
"github.com/OffchainLabs/prysm/v7/consensus-types/hdiff"
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
"github.com/OffchainLabs/prysm/v7/monitoring/tracing/trace"
"github.com/pkg/errors"
bolt "go.etcd.io/bbolt"
)
const (
stateSuffix = "_s"
validatorSuffix = "_v"
balancesSuffix = "_b"
)
/*
We use a level-based approach to save state diffs. Each level corresponds to an exponent of 2 (exponents[lvl]).
The data at level 0 is saved every 2**exponent[0] slots and always contains a full state snapshot that is used as a base for the delta saved at other levels.
*/
// saveStateByDiff takes a state and decides between saving a full state snapshot or a diff.
func (s *Store) saveStateByDiff(ctx context.Context, st state.ReadOnlyBeaconState) error {
_, span := trace.StartSpan(ctx, "BeaconDB.saveStateByDiff")
defer span.End()
if st == nil {
return errors.New("state is nil")
}
slot := st.Slot()
offset := s.getOffset()
if uint64(slot) < offset {
return ErrSlotBeforeOffset
}
// Find the level to save the state.
lvl := computeLevel(offset, slot)
if lvl == -1 {
return nil
}
// Save full state if level is 0.
if lvl == 0 {
return s.saveFullSnapshot(st)
}
// Get anchor state to compute the diff from.
anchorState, err := s.getAnchorState(offset, lvl, slot)
if err != nil {
return err
}
return s.saveHdiff(lvl, anchorState, st)
}
// stateByDiff retrieves the full state for a given slot.
func (s *Store) stateByDiff(ctx context.Context, slot primitives.Slot) (state.BeaconState, error) {
offset := s.getOffset()
if uint64(slot) < offset {
return nil, ErrSlotBeforeOffset
}
snapshot, diffChain, err := s.getBaseAndDiffChain(offset, slot)
if err != nil {
return nil, err
}
for _, diff := range diffChain {
if err := ctx.Err(); err != nil {
return nil, err
}
snapshot, err = hdiff.ApplyDiff(ctx, snapshot, diff)
if err != nil {
return nil, err
}
}
return snapshot, nil
}
// saveHdiff computes the diff between the anchor state and the current state and saves it to the database.
// This function needs to be called only with the latest finalized state, and in a strictly increasing slot order.
func (s *Store) saveHdiff(lvl int, anchor, st state.ReadOnlyBeaconState) error {
slot := uint64(st.Slot())
key := makeKeyForStateDiffTree(lvl, slot)
diff, err := hdiff.Diff(anchor, st)
if err != nil {
return err
}
err = s.db.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket(stateDiffBucket)
if bucket == nil {
return bolt.ErrBucketNotFound
}
buf := append(key, stateSuffix...)
if err := bucket.Put(buf, diff.StateDiff); err != nil {
return err
}
buf = append(key, validatorSuffix...)
if err := bucket.Put(buf, diff.ValidatorDiffs); err != nil {
return err
}
buf = append(key, balancesSuffix...)
if err := bucket.Put(buf, diff.BalancesDiff); err != nil {
return err
}
return nil
})
if err != nil {
return err
}
// Save the full state to the cache (if not the last level).
if lvl != len(flags.Get().StateDiffExponents)-1 {
err = s.stateDiffCache.setAnchor(lvl, st)
if err != nil {
return err
}
}
return nil
}
// SaveFullSnapshot saves the full level 0 state snapshot to the database.
func (s *Store) saveFullSnapshot(st state.ReadOnlyBeaconState) error {
slot := uint64(st.Slot())
key := makeKeyForStateDiffTree(0, slot)
stateBytes, err := st.MarshalSSZ()
if err != nil {
return err
}
// add version key to value
enc, err := addKey(st.Version(), stateBytes)
if err != nil {
return err
}
err = s.db.Update(func(tx *bolt.Tx) error {
bucket := tx.Bucket(stateDiffBucket)
if bucket == nil {
return bolt.ErrBucketNotFound
}
if err := bucket.Put(key, enc); err != nil {
return err
}
return nil
})
if err != nil {
return err
}
// Save the full state to the cache, and invalidate other levels.
s.stateDiffCache.clearAnchors()
err = s.stateDiffCache.setAnchor(0, st)
if err != nil {
return err
}
return nil
}
func (s *Store) getDiff(lvl int, slot uint64) (hdiff.HdiffBytes, error) {
key := makeKeyForStateDiffTree(lvl, slot)
var stateDiff []byte
var validatorDiff []byte
var balancesDiff []byte
err := s.db.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket(stateDiffBucket)
if bucket == nil {
return bolt.ErrBucketNotFound
}
buf := append(key, stateSuffix...)
stateDiff = bucket.Get(buf)
if stateDiff == nil {
return errors.New("state diff not found")
}
buf = append(key, validatorSuffix...)
validatorDiff = bucket.Get(buf)
if validatorDiff == nil {
return errors.New("validator diff not found")
}
buf = append(key, balancesSuffix...)
balancesDiff = bucket.Get(buf)
if balancesDiff == nil {
return errors.New("balances diff not found")
}
return nil
})
if err != nil {
return hdiff.HdiffBytes{}, err
}
return hdiff.HdiffBytes{
StateDiff: stateDiff,
ValidatorDiffs: validatorDiff,
BalancesDiff: balancesDiff,
}, nil
}
func (s *Store) getFullSnapshot(slot uint64) (state.BeaconState, error) {
key := makeKeyForStateDiffTree(0, slot)
var enc []byte
err := s.db.View(func(tx *bolt.Tx) error {
bucket := tx.Bucket(stateDiffBucket)
if bucket == nil {
return bolt.ErrBucketNotFound
}
enc = bucket.Get(key)
if enc == nil {
return errors.New("state not found")
}
return nil
})
if err != nil {
return nil, err
}
return decodeStateSnapshot(enc)
}

View File

@@ -0,0 +1,77 @@
package kv
import (
"encoding/binary"
"errors"
"sync"
"github.com/OffchainLabs/prysm/v7/beacon-chain/state"
"github.com/OffchainLabs/prysm/v7/cmd/beacon-chain/flags"
"go.etcd.io/bbolt"
)
type stateDiffCache struct {
sync.RWMutex
anchors []state.ReadOnlyBeaconState
offset uint64
}
func newStateDiffCache(s *Store) (*stateDiffCache, error) {
var offset uint64
err := s.db.View(func(tx *bbolt.Tx) error {
bucket := tx.Bucket(stateDiffBucket)
if bucket == nil {
return bbolt.ErrBucketNotFound
}
offsetBytes := bucket.Get([]byte("offset"))
if offsetBytes == nil {
return errors.New("state diff cache: offset not found")
}
offset = binary.LittleEndian.Uint64(offsetBytes)
return nil
})
if err != nil {
return nil, err
}
return &stateDiffCache{
anchors: make([]state.ReadOnlyBeaconState, len(flags.Get().StateDiffExponents)-1), // -1 because last level doesn't need to be cached
offset: offset,
}, nil
}
func (c *stateDiffCache) getAnchor(level int) state.ReadOnlyBeaconState {
c.RLock()
defer c.RUnlock()
return c.anchors[level]
}
func (c *stateDiffCache) setAnchor(level int, anchor state.ReadOnlyBeaconState) error {
c.Lock()
defer c.Unlock()
if level >= len(c.anchors) || level < 0 {
return errors.New("state diff cache: anchor level out of range")
}
c.anchors[level] = anchor
return nil
}
func (c *stateDiffCache) getOffset() uint64 {
c.RLock()
defer c.RUnlock()
return c.offset
}
func (c *stateDiffCache) setOffset(offset uint64) {
c.Lock()
defer c.Unlock()
c.offset = offset
}
func (c *stateDiffCache) clearAnchors() {
c.Lock()
defer c.Unlock()
c.anchors = make([]state.ReadOnlyBeaconState, len(flags.Get().StateDiffExponents)-1) // -1 because last level doesn't need to be cached
}

View File

@@ -0,0 +1,250 @@
package kv
import (
"context"
"encoding/binary"
"errors"
"fmt"
"github.com/OffchainLabs/prysm/v7/beacon-chain/state"
statenative "github.com/OffchainLabs/prysm/v7/beacon-chain/state/state-native"
"github.com/OffchainLabs/prysm/v7/cmd/beacon-chain/flags"
"github.com/OffchainLabs/prysm/v7/consensus-types/hdiff"
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
"github.com/OffchainLabs/prysm/v7/math"
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
"github.com/OffchainLabs/prysm/v7/runtime/version"
"go.etcd.io/bbolt"
)
var (
offsetKey = []byte("offset")
ErrSlotBeforeOffset = errors.New("slot is before root offset")
)
func makeKeyForStateDiffTree(level int, slot uint64) []byte {
buf := make([]byte, 16)
buf[0] = byte(level)
binary.LittleEndian.PutUint64(buf[1:], slot)
return buf
}
func (s *Store) getAnchorState(offset uint64, lvl int, slot primitives.Slot) (anchor state.ReadOnlyBeaconState, err error) {
if lvl <= 0 || lvl > len(flags.Get().StateDiffExponents) {
return nil, errors.New("invalid value for level")
}
if uint64(slot) < offset {
return nil, ErrSlotBeforeOffset
}
relSlot := uint64(slot) - offset
prevExp := flags.Get().StateDiffExponents[lvl-1]
if prevExp < 2 || prevExp >= 64 {
return nil, fmt.Errorf("state diff exponent %d out of range for uint64", prevExp)
}
span := math.PowerOf2(uint64(prevExp))
anchorSlot := primitives.Slot(uint64(slot) - relSlot%span)
// anchorLvl can be [0, lvl-1]
anchorLvl := computeLevel(offset, anchorSlot)
if anchorLvl == -1 {
return nil, errors.New("could not compute anchor level")
}
// Check if we have the anchor in cache.
anchor = s.stateDiffCache.getAnchor(anchorLvl)
if anchor != nil {
return anchor, nil
}
// If not, load it from the database.
anchor, err = s.stateByDiff(context.Background(), anchorSlot)
if err != nil {
return nil, err
}
// Save it in the cache.
err = s.stateDiffCache.setAnchor(anchorLvl, anchor)
if err != nil {
return nil, err
}
return anchor, nil
}
// computeLevel computes the level in the diff tree. Returns -1 in case slot should not be in tree.
func computeLevel(offset uint64, slot primitives.Slot) int {
rel := uint64(slot) - offset
for i, exp := range flags.Get().StateDiffExponents {
if exp < 2 || exp >= 64 {
return -1
}
span := math.PowerOf2(uint64(exp))
if rel%span == 0 {
return i
}
}
// If rel isnt on any of the boundaries, we should ignore saving it.
return -1
}
func (s *Store) setOffset(slot primitives.Slot) error {
err := s.db.Update(func(tx *bbolt.Tx) error {
bucket := tx.Bucket(stateDiffBucket)
if bucket == nil {
return bbolt.ErrBucketNotFound
}
offsetBytes := bucket.Get(offsetKey)
if offsetBytes != nil {
return fmt.Errorf("offset already set to %d", binary.LittleEndian.Uint64(offsetBytes))
}
offsetBytes = make([]byte, 8)
binary.LittleEndian.PutUint64(offsetBytes, uint64(slot))
if err := bucket.Put(offsetKey, offsetBytes); err != nil {
return err
}
return nil
})
if err != nil {
return err
}
// Save the offset in the cache.
s.stateDiffCache.setOffset(uint64(slot))
return nil
}
func (s *Store) getOffset() uint64 {
return s.stateDiffCache.getOffset()
}
func keyForSnapshot(v int) ([]byte, error) {
switch v {
case version.Fulu:
return fuluKey, nil
case version.Electra:
return ElectraKey, nil
case version.Deneb:
return denebKey, nil
case version.Capella:
return capellaKey, nil
case version.Bellatrix:
return bellatrixKey, nil
case version.Altair:
return altairKey, nil
case version.Phase0:
return phase0Key, nil
default:
return nil, errors.New("unsupported fork")
}
}
func addKey(v int, bytes []byte) ([]byte, error) {
key, err := keyForSnapshot(v)
if err != nil {
return nil, err
}
enc := make([]byte, len(key)+len(bytes))
copy(enc, key)
copy(enc[len(key):], bytes)
return enc, nil
}
func decodeStateSnapshot(enc []byte) (state.BeaconState, error) {
switch {
case hasFuluKey(enc):
var fuluState ethpb.BeaconStateFulu
if err := fuluState.UnmarshalSSZ(enc[len(fuluKey):]); err != nil {
return nil, err
}
return statenative.InitializeFromProtoUnsafeFulu(&fuluState)
case HasElectraKey(enc):
var electraState ethpb.BeaconStateElectra
if err := electraState.UnmarshalSSZ(enc[len(ElectraKey):]); err != nil {
return nil, err
}
return statenative.InitializeFromProtoUnsafeElectra(&electraState)
case hasDenebKey(enc):
var denebState ethpb.BeaconStateDeneb
if err := denebState.UnmarshalSSZ(enc[len(denebKey):]); err != nil {
return nil, err
}
return statenative.InitializeFromProtoUnsafeDeneb(&denebState)
case hasCapellaKey(enc):
var capellaState ethpb.BeaconStateCapella
if err := capellaState.UnmarshalSSZ(enc[len(capellaKey):]); err != nil {
return nil, err
}
return statenative.InitializeFromProtoUnsafeCapella(&capellaState)
case hasBellatrixKey(enc):
var bellatrixState ethpb.BeaconStateBellatrix
if err := bellatrixState.UnmarshalSSZ(enc[len(bellatrixKey):]); err != nil {
return nil, err
}
return statenative.InitializeFromProtoUnsafeBellatrix(&bellatrixState)
case hasAltairKey(enc):
var altairState ethpb.BeaconStateAltair
if err := altairState.UnmarshalSSZ(enc[len(altairKey):]); err != nil {
return nil, err
}
return statenative.InitializeFromProtoUnsafeAltair(&altairState)
case hasPhase0Key(enc):
var phase0State ethpb.BeaconState
if err := phase0State.UnmarshalSSZ(enc[len(phase0Key):]); err != nil {
return nil, err
}
return statenative.InitializeFromProtoUnsafePhase0(&phase0State)
default:
return nil, errors.New("unsupported fork")
}
}
func (s *Store) getBaseAndDiffChain(offset uint64, slot primitives.Slot) (state.BeaconState, []hdiff.HdiffBytes, error) {
if uint64(slot) < offset {
return nil, nil, ErrSlotBeforeOffset
}
rel := uint64(slot) - offset
lvl := computeLevel(offset, slot)
if lvl == -1 {
return nil, nil, errors.New("slot not in tree")
}
exponents := flags.Get().StateDiffExponents
baseSpan := math.PowerOf2(uint64(exponents[0]))
baseAnchorSlot := uint64(slot) - rel%baseSpan
type diffItem struct {
level int
slot uint64
}
var diffChainItems []diffItem
lastSeenAnchorSlot := baseAnchorSlot
for i, exp := range exponents[1 : lvl+1] {
span := math.PowerOf2(uint64(exp))
diffSlot := rel / span * span
if diffSlot == lastSeenAnchorSlot {
continue
}
diffChainItems = append(diffChainItems, diffItem{level: i + 1, slot: diffSlot + offset})
lastSeenAnchorSlot = diffSlot
}
baseSnapshot, err := s.getFullSnapshot(baseAnchorSlot)
if err != nil {
return nil, nil, err
}
diffChain := make([]hdiff.HdiffBytes, 0, len(diffChainItems))
for _, item := range diffChainItems {
diff, err := s.getDiff(item.level, item.slot)
if err != nil {
return nil, nil, err
}
diffChain = append(diffChain, diff)
}
return baseSnapshot, diffChain, nil
}

View File

@@ -0,0 +1,662 @@
package kv
import (
"context"
"encoding/binary"
"fmt"
"math/rand"
"testing"
"github.com/OffchainLabs/prysm/v7/beacon-chain/state"
"github.com/OffchainLabs/prysm/v7/cmd/beacon-chain/flags"
"github.com/OffchainLabs/prysm/v7/config/params"
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
"github.com/OffchainLabs/prysm/v7/math"
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
"github.com/OffchainLabs/prysm/v7/runtime/version"
"github.com/OffchainLabs/prysm/v7/testing/require"
"github.com/OffchainLabs/prysm/v7/testing/util"
"go.etcd.io/bbolt"
)
func TestStateDiff_LoadOrInitOffset(t *testing.T) {
setDefaultStateDiffExponents()
db := setupDB(t)
err := setOffsetInDB(db, 10)
require.NoError(t, err)
offset := db.getOffset()
require.Equal(t, uint64(10), offset)
err = db.setOffset(10)
require.ErrorContains(t, "offset already set", err)
offset = db.getOffset()
require.Equal(t, uint64(10), offset)
}
func TestStateDiff_ComputeLevel(t *testing.T) {
db := setupDB(t)
setDefaultStateDiffExponents()
err := setOffsetInDB(db, 0)
require.NoError(t, err)
offset := db.getOffset()
// 2 ** 21
lvl := computeLevel(offset, primitives.Slot(math.PowerOf2(21)))
require.Equal(t, 0, lvl)
// 2 ** 21 * 3
lvl = computeLevel(offset, primitives.Slot(math.PowerOf2(21)*3))
require.Equal(t, 0, lvl)
// 2 ** 18
lvl = computeLevel(offset, primitives.Slot(math.PowerOf2(18)))
require.Equal(t, 1, lvl)
// 2 ** 18 * 3
lvl = computeLevel(offset, primitives.Slot(math.PowerOf2(18)*3))
require.Equal(t, 1, lvl)
// 2 ** 16
lvl = computeLevel(offset, primitives.Slot(math.PowerOf2(16)))
require.Equal(t, 2, lvl)
// 2 ** 16 * 3
lvl = computeLevel(offset, primitives.Slot(math.PowerOf2(16)*3))
require.Equal(t, 2, lvl)
// 2 ** 13
lvl = computeLevel(offset, primitives.Slot(math.PowerOf2(13)))
require.Equal(t, 3, lvl)
// 2 ** 13 * 3
lvl = computeLevel(offset, primitives.Slot(math.PowerOf2(13)*3))
require.Equal(t, 3, lvl)
// 2 ** 11
lvl = computeLevel(offset, primitives.Slot(math.PowerOf2(11)))
require.Equal(t, 4, lvl)
// 2 ** 11 * 3
lvl = computeLevel(offset, primitives.Slot(math.PowerOf2(11)*3))
require.Equal(t, 4, lvl)
// 2 ** 9
lvl = computeLevel(offset, primitives.Slot(math.PowerOf2(9)))
require.Equal(t, 5, lvl)
// 2 ** 9 * 3
lvl = computeLevel(offset, primitives.Slot(math.PowerOf2(9)*3))
require.Equal(t, 5, lvl)
// 2 ** 5
lvl = computeLevel(offset, primitives.Slot(math.PowerOf2(5)))
require.Equal(t, 6, lvl)
// 2 ** 5 * 3
lvl = computeLevel(offset, primitives.Slot(math.PowerOf2(5)*3))
require.Equal(t, 6, lvl)
// 2 ** 7
lvl = computeLevel(offset, primitives.Slot(math.PowerOf2(7)))
require.Equal(t, 6, lvl)
// 2 ** 5 + 1
lvl = computeLevel(offset, primitives.Slot(math.PowerOf2(5)+1))
require.Equal(t, -1, lvl)
// 2 ** 5 + 16
lvl = computeLevel(offset, primitives.Slot(math.PowerOf2(5)+16))
require.Equal(t, -1, lvl)
// 2 ** 5 + 32
lvl = computeLevel(offset, primitives.Slot(math.PowerOf2(5)+32))
require.Equal(t, 6, lvl)
}
func TestStateDiff_SaveFullSnapshot(t *testing.T) {
setDefaultStateDiffExponents()
for v := range version.All() {
t.Run(version.String(v), func(t *testing.T) {
db := setupDB(t)
// Create state with slot 0
st, enc := createState(t, 0, v)
err := setOffsetInDB(db, 0)
require.NoError(t, err)
err = db.saveStateByDiff(context.Background(), st)
require.NoError(t, err)
err = db.db.View(func(tx *bbolt.Tx) error {
bucket := tx.Bucket(stateDiffBucket)
if bucket == nil {
return bbolt.ErrBucketNotFound
}
s := bucket.Get(makeKeyForStateDiffTree(0, uint64(0)))
if s == nil {
return bbolt.ErrIncompatibleValue
}
require.DeepSSZEqual(t, enc, s)
return nil
})
require.NoError(t, err)
})
}
}
func TestStateDiff_SaveAndReadFullSnapshot(t *testing.T) {
setDefaultStateDiffExponents()
for v := range version.All() {
t.Run(version.String(v), func(t *testing.T) {
db := setupDB(t)
st, _ := createState(t, 0, v)
err := setOffsetInDB(db, 0)
require.NoError(t, err)
err = db.saveStateByDiff(context.Background(), st)
require.NoError(t, err)
readSt, err := db.stateByDiff(context.Background(), 0)
require.NoError(t, err)
require.NotNil(t, readSt)
stSSZ, err := st.MarshalSSZ()
require.NoError(t, err)
readStSSZ, err := readSt.MarshalSSZ()
require.NoError(t, err)
require.DeepSSZEqual(t, stSSZ, readStSSZ)
})
}
}
func TestStateDiff_SaveDiff(t *testing.T) {
setDefaultStateDiffExponents()
for v := range version.All() {
t.Run(version.String(v), func(t *testing.T) {
db := setupDB(t)
// Create state with slot 2**21
slot := primitives.Slot(math.PowerOf2(21))
st, enc := createState(t, slot, v)
err := setOffsetInDB(db, uint64(slot))
require.NoError(t, err)
err = db.saveStateByDiff(context.Background(), st)
require.NoError(t, err)
err = db.db.View(func(tx *bbolt.Tx) error {
bucket := tx.Bucket(stateDiffBucket)
if bucket == nil {
return bbolt.ErrBucketNotFound
}
s := bucket.Get(makeKeyForStateDiffTree(0, uint64(slot)))
if s == nil {
return bbolt.ErrIncompatibleValue
}
require.DeepSSZEqual(t, enc, s)
return nil
})
require.NoError(t, err)
// create state with slot 2**18 (+2**21)
slot = primitives.Slot(math.PowerOf2(18) + math.PowerOf2(21))
st, _ = createState(t, slot, v)
err = db.saveStateByDiff(context.Background(), st)
require.NoError(t, err)
key := makeKeyForStateDiffTree(1, uint64(slot))
err = db.db.View(func(tx *bbolt.Tx) error {
bucket := tx.Bucket(stateDiffBucket)
if bucket == nil {
return bbolt.ErrBucketNotFound
}
buf := append(key, "_s"...)
s := bucket.Get(buf)
if s == nil {
return bbolt.ErrIncompatibleValue
}
buf = append(key, "_v"...)
v := bucket.Get(buf)
if v == nil {
return bbolt.ErrIncompatibleValue
}
buf = append(key, "_b"...)
b := bucket.Get(buf)
if b == nil {
return bbolt.ErrIncompatibleValue
}
return nil
})
require.NoError(t, err)
})
}
}
func TestStateDiff_SaveAndReadDiff(t *testing.T) {
setDefaultStateDiffExponents()
for v := range version.All() {
t.Run(version.String(v), func(t *testing.T) {
db := setupDB(t)
st, _ := createState(t, 0, v)
err := setOffsetInDB(db, 0)
require.NoError(t, err)
err = db.saveStateByDiff(context.Background(), st)
require.NoError(t, err)
slot := primitives.Slot(math.PowerOf2(5))
st, _ = createState(t, slot, v)
err = db.saveStateByDiff(context.Background(), st)
require.NoError(t, err)
readSt, err := db.stateByDiff(context.Background(), slot)
require.NoError(t, err)
require.NotNil(t, readSt)
stSSZ, err := st.MarshalSSZ()
require.NoError(t, err)
readStSSZ, err := readSt.MarshalSSZ()
require.NoError(t, err)
require.DeepSSZEqual(t, stSSZ, readStSSZ)
})
}
}
func TestStateDiff_SaveAndReadDiff_WithRepetitiveAnchorSlots(t *testing.T) {
globalFlags := flags.GlobalFlags{
StateDiffExponents: []int{20, 14, 10, 7, 5},
}
flags.Init(&globalFlags)
for v := range version.All() {
t.Run(version.String(v), func(t *testing.T) {
db := setupDB(t)
err := setOffsetInDB(db, 0)
st, _ := createState(t, 0, v)
require.NoError(t, err)
err = db.saveStateByDiff(context.Background(), st)
require.NoError(t, err)
slot := primitives.Slot(math.PowerOf2(11))
st, _ = createState(t, slot, v)
err = db.saveStateByDiff(context.Background(), st)
require.NoError(t, err)
slot = primitives.Slot(math.PowerOf2(11) + math.PowerOf2(5))
st, _ = createState(t, slot, v)
err = db.saveStateByDiff(context.Background(), st)
require.NoError(t, err)
readSt, err := db.stateByDiff(context.Background(), slot)
require.NoError(t, err)
require.NotNil(t, readSt)
stSSZ, err := st.MarshalSSZ()
require.NoError(t, err)
readStSSZ, err := readSt.MarshalSSZ()
require.NoError(t, err)
require.DeepSSZEqual(t, stSSZ, readStSSZ)
})
}
}
func TestStateDiff_SaveAndReadDiff_MultipleLevels(t *testing.T) {
setDefaultStateDiffExponents()
for v := range version.All() {
t.Run(version.String(v), func(t *testing.T) {
db := setupDB(t)
st, _ := createState(t, 0, v)
err := setOffsetInDB(db, 0)
require.NoError(t, err)
err = db.saveStateByDiff(context.Background(), st)
require.NoError(t, err)
slot := primitives.Slot(math.PowerOf2(11))
st, _ = createState(t, slot, v)
err = db.saveStateByDiff(context.Background(), st)
require.NoError(t, err)
readSt, err := db.stateByDiff(context.Background(), slot)
require.NoError(t, err)
require.NotNil(t, readSt)
stSSZ, err := st.MarshalSSZ()
require.NoError(t, err)
readStSSZ, err := readSt.MarshalSSZ()
require.NoError(t, err)
require.DeepSSZEqual(t, stSSZ, readStSSZ)
slot = primitives.Slot(math.PowerOf2(11) + math.PowerOf2(9))
st, _ = createState(t, slot, v)
err = db.saveStateByDiff(context.Background(), st)
require.NoError(t, err)
readSt, err = db.stateByDiff(context.Background(), slot)
require.NoError(t, err)
require.NotNil(t, readSt)
stSSZ, err = st.MarshalSSZ()
require.NoError(t, err)
readStSSZ, err = readSt.MarshalSSZ()
require.NoError(t, err)
require.DeepSSZEqual(t, stSSZ, readStSSZ)
slot = primitives.Slot(math.PowerOf2(11) + math.PowerOf2(9) + math.PowerOf2(5))
st, _ = createState(t, slot, v)
err = db.saveStateByDiff(context.Background(), st)
require.NoError(t, err)
readSt, err = db.stateByDiff(context.Background(), slot)
require.NoError(t, err)
require.NotNil(t, readSt)
stSSZ, err = st.MarshalSSZ()
require.NoError(t, err)
readStSSZ, err = readSt.MarshalSSZ()
require.NoError(t, err)
require.DeepSSZEqual(t, stSSZ, readStSSZ)
})
}
}
func TestStateDiff_SaveAndReadDiffForkTransition(t *testing.T) {
setDefaultStateDiffExponents()
for v := range version.All()[:len(version.All())-1] {
t.Run(version.String(v), func(t *testing.T) {
db := setupDB(t)
st, _ := createState(t, 0, v)
err := setOffsetInDB(db, 0)
require.NoError(t, err)
err = db.saveStateByDiff(context.Background(), st)
require.NoError(t, err)
slot := primitives.Slot(math.PowerOf2(5))
st, _ = createState(t, slot, v+1)
err = db.saveStateByDiff(context.Background(), st)
require.NoError(t, err)
readSt, err := db.stateByDiff(context.Background(), slot)
require.NoError(t, err)
require.NotNil(t, readSt)
stSSZ, err := st.MarshalSSZ()
require.NoError(t, err)
readStSSZ, err := readSt.MarshalSSZ()
require.NoError(t, err)
require.DeepSSZEqual(t, stSSZ, readStSSZ)
})
}
}
func TestStateDiff_OffsetCache(t *testing.T) {
setDefaultStateDiffExponents()
// test for slot numbers 0 and 1 for every version
for slotNum := range 2 {
for v := range version.All() {
t.Run(fmt.Sprintf("slotNum=%d,%s", slotNum, version.String(v)), func(t *testing.T) {
db := setupDB(t)
slot := primitives.Slot(slotNum)
err := setOffsetInDB(db, uint64(slot))
require.NoError(t, err)
st, _ := createState(t, slot, v)
err = db.saveStateByDiff(context.Background(), st)
require.NoError(t, err)
offset := db.stateDiffCache.getOffset()
require.Equal(t, uint64(slotNum), offset)
slot2 := primitives.Slot(uint64(slotNum) + math.PowerOf2(uint64(flags.Get().StateDiffExponents[0])))
st2, _ := createState(t, slot2, v)
err = db.saveStateByDiff(context.Background(), st2)
require.NoError(t, err)
offset = db.stateDiffCache.getOffset()
require.Equal(t, uint64(slot), offset)
})
}
}
}
func TestStateDiff_AnchorCache(t *testing.T) {
setDefaultStateDiffExponents()
for v := range version.All() {
t.Run(version.String(v), func(t *testing.T) {
exponents := flags.Get().StateDiffExponents
localCache := make([]state.ReadOnlyBeaconState, len(exponents)-1)
db := setupDB(t)
err := setOffsetInDB(db, 0) // lvl 0
require.NoError(t, err)
// at first the cache should be empty
for i := 0; i < len(flags.Get().StateDiffExponents)-1; i++ {
anchor := db.stateDiffCache.getAnchor(i)
require.IsNil(t, anchor)
}
// add level 0
slot := primitives.Slot(0) // offset 0 is already set
st, _ := createState(t, slot, v)
err = db.saveStateByDiff(context.Background(), st)
require.NoError(t, err)
localCache[0] = st
// level 0 should be the same
require.DeepEqual(t, localCache[0], db.stateDiffCache.getAnchor(0))
// rest of the cache should be nil
for i := 1; i < len(exponents)-1; i++ {
require.IsNil(t, db.stateDiffCache.getAnchor(i))
}
// skip last level as it does not get cached
for i := len(exponents) - 2; i > 0; i-- {
slot = primitives.Slot(math.PowerOf2(uint64(exponents[i])))
st, _ := createState(t, slot, v)
err = db.saveStateByDiff(context.Background(), st)
require.NoError(t, err)
localCache[i] = st
// anchor cache must match local cache
for i := 0; i < len(exponents)-1; i++ {
if localCache[i] == nil {
require.IsNil(t, db.stateDiffCache.getAnchor(i))
continue
}
localSSZ, err := localCache[i].MarshalSSZ()
require.NoError(t, err)
anchorSSZ, err := db.stateDiffCache.getAnchor(i).MarshalSSZ()
require.NoError(t, err)
require.DeepSSZEqual(t, localSSZ, anchorSSZ)
}
}
// moving to a new tree should invalidate the cache except for level 0
twoTo21 := math.PowerOf2(21)
slot = primitives.Slot(twoTo21)
st, _ = createState(t, slot, v)
err = db.saveStateByDiff(context.Background(), st)
require.NoError(t, err)
localCache = make([]state.ReadOnlyBeaconState, len(exponents)-1)
localCache[0] = st
// level 0 should be the same
require.DeepEqual(t, localCache[0], db.stateDiffCache.getAnchor(0))
// rest of the cache should be nil
for i := 1; i < len(exponents)-1; i++ {
require.IsNil(t, db.stateDiffCache.getAnchor(i))
}
})
}
}
func TestStateDiff_EncodingAndDecoding(t *testing.T) {
for v := range version.All() {
t.Run(version.String(v), func(t *testing.T) {
st, enc := createState(t, 0, v) // this has addKey called inside
stDecoded, err := decodeStateSnapshot(enc)
require.NoError(t, err)
st1ssz, err := st.MarshalSSZ()
require.NoError(t, err)
st2ssz, err := stDecoded.MarshalSSZ()
require.NoError(t, err)
require.DeepSSZEqual(t, st1ssz, st2ssz)
})
}
}
func createState(t *testing.T, slot primitives.Slot, v int) (state.ReadOnlyBeaconState, []byte) {
p := params.BeaconConfig()
var st state.BeaconState
var err error
switch v {
case version.Phase0:
st, err = util.NewBeaconState()
require.NoError(t, err)
err = st.SetFork(&ethpb.Fork{
PreviousVersion: p.GenesisForkVersion,
CurrentVersion: p.GenesisForkVersion,
Epoch: 0,
})
require.NoError(t, err)
case version.Altair:
st, err = util.NewBeaconStateAltair()
require.NoError(t, err)
err = st.SetFork(&ethpb.Fork{
PreviousVersion: p.GenesisForkVersion,
CurrentVersion: p.AltairForkVersion,
Epoch: p.AltairForkEpoch,
})
require.NoError(t, err)
case version.Bellatrix:
st, err = util.NewBeaconStateBellatrix()
require.NoError(t, err)
err = st.SetFork(&ethpb.Fork{
PreviousVersion: p.AltairForkVersion,
CurrentVersion: p.BellatrixForkVersion,
Epoch: p.BellatrixForkEpoch,
})
require.NoError(t, err)
case version.Capella:
st, err = util.NewBeaconStateCapella()
require.NoError(t, err)
err = st.SetFork(&ethpb.Fork{
PreviousVersion: p.BellatrixForkVersion,
CurrentVersion: p.CapellaForkVersion,
Epoch: p.CapellaForkEpoch,
})
require.NoError(t, err)
case version.Deneb:
st, err = util.NewBeaconStateDeneb()
require.NoError(t, err)
err = st.SetFork(&ethpb.Fork{
PreviousVersion: p.CapellaForkVersion,
CurrentVersion: p.DenebForkVersion,
Epoch: p.DenebForkEpoch,
})
require.NoError(t, err)
case version.Electra:
st, err = util.NewBeaconStateElectra()
require.NoError(t, err)
err = st.SetFork(&ethpb.Fork{
PreviousVersion: p.DenebForkVersion,
CurrentVersion: p.ElectraForkVersion,
Epoch: p.ElectraForkEpoch,
})
require.NoError(t, err)
case version.Fulu:
st, err = util.NewBeaconStateFulu()
require.NoError(t, err)
err = st.SetFork(&ethpb.Fork{
PreviousVersion: p.ElectraForkVersion,
CurrentVersion: p.FuluForkVersion,
Epoch: p.FuluForkEpoch,
})
require.NoError(t, err)
default:
t.Fatalf("unsupported version: %d", v)
}
err = st.SetSlot(slot)
require.NoError(t, err)
slashings := make([]uint64, 8192)
slashings[0] = uint64(rand.Intn(10))
err = st.SetSlashings(slashings)
require.NoError(t, err)
stssz, err := st.MarshalSSZ()
require.NoError(t, err)
enc, err := addKey(v, stssz)
require.NoError(t, err)
return st, enc
}
func setOffsetInDB(s *Store, offset uint64) error {
err := s.db.Update(func(tx *bbolt.Tx) error {
bucket := tx.Bucket(stateDiffBucket)
if bucket == nil {
return bbolt.ErrBucketNotFound
}
offsetBytes := bucket.Get(offsetKey)
if offsetBytes != nil {
return fmt.Errorf("offset already set to %d", binary.LittleEndian.Uint64(offsetBytes))
}
offsetBytes = make([]byte, 8)
binary.LittleEndian.PutUint64(offsetBytes, offset)
if err := bucket.Put(offsetKey, offsetBytes); err != nil {
return err
}
return nil
})
if err != nil {
return err
}
sdCache, err := newStateDiffCache(s)
if err != nil {
return err
}
s.stateDiffCache = sdCache
return nil
}
func setDefaultStateDiffExponents() {
globalFlags := flags.GlobalFlags{
StateDiffExponents: []int{21, 18, 16, 13, 11, 9, 5},
}
flags.Init(&globalFlags)
}

View File

@@ -1,6 +1,7 @@
package kv
import (
"context"
"crypto/rand"
"encoding/binary"
mathRand "math/rand"
@@ -9,6 +10,7 @@ import (
"time"
"github.com/OffchainLabs/prysm/v7/beacon-chain/state"
"github.com/OffchainLabs/prysm/v7/cmd/beacon-chain/flags"
"github.com/OffchainLabs/prysm/v7/config/features"
fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams"
"github.com/OffchainLabs/prysm/v7/config/params"
@@ -17,11 +19,14 @@ import (
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
"github.com/OffchainLabs/prysm/v7/encoding/bytesutil"
"github.com/OffchainLabs/prysm/v7/genesis"
"github.com/OffchainLabs/prysm/v7/math"
enginev1 "github.com/OffchainLabs/prysm/v7/proto/engine/v1"
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
"github.com/OffchainLabs/prysm/v7/runtime/version"
"github.com/OffchainLabs/prysm/v7/testing/assert"
"github.com/OffchainLabs/prysm/v7/testing/require"
"github.com/OffchainLabs/prysm/v7/testing/util"
logTest "github.com/sirupsen/logrus/hooks/test"
bolt "go.etcd.io/bbolt"
)
@@ -1329,3 +1334,297 @@ func TestStore_CleanUpDirtyStates_NoOriginRoot(t *testing.T) {
}
}
}
func TestStore_CanSaveRetrieveStateUsingStateDiff(t *testing.T) {
t.Run("No state summary or block", func(t *testing.T) {
db := setupDB(t)
featCfg := &features.Flags{}
featCfg.EnableStateDiff = true
reset := features.InitWithReset(featCfg)
defer reset()
setDefaultStateDiffExponents()
err := setOffsetInDB(db, 0)
require.NoError(t, err)
readSt, err := db.State(context.Background(), [32]byte{'A'})
require.IsNil(t, readSt)
require.ErrorContains(t, "neither state summary nor block found", err)
})
t.Run("Slot not in tree", func(t *testing.T) {
db := setupDB(t)
featCfg := &features.Flags{}
featCfg.EnableStateDiff = true
reset := features.InitWithReset(featCfg)
defer reset()
setDefaultStateDiffExponents()
err := setOffsetInDB(db, 0)
require.NoError(t, err)
r := bytesutil.ToBytes32([]byte{'A'})
ss := &ethpb.StateSummary{Slot: 1, Root: r[:]} // slot 1 not in tree
err = db.SaveStateSummary(context.Background(), ss)
require.NoError(t, err)
readSt, err := db.State(context.Background(), r)
require.ErrorContains(t, "slot not in tree", err)
require.IsNil(t, readSt)
})
t.Run("State not found", func(t *testing.T) {
db := setupDB(t)
featCfg := &features.Flags{}
featCfg.EnableStateDiff = true
reset := features.InitWithReset(featCfg)
defer reset()
setDefaultStateDiffExponents()
err := setOffsetInDB(db, 0)
require.NoError(t, err)
r := bytesutil.ToBytes32([]byte{'A'})
ss := &ethpb.StateSummary{Slot: 32, Root: r[:]} // slot 32 is in tree
err = db.SaveStateSummary(context.Background(), ss)
require.NoError(t, err)
readSt, err := db.State(context.Background(), r)
require.ErrorContains(t, "state not found", err)
require.IsNil(t, readSt)
})
t.Run("Full state snapshot", func(t *testing.T) {
t.Run("using state summary", func(t *testing.T) {
for v := range version.All() {
t.Run(version.String(v), func(t *testing.T) {
db := setupDB(t)
featCfg := &features.Flags{}
featCfg.EnableStateDiff = true
reset := features.InitWithReset(featCfg)
defer reset()
setDefaultStateDiffExponents()
err := setOffsetInDB(db, 0)
require.NoError(t, err)
st, _ := createState(t, 0, v)
err = db.saveStateByDiff(context.Background(), st)
require.NoError(t, err)
r := bytesutil.ToBytes32([]byte{'A'})
ss := &ethpb.StateSummary{Slot: 0, Root: r[:]}
err = db.SaveStateSummary(context.Background(), ss)
require.NoError(t, err)
readSt, err := db.State(context.Background(), r)
require.NoError(t, err)
require.NotNil(t, readSt)
stSSZ, err := st.MarshalSSZ()
require.NoError(t, err)
readStSSZ, err := readSt.MarshalSSZ()
require.NoError(t, err)
require.DeepSSZEqual(t, stSSZ, readStSSZ)
})
}
})
t.Run("using block", func(t *testing.T) {
for v := range version.All() {
t.Run(version.String(v), func(t *testing.T) {
db := setupDB(t)
featCfg := &features.Flags{}
featCfg.EnableStateDiff = true
reset := features.InitWithReset(featCfg)
defer reset()
setDefaultStateDiffExponents()
err := setOffsetInDB(db, 0)
require.NoError(t, err)
st, _ := createState(t, 0, v)
err = db.saveStateByDiff(context.Background(), st)
require.NoError(t, err)
blk := util.NewBeaconBlock()
blk.Block.Slot = 0
signedBlk, err := blocks.NewSignedBeaconBlock(blk)
require.NoError(t, err)
err = db.SaveBlock(context.Background(), signedBlk)
require.NoError(t, err)
r, err := signedBlk.Block().HashTreeRoot()
require.NoError(t, err)
readSt, err := db.State(context.Background(), r)
require.NoError(t, err)
require.NotNil(t, readSt)
stSSZ, err := st.MarshalSSZ()
require.NoError(t, err)
readStSSZ, err := readSt.MarshalSSZ()
require.NoError(t, err)
require.DeepSSZEqual(t, stSSZ, readStSSZ)
})
}
})
})
t.Run("Diffed state", func(t *testing.T) {
t.Run("using state summary", func(t *testing.T) {
for v := range version.All() {
t.Run(version.String(v), func(t *testing.T) {
db := setupDB(t)
featCfg := &features.Flags{}
featCfg.EnableStateDiff = true
reset := features.InitWithReset(featCfg)
defer reset()
setDefaultStateDiffExponents()
exponents := flags.Get().StateDiffExponents
err := setOffsetInDB(db, 0)
require.NoError(t, err)
st, _ := createState(t, 0, v)
err = db.saveStateByDiff(context.Background(), st)
require.NoError(t, err)
slot := primitives.Slot(math.PowerOf2(uint64(exponents[len(exponents)-2])))
st, _ = createState(t, slot, v)
err = db.saveStateByDiff(context.Background(), st)
require.NoError(t, err)
slot = primitives.Slot(math.PowerOf2(uint64(exponents[len(exponents)-2])) + math.PowerOf2(uint64(exponents[len(exponents)-1])))
st, _ = createState(t, slot, v)
err = db.saveStateByDiff(context.Background(), st)
require.NoError(t, err)
r := bytesutil.ToBytes32([]byte{'A'})
ss := &ethpb.StateSummary{Slot: slot, Root: r[:]}
err = db.SaveStateSummary(context.Background(), ss)
require.NoError(t, err)
readSt, err := db.State(context.Background(), r)
require.NoError(t, err)
require.NotNil(t, readSt)
stSSZ, err := st.MarshalSSZ()
require.NoError(t, err)
readStSSZ, err := readSt.MarshalSSZ()
require.NoError(t, err)
require.DeepSSZEqual(t, stSSZ, readStSSZ)
})
}
})
t.Run("using block", func(t *testing.T) {
for v := range version.All() {
t.Run(version.String(v), func(t *testing.T) {
db := setupDB(t)
featCfg := &features.Flags{}
featCfg.EnableStateDiff = true
reset := features.InitWithReset(featCfg)
defer reset()
setDefaultStateDiffExponents()
exponents := flags.Get().StateDiffExponents
err := setOffsetInDB(db, 0)
require.NoError(t, err)
st, _ := createState(t, 0, v)
err = db.saveStateByDiff(context.Background(), st)
require.NoError(t, err)
slot := primitives.Slot(math.PowerOf2(uint64(exponents[len(exponents)-2])))
st, _ = createState(t, slot, v)
err = db.saveStateByDiff(context.Background(), st)
require.NoError(t, err)
slot = primitives.Slot(math.PowerOf2(uint64(exponents[len(exponents)-2])) + math.PowerOf2(uint64(exponents[len(exponents)-1])))
st, _ = createState(t, slot, v)
err = db.saveStateByDiff(context.Background(), st)
require.NoError(t, err)
blk := util.NewBeaconBlock()
blk.Block.Slot = slot
signedBlk, err := blocks.NewSignedBeaconBlock(blk)
require.NoError(t, err)
err = db.SaveBlock(context.Background(), signedBlk)
require.NoError(t, err)
r, err := signedBlk.Block().HashTreeRoot()
require.NoError(t, err)
readSt, err := db.State(context.Background(), r)
require.NoError(t, err)
require.NotNil(t, readSt)
stSSZ, err := st.MarshalSSZ()
require.NoError(t, err)
readStSSZ, err := readSt.MarshalSSZ()
require.NoError(t, err)
require.DeepSSZEqual(t, stSSZ, readStSSZ)
})
}
})
})
}
func TestStore_HasStateUsingStateDiff(t *testing.T) {
t.Run("No state summary or block", func(t *testing.T) {
hook := logTest.NewGlobal()
db := setupDB(t)
featCfg := &features.Flags{}
featCfg.EnableStateDiff = true
reset := features.InitWithReset(featCfg)
defer reset()
setDefaultStateDiffExponents()
err := setOffsetInDB(db, 0)
require.NoError(t, err)
hasSt := db.HasState(t.Context(), [32]byte{'A'})
require.Equal(t, false, hasSt)
require.LogsContain(t, hook, "neither state summary nor block found")
})
t.Run("slot in tree or not", func(t *testing.T) {
db := setupDB(t)
featCfg := &features.Flags{}
featCfg.EnableStateDiff = true
reset := features.InitWithReset(featCfg)
defer reset()
setDefaultStateDiffExponents()
err := setOffsetInDB(db, 0)
require.NoError(t, err)
testCases := []struct {
slot primitives.Slot
expected bool
}{
{slot: 1, expected: false}, // slot 1 not in tree
{slot: 32, expected: true}, // slot 32 in tree
{slot: 0, expected: true}, // slot 0 in tree
{slot: primitives.Slot(math.PowerOf2(21)), expected: true}, // slot in tree
{slot: primitives.Slot(math.PowerOf2(21) - 1), expected: false}, // slot not in tree
{slot: primitives.Slot(math.PowerOf2(22)), expected: true}, // slot in tree
}
for _, tc := range testCases {
r := bytesutil.ToBytes32([]byte{'A'})
ss := &ethpb.StateSummary{Slot: tc.slot, Root: r[:]}
err = db.SaveStateSummary(t.Context(), ss)
require.NoError(t, err)
hasSt := db.HasState(t.Context(), r)
require.Equal(t, tc.expected, hasSt)
}
})
}

View File

@@ -33,6 +33,10 @@ func TestLightClient_NewLightClientOptimisticUpdateFromBeaconState(t *testing.T)
params.OverrideBeaconConfig(cfg)
for _, testVersion := range version.All()[1:] {
if testVersion == version.Gloas {
// TODO(16027): Unskip light client tests for Gloas
continue
}
t.Run(version.String(testVersion), func(t *testing.T) {
l := util.NewTestLightClient(t, testVersion)

View File

@@ -280,7 +280,10 @@ func configureBeacon(cliCtx *cli.Context) error {
return errors.Wrap(err, "could not configure beacon chain")
}
flags.ConfigureGlobalFlags(cliCtx)
err := flags.ConfigureGlobalFlags(cliCtx)
if err != nil {
return errors.Wrap(err, "could not configure global flags")
}
if err := configureChainConfig(cliCtx); err != nil {
return errors.Wrap(err, "could not configure chain config")
@@ -660,6 +663,7 @@ func (b *BeaconNode) registerP2P(cliCtx *cli.Context) error {
EnableUPnP: cliCtx.Bool(cmd.EnableUPnPFlag.Name),
StateNotifier: b,
DB: b.db,
StateGen: b.stateGen,
ClockWaiter: b.clockWaiter,
})
if err != nil {

View File

@@ -56,6 +56,7 @@ go_library(
"//beacon-chain/p2p/peers/scorers:go_default_library",
"//beacon-chain/p2p/types:go_default_library",
"//beacon-chain/startup:go_default_library",
"//beacon-chain/state/stategen:go_default_library",
"//cmd/beacon-chain/flags:go_default_library",
"//config/features:go_default_library",
"//config/fieldparams:go_default_library",
@@ -153,6 +154,7 @@ go_test(
"//beacon-chain/core/helpers:go_default_library",
"//beacon-chain/core/peerdas:go_default_library",
"//beacon-chain/core/signing:go_default_library",
"//beacon-chain/db/iface:go_default_library",
"//beacon-chain/db/testing:go_default_library",
"//beacon-chain/p2p/encoder:go_default_library",
"//beacon-chain/p2p/peers:go_default_library",
@@ -161,6 +163,7 @@ go_test(
"//beacon-chain/p2p/testing:go_default_library",
"//beacon-chain/p2p/types:go_default_library",
"//beacon-chain/startup:go_default_library",
"//beacon-chain/state/stategen/mock:go_default_library",
"//cmd/beacon-chain/flags:go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",

View File

@@ -7,6 +7,7 @@ import (
statefeed "github.com/OffchainLabs/prysm/v7/beacon-chain/core/feed/state"
"github.com/OffchainLabs/prysm/v7/beacon-chain/db"
"github.com/OffchainLabs/prysm/v7/beacon-chain/startup"
"github.com/OffchainLabs/prysm/v7/beacon-chain/state/stategen"
"github.com/sirupsen/logrus"
)
@@ -49,6 +50,7 @@ type Config struct {
IPColocationWhitelist []*net.IPNet
StateNotifier statefeed.Notifier
DB db.ReadOnlyDatabaseWithSeqNum
StateGen stategen.StateManager
ClockWaiter startup.ClockWaiter
}

View File

@@ -156,29 +156,13 @@ func (s *Service) retrieveActiveValidators() (uint64, error) {
if s.activeValidatorCount != 0 {
return s.activeValidatorCount, nil
}
rt := s.cfg.DB.LastArchivedRoot(s.ctx)
if rt == params.BeaconConfig().ZeroHash {
genState, err := s.cfg.DB.GenesisState(s.ctx)
if err != nil {
return 0, err
}
if genState == nil || genState.IsNil() {
return 0, errors.New("no genesis state exists")
}
activeVals, err := helpers.ActiveValidatorCount(context.Background(), genState, coreTime.CurrentEpoch(genState))
if err != nil {
return 0, err
}
// Cache active validator count
s.activeValidatorCount = activeVals
return activeVals, nil
}
bState, err := s.cfg.DB.State(s.ctx, rt)
finalizedCheckpoint, err := s.cfg.DB.FinalizedCheckpoint(s.ctx)
if err != nil {
return 0, err
}
if bState == nil || bState.IsNil() {
return 0, errors.Errorf("no state with root %#x exists", rt)
bState, err := s.cfg.StateGen.StateByRoot(s.ctx, [32]byte(finalizedCheckpoint.Root))
if err != nil {
return 0, err
}
activeVals, err := helpers.ActiveValidatorCount(context.Background(), bState, coreTime.CurrentEpoch(bState))
if err != nil {

View File

@@ -1,10 +1,14 @@
package p2p
import (
"context"
"testing"
iface "github.com/OffchainLabs/prysm/v7/beacon-chain/db/iface"
dbutil "github.com/OffchainLabs/prysm/v7/beacon-chain/db/testing"
mockstategen "github.com/OffchainLabs/prysm/v7/beacon-chain/state/stategen/mock"
"github.com/OffchainLabs/prysm/v7/config/params"
"github.com/OffchainLabs/prysm/v7/encoding/bytesutil"
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
"github.com/OffchainLabs/prysm/v7/testing/assert"
"github.com/OffchainLabs/prysm/v7/testing/require"
@@ -20,9 +24,11 @@ func TestCorrect_ActiveValidatorsCount(t *testing.T) {
params.OverrideBeaconConfig(cfg)
db := dbutil.SetupDB(t)
wrappedDB := &finalizedCheckpointDB{ReadOnlyDatabaseWithSeqNum: db}
stateGen := mockstategen.NewService()
s := &Service{
ctx: t.Context(),
cfg: &Config{DB: db},
cfg: &Config{DB: wrappedDB, StateGen: stateGen},
}
bState, err := util.NewBeaconState(func(state *ethpb.BeaconState) error {
validators := make([]*ethpb.Validator, params.BeaconConfig().MinGenesisActiveValidatorCount)
@@ -39,6 +45,10 @@ func TestCorrect_ActiveValidatorsCount(t *testing.T) {
})
require.NoError(t, err)
require.NoError(t, db.SaveGenesisData(s.ctx, bState))
checkpoint, err := db.FinalizedCheckpoint(s.ctx)
require.NoError(t, err)
wrappedDB.finalized = checkpoint
stateGen.AddStateForRoot(bState, bytesutil.ToBytes32(checkpoint.Root))
vals, err := s.retrieveActiveValidators()
assert.NoError(t, err, "genesis state not retrieved")
@@ -52,7 +62,10 @@ func TestCorrect_ActiveValidatorsCount(t *testing.T) {
}))
}
require.NoError(t, bState.SetSlot(10000))
require.NoError(t, db.SaveState(s.ctx, bState, [32]byte{'a'}))
rootA := [32]byte{'a'}
require.NoError(t, db.SaveState(s.ctx, bState, rootA))
wrappedDB.finalized = &ethpb.Checkpoint{Root: rootA[:]}
stateGen.AddStateForRoot(bState, rootA)
// Reset count
s.activeValidatorCount = 0
@@ -77,3 +90,15 @@ func TestLoggingParameters(_ *testing.T) {
logGossipParameters("testing", defaultLightClientOptimisticUpdateTopicParams())
logGossipParameters("testing", defaultLightClientFinalityUpdateTopicParams())
}
type finalizedCheckpointDB struct {
iface.ReadOnlyDatabaseWithSeqNum
finalized *ethpb.Checkpoint
}
func (f *finalizedCheckpointDB) FinalizedCheckpoint(ctx context.Context) (*ethpb.Checkpoint, error) {
if f.finalized != nil {
return f.finalized, nil
}
return f.ReadOnlyDatabaseWithSeqNum.FinalizedCheckpoint(ctx)
}

View File

@@ -77,12 +77,12 @@ func InitializeDataMaps() {
},
bytesutil.ToBytes4(params.BeaconConfig().ElectraForkVersion): func() (interfaces.ReadOnlySignedBeaconBlock, error) {
return blocks.NewSignedBeaconBlock(
&ethpb.SignedBeaconBlockElectra{Block: &ethpb.BeaconBlockElectra{Body: &ethpb.BeaconBlockBodyElectra{ExecutionPayload: &enginev1.ExecutionPayloadDeneb{}}}},
&ethpb.SignedBeaconBlockElectra{Block: &ethpb.BeaconBlockElectra{Body: &ethpb.BeaconBlockBodyElectra{ExecutionPayload: &enginev1.ExecutionPayloadDeneb{}, ExecutionRequests: &enginev1.ExecutionRequests{}}}},
)
},
bytesutil.ToBytes4(params.BeaconConfig().FuluForkVersion): func() (interfaces.ReadOnlySignedBeaconBlock, error) {
return blocks.NewSignedBeaconBlock(
&ethpb.SignedBeaconBlockFulu{Block: &ethpb.BeaconBlockElectra{Body: &ethpb.BeaconBlockBodyElectra{ExecutionPayload: &enginev1.ExecutionPayloadDeneb{}}}},
&ethpb.SignedBeaconBlockFulu{Block: &ethpb.BeaconBlockElectra{Body: &ethpb.BeaconBlockBodyElectra{ExecutionPayload: &enginev1.ExecutionPayloadDeneb{}, ExecutionRequests: &enginev1.ExecutionRequests{}}}},
)
},
}

View File

@@ -47,6 +47,10 @@ func TestLightClientHandler_GetLightClientBootstrap(t *testing.T) {
params.OverrideBeaconConfig(cfg)
for _, testVersion := range version.All()[1:] {
if testVersion == version.Gloas {
// TODO(16027): Unskip light client tests for Gloas
continue
}
t.Run(version.String(testVersion), func(t *testing.T) {
l := util.NewTestLightClient(t, testVersion)
@@ -178,6 +182,10 @@ func TestLightClientHandler_GetLightClientByRange(t *testing.T) {
t.Run("can save retrieve", func(t *testing.T) {
for _, testVersion := range version.All()[1:] {
if testVersion == version.Gloas {
// TODO(16027): Unskip light client tests for Gloas
continue
}
t.Run(version.String(testVersion), func(t *testing.T) {
slot := primitives.Slot(params.BeaconConfig().VersionToForkEpochMap()[testVersion] * primitives.Epoch(config.SlotsPerEpoch)).Add(1)
@@ -732,6 +740,10 @@ func TestLightClientHandler_GetLightClientFinalityUpdate(t *testing.T) {
})
for _, testVersion := range version.All()[1:] {
if testVersion == version.Gloas {
// TODO(16027): Unskip light client tests for Gloas
continue
}
t.Run(version.String(testVersion), func(t *testing.T) {
ctx := t.Context()
@@ -827,6 +839,10 @@ func TestLightClientHandler_GetLightClientOptimisticUpdate(t *testing.T) {
})
for _, testVersion := range version.All()[1:] {
if testVersion == version.Gloas {
// TODO(16027): Unskip light client tests for Gloas
continue
}
t.Run(version.String(testVersion), func(t *testing.T) {
ctx := t.Context()
l := util.NewTestLightClient(t, testVersion)

View File

@@ -99,6 +99,7 @@ go_test(
"@com_github_ethereum_go_ethereum//common/hexutil: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_uber_go_mock//gomock:go_default_library",
],

View File

@@ -835,12 +835,17 @@ func (s *Server) PrepareBeaconProposer(w http.ResponseWriter, r *http.Request) {
s.TrackedValidatorsCache.Set(val)
validatorIndices = append(validatorIndices, primitives.ValidatorIndex(validatorIndex))
}
if len(validatorIndices) == 0 {
return
}
log.WithFields(logrus.Fields{
"validatorIndices": validatorIndices,
}).Info("Updated fee recipient addresses")
log := log.WithField("validatorCount", len(validatorIndices))
if logrus.GetLevel() >= logrus.TraceLevel {
log = log.WithField("validatorIndices", validatorIndices)
}
log.Debug("Updated fee recipient addresses")
}
// GetAttesterDuties requests the beacon node to provide a set of attestation duties,

View File

@@ -44,6 +44,7 @@ import (
"github.com/OffchainLabs/prysm/v7/time/slots"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
logTest "github.com/sirupsen/logrus/hooks/test"
)
@@ -2854,6 +2855,8 @@ func TestPrepareBeaconProposer(t *testing.T) {
func TestProposer_PrepareBeaconProposerOverlapping(t *testing.T) {
hook := logTest.NewGlobal()
logrus.SetLevel(logrus.DebugLevel)
db := dbutil.SetupDB(t)
// New validator

View File

@@ -547,11 +547,19 @@ func (vs *Server) PrepareBeaconProposer(
vs.TrackedValidatorsCache.Set(val)
validatorIndices = append(validatorIndices, r.ValidatorIndex)
}
if len(validatorIndices) != 0 {
log.WithFields(logrus.Fields{
"validatorCount": len(validatorIndices),
}).Debug("Updated fee recipient addresses for validator indices")
if len(validatorIndices) == 0 {
return &emptypb.Empty{}, nil
}
log := log.WithField("validatorCount", len(validatorIndices))
if logrus.GetLevel() >= logrus.TraceLevel {
log = log.WithField("validatorIndices", validatorIndices)
}
log.Debug("Updated fee recipient addresses")
return &emptypb.Empty{}, nil
}

View File

@@ -53,6 +53,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
logTest "github.com/sirupsen/logrus/hooks/test"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
@@ -3162,6 +3163,8 @@ func TestProposer_PrepareBeaconProposer(t *testing.T) {
func TestProposer_PrepareBeaconProposerOverlapping(t *testing.T) {
hook := logTest.NewGlobal()
logrus.SetLevel(logrus.DebugLevel)
db := dbutil.SetupDB(t)
ctx := t.Context()
proposerServer := &Server{
@@ -3178,13 +3181,13 @@ func TestProposer_PrepareBeaconProposerOverlapping(t *testing.T) {
}
_, err := proposerServer.PrepareBeaconProposer(ctx, req)
require.NoError(t, err)
require.LogsContain(t, hook, "Updated fee recipient addresses for validator indices")
require.LogsContain(t, hook, "Updated fee recipient addresses")
// Same validator
hook.Reset()
_, err = proposerServer.PrepareBeaconProposer(ctx, req)
require.NoError(t, err)
require.LogsContain(t, hook, "Updated fee recipient addresses for validator indices")
require.LogsContain(t, hook, "Updated fee recipient addresses")
// Same validator with different fee recipient
hook.Reset()
@@ -3196,7 +3199,7 @@ func TestProposer_PrepareBeaconProposerOverlapping(t *testing.T) {
}
_, err = proposerServer.PrepareBeaconProposer(ctx, req)
require.NoError(t, err)
require.LogsContain(t, hook, "Updated fee recipient addresses for validator indices")
require.LogsContain(t, hook, "Updated fee recipient addresses")
// More than one validator
hook.Reset()
@@ -3209,13 +3212,13 @@ func TestProposer_PrepareBeaconProposerOverlapping(t *testing.T) {
}
_, err = proposerServer.PrepareBeaconProposer(ctx, req)
require.NoError(t, err)
require.LogsContain(t, hook, "Updated fee recipient addresses for validator indices")
require.LogsContain(t, hook, "Updated fee recipient addresses")
// Same validators
hook.Reset()
_, err = proposerServer.PrepareBeaconProposer(ctx, req)
require.NoError(t, err)
require.LogsContain(t, hook, "Updated fee recipient addresses for validator indices")
require.LogsContain(t, hook, "Updated fee recipient addresses")
}
func BenchmarkServer_PrepareBeaconProposer(b *testing.B) {

View File

@@ -23,6 +23,7 @@ go_library(
"getters_sync_committee.go",
"getters_validator.go",
"getters_withdrawal.go",
"gloas.go",
"hasher.go",
"multi_value_slices.go",
"proofs.go",

View File

@@ -70,6 +70,14 @@ type BeaconState struct {
pendingConsolidations []*ethpb.PendingConsolidation // pending_consolidations: List[PendingConsolidation, PENDING_CONSOLIDATIONS_LIMIT]
proposerLookahead []primitives.ValidatorIndex // proposer_look_ahead: List[uint64, (MIN_LOOKAHEAD + 1)*SLOTS_PER_EPOCH]
// Gloas fields
latestExecutionPayloadBid *ethpb.ExecutionPayloadBid
executionPayloadAvailability []byte
builderPendingPayments []*ethpb.BuilderPendingPayment
builderPendingWithdrawals []*ethpb.BuilderPendingWithdrawal
latestBlockHash []byte
latestWithdrawalsRoot []byte
id uint64
lock sync.RWMutex
dirtyFields map[types.FieldIndex]bool
@@ -125,6 +133,12 @@ type beaconStateMarshalable struct {
PendingPartialWithdrawals []*ethpb.PendingPartialWithdrawal `json:"pending_partial_withdrawals" yaml:"pending_partial_withdrawals"`
PendingConsolidations []*ethpb.PendingConsolidation `json:"pending_consolidations" yaml:"pending_consolidations"`
ProposerLookahead []primitives.ValidatorIndex `json:"proposer_look_ahead" yaml:"proposer_look_ahead"`
LatestExecutionPayloadBid *ethpb.ExecutionPayloadBid `json:"latest_execution_payload_bid" yaml:"latest_execution_payload_bid"`
ExecutionPayloadAvailability []byte `json:"execution_payload_availability" yaml:"execution_payload_availability"`
BuilderPendingPayments []*ethpb.BuilderPendingPayment `json:"builder_pending_payments" yaml:"builder_pending_payments"`
BuilderPendingWithdrawals []*ethpb.BuilderPendingWithdrawal `json:"builder_pending_withdrawals" yaml:"builder_pending_withdrawals"`
LatestBlockHash []byte `json:"latest_block_hash" yaml:"latest_block_hash"`
LatestWithdrawalsRoot []byte `json:"latest_withdrawals_root" yaml:"latest_withdrawals_root"`
}
func (b *BeaconState) MarshalJSON() ([]byte, error) {
@@ -179,6 +193,12 @@ func (b *BeaconState) MarshalJSON() ([]byte, error) {
PendingPartialWithdrawals: b.pendingPartialWithdrawals,
PendingConsolidations: b.pendingConsolidations,
ProposerLookahead: b.proposerLookahead,
LatestExecutionPayloadBid: b.latestExecutionPayloadBid,
ExecutionPayloadAvailability: b.executionPayloadAvailability,
BuilderPendingPayments: b.builderPendingPayments,
BuilderPendingWithdrawals: b.builderPendingWithdrawals,
LatestBlockHash: b.latestBlockHash,
LatestWithdrawalsRoot: b.latestWithdrawalsRoot,
}
return json.Marshal(marshalable)
}

View File

@@ -259,6 +259,57 @@ func (b *BeaconState) ToProtoUnsafe() any {
PendingConsolidations: b.pendingConsolidations,
ProposerLookahead: lookahead,
}
case version.Gloas:
lookahead := make([]uint64, len(b.proposerLookahead))
for i, v := range b.proposerLookahead {
lookahead[i] = uint64(v)
}
return &ethpb.BeaconStateGloas{
GenesisTime: b.genesisTime,
GenesisValidatorsRoot: gvrCopy[:],
Slot: b.slot,
Fork: b.fork,
LatestBlockHeader: b.latestBlockHeader,
BlockRoots: br,
StateRoots: sr,
HistoricalRoots: b.historicalRoots.Slice(),
Eth1Data: b.eth1Data,
Eth1DataVotes: b.eth1DataVotes,
Eth1DepositIndex: b.eth1DepositIndex,
Validators: vals,
Balances: bals,
RandaoMixes: rm,
Slashings: b.slashings,
PreviousEpochParticipation: b.previousEpochParticipation,
CurrentEpochParticipation: b.currentEpochParticipation,
JustificationBits: b.justificationBits,
PreviousJustifiedCheckpoint: b.previousJustifiedCheckpoint,
CurrentJustifiedCheckpoint: b.currentJustifiedCheckpoint,
FinalizedCheckpoint: b.finalizedCheckpoint,
InactivityScores: inactivityScores,
CurrentSyncCommittee: b.currentSyncCommittee,
NextSyncCommittee: b.nextSyncCommittee,
LatestExecutionPayloadBid: b.latestExecutionPayloadBid,
NextWithdrawalIndex: b.nextWithdrawalIndex,
NextWithdrawalValidatorIndex: b.nextWithdrawalValidatorIndex,
HistoricalSummaries: b.historicalSummaries,
DepositRequestsStartIndex: b.depositRequestsStartIndex,
DepositBalanceToConsume: b.depositBalanceToConsume,
ExitBalanceToConsume: b.exitBalanceToConsume,
EarliestExitEpoch: b.earliestExitEpoch,
ConsolidationBalanceToConsume: b.consolidationBalanceToConsume,
EarliestConsolidationEpoch: b.earliestConsolidationEpoch,
PendingDeposits: b.pendingDeposits,
PendingPartialWithdrawals: b.pendingPartialWithdrawals,
PendingConsolidations: b.pendingConsolidations,
ProposerLookahead: lookahead,
ExecutionPayloadAvailability: b.executionPayloadAvailability,
BuilderPendingPayments: b.builderPendingPayments,
BuilderPendingWithdrawals: b.builderPendingWithdrawals,
LatestBlockHash: b.latestBlockHash,
LatestWithdrawalsRoot: b.latestWithdrawalsRoot,
}
default:
return nil
}
@@ -510,6 +561,57 @@ func (b *BeaconState) ToProto() any {
PendingConsolidations: b.pendingConsolidationsVal(),
ProposerLookahead: lookahead,
}
case version.Gloas:
lookahead := make([]uint64, len(b.proposerLookahead))
for i, v := range b.proposerLookahead {
lookahead[i] = uint64(v)
}
return &ethpb.BeaconStateGloas{
GenesisTime: b.genesisTime,
GenesisValidatorsRoot: gvrCopy[:],
Slot: b.slot,
Fork: b.forkVal(),
LatestBlockHeader: b.latestBlockHeaderVal(),
BlockRoots: br,
StateRoots: sr,
HistoricalRoots: b.historicalRoots.Slice(),
Eth1Data: b.eth1DataVal(),
Eth1DataVotes: b.eth1DataVotesVal(),
Eth1DepositIndex: b.eth1DepositIndex,
Validators: b.validatorsVal(),
Balances: b.balancesVal(),
RandaoMixes: rm,
Slashings: b.slashingsVal(),
PreviousEpochParticipation: b.previousEpochParticipationVal(),
CurrentEpochParticipation: b.currentEpochParticipationVal(),
JustificationBits: b.justificationBitsVal(),
PreviousJustifiedCheckpoint: b.previousJustifiedCheckpointVal(),
CurrentJustifiedCheckpoint: b.currentJustifiedCheckpointVal(),
FinalizedCheckpoint: b.finalizedCheckpointVal(),
InactivityScores: b.inactivityScoresVal(),
CurrentSyncCommittee: b.currentSyncCommitteeVal(),
NextSyncCommittee: b.nextSyncCommitteeVal(),
LatestExecutionPayloadBid: b.latestExecutionPayloadBid.Copy(),
NextWithdrawalIndex: b.nextWithdrawalIndex,
NextWithdrawalValidatorIndex: b.nextWithdrawalValidatorIndex,
HistoricalSummaries: b.historicalSummariesVal(),
DepositRequestsStartIndex: b.depositRequestsStartIndex,
DepositBalanceToConsume: b.depositBalanceToConsume,
ExitBalanceToConsume: b.exitBalanceToConsume,
EarliestExitEpoch: b.earliestExitEpoch,
ConsolidationBalanceToConsume: b.consolidationBalanceToConsume,
EarliestConsolidationEpoch: b.earliestConsolidationEpoch,
PendingDeposits: b.pendingDepositsVal(),
PendingPartialWithdrawals: b.pendingPartialWithdrawalsVal(),
PendingConsolidations: b.pendingConsolidationsVal(),
ProposerLookahead: lookahead,
ExecutionPayloadAvailability: b.executionPayloadAvailabilityVal(),
BuilderPendingPayments: b.builderPendingPaymentsVal(),
BuilderPendingWithdrawals: b.builderPendingWithdrawalsVal(),
LatestBlockHash: b.latestBlockHashVal(),
LatestWithdrawalsRoot: b.latestWithdrawalsRootVal(),
}
default:
return nil
}

View File

@@ -0,0 +1,74 @@
package state_native
import (
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
)
// executionPayloadAvailabilityVal returns a copy of the execution payload availability.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) executionPayloadAvailabilityVal() []byte {
if b.executionPayloadAvailability == nil {
return nil
}
availability := make([]byte, len(b.executionPayloadAvailability))
copy(availability, b.executionPayloadAvailability)
return availability
}
// builderPendingPaymentsVal returns a copy of the builder pending payments.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) builderPendingPaymentsVal() []*ethpb.BuilderPendingPayment {
if b.builderPendingPayments == nil {
return nil
}
payments := make([]*ethpb.BuilderPendingPayment, len(b.builderPendingPayments))
for i, payment := range b.builderPendingPayments {
payments[i] = payment.Copy()
}
return payments
}
// builderPendingWithdrawalsVal returns a copy of the builder pending withdrawals.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) builderPendingWithdrawalsVal() []*ethpb.BuilderPendingWithdrawal {
if b.builderPendingWithdrawals == nil {
return nil
}
withdrawals := make([]*ethpb.BuilderPendingWithdrawal, len(b.builderPendingWithdrawals))
for i, withdrawal := range b.builderPendingWithdrawals {
withdrawals[i] = withdrawal.Copy()
}
return withdrawals
}
// latestBlockHashVal returns a copy of the latest block hash.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) latestBlockHashVal() []byte {
if b.latestBlockHash == nil {
return nil
}
hash := make([]byte, len(b.latestBlockHash))
copy(hash, b.latestBlockHash)
return hash
}
// latestWithdrawalsRootVal returns a copy of the latest withdrawals root.
// This assumes that a lock is already held on BeaconState.
func (b *BeaconState) latestWithdrawalsRootVal() []byte {
if b.latestWithdrawalsRoot == nil {
return nil
}
root := make([]byte, len(b.latestWithdrawalsRoot))
copy(root, b.latestWithdrawalsRoot)
return root
}

View File

@@ -43,6 +43,8 @@ func ComputeFieldRootsWithHasher(ctx context.Context, state *BeaconState) ([][]b
fieldRoots = make([][]byte, params.BeaconConfig().BeaconStateElectraFieldCount)
case version.Fulu:
fieldRoots = make([][]byte, params.BeaconConfig().BeaconStateFuluFieldCount)
case version.Gloas:
fieldRoots = make([][]byte, params.BeaconConfig().BeaconStateGloasFieldCount)
default:
return nil, fmt.Errorf("unknown state version %s", version.String(state.version))
}
@@ -245,7 +247,7 @@ func ComputeFieldRootsWithHasher(ctx context.Context, state *BeaconState) ([][]b
fieldRoots[types.LatestExecutionPayloadHeaderCapella.RealPosition()] = executionPayloadRoot[:]
}
if state.version >= version.Deneb {
if state.version >= version.Deneb && state.version < version.Gloas {
// Execution payload root.
executionPayloadRoot, err := state.latestExecutionPayloadHeaderDeneb.HashTreeRoot()
if err != nil {
@@ -254,6 +256,16 @@ func ComputeFieldRootsWithHasher(ctx context.Context, state *BeaconState) ([][]b
fieldRoots[types.LatestExecutionPayloadHeaderDeneb.RealPosition()] = executionPayloadRoot[:]
}
if state.version >= version.Gloas {
// Execution payload bid root for Gloas.
bidRoot, err := state.latestExecutionPayloadBid.HashTreeRoot()
if err != nil {
return nil, err
}
fieldRoots[types.LatestExecutionPayloadBid.RealPosition()] = bidRoot[:]
}
if state.version >= version.Capella {
// Next withdrawal index root.
nextWithdrawalIndexRoot := make([]byte, 32)
@@ -328,5 +340,34 @@ func ComputeFieldRootsWithHasher(ctx context.Context, state *BeaconState) ([][]b
}
fieldRoots[types.ProposerLookahead.RealPosition()] = proposerLookaheadRoot[:]
}
if state.version >= version.Gloas {
epaRoot, err := stateutil.ExecutionPayloadAvailabilityRoot(state.executionPayloadAvailability)
if err != nil {
return nil, errors.Wrap(err, "could not compute execution payload availability merkleization")
}
fieldRoots[types.ExecutionPayloadAvailability.RealPosition()] = epaRoot[:]
bppRoot, err := stateutil.BuilderPendingPaymentsRoot(state.builderPendingPayments)
if err != nil {
return nil, errors.Wrap(err, "could not compute builder pending payments merkleization")
}
fieldRoots[types.BuilderPendingPayments.RealPosition()] = bppRoot[:]
bpwRoot, err := stateutil.BuilderPendingWithdrawalsRoot(state.builderPendingWithdrawals)
if err != nil {
return nil, errors.Wrap(err, "could not compute builder pending withdrawals merkleization")
}
fieldRoots[types.BuilderPendingWithdrawals.RealPosition()] = bpwRoot[:]
lbhRoot := bytesutil.ToBytes32(state.latestBlockHash)
fieldRoots[types.LatestBlockHash.RealPosition()] = lbhRoot[:]
lwrRoot := bytesutil.ToBytes32(state.latestWithdrawalsRoot)
fieldRoots[types.LatestWithdrawalsRoot.RealPosition()] = lwrRoot[:]
}
return fieldRoots, nil
}

View File

@@ -79,24 +79,25 @@ var (
bellatrixFields = append(altairFields, types.LatestExecutionPayloadHeader)
capellaFields = append(
altairFields,
types.LatestExecutionPayloadHeaderCapella,
withdrawalAndHistoricalSummaryFields = []types.FieldIndex{
types.NextWithdrawalIndex,
types.NextWithdrawalValidatorIndex,
types.HistoricalSummaries,
)
}
denebFields = append(
capellaFields = slices.Concat(
altairFields,
types.LatestExecutionPayloadHeaderDeneb,
types.NextWithdrawalIndex,
types.NextWithdrawalValidatorIndex,
types.HistoricalSummaries,
[]types.FieldIndex{types.LatestExecutionPayloadHeaderCapella},
withdrawalAndHistoricalSummaryFields,
)
electraFields = append(
denebFields,
denebFields = slices.Concat(
altairFields,
[]types.FieldIndex{types.LatestExecutionPayloadHeaderDeneb},
withdrawalAndHistoricalSummaryFields,
)
electraAdditionalFields = []types.FieldIndex{
types.DepositRequestsStartIndex,
types.DepositBalanceToConsume,
types.ExitBalanceToConsume,
@@ -106,12 +107,34 @@ var (
types.PendingDeposits,
types.PendingPartialWithdrawals,
types.PendingConsolidations,
}
electraFields = slices.Concat(
denebFields,
electraAdditionalFields,
)
fuluFields = append(
electraFields,
types.ProposerLookahead,
)
gloasAdditionalFields = []types.FieldIndex{
types.ExecutionPayloadAvailability,
types.BuilderPendingPayments,
types.BuilderPendingWithdrawals,
types.LatestBlockHash,
types.LatestWithdrawalsRoot,
}
gloasFields = slices.Concat(
altairFields,
[]types.FieldIndex{types.LatestExecutionPayloadBid},
withdrawalAndHistoricalSummaryFields,
electraAdditionalFields,
[]types.FieldIndex{types.ProposerLookahead},
gloasAdditionalFields,
)
)
const (
@@ -122,6 +145,7 @@ const (
denebSharedFieldRefCount = 7
electraSharedFieldRefCount = 10
fuluSharedFieldRefCount = 11
gloasSharedFieldRefCount = 12 // Adds PendingBuilderWithdrawal to the shared-ref set and LatestExecutionPayloadHeader is removed
)
// InitializeFromProtoPhase0 the beacon state from a protobuf representation.
@@ -159,6 +183,11 @@ func InitializeFromProtoFulu(st *ethpb.BeaconStateFulu) (state.BeaconState, erro
return InitializeFromProtoUnsafeFulu(proto.Clone(st).(*ethpb.BeaconStateFulu))
}
// InitializeFromProtoGloas the beacon state from a protobuf representation.
func InitializeFromProtoGloas(st *ethpb.BeaconStateGloas) (state.BeaconState, error) {
return InitializeFromProtoUnsafeGloas(proto.Clone(st).(*ethpb.BeaconStateGloas))
}
// InitializeFromProtoUnsafePhase0 directly uses the beacon state protobuf fields
// and sets them as fields of the BeaconState type.
func InitializeFromProtoUnsafePhase0(st *ethpb.BeaconState) (state.BeaconState, error) {
@@ -736,6 +765,111 @@ func InitializeFromProtoUnsafeFulu(st *ethpb.BeaconStateFulu) (state.BeaconState
return b, nil
}
// InitializeFromProtoUnsafeGloas directly uses the beacon state protobuf fields
// and sets them as fields of the BeaconState type.
func InitializeFromProtoUnsafeGloas(st *ethpb.BeaconStateGloas) (state.BeaconState, error) {
if st == nil {
return nil, errors.New("received nil state")
}
hRoots := customtypes.HistoricalRoots(make([][32]byte, len(st.HistoricalRoots)))
for i, r := range st.HistoricalRoots {
hRoots[i] = bytesutil.ToBytes32(r)
}
proposerLookahead := make([]primitives.ValidatorIndex, len(st.ProposerLookahead))
for i, v := range st.ProposerLookahead {
proposerLookahead[i] = primitives.ValidatorIndex(v)
}
fieldCount := params.BeaconConfig().BeaconStateGloasFieldCount
b := &BeaconState{
version: version.Gloas,
genesisTime: st.GenesisTime,
genesisValidatorsRoot: bytesutil.ToBytes32(st.GenesisValidatorsRoot),
slot: st.Slot,
fork: st.Fork,
latestBlockHeader: st.LatestBlockHeader,
historicalRoots: hRoots,
eth1Data: st.Eth1Data,
eth1DataVotes: st.Eth1DataVotes,
eth1DepositIndex: st.Eth1DepositIndex,
slashings: st.Slashings,
previousEpochParticipation: st.PreviousEpochParticipation,
currentEpochParticipation: st.CurrentEpochParticipation,
justificationBits: st.JustificationBits,
previousJustifiedCheckpoint: st.PreviousJustifiedCheckpoint,
currentJustifiedCheckpoint: st.CurrentJustifiedCheckpoint,
finalizedCheckpoint: st.FinalizedCheckpoint,
currentSyncCommittee: st.CurrentSyncCommittee,
nextSyncCommittee: st.NextSyncCommittee,
nextWithdrawalIndex: st.NextWithdrawalIndex,
nextWithdrawalValidatorIndex: st.NextWithdrawalValidatorIndex,
historicalSummaries: st.HistoricalSummaries,
depositRequestsStartIndex: st.DepositRequestsStartIndex,
depositBalanceToConsume: st.DepositBalanceToConsume,
exitBalanceToConsume: st.ExitBalanceToConsume,
earliestExitEpoch: st.EarliestExitEpoch,
consolidationBalanceToConsume: st.ConsolidationBalanceToConsume,
earliestConsolidationEpoch: st.EarliestConsolidationEpoch,
pendingDeposits: st.PendingDeposits,
pendingPartialWithdrawals: st.PendingPartialWithdrawals,
pendingConsolidations: st.PendingConsolidations,
proposerLookahead: proposerLookahead,
latestExecutionPayloadBid: st.LatestExecutionPayloadBid,
executionPayloadAvailability: st.ExecutionPayloadAvailability,
builderPendingPayments: st.BuilderPendingPayments,
builderPendingWithdrawals: st.BuilderPendingWithdrawals,
latestBlockHash: st.LatestBlockHash,
latestWithdrawalsRoot: st.LatestWithdrawalsRoot,
dirtyFields: make(map[types.FieldIndex]bool, fieldCount),
dirtyIndices: make(map[types.FieldIndex][]uint64, fieldCount),
stateFieldLeaves: make(map[types.FieldIndex]*fieldtrie.FieldTrie, fieldCount),
rebuildTrie: make(map[types.FieldIndex]bool, fieldCount),
valMapHandler: stateutil.NewValMapHandler(st.Validators),
}
b.blockRootsMultiValue = NewMultiValueBlockRoots(st.BlockRoots)
b.stateRootsMultiValue = NewMultiValueStateRoots(st.StateRoots)
b.randaoMixesMultiValue = NewMultiValueRandaoMixes(st.RandaoMixes)
b.balancesMultiValue = NewMultiValueBalances(st.Balances)
b.validatorsMultiValue = NewMultiValueValidators(st.Validators)
b.inactivityScoresMultiValue = NewMultiValueInactivityScores(st.InactivityScores)
b.sharedFieldReferences = make(map[types.FieldIndex]*stateutil.Reference, gloasSharedFieldRefCount)
for _, f := range gloasFields {
b.dirtyFields[f] = true
b.rebuildTrie[f] = true
b.dirtyIndices[f] = []uint64{}
trie, err := fieldtrie.NewFieldTrie(f, types.BasicArray, nil, 0)
if err != nil {
return nil, err
}
b.stateFieldLeaves[f] = trie
}
// Initialize field reference tracking for shared data.
b.sharedFieldReferences[types.HistoricalRoots] = stateutil.NewRef(1)
b.sharedFieldReferences[types.Eth1DataVotes] = stateutil.NewRef(1)
b.sharedFieldReferences[types.Slashings] = stateutil.NewRef(1)
b.sharedFieldReferences[types.PreviousEpochParticipationBits] = stateutil.NewRef(1)
b.sharedFieldReferences[types.CurrentEpochParticipationBits] = stateutil.NewRef(1)
b.sharedFieldReferences[types.HistoricalSummaries] = stateutil.NewRef(1)
b.sharedFieldReferences[types.PendingDeposits] = stateutil.NewRef(1)
b.sharedFieldReferences[types.PendingPartialWithdrawals] = stateutil.NewRef(1)
b.sharedFieldReferences[types.PendingConsolidations] = stateutil.NewRef(1)
b.sharedFieldReferences[types.ProposerLookahead] = stateutil.NewRef(1)
b.sharedFieldReferences[types.BuilderPendingWithdrawals] = stateutil.NewRef(1) // New in Gloas.
state.Count.Inc()
// Finalizer runs when dst is being destroyed in garbage collection.
runtime.SetFinalizer(b, finalizerCleanup)
return b, nil
}
// Copy returns a deep copy of the beacon state.
func (b *BeaconState) Copy() state.BeaconState {
b.lock.RLock()
@@ -757,6 +891,8 @@ func (b *BeaconState) Copy() state.BeaconState {
fieldCount = params.BeaconConfig().BeaconStateElectraFieldCount
case version.Fulu:
fieldCount = params.BeaconConfig().BeaconStateFuluFieldCount
case version.Gloas:
fieldCount = params.BeaconConfig().BeaconStateGloasFieldCount
}
dst := &BeaconState{
@@ -811,6 +947,12 @@ func (b *BeaconState) Copy() state.BeaconState {
latestExecutionPayloadHeader: b.latestExecutionPayloadHeader.Copy(),
latestExecutionPayloadHeaderCapella: b.latestExecutionPayloadHeaderCapella.Copy(),
latestExecutionPayloadHeaderDeneb: b.latestExecutionPayloadHeaderDeneb.Copy(),
latestExecutionPayloadBid: b.latestExecutionPayloadBid.Copy(),
executionPayloadAvailability: b.executionPayloadAvailabilityVal(),
builderPendingPayments: b.builderPendingPaymentsVal(),
builderPendingWithdrawals: b.builderPendingWithdrawalsVal(),
latestBlockHash: b.latestBlockHashVal(),
latestWithdrawalsRoot: b.latestWithdrawalsRootVal(),
id: types.Enumerator.Inc(),
@@ -847,6 +989,8 @@ func (b *BeaconState) Copy() state.BeaconState {
dst.sharedFieldReferences = make(map[types.FieldIndex]*stateutil.Reference, electraSharedFieldRefCount)
case version.Fulu:
dst.sharedFieldReferences = make(map[types.FieldIndex]*stateutil.Reference, fuluSharedFieldRefCount)
case version.Gloas:
dst.sharedFieldReferences = make(map[types.FieldIndex]*stateutil.Reference, gloasSharedFieldRefCount)
}
for field, ref := range b.sharedFieldReferences {
@@ -942,6 +1086,8 @@ func (b *BeaconState) initializeMerkleLayers(ctx context.Context) error {
b.dirtyFields = make(map[types.FieldIndex]bool, params.BeaconConfig().BeaconStateElectraFieldCount)
case version.Fulu:
b.dirtyFields = make(map[types.FieldIndex]bool, params.BeaconConfig().BeaconStateFuluFieldCount)
case version.Gloas:
b.dirtyFields = make(map[types.FieldIndex]bool, params.BeaconConfig().BeaconStateGloasFieldCount)
default:
return fmt.Errorf("unknown state version (%s) when computing dirty fields in merklization", version.String(b.version))
}
@@ -1180,6 +1326,19 @@ func (b *BeaconState) rootSelector(ctx context.Context, field types.FieldIndex)
return stateutil.PendingConsolidationsRoot(b.pendingConsolidations)
case types.ProposerLookahead:
return stateutil.ProposerLookaheadRoot(b.proposerLookahead)
case types.LatestExecutionPayloadBid:
return b.latestExecutionPayloadBid.HashTreeRoot()
case types.ExecutionPayloadAvailability:
return stateutil.ExecutionPayloadAvailabilityRoot(b.executionPayloadAvailability)
case types.BuilderPendingPayments:
return stateutil.BuilderPendingPaymentsRoot(b.builderPendingPayments)
case types.BuilderPendingWithdrawals:
return stateutil.BuilderPendingWithdrawalsRoot(b.builderPendingWithdrawals)
case types.LatestBlockHash:
return bytesutil.ToBytes32(b.latestBlockHash), nil
case types.LatestWithdrawalsRoot:
return bytesutil.ToBytes32(b.latestWithdrawalsRoot), nil
}
return [32]byte{}, errors.New("invalid field index provided")
}

View File

@@ -88,6 +88,8 @@ func (f FieldIndex) String() string {
return "latestExecutionPayloadHeaderCapella"
case LatestExecutionPayloadHeaderDeneb:
return "latestExecutionPayloadHeaderDeneb"
case LatestExecutionPayloadBid:
return "latestExecutionPayloadBid"
case NextWithdrawalIndex:
return "nextWithdrawalIndex"
case NextWithdrawalValidatorIndex:
@@ -114,6 +116,16 @@ func (f FieldIndex) String() string {
return "pendingConsolidations"
case ProposerLookahead:
return "proposerLookahead"
case ExecutionPayloadAvailability:
return "executionPayloadAvailability"
case BuilderPendingPayments:
return "builderPendingPayments"
case BuilderPendingWithdrawals:
return "builderPendingWithdrawals"
case LatestBlockHash:
return "latestBlockHash"
case LatestWithdrawalsRoot:
return "latestWithdrawalsRoot"
default:
return fmt.Sprintf("unknown field index number: %d", f)
}
@@ -171,7 +183,7 @@ func (f FieldIndex) RealPosition() int {
return 22
case NextSyncCommittee:
return 23
case LatestExecutionPayloadHeader, LatestExecutionPayloadHeaderCapella, LatestExecutionPayloadHeaderDeneb:
case LatestExecutionPayloadHeader, LatestExecutionPayloadHeaderCapella, LatestExecutionPayloadHeaderDeneb, LatestExecutionPayloadBid:
return 24
case NextWithdrawalIndex:
return 25
@@ -199,6 +211,16 @@ func (f FieldIndex) RealPosition() int {
return 36
case ProposerLookahead:
return 37
case ExecutionPayloadAvailability:
return 38
case BuilderPendingPayments:
return 39
case BuilderPendingWithdrawals:
return 40
case LatestBlockHash:
return 41
case LatestWithdrawalsRoot:
return 42
default:
return -1
}
@@ -251,6 +273,7 @@ const (
LatestExecutionPayloadHeader
LatestExecutionPayloadHeaderCapella
LatestExecutionPayloadHeaderDeneb
LatestExecutionPayloadBid // Gloas: EIP-7732
NextWithdrawalIndex
NextWithdrawalValidatorIndex
HistoricalSummaries
@@ -264,6 +287,11 @@ const (
PendingPartialWithdrawals // Electra: EIP-7251
PendingConsolidations // Electra: EIP-7251
ProposerLookahead // Fulu: EIP-7917
ExecutionPayloadAvailability // Gloas: EIP-7732
BuilderPendingPayments // Gloas: EIP-7732
BuilderPendingWithdrawals // Gloas: EIP-7732
LatestBlockHash // Gloas: EIP-7732
LatestWithdrawalsRoot // Gloas: EIP-7732
)
// Enumerator keeps track of the number of states created since the node's start.

View File

@@ -4,7 +4,10 @@ go_library(
name = "go_default_library",
srcs = [
"block_header_root.go",
"builder_pending_payments_root.go",
"builder_pending_withdrawals_root.go",
"eth1_root.go",
"execution_payload_availability_root.go",
"field_root_attestation.go",
"field_root_eth1.go",
"field_root_validator.go",

View File

@@ -0,0 +1,22 @@
package stateutil
import (
"github.com/OffchainLabs/prysm/v7/encoding/ssz"
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
)
// BuilderPendingPaymentsRoot computes the merkle root of a slice of BuilderPendingPayment.
func BuilderPendingPaymentsRoot(slice []*ethpb.BuilderPendingPayment) ([32]byte, error) {
roots := make([][32]byte, len(slice))
for i, payment := range slice {
r, err := payment.HashTreeRoot()
if err != nil {
return [32]byte{}, err
}
roots[i] = r
}
return ssz.MerkleizeVector(roots, uint64(len(roots))), nil
}

View File

@@ -0,0 +1,12 @@
package stateutil
import (
fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams"
"github.com/OffchainLabs/prysm/v7/encoding/ssz"
ethpb "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
)
// BuilderPendingWithdrawalsRoot computes the SSZ root of a slice of BuilderPendingWithdrawal.
func BuilderPendingWithdrawalsRoot(slice []*ethpb.BuilderPendingWithdrawal) ([32]byte, error) {
return ssz.SliceRoot(slice, fieldparams.BuilderPendingWithdrawalsLimit)
}

View File

@@ -0,0 +1,25 @@
package stateutil
import (
"fmt"
"github.com/OffchainLabs/prysm/v7/encoding/ssz"
)
// ExecutionPayloadAvailabilityRoot computes the merkle root of an execution payload availability bitvector.
func ExecutionPayloadAvailabilityRoot(bitvector []byte) ([32]byte, error) {
chunkCount := (len(bitvector) + 31) / 32
chunks := make([][32]byte, chunkCount)
for i := range chunks {
start := i * 32
end := min(start+32, len(bitvector))
copy(chunks[i][:], bitvector[start:end])
}
root, err := ssz.BitwiseMerkleize(chunks, uint64(len(chunks)), uint64(len(chunks)))
if err != nil {
return [32]byte{}, fmt.Errorf("could not merkleize execution payload availability: %w", err)
}
return root, nil
}

View File

@@ -80,17 +80,10 @@ func (s *Service) updateCustodyInfoIfNeeded() error {
return errors.Wrap(err, "p2p update custody info")
}
// Update the p2p earliest available slot metric
earliestAvailableSlotP2P.Set(float64(storedEarliestSlot))
dbEarliestSlot, _, err := s.cfg.beaconDB.UpdateCustodyInfo(s.ctx, storedEarliestSlot, storedGroupCount)
if err != nil {
if _, _, err := s.cfg.beaconDB.UpdateCustodyInfo(s.ctx, storedEarliestSlot, storedGroupCount); err != nil {
return errors.Wrap(err, "beacon db update custody info")
}
// Update the DB earliest available slot metric
earliestAvailableSlotDB.Set(float64(dbEarliestSlot))
return nil
}

View File

@@ -133,6 +133,12 @@ var (
Help: "Time to verify gossiped attestations",
},
)
attestationVerificationGossipSummary = promauto.NewSummary(
prometheus.SummaryOpts{
Name: "gossip_attestation_verification_milliseconds",
Help: "Time to verify gossiped attestations",
},
)
blockVerificationGossipSummary = promauto.NewSummary(
prometheus.SummaryOpts{
Name: "gossip_block_verification_milliseconds",
@@ -230,17 +236,6 @@ var (
Buckets: []float64{100, 250, 500, 750, 1000, 1500, 2000, 4000, 8000, 12000, 16000},
},
)
// Custody earliest available slot metrics
earliestAvailableSlotP2P = promauto.NewGauge(prometheus.GaugeOpts{
Name: "custody_earliest_available_slot_p2p",
Help: "The earliest available slot tracked by the p2p service for custody purposes",
})
earliestAvailableSlotDB = promauto.NewGauge(prometheus.GaugeOpts{
Name: "custody_earliest_available_slot_db",
Help: "The earliest available slot tracked by the database for custody purposes",
})
)
func (s *Service) updateMetrics() {

View File

@@ -7,6 +7,7 @@ import (
"reflect"
"slices"
"strings"
"time"
"github.com/OffchainLabs/prysm/v7/beacon-chain/blockchain"
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/blocks"
@@ -41,6 +42,11 @@ func (s *Service) validateCommitteeIndexBeaconAttestation(
pid peer.ID,
msg *pubsub.Message,
) (pubsub.ValidationResult, error) {
start := time.Now()
defer func() {
attestationVerificationGossipSummary.Observe(float64(time.Since(start).Milliseconds()))
}()
if pid == s.cfg.p2p.PeerID() {
return pubsub.ValidationAccept, nil
}

View File

@@ -75,17 +75,8 @@ func (s *Service) validateBeaconBlockPubSub(ctx context.Context, pid peer.ID, ms
return pubsub.ValidationReject, errors.New("block.Block is nil")
}
// Broadcast the block on both block and operation feeds to notify other services in the beacon node
// Broadcast the block on a feed to notify other services in the beacon node
// of a received block (even if it does not process correctly through a state transition).
if s.cfg.operationNotifier != nil {
s.cfg.operationNotifier.OperationFeed().Send(&feed.Event{
Type: operation.BlockGossipReceived,
Data: &operation.BlockGossipReceivedData{
SignedBlock: blk,
},
})
}
s.cfg.blockNotifier.BlockFeed().Send(&feed.Event{
Type: blockfeed.ReceivedBlock,
Data: &blockfeed.ReceivedBlockData{
@@ -247,6 +238,16 @@ func (s *Service) validateBeaconBlockPubSub(ctx context.Context, pid peer.ID, ms
blockArrivalGossipSummary.Observe(float64(sinceSlotStartTime.Milliseconds()))
blockVerificationGossipSummary.Observe(float64(validationTime.Milliseconds()))
if s.cfg.operationNotifier != nil {
s.cfg.operationNotifier.OperationFeed().Send(&feed.Event{
Type: operation.BlockGossipReceived,
Data: &operation.BlockGossipReceivedData{
SignedBlock: blk,
},
})
}
return pubsub.ValidationAccept, nil
}

View File

@@ -11,6 +11,8 @@ import (
"github.com/OffchainLabs/prysm/v7/async/abool"
mock "github.com/OffchainLabs/prysm/v7/beacon-chain/blockchain/testing"
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/feed"
opfeed "github.com/OffchainLabs/prysm/v7/beacon-chain/core/feed/operation"
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/helpers"
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/signing"
coreTime "github.com/OffchainLabs/prysm/v7/beacon-chain/core/time"
@@ -79,17 +81,21 @@ func TestValidateBeaconBlockPubSub_InvalidSignature(t *testing.T) {
}
r := &Service{
cfg: &config{
beaconDB: db,
p2p: p,
initialSync: &mockSync.Sync{IsSyncing: false},
chain: chainService,
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
blockNotifier: chainService.BlockNotifier(),
stateGen: stateGen,
beaconDB: db,
p2p: p,
initialSync: &mockSync.Sync{IsSyncing: false},
chain: chainService,
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
blockNotifier: chainService.BlockNotifier(),
operationNotifier: chainService.OperationNotifier(),
stateGen: stateGen,
},
seenBlockCache: lruwrpr.New(10),
badBlockCache: lruwrpr.New(10),
}
opChannel := make(chan *feed.Event, 1)
opSub := r.cfg.operationNotifier.OperationFeed().Subscribe(opChannel)
defer opSub.Unsubscribe()
buf := new(bytes.Buffer)
_, err = p.Encoding().EncodeGossip(buf, msg)
@@ -108,6 +114,13 @@ func TestValidateBeaconBlockPubSub_InvalidSignature(t *testing.T) {
require.ErrorContains(t, "invalid signature", err)
result := res == pubsub.ValidationReject
assert.Equal(t, true, result)
select {
case event := <-opChannel:
assert.NotEqual(t, opfeed.BlockGossipReceived, event.Type, "BlockGossipReceived event should not be sent")
default:
// this case is needed, otherwise the test will never finish
}
}
func TestValidateBeaconBlockPubSub_InvalidSignature_MarksBlockAsBad(t *testing.T) {
@@ -145,17 +158,21 @@ func TestValidateBeaconBlockPubSub_InvalidSignature_MarksBlockAsBad(t *testing.T
}
r := &Service{
cfg: &config{
beaconDB: db,
p2p: p,
initialSync: &mockSync.Sync{IsSyncing: false},
chain: chainService,
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
blockNotifier: chainService.BlockNotifier(),
stateGen: stateGen,
beaconDB: db,
p2p: p,
initialSync: &mockSync.Sync{IsSyncing: false},
chain: chainService,
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
blockNotifier: chainService.BlockNotifier(),
operationNotifier: chainService.OperationNotifier(),
stateGen: stateGen,
},
seenBlockCache: lruwrpr.New(10),
badBlockCache: lruwrpr.New(10),
}
opChannel := make(chan *feed.Event, 1)
opSub := r.cfg.operationNotifier.OperationFeed().Subscribe(opChannel)
defer opSub.Unsubscribe()
blockRoot, err := msg.Block.HashTreeRoot()
require.NoError(t, err)
@@ -183,6 +200,13 @@ func TestValidateBeaconBlockPubSub_InvalidSignature_MarksBlockAsBad(t *testing.T
// Verify block is now marked as bad after invalid signature
assert.Equal(t, true, r.hasBadBlock(blockRoot), "block should be marked as bad after invalid signature")
select {
case event := <-opChannel:
assert.NotEqual(t, opfeed.BlockGossipReceived, event.Type, "BlockGossipReceived event should not be sent")
default:
// this case is needed, otherwise the test will never finish
}
}
func TestValidateBeaconBlockPubSub_BlockAlreadyPresentInDB(t *testing.T) {
@@ -198,16 +222,20 @@ func TestValidateBeaconBlockPubSub_BlockAlreadyPresentInDB(t *testing.T) {
chainService := &mock.ChainService{Genesis: time.Now()}
r := &Service{
cfg: &config{
beaconDB: db,
p2p: p,
initialSync: &mockSync.Sync{IsSyncing: false},
chain: chainService,
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
blockNotifier: chainService.BlockNotifier(),
beaconDB: db,
p2p: p,
initialSync: &mockSync.Sync{IsSyncing: false},
chain: chainService,
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
blockNotifier: chainService.BlockNotifier(),
operationNotifier: chainService.OperationNotifier(),
},
seenBlockCache: lruwrpr.New(10),
badBlockCache: lruwrpr.New(10),
}
opChannel := make(chan *feed.Event, 1)
opSub := r.cfg.operationNotifier.OperationFeed().Subscribe(opChannel)
defer opSub.Unsubscribe()
buf := new(bytes.Buffer)
_, err := p.Encoding().EncodeGossip(buf, msg)
@@ -226,6 +254,13 @@ func TestValidateBeaconBlockPubSub_BlockAlreadyPresentInDB(t *testing.T) {
res, err := r.validateBeaconBlockPubSub(ctx, "", m)
assert.NoError(t, err)
assert.Equal(t, res, pubsub.ValidationIgnore, "block present in DB should be ignored")
select {
case event := <-opChannel:
assert.NotEqual(t, opfeed.BlockGossipReceived, event.Type, "BlockGossipReceived event should not be sent")
default:
// this case is needed, otherwise the test will never finish
}
}
func TestValidateBeaconBlockPubSub_CanRecoverStateSummary(t *testing.T) {
@@ -260,19 +295,24 @@ func TestValidateBeaconBlockPubSub_CanRecoverStateSummary(t *testing.T) {
}
r := &Service{
cfg: &config{
beaconDB: db,
p2p: p,
initialSync: &mockSync.Sync{IsSyncing: false},
chain: chainService,
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
blockNotifier: chainService.BlockNotifier(),
stateGen: stateGen,
beaconDB: db,
p2p: p,
initialSync: &mockSync.Sync{IsSyncing: false},
chain: chainService,
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
blockNotifier: chainService.BlockNotifier(),
operationNotifier: chainService.OperationNotifier(),
stateGen: stateGen,
},
seenBlockCache: lruwrpr.New(10),
badBlockCache: lruwrpr.New(10),
slotToPendingBlocks: gcache.New(time.Second, 2*time.Second),
seenPendingBlocks: make(map[[32]byte]bool),
}
opChannel := make(chan *feed.Event, 1)
opSub := r.cfg.operationNotifier.OperationFeed().Subscribe(opChannel)
defer opSub.Unsubscribe()
buf := new(bytes.Buffer)
_, err = p.Encoding().EncodeGossip(buf, msg)
require.NoError(t, err)
@@ -291,6 +331,17 @@ func TestValidateBeaconBlockPubSub_CanRecoverStateSummary(t *testing.T) {
result := res == pubsub.ValidationAccept
assert.Equal(t, true, result)
assert.NotNil(t, m.ValidatorData, "Decoded message was not set on the message validator data")
blockGossipFound := false
select {
case event := <-opChannel:
if event.Type == opfeed.BlockGossipReceived {
blockGossipFound = true
}
default:
// this case is needed, otherwise the test will never finish
}
assert.Equal(t, true, blockGossipFound, "BlockGossipReceived event should be sent")
}
func TestValidateBeaconBlockPubSub_IsInCache(t *testing.T) {
@@ -326,19 +377,24 @@ func TestValidateBeaconBlockPubSub_IsInCache(t *testing.T) {
}
r := &Service{
cfg: &config{
beaconDB: db,
p2p: p,
initialSync: &mockSync.Sync{IsSyncing: false},
chain: chainService,
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
blockNotifier: chainService.BlockNotifier(),
stateGen: stateGen,
beaconDB: db,
p2p: p,
initialSync: &mockSync.Sync{IsSyncing: false},
chain: chainService,
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
blockNotifier: chainService.BlockNotifier(),
operationNotifier: chainService.OperationNotifier(),
stateGen: stateGen,
},
seenBlockCache: lruwrpr.New(10),
badBlockCache: lruwrpr.New(10),
slotToPendingBlocks: gcache.New(time.Second, 2*time.Second),
seenPendingBlocks: make(map[[32]byte]bool),
}
opChannel := make(chan *feed.Event, 1)
opSub := r.cfg.operationNotifier.OperationFeed().Subscribe(opChannel)
defer opSub.Unsubscribe()
buf := new(bytes.Buffer)
_, err = p.Encoding().EncodeGossip(buf, msg)
require.NoError(t, err)
@@ -357,6 +413,17 @@ func TestValidateBeaconBlockPubSub_IsInCache(t *testing.T) {
result := res == pubsub.ValidationAccept
assert.Equal(t, true, result)
assert.NotNil(t, m.ValidatorData, "Decoded message was not set on the message validator data")
blockGossipFound := false
select {
case event := <-opChannel:
if event.Type == opfeed.BlockGossipReceived {
blockGossipFound = true
}
default:
// this case is needed, otherwise the test will never finish
}
assert.Equal(t, true, blockGossipFound, "BlockGossipReceived event should be sent")
}
func TestValidateBeaconBlockPubSub_ValidProposerSignature(t *testing.T) {
@@ -392,19 +459,24 @@ func TestValidateBeaconBlockPubSub_ValidProposerSignature(t *testing.T) {
}
r := &Service{
cfg: &config{
beaconDB: db,
p2p: p,
initialSync: &mockSync.Sync{IsSyncing: false},
chain: chainService,
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
blockNotifier: chainService.BlockNotifier(),
stateGen: stateGen,
beaconDB: db,
p2p: p,
initialSync: &mockSync.Sync{IsSyncing: false},
chain: chainService,
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
blockNotifier: chainService.BlockNotifier(),
operationNotifier: chainService.OperationNotifier(),
stateGen: stateGen,
},
seenBlockCache: lruwrpr.New(10),
badBlockCache: lruwrpr.New(10),
slotToPendingBlocks: gcache.New(time.Second, 2*time.Second),
seenPendingBlocks: make(map[[32]byte]bool),
}
opChannel := make(chan *feed.Event, 1)
opSub := r.cfg.operationNotifier.OperationFeed().Subscribe(opChannel)
defer opSub.Unsubscribe()
buf := new(bytes.Buffer)
_, err = p.Encoding().EncodeGossip(buf, msg)
require.NoError(t, err)
@@ -423,6 +495,17 @@ func TestValidateBeaconBlockPubSub_ValidProposerSignature(t *testing.T) {
result := res == pubsub.ValidationAccept
assert.Equal(t, true, result)
assert.NotNil(t, m.ValidatorData, "Decoded message was not set on the message validator data")
blockGossipFound := false
select {
case event := <-opChannel:
if event.Type == opfeed.BlockGossipReceived {
blockGossipFound = true
}
default:
// this case is needed, otherwise the test will never finish
}
assert.Equal(t, true, blockGossipFound, "BlockGossipReceived event should be sent")
}
func TestValidateBeaconBlockPubSub_WithLookahead(t *testing.T) {
@@ -460,13 +543,14 @@ func TestValidateBeaconBlockPubSub_WithLookahead(t *testing.T) {
}}
r := &Service{
cfg: &config{
beaconDB: db,
p2p: p,
initialSync: &mockSync.Sync{IsSyncing: false},
chain: chainService,
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
blockNotifier: chainService.BlockNotifier(),
stateGen: stateGen,
beaconDB: db,
p2p: p,
initialSync: &mockSync.Sync{IsSyncing: false},
chain: chainService,
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
blockNotifier: chainService.BlockNotifier(),
operationNotifier: chainService.OperationNotifier(),
stateGen: stateGen,
},
seenBlockCache: lruwrpr.New(10),
badBlockCache: lruwrpr.New(10),
@@ -474,6 +558,10 @@ func TestValidateBeaconBlockPubSub_WithLookahead(t *testing.T) {
seenPendingBlocks: make(map[[32]byte]bool),
subHandler: newSubTopicHandler(),
}
opChannel := make(chan *feed.Event, 1)
opSub := r.cfg.operationNotifier.OperationFeed().Subscribe(opChannel)
defer opSub.Unsubscribe()
buf := new(bytes.Buffer)
_, err = p.Encoding().EncodeGossip(buf, msg)
require.NoError(t, err)
@@ -492,6 +580,17 @@ func TestValidateBeaconBlockPubSub_WithLookahead(t *testing.T) {
result := res == pubsub.ValidationAccept
assert.Equal(t, true, result)
assert.NotNil(t, m.ValidatorData, "Decoded message was not set on the message validator data")
blockGossipFound := false
select {
case event := <-opChannel:
if event.Type == opfeed.BlockGossipReceived {
blockGossipFound = true
}
default:
// this case is needed, otherwise the test will never finish
}
assert.Equal(t, true, blockGossipFound, "BlockGossipReceived event should be sent")
}
func TestValidateBeaconBlockPubSub_AdvanceEpochsForState(t *testing.T) {
@@ -529,19 +628,24 @@ func TestValidateBeaconBlockPubSub_AdvanceEpochsForState(t *testing.T) {
}}
r := &Service{
cfg: &config{
beaconDB: db,
p2p: p,
initialSync: &mockSync.Sync{IsSyncing: false},
chain: chainService,
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
blockNotifier: chainService.BlockNotifier(),
stateGen: stateGen,
beaconDB: db,
p2p: p,
initialSync: &mockSync.Sync{IsSyncing: false},
chain: chainService,
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
blockNotifier: chainService.BlockNotifier(),
operationNotifier: chainService.OperationNotifier(),
stateGen: stateGen,
},
seenBlockCache: lruwrpr.New(10),
badBlockCache: lruwrpr.New(10),
slotToPendingBlocks: gcache.New(time.Second, 2*time.Second),
seenPendingBlocks: make(map[[32]byte]bool),
}
opChannel := make(chan *feed.Event, 1)
opSub := r.cfg.operationNotifier.OperationFeed().Subscribe(opChannel)
defer opSub.Unsubscribe()
buf := new(bytes.Buffer)
_, err = p.Encoding().EncodeGossip(buf, msg)
require.NoError(t, err)
@@ -560,6 +664,17 @@ func TestValidateBeaconBlockPubSub_AdvanceEpochsForState(t *testing.T) {
result := res == pubsub.ValidationAccept
assert.Equal(t, true, result)
assert.NotNil(t, m.ValidatorData, "Decoded message was not set on the message validator data")
blockGossipFound := false
select {
case event := <-opChannel:
if event.Type == opfeed.BlockGossipReceived {
blockGossipFound = true
}
default:
// this case is needed, otherwise the test will never finish
}
assert.Equal(t, true, blockGossipFound, "BlockGossipReceived event should be sent")
}
func TestValidateBeaconBlockPubSub_Syncing(t *testing.T) {
@@ -580,13 +695,17 @@ func TestValidateBeaconBlockPubSub_Syncing(t *testing.T) {
}}
r := &Service{
cfg: &config{
beaconDB: db,
p2p: p,
initialSync: &mockSync.Sync{IsSyncing: true},
chain: chainService,
blockNotifier: chainService.BlockNotifier(),
beaconDB: db,
p2p: p,
initialSync: &mockSync.Sync{IsSyncing: true},
chain: chainService,
blockNotifier: chainService.BlockNotifier(),
operationNotifier: chainService.OperationNotifier(),
},
}
opChannel := make(chan *feed.Event, 1)
opSub := r.cfg.operationNotifier.OperationFeed().Subscribe(opChannel)
defer opSub.Unsubscribe()
buf := new(bytes.Buffer)
_, err = p.Encoding().EncodeGossip(buf, msg)
@@ -601,6 +720,13 @@ func TestValidateBeaconBlockPubSub_Syncing(t *testing.T) {
res, err := r.validateBeaconBlockPubSub(ctx, "", m)
assert.NoError(t, err)
assert.Equal(t, res, pubsub.ValidationIgnore, "block is ignored until fully synced")
select {
case event := <-opChannel:
assert.NotEqual(t, opfeed.BlockGossipReceived, event.Type, "BlockGossipReceived event should not be sent")
default:
// this case is needed, otherwise the test will never finish
}
}
func TestValidateBeaconBlockPubSub_IgnoreAndQueueBlocksFromNearFuture(t *testing.T) {
@@ -636,13 +762,14 @@ func TestValidateBeaconBlockPubSub_IgnoreAndQueueBlocksFromNearFuture(t *testing
State: beaconState}
r := &Service{
cfg: &config{
p2p: p,
beaconDB: db,
initialSync: &mockSync.Sync{IsSyncing: false},
chain: chainService,
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
blockNotifier: chainService.BlockNotifier(),
stateGen: stateGen,
p2p: p,
beaconDB: db,
initialSync: &mockSync.Sync{IsSyncing: false},
chain: chainService,
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
blockNotifier: chainService.BlockNotifier(),
operationNotifier: chainService.OperationNotifier(),
stateGen: stateGen,
},
chainStarted: abool.New(),
seenBlockCache: lruwrpr.New(10),
@@ -650,6 +777,9 @@ func TestValidateBeaconBlockPubSub_IgnoreAndQueueBlocksFromNearFuture(t *testing
slotToPendingBlocks: gcache.New(time.Second, 2*time.Second),
seenPendingBlocks: make(map[[32]byte]bool),
}
opChannel := make(chan *feed.Event, 1)
opSub := r.cfg.operationNotifier.OperationFeed().Subscribe(opChannel)
defer opSub.Unsubscribe()
buf := new(bytes.Buffer)
_, err = p.Encoding().EncodeGossip(buf, msg)
@@ -670,6 +800,13 @@ func TestValidateBeaconBlockPubSub_IgnoreAndQueueBlocksFromNearFuture(t *testing
// check if the block is inserted in the Queue
assert.Equal(t, true, len(r.pendingBlocksInCache(msg.Block.Slot)) == 1)
select {
case event := <-opChannel:
assert.NotEqual(t, opfeed.BlockGossipReceived, event.Type, "BlockGossipReceived event should not be sent")
default:
// this case is needed, otherwise the test will never finish
}
}
func TestValidateBeaconBlockPubSub_RejectBlocksFromFuture(t *testing.T) {
@@ -688,12 +825,13 @@ func TestValidateBeaconBlockPubSub_RejectBlocksFromFuture(t *testing.T) {
chainService := &mock.ChainService{Genesis: time.Now()}
r := &Service{
cfg: &config{
p2p: p,
beaconDB: db,
initialSync: &mockSync.Sync{IsSyncing: false},
chain: chainService,
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
blockNotifier: chainService.BlockNotifier(),
p2p: p,
beaconDB: db,
initialSync: &mockSync.Sync{IsSyncing: false},
chain: chainService,
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
blockNotifier: chainService.BlockNotifier(),
operationNotifier: chainService.OperationNotifier(),
},
chainStarted: abool.New(),
seenBlockCache: lruwrpr.New(10),
@@ -701,6 +839,9 @@ func TestValidateBeaconBlockPubSub_RejectBlocksFromFuture(t *testing.T) {
slotToPendingBlocks: gcache.New(time.Second, 2*time.Second),
seenPendingBlocks: make(map[[32]byte]bool),
}
opChannel := make(chan *feed.Event, 1)
opSub := r.cfg.operationNotifier.OperationFeed().Subscribe(opChannel)
defer opSub.Unsubscribe()
buf := new(bytes.Buffer)
_, err = p.Encoding().EncodeGossip(buf, msg)
@@ -718,6 +859,13 @@ func TestValidateBeaconBlockPubSub_RejectBlocksFromFuture(t *testing.T) {
res, err := r.validateBeaconBlockPubSub(ctx, "", m)
assert.NoError(t, err)
assert.Equal(t, res, pubsub.ValidationIgnore, "block from the future should be ignored")
select {
case event := <-opChannel:
assert.NotEqual(t, opfeed.BlockGossipReceived, event.Type, "BlockGossipReceived event should not be sent")
default:
// this case is needed, otherwise the test will never finish
}
}
func TestValidateBeaconBlockPubSub_RejectBlocksFromThePast(t *testing.T) {
@@ -742,16 +890,20 @@ func TestValidateBeaconBlockPubSub_RejectBlocksFromThePast(t *testing.T) {
}
r := &Service{
cfg: &config{
beaconDB: db,
p2p: p,
initialSync: &mockSync.Sync{IsSyncing: false},
chain: chainService,
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
blockNotifier: chainService.BlockNotifier(),
beaconDB: db,
p2p: p,
initialSync: &mockSync.Sync{IsSyncing: false},
chain: chainService,
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
blockNotifier: chainService.BlockNotifier(),
operationNotifier: chainService.OperationNotifier(),
},
seenBlockCache: lruwrpr.New(10),
badBlockCache: lruwrpr.New(10),
}
opChannel := make(chan *feed.Event, 1)
opSub := r.cfg.operationNotifier.OperationFeed().Subscribe(opChannel)
defer opSub.Unsubscribe()
buf := new(bytes.Buffer)
_, err = p.Encoding().EncodeGossip(buf, msg)
@@ -769,6 +921,13 @@ func TestValidateBeaconBlockPubSub_RejectBlocksFromThePast(t *testing.T) {
res, err := r.validateBeaconBlockPubSub(ctx, "", m)
require.ErrorContains(t, "greater or equal to block slot", err)
assert.Equal(t, res, pubsub.ValidationIgnore, "block from the past should be ignored")
select {
case event := <-opChannel:
assert.NotEqual(t, opfeed.BlockGossipReceived, event.Type, "BlockGossipReceived event should not be sent")
default:
// this case is needed, otherwise the test will never finish
}
}
func TestValidateBeaconBlockPubSub_SeenProposerSlot(t *testing.T) {
@@ -813,19 +972,23 @@ func TestValidateBeaconBlockPubSub_SeenProposerSlot(t *testing.T) {
}
r := &Service{
cfg: &config{
beaconDB: db,
p2p: p,
initialSync: &mockSync.Sync{IsSyncing: false},
chain: chainService,
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
blockNotifier: chainService.BlockNotifier(),
slashingPool: slashingPool,
beaconDB: db,
p2p: p,
initialSync: &mockSync.Sync{IsSyncing: false},
chain: chainService,
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
blockNotifier: chainService.BlockNotifier(),
operationNotifier: chainService.OperationNotifier(),
slashingPool: slashingPool,
},
seenBlockCache: lruwrpr.New(10),
badBlockCache: lruwrpr.New(10),
slotToPendingBlocks: gcache.New(time.Second, 2*time.Second),
seenPendingBlocks: make(map[[32]byte]bool),
}
opChannel := make(chan *feed.Event, 1)
opSub := r.cfg.operationNotifier.OperationFeed().Subscribe(opChannel)
defer opSub.Unsubscribe()
// Mark the proposer/slot as seen
r.setSeenBlockIndexSlot(msg.Block.Slot, msg.Block.ProposerIndex)
@@ -853,6 +1016,13 @@ func TestValidateBeaconBlockPubSub_SeenProposerSlot(t *testing.T) {
// Verify no slashings were created
assert.Equal(t, 0, len(slashingPool.PendingPropSlashings), "Expected no slashings for same signature")
select {
case event := <-opChannel:
assert.NotEqual(t, opfeed.BlockGossipReceived, event.Type, "BlockGossipReceived event should not be sent")
default:
// this case is needed, otherwise the test will never finish
}
}
func TestValidateBeaconBlockPubSub_FilterByFinalizedEpoch(t *testing.T) {
@@ -875,17 +1045,21 @@ func TestValidateBeaconBlockPubSub_FilterByFinalizedEpoch(t *testing.T) {
r := &Service{
cfg: &config{
beaconDB: db,
p2p: p,
chain: chain,
clock: startup.NewClock(chain.Genesis, chain.ValidatorsRoot),
blockNotifier: chain.BlockNotifier(),
attPool: attestations.NewPool(),
initialSync: &mockSync.Sync{IsSyncing: false},
beaconDB: db,
p2p: p,
chain: chain,
clock: startup.NewClock(chain.Genesis, chain.ValidatorsRoot),
blockNotifier: chain.BlockNotifier(),
operationNotifier: chain.OperationNotifier(),
attPool: attestations.NewPool(),
initialSync: &mockSync.Sync{IsSyncing: false},
},
seenBlockCache: lruwrpr.New(10),
badBlockCache: lruwrpr.New(10),
}
opChannel := make(chan *feed.Event, 1)
opSub := r.cfg.operationNotifier.OperationFeed().Subscribe(opChannel)
defer opSub.Unsubscribe()
b := util.NewBeaconBlock()
b.Block.Slot = 1
@@ -922,6 +1096,13 @@ func TestValidateBeaconBlockPubSub_FilterByFinalizedEpoch(t *testing.T) {
res, err = r.validateBeaconBlockPubSub(t.Context(), "", m)
assert.NoError(t, err)
assert.Equal(t, pubsub.ValidationIgnore, res)
select {
case event := <-opChannel:
assert.NotEqual(t, opfeed.BlockGossipReceived, event.Type, "BlockGossipReceived event should not be sent")
default:
// this case is needed, otherwise the test will never finish
}
}
func TestValidateBeaconBlockPubSub_ParentNotFinalizedDescendant(t *testing.T) {
@@ -960,19 +1141,24 @@ func TestValidateBeaconBlockPubSub_ParentNotFinalizedDescendant(t *testing.T) {
}
r := &Service{
cfg: &config{
beaconDB: db,
p2p: p,
initialSync: &mockSync.Sync{IsSyncing: false},
chain: chainService,
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
blockNotifier: chainService.BlockNotifier(),
stateGen: stateGen,
beaconDB: db,
p2p: p,
initialSync: &mockSync.Sync{IsSyncing: false},
chain: chainService,
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
blockNotifier: chainService.BlockNotifier(),
operationNotifier: chainService.OperationNotifier(),
stateGen: stateGen,
},
seenBlockCache: lruwrpr.New(10),
badBlockCache: lruwrpr.New(10),
slotToPendingBlocks: gcache.New(time.Second, 2*time.Second),
seenPendingBlocks: make(map[[32]byte]bool),
}
opChannel := make(chan *feed.Event, 1)
opSub := r.cfg.operationNotifier.OperationFeed().Subscribe(opChannel)
defer opSub.Unsubscribe()
buf := new(bytes.Buffer)
_, err = p.Encoding().EncodeGossip(buf, msg)
require.NoError(t, err)
@@ -989,6 +1175,13 @@ func TestValidateBeaconBlockPubSub_ParentNotFinalizedDescendant(t *testing.T) {
res, err := r.validateBeaconBlockPubSub(ctx, "", m)
assert.Equal(t, pubsub.ValidationReject, res, "Wrong validation result returned")
require.ErrorContains(t, "not descendant of finalized checkpoint", err)
select {
case event := <-opChannel:
assert.NotEqual(t, opfeed.BlockGossipReceived, event.Type, "BlockGossipReceived event should not be sent")
default:
// this case is needed, otherwise the test will never finish
}
}
func TestValidateBeaconBlockPubSub_InvalidParentBlock(t *testing.T) {
@@ -1026,19 +1219,24 @@ func TestValidateBeaconBlockPubSub_InvalidParentBlock(t *testing.T) {
}}
r := &Service{
cfg: &config{
beaconDB: db,
p2p: p,
initialSync: &mockSync.Sync{IsSyncing: false},
chain: chainService,
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
blockNotifier: chainService.BlockNotifier(),
stateGen: stateGen,
beaconDB: db,
p2p: p,
initialSync: &mockSync.Sync{IsSyncing: false},
chain: chainService,
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
blockNotifier: chainService.BlockNotifier(),
operationNotifier: chainService.OperationNotifier(),
stateGen: stateGen,
},
seenBlockCache: lruwrpr.New(10),
badBlockCache: lruwrpr.New(10),
slotToPendingBlocks: gcache.New(time.Second, 2*time.Second),
seenPendingBlocks: make(map[[32]byte]bool),
}
opChannel := make(chan *feed.Event, 1)
opSub := r.cfg.operationNotifier.OperationFeed().Subscribe(opChannel)
defer opSub.Unsubscribe()
buf := new(bytes.Buffer)
_, err = p.Encoding().EncodeGossip(buf, msg)
require.NoError(t, err)
@@ -1088,6 +1286,13 @@ func TestValidateBeaconBlockPubSub_InvalidParentBlock(t *testing.T) {
require.ErrorContains(t, "has an invalid parent", err)
// Expect block with bad parent to fail too
assert.Equal(t, res, pubsub.ValidationReject, "block with invalid parent should be ignored")
select {
case event := <-opChannel:
assert.NotEqual(t, opfeed.BlockGossipReceived, event.Type, "BlockGossipReceived event should not be sent")
default:
// this case is needed, otherwise the test will never finish
}
}
func TestValidateBeaconBlockPubSub_InsertValidPendingBlock(t *testing.T) {
@@ -1120,19 +1325,24 @@ func TestValidateBeaconBlockPubSub_InsertValidPendingBlock(t *testing.T) {
}}
r := &Service{
cfg: &config{
beaconDB: db,
p2p: p,
initialSync: &mockSync.Sync{IsSyncing: false},
chain: chainService,
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
blockNotifier: chainService.BlockNotifier(),
stateGen: stateGen,
beaconDB: db,
p2p: p,
initialSync: &mockSync.Sync{IsSyncing: false},
chain: chainService,
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
blockNotifier: chainService.BlockNotifier(),
operationNotifier: chainService.OperationNotifier(),
stateGen: stateGen,
},
seenBlockCache: lruwrpr.New(10),
badBlockCache: lruwrpr.New(10),
slotToPendingBlocks: gcache.New(time.Second, 2*time.Second),
seenPendingBlocks: make(map[[32]byte]bool),
}
opChannel := make(chan *feed.Event, 1)
opSub := r.cfg.operationNotifier.OperationFeed().Subscribe(opChannel)
defer opSub.Unsubscribe()
buf := new(bytes.Buffer)
_, err = p.Encoding().EncodeGossip(buf, msg)
require.NoError(t, err)
@@ -1152,6 +1362,13 @@ func TestValidateBeaconBlockPubSub_InsertValidPendingBlock(t *testing.T) {
bRoot, err = msg.Block.HashTreeRoot()
assert.NoError(t, err)
assert.Equal(t, true, r.seenPendingBlocks[bRoot])
select {
case event := <-opChannel:
assert.NotEqual(t, opfeed.BlockGossipReceived, event.Type, "BlockGossipReceived event should not be sent")
default:
// this case is needed, otherwise the test will never finish
}
}
func TestValidateBeaconBlockPubSub_RejectBlocksFromBadParent(t *testing.T) {
@@ -1203,19 +1420,24 @@ func TestValidateBeaconBlockPubSub_RejectBlocksFromBadParent(t *testing.T) {
}
r := &Service{
cfg: &config{
beaconDB: db,
p2p: p,
initialSync: &mockSync.Sync{IsSyncing: false},
chain: chainService,
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
blockNotifier: chainService.BlockNotifier(),
stateGen: stateGen,
beaconDB: db,
p2p: p,
initialSync: &mockSync.Sync{IsSyncing: false},
chain: chainService,
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
blockNotifier: chainService.BlockNotifier(),
operationNotifier: chainService.OperationNotifier(),
stateGen: stateGen,
},
seenBlockCache: lruwrpr.New(10),
badBlockCache: lruwrpr.New(10),
slotToPendingBlocks: gcache.New(time.Second, 2*time.Second),
seenPendingBlocks: make(map[[32]byte]bool),
}
opChannel := make(chan *feed.Event, 1)
opSub := r.cfg.operationNotifier.OperationFeed().Subscribe(opChannel)
defer opSub.Unsubscribe()
r.setBadBlock(ctx, bytesutil.ToBytes32(msg.Block.ParentRoot))
buf := new(bytes.Buffer)
@@ -1234,6 +1456,13 @@ func TestValidateBeaconBlockPubSub_RejectBlocksFromBadParent(t *testing.T) {
res, err := r.validateBeaconBlockPubSub(ctx, "", m)
assert.ErrorContains(t, "invalid parent", err)
assert.Equal(t, res, pubsub.ValidationReject)
select {
case event := <-opChannel:
assert.NotEqual(t, opfeed.BlockGossipReceived, event.Type, "BlockGossipReceived event should not be sent")
default:
// this case is needed, otherwise the test will never finish
}
}
func TestService_setBadBlock_DoesntSetWithContextErr(t *testing.T) {
@@ -1311,17 +1540,21 @@ func TestValidateBeaconBlockPubSub_ValidExecutionPayload(t *testing.T) {
}
r := &Service{
cfg: &config{
beaconDB: db,
p2p: p,
initialSync: &mockSync.Sync{IsSyncing: false},
chain: chainService,
blockNotifier: chainService.BlockNotifier(),
stateGen: stateGen,
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
beaconDB: db,
p2p: p,
initialSync: &mockSync.Sync{IsSyncing: false},
chain: chainService,
blockNotifier: chainService.BlockNotifier(),
operationNotifier: chainService.OperationNotifier(),
stateGen: stateGen,
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
},
seenBlockCache: lruwrpr.New(10),
badBlockCache: lruwrpr.New(10),
}
opChannel := make(chan *feed.Event, 1)
opSub := r.cfg.operationNotifier.OperationFeed().Subscribe(opChannel)
defer opSub.Unsubscribe()
buf := new(bytes.Buffer)
_, err = p.Encoding().EncodeGossip(buf, msg)
@@ -1342,6 +1575,17 @@ func TestValidateBeaconBlockPubSub_ValidExecutionPayload(t *testing.T) {
require.NoError(t, err)
result := res == pubsub.ValidationAccept
require.Equal(t, true, result)
blockGossipFound := false
select {
case event := <-opChannel:
if event.Type == opfeed.BlockGossipReceived {
blockGossipFound = true
}
default:
// this case is needed, otherwise the test will never finish
}
assert.Equal(t, true, blockGossipFound, "BlockGossipReceived event should be sent")
}
func TestValidateBeaconBlockPubSub_InvalidPayloadTimestamp(t *testing.T) {
@@ -1383,17 +1627,21 @@ func TestValidateBeaconBlockPubSub_InvalidPayloadTimestamp(t *testing.T) {
}}
r := &Service{
cfg: &config{
beaconDB: db,
p2p: p,
initialSync: &mockSync.Sync{IsSyncing: false},
chain: chainService,
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
blockNotifier: chainService.BlockNotifier(),
stateGen: stateGen,
beaconDB: db,
p2p: p,
initialSync: &mockSync.Sync{IsSyncing: false},
chain: chainService,
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
blockNotifier: chainService.BlockNotifier(),
operationNotifier: chainService.OperationNotifier(),
stateGen: stateGen,
},
seenBlockCache: lruwrpr.New(10),
badBlockCache: lruwrpr.New(10),
}
opChannel := make(chan *feed.Event, 1)
opSub := r.cfg.operationNotifier.OperationFeed().Subscribe(opChannel)
defer opSub.Unsubscribe()
buf := new(bytes.Buffer)
_, err = p.Encoding().EncodeGossip(buf, msg)
@@ -1413,6 +1661,13 @@ func TestValidateBeaconBlockPubSub_InvalidPayloadTimestamp(t *testing.T) {
require.NotNil(t, err)
result := res == pubsub.ValidationReject
assert.Equal(t, true, result)
select {
case event := <-opChannel:
assert.NotEqual(t, opfeed.BlockGossipReceived, event.Type, "BlockGossipReceived event should not be sent")
default:
// this case is needed, otherwise the test will never finish
}
}
func Test_validateBellatrixBeaconBlock(t *testing.T) {
@@ -1549,17 +1804,21 @@ func Test_validateBeaconBlockProcessingWhenParentIsOptimistic(t *testing.T) {
}
r := &Service{
cfg: &config{
beaconDB: db,
p2p: p,
initialSync: &mockSync.Sync{IsSyncing: false},
chain: chainService,
blockNotifier: chainService.BlockNotifier(),
stateGen: stateGen,
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
beaconDB: db,
p2p: p,
initialSync: &mockSync.Sync{IsSyncing: false},
chain: chainService,
blockNotifier: chainService.BlockNotifier(),
operationNotifier: chainService.OperationNotifier(),
stateGen: stateGen,
clock: startup.NewClock(chainService.Genesis, chainService.ValidatorsRoot),
},
seenBlockCache: lruwrpr.New(10),
badBlockCache: lruwrpr.New(10),
}
opChannel := make(chan *feed.Event, 1)
opSub := r.cfg.operationNotifier.OperationFeed().Subscribe(opChannel)
defer opSub.Unsubscribe()
buf := new(bytes.Buffer)
_, err = p.Encoding().EncodeGossip(buf, msg)
@@ -1580,6 +1839,17 @@ func Test_validateBeaconBlockProcessingWhenParentIsOptimistic(t *testing.T) {
require.NoError(t, err)
result := res == pubsub.ValidationAccept
assert.Equal(t, true, result)
blockGossipFound := false
select {
case event := <-opChannel:
if event.Type == opfeed.BlockGossipReceived {
blockGossipFound = true
}
default:
// this case is needed, otherwise the test will never finish
}
assert.Equal(t, true, blockGossipFound, "BlockGossipReceived event should be sent")
}
func Test_getBlockFields(t *testing.T) {

View File

@@ -0,0 +1,3 @@
## Changed
- Avoid redundant WithHttpEndpoint when JWT is provided

View File

@@ -0,0 +1,3 @@
### Added
- Integrate state-diff into `HasState()`.

View File

@@ -0,0 +1,3 @@
### Ignored
- Refactor finding slot by block root using state summary and block to its own function.

View File

@@ -0,0 +1,3 @@
### Fixed
- Fix state diff repetitive anchor slot bug.

View File

@@ -0,0 +1,4 @@
### Added
- Add initial configs for the state-diff feature.
- Add kv functions for the state-diff feature.

View File

@@ -0,0 +1,3 @@
### Added
- Integrate state-diff into `State()`.

View File

@@ -0,0 +1,2 @@
### Added
- prometheus metric `gossip_attestation_verification_milliseconds` to track attestation gossip topic validation latency.

View File

@@ -0,0 +1,4 @@
### Changed
- Downgraded log level from INFO to DEBUG on PrepareBeaconProposer updated fee recipients.
- Change the logging behaviour of Updated fee recipients to only log count of validators at Debug level and all validator indices at Trace level.

View File

@@ -0,0 +1,3 @@
### Changed
- Stop emitting payload attribute events during late block handling when we are not proposing the next slot

View File

@@ -0,0 +1,2 @@
### Fixed
- Check the JWT secret length is exactly 256 bits (32 bytes) as per Engine API specification

View File

@@ -0,0 +1,3 @@
### Changed
- Initialize the `ExecutionRequests` field in gossip block map.

View File

@@ -0,0 +1,3 @@
### Fixed
- Move `BlockGossipReceived` event to the end of gossip validation.

View File

@@ -0,0 +1,3 @@
### Removed
- Remove validator cross-client from end-to-end tests.

View File

@@ -0,0 +1,3 @@
### Ignored
- Replace fixed sleep delays with active polling in prometheus service test to improve test reliability.

View File

@@ -0,0 +1,3 @@
### Removed
- Reverted the eas metric as it currently has a bug. Will be fixed later.

View File

@@ -0,0 +1,3 @@
### Added
- Add supported version for fork versions

View File

@@ -0,0 +1,3 @@
### Added
- Implement Gloas state

View File

@@ -0,0 +1,3 @@
### Added
- Implement Gloas fork support in consensus-types/blocks with factory methods, getters, setters, and proto handling

View File

@@ -0,0 +1,3 @@
### Ignored
- P2p: wire stategen into service for last finalized state and use it for active validator count

View File

@@ -24,12 +24,13 @@ func FlagOptions(c *cli.Context) ([]execution.Option, error) {
}
headers := strings.Split(c.String(flags.ExecutionEngineHeaders.Name), ",")
opts := []execution.Option{
execution.WithHttpEndpoint(endpoint),
execution.WithEth1HeaderRequestLimit(c.Uint64(flags.Eth1HeaderReqLimit.Name)),
execution.WithHeaders(headers),
}
if len(jwtSecret) > 0 {
opts = append(opts, execution.WithHttpEndpointAndJWTSecret(endpoint, jwtSecret))
} else {
opts = append(opts, execution.WithHttpEndpoint(endpoint))
}
return opts, nil
}
@@ -41,7 +42,7 @@ func FlagOptions(c *cli.Context) ([]execution.Option, error) {
//
// The secret must be stored as a hex-encoded string within a file in the filesystem.
// If the --jwt-secret flag is provided to Prysm, but the file cannot be read, or does not contain a hex-encoded
// key of at least 256 bits, the client should treat this as an error and abort the startup.
// key of 256 bits, the client should treat this as an error and abort the startup.
func parseJWTSecretFromFile(c *cli.Context) ([]byte, error) {
jwtSecretFile := c.String(flags.ExecutionJWTSecretFlag.Name)
if jwtSecretFile == "" {
@@ -59,8 +60,8 @@ func parseJWTSecretFromFile(c *cli.Context) ([]byte, error) {
if err != nil {
return nil, err
}
if len(secret) < 32 {
return nil, errors.New("provided JWT secret should be a hex string of at least 32 bytes")
if len(secret) != 32 {
return nil, errors.New("provided JWT secret should be a hex string of 32 bytes")
}
log.Infof("Finished reading JWT secret from %s", jwtSecretFile)
return secret, nil

View File

@@ -63,7 +63,19 @@ func Test_parseJWTSecretFromFile(t *testing.T) {
set.String(flags.ExecutionJWTSecretFlag.Name, fullPath, "")
ctx := cli.NewContext(&app, set, nil)
_, err := parseJWTSecretFromFile(ctx)
require.ErrorContains(t, "should be a hex string of at least 32 bytes", err)
require.ErrorContains(t, "should be a hex string of 32 bytes", err)
})
t.Run("more than 32 bytes", func(t *testing.T) {
app := cli.App{}
set := flag.NewFlagSet("test", 0)
fullPath := filepath.Join(t.TempDir(), "foohex")
secret := bytesutil.PadTo([]byte("foo"), 33)
hexData := fmt.Sprintf("%#x", secret)
require.NoError(t, file.WriteFile(fullPath, []byte(hexData)))
set.String(flags.ExecutionJWTSecretFlag.Name, fullPath, "")
ctx := cli.NewContext(&app, set, nil)
_, err := parseJWTSecretFromFile(ctx)
require.ErrorContains(t, "should be a hex string of 32 bytes", err)
})
t.Run("bad data", func(t *testing.T) {
app := cli.App{}

View File

@@ -18,7 +18,9 @@ go_library(
],
deps = [
"//cmd:go_default_library",
"//config/features:go_default_library",
"//config/params:go_default_library",
"@com_github_pkg_errors//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@com_github_urfave_cli_v2//:go_default_library",
],
@@ -26,7 +28,13 @@ go_library(
go_test(
name = "go_default_test",
srcs = ["api_module_test.go"],
srcs = [
"api_module_test.go",
"config_test.go",
],
embed = [":go_default_library"],
deps = ["//testing/assert:go_default_library"],
deps = [
"//testing/assert:go_default_library",
"//testing/require:go_default_library",
],
)

View File

@@ -345,4 +345,10 @@ var (
Usage: "Maximum number of signatures to batch verify at once for beacon attestation p2p gossip.",
Value: 1000,
}
// StateDiffExponents defines the state diff tree hierarchy levels.
StateDiffExponents = &cli.IntSliceFlag{
Name: "state-diff-exponents",
Usage: "A comma-separated list of exponents (of 2) in decreasing order, defining the state diff hierarchy levels. The last exponent must be greater than or equal to 5.",
Value: cli.NewIntSlice(21, 18, 16, 13, 11, 9, 5),
}
)

View File

@@ -2,9 +2,13 @@ package flags
import (
"github.com/OffchainLabs/prysm/v7/cmd"
"github.com/OffchainLabs/prysm/v7/config/features"
"github.com/pkg/errors"
"github.com/urfave/cli/v2"
)
const maxStateDiffExponents = 30
// GlobalFlags specifies all the global flags for the
// beacon node.
type GlobalFlags struct {
@@ -19,6 +23,7 @@ type GlobalFlags struct {
BlobBatchLimitBurstFactor int
DataColumnBatchLimit int
DataColumnBatchLimitBurstFactor int
StateDiffExponents []int
}
var globalConfig *GlobalFlags
@@ -38,7 +43,7 @@ func Init(c *GlobalFlags) {
// ConfigureGlobalFlags initializes the global config.
// based on the provided cli context.
func ConfigureGlobalFlags(ctx *cli.Context) {
func ConfigureGlobalFlags(ctx *cli.Context) error {
cfg := &GlobalFlags{}
if ctx.Bool(SubscribeToAllSubnets.Name) {
@@ -51,6 +56,18 @@ func ConfigureGlobalFlags(ctx *cli.Context) {
cfg.SubscribeAllDataSubnets = true
}
// State-diff-exponents
cfg.StateDiffExponents = ctx.IntSlice(StateDiffExponents.Name)
if features.Get().EnableStateDiff {
if err := validateStateDiffExponents(cfg.StateDiffExponents); err != nil {
return err
}
} else {
if ctx.IsSet(StateDiffExponents.Name) {
log.Warn("--state-diff-exponents is set but --enable-state-diff is not; the value will be ignored.")
}
}
cfg.BlockBatchLimit = ctx.Int(BlockBatchLimit.Name)
cfg.BlockBatchLimitBurstFactor = ctx.Int(BlockBatchLimitBurstFactor.Name)
cfg.BlobBatchLimit = ctx.Int(BlobBatchLimit.Name)
@@ -63,6 +80,7 @@ func ConfigureGlobalFlags(ctx *cli.Context) {
configureMinimumPeers(ctx, cfg)
Init(cfg)
return nil
}
// MaxDialIsActive checks if the user has enabled the max dial flag.
@@ -78,3 +96,26 @@ func configureMinimumPeers(ctx *cli.Context, cfg *GlobalFlags) {
cfg.MinimumSyncPeers = maxPeers
}
}
// validateStateDiffExponents validates the provided exponents for state diffs with these constraints in mind:
// - Must contain between 1 and 15 values.
// - Exponents must be in strictly decreasing order.
// - Every exponent must be <= 30. (2^30 slots is more than 300 years at 12s slots)
// - The last (smallest) exponent must be >= 5. (This ensures diffs are at least 1 epoch apart)
func validateStateDiffExponents(exponents []int) error {
length := len(exponents)
if length == 0 || length > 15 {
return errors.New("state diff exponents must contain between 1 and 15 values")
}
if exponents[length-1] < 5 {
return errors.New("the last state diff exponent must be at least 5")
}
prev := maxStateDiffExponents + 1
for _, exp := range exponents {
if exp >= prev {
return errors.New("state diff exponents must be in strictly decreasing order, and each exponent must be <= 30")
}
prev = exp
}
return nil
}

View File

@@ -0,0 +1,39 @@
package flags
import (
"strconv"
"testing"
"github.com/OffchainLabs/prysm/v7/testing/require"
)
func TestValidateStateDiffExponents(t *testing.T) {
tests := []struct {
exponents []int
wantErr bool
errMsg string
}{
{exponents: []int{0, 1, 2}, wantErr: true, errMsg: "at least 5"},
{exponents: []int{1, 2, 3}, wantErr: true, errMsg: "at least 5"},
{exponents: []int{9, 8, 4}, wantErr: true, errMsg: "at least 5"},
{exponents: []int{3, 4, 5}, wantErr: true, errMsg: "decreasing"},
{exponents: []int{15, 14, 14, 12, 11}, wantErr: true, errMsg: "decreasing"},
{exponents: []int{15, 14, 13, 12, 11}, wantErr: false},
{exponents: []int{21, 18, 16, 13, 11, 9, 5}, wantErr: false},
{exponents: []int{30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 18, 16, 13, 11, 9, 5}, wantErr: true, errMsg: "between 1 and 15 values"},
{exponents: []int{}, wantErr: true, errMsg: "between 1 and 15 values"},
{exponents: []int{30, 18, 16, 13, 11, 9, 5}, wantErr: false},
{exponents: []int{31, 18, 16, 13, 11, 9, 5}, wantErr: true, errMsg: "<= 30"},
}
for i, tt := range tests {
t.Run(strconv.Itoa(i), func(t *testing.T) {
err := validateStateDiffExponents(tt.exponents)
if tt.wantErr {
require.ErrorContains(t, tt.errMsg, err)
} else {
require.NoError(t, err)
}
})
}
}

View File

@@ -51,6 +51,8 @@ type Flags struct {
EnableExperimentalAttestationPool bool // EnableExperimentalAttestationPool enables an experimental attestation pool design.
DisableDutiesV2 bool // DisableDutiesV2 sets validator client to use the get Duties endpoint
EnableWeb bool // EnableWeb enables the webui on the validator client
EnableStateDiff bool // EnableStateDiff enables the experimental state diff feature for the beacon node.
// Logging related toggles.
DisableGRPCConnectionLogs bool // Disables logging when a new grpc client has connected.
EnableFullSSZDataLogging bool // Enables logging for full ssz data on rejected gossip messages
@@ -284,6 +286,16 @@ func ConfigureBeaconChain(ctx *cli.Context) error {
cfg.DisableLastEpochTargets = true
}
if ctx.IsSet(EnableStateDiff.Name) {
logEnabled(EnableStateDiff)
cfg.EnableStateDiff = true
if ctx.IsSet(enableHistoricalSpaceRepresentation.Name) {
log.Warn("--enable-state-diff is enabled, ignoring --enable-historical-space-representation flag.")
cfg.EnableHistoricalSpaceRepresentation = false
}
}
cfg.AggregateIntervals = [3]time.Duration{aggregateFirstInterval.Value, aggregateSecondInterval.Value, aggregateThirdInterval.Value}
Init(cfg)
return nil

View File

@@ -172,6 +172,10 @@ var (
Name: "enable-experimental-attestation-pool",
Usage: "Enables an experimental attestation pool design.",
}
EnableStateDiff = &cli.BoolFlag{
Name: "enable-state-diff",
Usage: "Enables the experimental state diff feature.",
}
// forceHeadFlag is a flag to force the head of the beacon chain to a specific block.
forceHeadFlag = &cli.StringFlag{
Name: "sync-from",

View File

@@ -44,6 +44,7 @@ const (
MaxAttesterSlashingsElectra = 1 // Maximum number of attester slashings in a block.
MaxRandomByte = uint64(1<<8 - 1) // MaxRandomByte defines max for a random byte using for proposer and sync committee sampling.
MaxRandomValueElectra = uint64(1<<16 - 1) // MaxRandomValueElectra defines max for a random value using for proposer and sync committee sampling.
BuilderPendingWithdrawalsLimit = 1048576 // Maximum number of builder pending withdrawals.
// Introduced in Fulu network upgrade.
NumberOfColumns = 128 // NumberOfColumns refers to the specified number of data columns that can exist in a network.

View File

@@ -44,6 +44,7 @@ const (
MaxAttesterSlashingsElectra = 1 // Maximum number of attester slashings in a block.
MaxRandomByte = uint64(1<<8 - 1) // Maximum value for a random value using for proposer and sync committee sampling.
MaxRandomValueElectra = uint64(1<<16 - 1) // Maximum value for a random value using for proposer and sync committee sampling.
BuilderPendingWithdrawalsLimit = 1048576 // Maximum number of builder pending withdrawals.
// Introduced in Fulu network upgrade.
NumberOfColumns = 128 // NumberOfColumns refers to the specified number of data columns that can exist in a network.

View File

@@ -162,6 +162,7 @@ type BeaconChainConfig struct {
BeaconStateDenebFieldCount int // BeaconStateDenebFieldCount defines how many fields are in beacon state post upgrade to Deneb.
BeaconStateElectraFieldCount int // BeaconStateElectraFieldCount defines how many fields are in beacon state post upgrade to Electra.
BeaconStateFuluFieldCount int // BeaconStateFuluFieldCount defines how many fields are in beacon state post upgrade to Fulu.
BeaconStateGloasFieldCount int // BeaconStateGloasFieldCount defines how many fields are in beacon state post upgrade to Gloas.
// Slasher constants.
WeakSubjectivityPeriod primitives.Epoch // WeakSubjectivityPeriod defines the time period expressed in number of epochs were proof of stake network should validate block headers and attestations for slashable events.

View File

@@ -206,6 +206,7 @@ var mainnetBeaconConfig = &BeaconChainConfig{
BeaconStateDenebFieldCount: 28,
BeaconStateElectraFieldCount: 37,
BeaconStateFuluFieldCount: 38,
BeaconStateGloasFieldCount: 43,
// Slasher related values.
WeakSubjectivityPeriod: 54000,

View File

@@ -53,6 +53,7 @@ go_test(
"roblob_test.go",
"roblock_test.go",
"rodatacolumn_test.go",
"setters_test.go",
],
embed = [":go_default_library"],
deps = [

View File

@@ -82,6 +82,8 @@ func NewSignedBeaconBlock(i any) (interfaces.SignedBeaconBlock, error) {
return initBlindedSignedBlockFromProtoFulu(b)
case *eth.GenericSignedBeaconBlock_BlindedFulu:
return initBlindedSignedBlockFromProtoFulu(b.BlindedFulu)
case *eth.SignedBeaconBlockGloas:
return initSignedBlockFromProtoGloas(b)
default:
return nil, errors.Wrapf(ErrUnsupportedSignedBeaconBlock, "unable to create block from type %T", i)
}
@@ -138,6 +140,8 @@ func NewBeaconBlock(i any) (interfaces.ReadOnlyBeaconBlock, error) {
return initBlindedBlockFromProtoFulu(b)
case *eth.GenericBeaconBlock_BlindedFulu:
return initBlindedBlockFromProtoFulu(b.BlindedFulu)
case *eth.BeaconBlockGloas:
return initBlockFromProtoGloas(b)
default:
return nil, errors.Wrapf(errUnsupportedBeaconBlock, "unable to create block from type %T", i)
}
@@ -168,6 +172,8 @@ func NewBeaconBlockBody(i any) (interfaces.ReadOnlyBeaconBlockBody, error) {
return initBlockBodyFromProtoElectra(b)
case *eth.BlindedBeaconBlockBodyElectra:
return initBlindedBlockBodyFromProtoElectra(b)
case *eth.BeaconBlockBodyGloas:
return initBlockBodyFromProtoGloas(b)
default:
return nil, errors.Wrapf(errUnsupportedBeaconBlockBody, "unable to create block body from type %T", i)
}
@@ -260,6 +266,12 @@ func BuildSignedBeaconBlock(blk interfaces.ReadOnlyBeaconBlock, signature []byte
return nil, errIncorrectBlockVersion
}
return NewSignedBeaconBlock(&eth.SignedBeaconBlockFulu{Block: pb, Signature: signature})
case version.Gloas:
pb, ok := pb.(*eth.BeaconBlockGloas)
if !ok {
return nil, errIncorrectBlockVersion
}
return NewSignedBeaconBlock(&eth.SignedBeaconBlockGloas{Block: pb, Signature: signature})
default:
return nil, errUnsupportedBeaconBlock
}

View File

@@ -161,6 +161,18 @@ func Test_NewSignedBeaconBlock(t *testing.T) {
assert.Equal(t, version.Deneb, b.Version())
assert.Equal(t, true, b.IsBlinded())
})
t.Run("SignedBeaconBlockGloas", func(t *testing.T) {
pb := &eth.SignedBeaconBlockGloas{
Block: &eth.BeaconBlockGloas{
Body: &eth.BeaconBlockBodyGloas{},
},
Signature: []byte("sig"),
}
b, err := NewSignedBeaconBlock(pb)
require.NoError(t, err)
assert.Equal(t, version.Gloas, b.Version())
assert.Equal(t, false, b.IsBlinded())
})
t.Run("nil", func(t *testing.T) {
_, err := NewSignedBeaconBlock(nil)
assert.ErrorContains(t, "received nil object", err)
@@ -276,6 +288,13 @@ func Test_NewBeaconBlock(t *testing.T) {
assert.Equal(t, version.Deneb, b.Version())
assert.Equal(t, true, b.IsBlinded())
})
t.Run("BeaconBlockGloas", func(t *testing.T) {
pb := &eth.BeaconBlockGloas{Body: &eth.BeaconBlockBodyGloas{}}
b, err := NewBeaconBlock(pb)
require.NoError(t, err)
assert.Equal(t, version.Gloas, b.Version())
assert.Equal(t, false, b.IsBlinded())
})
t.Run("nil", func(t *testing.T) {
_, err := NewBeaconBlock(nil)
assert.ErrorContains(t, "received nil object", err)
@@ -354,6 +373,15 @@ func Test_NewBeaconBlockBody(t *testing.T) {
assert.Equal(t, version.Deneb, b.version)
assert.Equal(t, true, b.IsBlinded())
})
t.Run("BeaconBlockBodyGloas", func(t *testing.T) {
pb := &eth.BeaconBlockBodyGloas{}
i, err := NewBeaconBlockBody(pb)
require.NoError(t, err)
b, ok := i.(*BeaconBlockBody)
require.Equal(t, true, ok)
assert.Equal(t, version.Gloas, b.version)
assert.Equal(t, false, b.IsBlinded())
})
t.Run("nil", func(t *testing.T) {
_, err := NewBeaconBlockBody(nil)
assert.ErrorContains(t, "received nil object", err)
@@ -425,6 +453,14 @@ func Test_BuildSignedBeaconBlock(t *testing.T) {
assert.Equal(t, version.Deneb, sb.Version())
assert.Equal(t, true, sb.IsBlinded())
})
t.Run("Gloas", func(t *testing.T) {
b := &BeaconBlock{version: version.Gloas, body: &BeaconBlockBody{version: version.Gloas}}
sb, err := BuildSignedBeaconBlock(b, sig[:])
require.NoError(t, err)
assert.DeepEqual(t, sig, sb.Signature())
assert.Equal(t, version.Gloas, sb.Version())
assert.Equal(t, false, sb.IsBlinded())
})
}
func TestBuildSignedBeaconBlockFromExecutionPayload(t *testing.T) {
@@ -535,4 +571,21 @@ func TestBuildSignedBeaconBlockFromExecutionPayload(t *testing.T) {
require.DeepEqual(t, uint64(123), payload.ExcessBlobGas)
require.DeepEqual(t, uint64(321), payload.BlobGasUsed)
})
t.Run("gloas execution unsupported", func(t *testing.T) {
base := &SignedBeaconBlock{
version: version.Gloas,
block: &BeaconBlock{version: version.Gloas, body: &BeaconBlockBody{version: version.Gloas}},
}
blinded := &testBlindedSignedBeaconBlock{SignedBeaconBlock: base}
_, err := BuildSignedBeaconBlockFromExecutionPayload(blinded, nil)
require.ErrorContains(t, "Execution is not supported for gloas", err)
})
}
type testBlindedSignedBeaconBlock struct {
*SignedBeaconBlock
}
func (b *testBlindedSignedBeaconBlock) IsBlinded() bool {
return true
}

View File

@@ -80,6 +80,8 @@ func (b *SignedBeaconBlock) Copy() (interfaces.SignedBeaconBlock, error) {
return initBlindedSignedBlockFromProtoFulu(pb.(*eth.SignedBlindedBeaconBlockFulu).Copy())
}
return initSignedBlockFromProtoFulu(pb.(*eth.SignedBeaconBlockFulu).Copy())
case version.Gloas:
return initSignedBlockFromProtoGloas(eth.CopySignedBeaconBlockGloas(pb.(*eth.SignedBeaconBlockGloas)))
default:
return nil, errIncorrectBlockVersion
}
@@ -157,6 +159,9 @@ func (b *SignedBeaconBlock) PbGenericBlock() (*eth.GenericSignedBeaconBlock, err
return &eth.GenericSignedBeaconBlock{
Block: &eth.GenericSignedBeaconBlock_Fulu{Fulu: bc},
}, nil
case version.Gloas:
// Gloas doesn't support GenericSignedBeaconBlock yet
return nil, errors.New("Gloas blocks don't support GenericSignedBeaconBlock conversion")
default:
return nil, errIncorrectBlockVersion
}
@@ -164,7 +169,7 @@ func (b *SignedBeaconBlock) PbGenericBlock() (*eth.GenericSignedBeaconBlock, err
// ToBlinded converts a non-blinded block to its blinded equivalent.
func (b *SignedBeaconBlock) ToBlinded() (interfaces.ReadOnlySignedBeaconBlock, error) {
if b.version < version.Bellatrix {
if b.version < version.Bellatrix || b.version >= version.Gloas {
return nil, ErrUnsupportedVersion
}
if b.IsBlinded() {
@@ -376,7 +381,7 @@ func (b *SignedBeaconBlock) Version() int {
// IsBlinded metadata on whether a block is blinded
func (b *SignedBeaconBlock) IsBlinded() bool {
return b.version >= version.Bellatrix && b.block.body.executionPayload == nil
return b.version < version.Gloas && b.version >= version.Bellatrix && b.block.body.executionPayload == nil
}
// Header converts the underlying protobuf object from blinded block to header format.
@@ -437,6 +442,8 @@ func (b *SignedBeaconBlock) MarshalSSZ() ([]byte, error) {
return pb.(*eth.SignedBlindedBeaconBlockFulu).MarshalSSZ()
}
return pb.(*eth.SignedBeaconBlockFulu).MarshalSSZ()
case version.Gloas:
return pb.(*eth.SignedBeaconBlockGloas).MarshalSSZ()
default:
return []byte{}, errIncorrectBlockVersion
}
@@ -479,6 +486,8 @@ func (b *SignedBeaconBlock) MarshalSSZTo(dst []byte) ([]byte, error) {
return pb.(*eth.SignedBlindedBeaconBlockFulu).MarshalSSZTo(dst)
}
return pb.(*eth.SignedBeaconBlockFulu).MarshalSSZTo(dst)
case version.Gloas:
return pb.(*eth.SignedBeaconBlockGloas).MarshalSSZTo(dst)
default:
return []byte{}, errIncorrectBlockVersion
}
@@ -526,6 +535,8 @@ func (b *SignedBeaconBlock) SizeSSZ() int {
return pb.(*eth.SignedBlindedBeaconBlockFulu).SizeSSZ()
}
return pb.(*eth.SignedBeaconBlockFulu).SizeSSZ()
case version.Gloas:
return pb.(*eth.SignedBeaconBlockGloas).SizeSSZ()
default:
panic(incorrectBlockVersion)
}
@@ -666,6 +677,16 @@ func (b *SignedBeaconBlock) UnmarshalSSZ(buf []byte) error {
return err
}
}
case version.Gloas:
pb := &eth.SignedBeaconBlockGloas{}
err := pb.UnmarshalSSZ(buf)
if err != nil {
return err
}
newBlock, err = initSignedBlockFromProtoGloas(pb)
if err != nil {
return err
}
default:
return errIncorrectBlockVersion
}
@@ -705,7 +726,7 @@ func (b *BeaconBlock) IsNil() bool {
// IsBlinded checks if the beacon block is a blinded block.
func (b *BeaconBlock) IsBlinded() bool {
return b.version >= version.Bellatrix && b.body.executionPayload == nil
return b.version < version.Gloas && b.version >= version.Bellatrix && b.body.executionPayload == nil
}
// Version of the underlying protobuf object.
@@ -749,6 +770,9 @@ func (b *BeaconBlock) HashTreeRoot() ([field_params.RootLength]byte, error) {
return pb.(*eth.BlindedBeaconBlockFulu).HashTreeRoot()
}
return pb.(*eth.BeaconBlockElectra).HashTreeRoot()
case version.Gloas:
return pb.(*eth.BeaconBlockGloas).HashTreeRoot()
default:
return [field_params.RootLength]byte{}, errIncorrectBlockVersion
}
@@ -790,6 +814,8 @@ func (b *BeaconBlock) HashTreeRootWith(h *ssz.Hasher) error {
return pb.(*eth.BlindedBeaconBlockFulu).HashTreeRootWith(h)
}
return pb.(*eth.BeaconBlockElectra).HashTreeRootWith(h)
case version.Gloas:
return pb.(*eth.BeaconBlockGloas).HashTreeRootWith(h)
default:
return errIncorrectBlockVersion
}
@@ -832,6 +858,8 @@ func (b *BeaconBlock) MarshalSSZ() ([]byte, error) {
return pb.(*eth.BlindedBeaconBlockFulu).MarshalSSZ()
}
return pb.(*eth.BeaconBlockElectra).MarshalSSZ()
case version.Gloas:
return pb.(*eth.BeaconBlockGloas).MarshalSSZ()
default:
return []byte{}, errIncorrectBlockVersion
}
@@ -874,6 +902,8 @@ func (b *BeaconBlock) MarshalSSZTo(dst []byte) ([]byte, error) {
return pb.(*eth.BlindedBeaconBlockFulu).MarshalSSZTo(dst)
}
return pb.(*eth.BeaconBlockElectra).MarshalSSZTo(dst)
case version.Gloas:
return pb.(*eth.BeaconBlockGloas).MarshalSSZTo(dst)
default:
return []byte{}, errIncorrectBlockVersion
}
@@ -921,6 +951,8 @@ func (b *BeaconBlock) SizeSSZ() int {
return pb.(*eth.BlindedBeaconBlockFulu).SizeSSZ()
}
return pb.(*eth.BeaconBlockElectra).SizeSSZ()
case version.Gloas:
return pb.(*eth.BeaconBlockGloas).SizeSSZ()
default:
panic(incorrectBodyVersion)
}
@@ -1061,6 +1093,16 @@ func (b *BeaconBlock) UnmarshalSSZ(buf []byte) error {
return err
}
}
case version.Gloas:
pb := &eth.BeaconBlockGloas{}
if err := pb.UnmarshalSSZ(buf); err != nil {
return err
}
var err error
newBlock, err = initBlockFromProtoGloas(pb)
if err != nil {
return err
}
default:
return errIncorrectBlockVersion
}
@@ -1200,15 +1242,13 @@ func (b *BeaconBlockBody) SyncAggregate() (*eth.SyncAggregate, error) {
// Execution returns the execution payload of the block body.
func (b *BeaconBlockBody) Execution() (interfaces.ExecutionData, error) {
switch b.version {
case version.Phase0, version.Altair:
if b.version <= version.Altair || b.version >= version.Gloas {
return nil, consensus_types.ErrNotSupported("Execution", b.version)
default:
if b.IsBlinded() {
return b.executionPayloadHeader, nil
}
return b.executionPayload, nil
}
if b.IsBlinded() {
return b.executionPayloadHeader, nil
}
return b.executionPayload, nil
}
func (b *BeaconBlockBody) BLSToExecutionChanges() ([]*eth.SignedBLSToExecutionChange, error) {
@@ -1233,12 +1273,28 @@ func (b *BeaconBlockBody) BlobKzgCommitments() ([][]byte, error) {
// ExecutionRequests returns the execution requests
func (b *BeaconBlockBody) ExecutionRequests() (*enginev1.ExecutionRequests, error) {
if b.version < version.Electra {
if b.version < version.Electra || b.version >= version.Gloas {
return nil, consensus_types.ErrNotSupported("ExecutionRequests", b.version)
}
return b.executionRequests, nil
}
// PayloadAttestations returns the payload attestations in the block.
func (b *BeaconBlockBody) PayloadAttestations() ([]*eth.PayloadAttestation, error) {
if b.version >= version.Gloas {
return b.payloadAttestations, nil
}
return nil, consensus_types.ErrNotSupported("PayloadAttestations", b.version)
}
// SignedExecutionPayloadBid returns the signed execution payload header in the block.
func (b *BeaconBlockBody) SignedExecutionPayloadBid() (*eth.SignedExecutionPayloadBid, error) {
if b.version >= version.Gloas {
return b.signedExecutionPayloadBid, nil
}
return nil, consensus_types.ErrNotSupported("SignedExecutionPayloadBid", b.version)
}
// Version returns the version of the beacon block body
func (b *BeaconBlockBody) Version() int {
return b.version
@@ -1280,6 +1336,8 @@ func (b *BeaconBlockBody) HashTreeRoot() ([field_params.RootLength]byte, error)
return pb.(*eth.BlindedBeaconBlockBodyElectra).HashTreeRoot()
}
return pb.(*eth.BeaconBlockBodyElectra).HashTreeRoot()
case version.Gloas:
return pb.(*eth.BeaconBlockBodyGloas).HashTreeRoot()
default:
return [field_params.RootLength]byte{}, errIncorrectBodyVersion
}
@@ -1287,5 +1345,5 @@ func (b *BeaconBlockBody) HashTreeRoot() ([field_params.RootLength]byte, error)
// IsBlinded checks if the beacon block body is a blinded block body.
func (b *BeaconBlockBody) IsBlinded() bool {
return b.version >= version.Bellatrix && b.executionPayload == nil
return b.version < version.Gloas && b.version >= version.Bellatrix && b.executionPayload == nil
}

View File

@@ -3,7 +3,9 @@ package blocks
import (
"testing"
bitfield "github.com/OffchainLabs/go-bitfield"
fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams"
consensus_types "github.com/OffchainLabs/prysm/v7/consensus-types"
"github.com/OffchainLabs/prysm/v7/consensus-types/interfaces"
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
"github.com/OffchainLabs/prysm/v7/encoding/bytesutil"
@@ -73,14 +75,54 @@ func Test_SignedBeaconBlock_IsNil(t *testing.T) {
}
func Test_SignedBeaconBlock_Copy(t *testing.T) {
bb := &BeaconBlockBody{}
b := &BeaconBlock{body: bb}
sb := &SignedBeaconBlock{block: b}
cp, err := sb.Copy()
require.NoError(t, err)
assert.NotEqual(t, cp, sb)
assert.NotEqual(t, cp.Block(), sb.block)
assert.NotEqual(t, cp.Block().Body(), sb.block.body)
t.Run("basic", func(t *testing.T) {
bb := &BeaconBlockBody{}
b := &BeaconBlock{body: bb}
sb := &SignedBeaconBlock{block: b}
cp, err := sb.Copy()
require.NoError(t, err)
assert.NotEqual(t, cp, sb)
assert.NotEqual(t, cp.Block(), sb.block)
assert.NotEqual(t, cp.Block().Body(), sb.block.body)
})
t.Run("gloas deep copy", func(t *testing.T) {
payload := []*eth.PayloadAttestation{{Signature: []byte{0x01}}}
payloadBid := &eth.SignedExecutionPayloadBid{Signature: []byte{0x02}}
sb := &SignedBeaconBlock{
version: version.Gloas,
block: &BeaconBlock{
version: version.Gloas,
body: &BeaconBlockBody{
version: version.Gloas,
payloadAttestations: payload,
signedExecutionPayloadBid: payloadBid,
},
},
}
cpIntf, err := sb.Copy()
require.NoError(t, err)
cp, ok := cpIntf.(*SignedBeaconBlock)
require.Equal(t, true, ok)
assert.NotEqual(t, sb, cp)
require.Equal(t, version.Gloas, cp.version)
att, err := cp.Block().Body().PayloadAttestations()
require.NoError(t, err)
require.DeepEqual(t, payload, att)
origAttSig := att[0].Signature[0]
payload[0].Signature[0] ^= 0xFF
require.Equal(t, origAttSig, att[0].Signature[0])
bid, err := cp.Block().Body().SignedExecutionPayloadBid()
require.NoError(t, err)
require.DeepEqual(t, payloadBid, bid)
origBidSig := bid.Signature[0]
payloadBid.Signature[0] ^= 0xFF
require.Equal(t, origBidSig, bid.Signature[0])
})
}
func Test_SignedBeaconBlock_Version(t *testing.T) {
@@ -122,6 +164,16 @@ func Test_SignedBeaconBlock_Header(t *testing.T) {
assert.DeepEqual(t, expectedHTR[:], h.Header.BodyRoot)
}
func Test_SignedBeaconBlock_PbGenericBlockGloasUnsupported(t *testing.T) {
sb := &SignedBeaconBlock{
version: version.Gloas,
block: &BeaconBlock{version: version.Gloas, body: &BeaconBlockBody{version: version.Gloas}},
}
_, err := sb.PbGenericBlock()
require.ErrorContains(t, "Gloas blocks don't support GenericSignedBeaconBlock conversion", err)
}
func Test_SignedBeaconBlock_UnmarshalSSZ(t *testing.T) {
pb := hydrateSignedBeaconBlock()
buf, err := pb.MarshalSSZ()
@@ -190,6 +242,17 @@ func Test_BeaconBlock_IsBlinded(t *testing.T) {
b1 := &SignedBeaconBlock{version: version.Bellatrix, block: &BeaconBlock{body: &BeaconBlockBody{executionPayloadHeader: executionPayloadHeader{}}}}
assert.Equal(t, true, b1.IsBlinded())
t.Run("gloas never blinded", func(t *testing.T) {
sb := &SignedBeaconBlock{version: version.Gloas, block: &BeaconBlock{body: &BeaconBlockBody{version: version.Gloas}}}
assert.Equal(t, false, sb.IsBlinded())
})
}
func Test_SignedBeaconBlock_ToBlinded_GloasUnsupported(t *testing.T) {
sb := &SignedBeaconBlock{version: version.Gloas, block: &BeaconBlock{version: version.Gloas, body: &BeaconBlockBody{version: version.Gloas}}}
_, err := sb.ToBlinded()
require.ErrorIs(t, err, ErrUnsupportedVersion)
}
func Test_BeaconBlock_Version(t *testing.T) {
@@ -324,6 +387,46 @@ func Test_BeaconBlockBody_Deposits(t *testing.T) {
assert.DeepSSZEqual(t, d, bb.Block().Body().Deposits())
}
func Test_BeaconBlockBody_PayloadAttestations(t *testing.T) {
t.Run("unsupported before gloas", func(t *testing.T) {
bb := &BeaconBlockBody{version: version.Fulu}
_, err := bb.PayloadAttestations()
require.ErrorIs(t, err, consensus_types.ErrUnsupportedField)
})
t.Run("gloas returns payload", func(t *testing.T) {
payload := []*eth.PayloadAttestation{{Signature: []byte{0x01}}}
sb := &SignedBeaconBlock{
version: version.Gloas,
block: &BeaconBlock{version: version.Gloas, body: &BeaconBlockBody{version: version.Gloas}},
}
require.NoError(t, sb.SetPayloadAttestations(payload))
got, err := sb.Block().Body().PayloadAttestations()
require.NoError(t, err)
require.DeepEqual(t, payload, got)
})
}
func Test_BeaconBlockBody_SignedExecutionPayloadBid(t *testing.T) {
t.Run("unsupported before gloas", func(t *testing.T) {
bb := &BeaconBlockBody{version: version.Fulu}
_, err := bb.SignedExecutionPayloadBid()
require.ErrorIs(t, err, consensus_types.ErrUnsupportedField)
})
t.Run("gloas returns bid", func(t *testing.T) {
bid := &eth.SignedExecutionPayloadBid{Signature: []byte{0xFF}}
sb := &SignedBeaconBlock{
version: version.Gloas,
block: &BeaconBlock{version: version.Gloas, body: &BeaconBlockBody{version: version.Gloas}},
}
require.NoError(t, sb.SetSignedExecutionPayloadBid(bid))
got, err := sb.Block().Body().SignedExecutionPayloadBid()
require.NoError(t, err)
require.DeepEqual(t, bid, got)
})
}
func Test_BeaconBlockBody_VoluntaryExits(t *testing.T) {
ve := make([]*eth.SignedVoluntaryExit, 0)
bb := &SignedBeaconBlock{block: &BeaconBlock{body: &BeaconBlockBody{}}}
@@ -400,6 +503,32 @@ func Test_BeaconBlockBody_Execution(t *testing.T) {
gas, err = eDenebHeader.ExcessBlobGas()
require.NoError(t, err)
require.DeepEqual(t, gas, uint64(223))
bb = &SignedBeaconBlock{version: version.Gloas, block: &BeaconBlock{version: version.Gloas, body: &BeaconBlockBody{version: version.Gloas}}}
_, err = bb.Block().Body().Execution()
require.ErrorIs(t, err, consensus_types.ErrUnsupportedField)
}
func Test_BeaconBlockBody_ExecutionRequests(t *testing.T) {
t.Run("unsupported before Electra", func(t *testing.T) {
bb := &BeaconBlockBody{version: version.Deneb}
_, err := bb.ExecutionRequests()
require.ErrorIs(t, err, consensus_types.ErrUnsupportedField)
})
t.Run("electra returns requests", func(t *testing.T) {
reqs := &pb.ExecutionRequests{}
bb := &BeaconBlockBody{version: version.Electra, executionRequests: reqs}
result, err := bb.ExecutionRequests()
require.NoError(t, err)
require.Equal(t, reqs, result)
})
t.Run("unsupported for Gloas", func(t *testing.T) {
bb := &BeaconBlockBody{version: version.Gloas}
_, err := bb.ExecutionRequests()
require.ErrorIs(t, err, consensus_types.ErrUnsupportedField)
})
}
func Test_BeaconBlockBody_HashTreeRoot(t *testing.T) {
@@ -413,6 +542,17 @@ func Test_BeaconBlockBody_HashTreeRoot(t *testing.T) {
assert.DeepEqual(t, expectedHTR, actualHTR)
}
func Test_BeaconBlockBody_HashTreeRootGloas(t *testing.T) {
pb := hydrateBeaconBlockBodyGloas()
expectedHTR, err := pb.HashTreeRoot()
require.NoError(t, err)
b, err := initBlockBodyFromProtoGloas(pb)
require.NoError(t, err)
actualHTR, err := b.HashTreeRoot()
require.NoError(t, err)
assert.DeepEqual(t, expectedHTR, actualHTR)
}
func hydrateSignedBeaconBlock() *eth.SignedBeaconBlock {
return &eth.SignedBeaconBlock{
Signature: make([]byte, fieldparams.BLSSignatureLength),
@@ -509,6 +649,43 @@ func hydrateBeaconBlockBodyCapella() *eth.BeaconBlockBodyCapella {
}
}
func hydrateBeaconBlockBodyGloas() *eth.BeaconBlockBodyGloas {
bits := bitfield.NewBitvector512()
bits.SetBitAt(0, true)
return &eth.BeaconBlockBodyGloas{
RandaoReveal: make([]byte, fieldparams.BLSSignatureLength),
Graffiti: make([]byte, fieldparams.RootLength),
Eth1Data: &eth.Eth1Data{
DepositRoot: make([]byte, fieldparams.RootLength),
BlockHash: make([]byte, fieldparams.RootLength),
},
SyncAggregate: &eth.SyncAggregate{
SyncCommitteeBits: make([]byte, fieldparams.SyncAggregateSyncCommitteeBytesLength),
SyncCommitteeSignature: make([]byte, fieldparams.BLSSignatureLength),
},
SignedExecutionPayloadBid: &eth.SignedExecutionPayloadBid{
Message: &eth.ExecutionPayloadBid{
ParentBlockHash: make([]byte, fieldparams.RootLength),
ParentBlockRoot: make([]byte, fieldparams.RootLength),
BlockHash: make([]byte, fieldparams.RootLength),
FeeRecipient: make([]byte, 20),
BlobKzgCommitmentsRoot: make([]byte, fieldparams.RootLength),
},
Signature: make([]byte, fieldparams.BLSSignatureLength),
},
PayloadAttestations: []*eth.PayloadAttestation{
{
AggregationBits: bits,
Data: &eth.PayloadAttestationData{
BeaconBlockRoot: make([]byte, fieldparams.RootLength),
},
Signature: make([]byte, fieldparams.BLSSignatureLength),
},
},
}
}
func hydrateBeaconBlockBodyDeneb() *eth.BeaconBlockBodyDeneb {
return &eth.BeaconBlockBodyDeneb{
RandaoReveal: make([]byte, fieldparams.BLSSignatureLength),

View File

@@ -185,6 +185,19 @@ func (b *SignedBeaconBlock) Proto() (proto.Message, error) { // nolint:gocognit
Block: block,
Signature: b.signature[:],
}, nil
case version.Gloas:
var block *eth.BeaconBlockGloas
if blockMessage != nil {
var ok bool
block, ok = blockMessage.(*eth.BeaconBlockGloas)
if !ok {
return nil, errIncorrectBlockVersion
}
}
return &eth.SignedBeaconBlockGloas{
Block: block,
Signature: b.signature[:],
}, nil
default:
return nil, errors.New("unsupported signed beacon block version")
}
@@ -399,6 +412,22 @@ func (b *BeaconBlock) Proto() (proto.Message, error) { // nolint:gocognit
StateRoot: b.stateRoot[:],
Body: body,
}, nil
case version.Gloas:
var body *eth.BeaconBlockBodyGloas
if bodyMessage != nil {
var ok bool
body, ok = bodyMessage.(*eth.BeaconBlockBodyGloas)
if !ok {
return nil, errIncorrectBodyVersion
}
}
return &eth.BeaconBlockGloas{
Slot: b.slot,
ProposerIndex: b.proposerIndex,
ParentRoot: b.parentRoot[:],
StateRoot: b.stateRoot[:],
Body: body,
}, nil
default:
return nil, fmt.Errorf("unsupported beacon block version: %s", version.String(b.version))
}
@@ -668,6 +697,21 @@ func (b *BeaconBlockBody) Proto() (proto.Message, error) {
BlobKzgCommitments: b.blobKzgCommitments,
ExecutionRequests: b.executionRequests,
}, nil
case version.Gloas:
return &eth.BeaconBlockBodyGloas{
RandaoReveal: b.randaoReveal[:],
Eth1Data: b.eth1Data,
Graffiti: b.graffiti[:],
ProposerSlashings: b.proposerSlashings,
AttesterSlashings: b.attesterSlashingsElectra,
Attestations: b.attestationsElectra,
Deposits: b.deposits,
VoluntaryExits: b.voluntaryExits,
SyncAggregate: b.syncAggregate,
BlsToExecutionChanges: b.blsToExecutionChanges,
SignedExecutionPayloadBid: b.signedExecutionPayloadBid,
PayloadAttestations: b.payloadAttestations,
}, nil
default:
return nil, errors.New("unsupported beacon block body version")
}
@@ -1477,3 +1521,67 @@ func initBlindedBlockBodyFromProtoFulu(pb *eth.BlindedBeaconBlockBodyElectra) (*
}
return b, nil
}
// ----------------------------------------------------------------------------
// Gloas
// ----------------------------------------------------------------------------
func initSignedBlockFromProtoGloas(pb *eth.SignedBeaconBlockGloas) (*SignedBeaconBlock, error) {
if pb == nil {
return nil, errNilBlock
}
block, err := initBlockFromProtoGloas(pb.Block)
if err != nil {
return nil, err
}
b := &SignedBeaconBlock{
version: version.Gloas,
block: block,
signature: bytesutil.ToBytes96(pb.Signature),
}
return b, nil
}
func initBlockFromProtoGloas(pb *eth.BeaconBlockGloas) (*BeaconBlock, error) {
if pb == nil {
return nil, errNilBlock
}
body, err := initBlockBodyFromProtoGloas(pb.Body)
if err != nil {
return nil, err
}
b := &BeaconBlock{
version: version.Gloas,
slot: pb.Slot,
proposerIndex: pb.ProposerIndex,
parentRoot: bytesutil.ToBytes32(pb.ParentRoot),
stateRoot: bytesutil.ToBytes32(pb.StateRoot),
body: body,
}
return b, nil
}
func initBlockBodyFromProtoGloas(pb *eth.BeaconBlockBodyGloas) (*BeaconBlockBody, error) {
if pb == nil {
return nil, errNilBlockBody
}
b := &BeaconBlockBody{
version: version.Gloas,
randaoReveal: bytesutil.ToBytes96(pb.RandaoReveal),
eth1Data: pb.Eth1Data,
graffiti: bytesutil.ToBytes32(pb.Graffiti),
proposerSlashings: pb.ProposerSlashings,
attesterSlashingsElectra: pb.AttesterSlashings,
attestationsElectra: pb.Attestations,
deposits: pb.Deposits,
voluntaryExits: pb.VoluntaryExits,
syncAggregate: pb.SyncAggregate,
blsToExecutionChanges: pb.BlsToExecutionChanges,
signedExecutionPayloadBid: pb.SignedExecutionPayloadBid,
payloadAttestations: pb.PayloadAttestations,
}
return b, nil
}

View File

@@ -1792,6 +1792,61 @@ func bodyBlindedElectra(t *testing.T) *BeaconBlockBody {
}
}
func TestSignedBeaconBlockProtoGloas(t *testing.T) {
payload := []*eth.PayloadAttestation{{Signature: []byte{0x01}}}
bid := &eth.SignedExecutionPayloadBid{Signature: []byte{0x02}}
sb := &SignedBeaconBlock{
version: version.Gloas,
block: &BeaconBlock{
version: version.Gloas,
body: &BeaconBlockBody{
version: version.Gloas,
payloadAttestations: payload,
signedExecutionPayloadBid: bid,
},
},
}
msg, err := sb.Proto()
require.NoError(t, err)
gloas, ok := msg.(*eth.SignedBeaconBlockGloas)
require.Equal(t, true, ok)
require.DeepEqual(t, payload, gloas.Block.Body.PayloadAttestations)
require.DeepEqual(t, bid, gloas.Block.Body.SignedExecutionPayloadBid)
}
func TestInitSignedBlockFromProtoGloas(t *testing.T) {
bits := bitfield.NewBitvector512()
bits.SetBitAt(0, true)
pb := &eth.SignedBeaconBlockGloas{
Block: &eth.BeaconBlockGloas{
Body: &eth.BeaconBlockBodyGloas{
PayloadAttestations: []*eth.PayloadAttestation{
{
AggregationBits: bits,
Signature: []byte{0x01},
},
},
SignedExecutionPayloadBid: &eth.SignedExecutionPayloadBid{Signature: []byte{0x02}},
},
},
Signature: []byte{0x03},
}
sb, err := initSignedBlockFromProtoGloas(pb)
require.NoError(t, err)
require.Equal(t, version.Gloas, sb.Version())
gotPayload, err := sb.Block().Body().PayloadAttestations()
require.NoError(t, err)
require.Equal(t, 1, len(gotPayload))
require.DeepEqual(t, pb.Block.Body.PayloadAttestations, gotPayload)
gotBid, err := sb.Block().Body().SignedExecutionPayloadBid()
require.NoError(t, err)
require.DeepEqual(t, pb.Block.Body.SignedExecutionPayloadBid, gotBid)
}
func getFields() fields {
b20 := make([]byte, 20)
b48 := make([]byte, 48)

View File

@@ -144,7 +144,7 @@ func (b *SignedBeaconBlock) SetSyncAggregate(s *eth.SyncAggregate) error {
// SetExecution sets the execution payload of the block body.
// This function is not thread safe, it is only used during block creation.
func (b *SignedBeaconBlock) SetExecution(e interfaces.ExecutionData) error {
if b.version == version.Phase0 || b.version == version.Altair {
if b.version == version.Phase0 || b.version == version.Altair || b.version >= version.Gloas {
return consensus_types.ErrNotSupported("Execution", b.version)
}
if e.IsBlinded() {
@@ -176,9 +176,27 @@ func (b *SignedBeaconBlock) SetBlobKzgCommitments(c [][]byte) error {
// SetExecutionRequests sets the execution requests in the block.
func (b *SignedBeaconBlock) SetExecutionRequests(req *enginev1.ExecutionRequests) error {
if b.version < version.Electra {
if b.version < version.Electra || b.version >= version.Gloas {
return consensus_types.ErrNotSupported("SetExecutionRequests", b.version)
}
b.block.body.executionRequests = req
return nil
}
// SetPayloadAttestations sets the payload attestations in the block.
func (b *SignedBeaconBlock) SetPayloadAttestations(pa []*eth.PayloadAttestation) error {
if b.version < version.Gloas {
return consensus_types.ErrNotSupported("SetPayloadAttestations", b.version)
}
b.block.body.payloadAttestations = pa
return nil
}
// SetSignedExecutionPayloadBid sets the signed execution payload header in the block.
func (b *SignedBeaconBlock) SetSignedExecutionPayloadBid(header *eth.SignedExecutionPayloadBid) error {
if b.version < version.Gloas {
return consensus_types.ErrNotSupported("SetSignedExecutionPayloadBid", b.version)
}
b.block.body.signedExecutionPayloadBid = header
return nil
}

View File

@@ -0,0 +1,107 @@
package blocks
import (
"testing"
bitfield "github.com/OffchainLabs/go-bitfield"
consensus_types "github.com/OffchainLabs/prysm/v7/consensus-types"
enginev1 "github.com/OffchainLabs/prysm/v7/proto/engine/v1"
eth "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
"github.com/OffchainLabs/prysm/v7/runtime/version"
"github.com/OffchainLabs/prysm/v7/testing/require"
)
func TestSignedBeaconBlock_SetPayloadAttestations(t *testing.T) {
t.Run("rejects pre-Gloas versions", func(t *testing.T) {
sb := newTestSignedBeaconBlock(version.Fulu)
payload := []*eth.PayloadAttestation{{}}
err := sb.SetPayloadAttestations(payload)
require.ErrorIs(t, err, consensus_types.ErrUnsupportedField)
require.IsNil(t, sb.block.body.payloadAttestations)
})
t.Run("sets payload attestations for Gloas", func(t *testing.T) {
sb := newTestSignedBeaconBlock(version.Gloas)
payload := []*eth.PayloadAttestation{
{
AggregationBits: bitfield.NewBitvector512(),
Data: &eth.PayloadAttestationData{
BeaconBlockRoot: []byte{0x01, 0x02},
PayloadPresent: true,
BlobDataAvailable: true,
},
Signature: []byte{0x03},
},
}
err := sb.SetPayloadAttestations(payload)
require.NoError(t, err)
require.DeepEqual(t, payload, sb.block.body.payloadAttestations)
})
}
func TestSignedBeaconBlock_SetSignedExecutionPayloadBid(t *testing.T) {
t.Run("rejects pre-Gloas versions", func(t *testing.T) {
sb := newTestSignedBeaconBlock(version.Fulu)
payloadBid := &eth.SignedExecutionPayloadBid{}
err := sb.SetSignedExecutionPayloadBid(payloadBid)
require.ErrorIs(t, err, consensus_types.ErrUnsupportedField)
require.IsNil(t, sb.block.body.signedExecutionPayloadBid)
})
t.Run("sets signed execution payload bid for Gloas", func(t *testing.T) {
sb := newTestSignedBeaconBlock(version.Gloas)
payloadBid := &eth.SignedExecutionPayloadBid{
Message: &eth.ExecutionPayloadBid{
ParentBlockHash: []byte{0xaa},
BlockHash: []byte{0xbb},
FeeRecipient: []byte{0xcc},
},
Signature: []byte{0xdd},
}
err := sb.SetSignedExecutionPayloadBid(payloadBid)
require.NoError(t, err)
require.Equal(t, payloadBid, sb.block.body.signedExecutionPayloadBid)
})
}
func TestSignedBeaconBlock_SetExecution(t *testing.T) {
t.Run("rejects Gloas version", func(t *testing.T) {
sb := newTestSignedBeaconBlock(version.Gloas)
payload := &enginev1.ExecutionPayload{}
wrapped, err := WrappedExecutionPayload(payload)
require.NoError(t, err)
err = sb.SetExecution(wrapped)
require.ErrorIs(t, err, consensus_types.ErrUnsupportedField)
})
}
func TestSignedBeaconBlock_SetExecutionRequests(t *testing.T) {
t.Run("rejects Gloas version", func(t *testing.T) {
sb := newTestSignedBeaconBlock(version.Gloas)
requests := &enginev1.ExecutionRequests{}
err := sb.SetExecutionRequests(requests)
require.ErrorIs(t, err, consensus_types.ErrUnsupportedField)
})
}
func newTestSignedBeaconBlock(ver int) *SignedBeaconBlock {
return &SignedBeaconBlock{
version: ver,
block: &BeaconBlock{
version: ver,
body: &BeaconBlockBody{
version: ver,
},
},
}
}

View File

@@ -40,23 +40,25 @@ var (
// BeaconBlockBody is the main beacon block body structure. It can represent any block type.
type BeaconBlockBody struct {
version int
randaoReveal [field_params.BLSSignatureLength]byte
eth1Data *eth.Eth1Data
graffiti [field_params.RootLength]byte
proposerSlashings []*eth.ProposerSlashing
attesterSlashings []*eth.AttesterSlashing
attesterSlashingsElectra []*eth.AttesterSlashingElectra
attestations []*eth.Attestation
attestationsElectra []*eth.AttestationElectra
deposits []*eth.Deposit
voluntaryExits []*eth.SignedVoluntaryExit
syncAggregate *eth.SyncAggregate
executionPayload interfaces.ExecutionData
executionPayloadHeader interfaces.ExecutionData
blsToExecutionChanges []*eth.SignedBLSToExecutionChange
blobKzgCommitments [][]byte
executionRequests *enginev1.ExecutionRequests
version int
randaoReveal [field_params.BLSSignatureLength]byte
eth1Data *eth.Eth1Data
graffiti [field_params.RootLength]byte
proposerSlashings []*eth.ProposerSlashing
attesterSlashings []*eth.AttesterSlashing
attesterSlashingsElectra []*eth.AttesterSlashingElectra
attestations []*eth.Attestation
attestationsElectra []*eth.AttestationElectra
deposits []*eth.Deposit
voluntaryExits []*eth.SignedVoluntaryExit
syncAggregate *eth.SyncAggregate
executionPayload interfaces.ExecutionData
executionPayloadHeader interfaces.ExecutionData
blsToExecutionChanges []*eth.SignedBLSToExecutionChange
blobKzgCommitments [][]byte
executionRequests *enginev1.ExecutionRequests
signedExecutionPayloadBid *eth.SignedExecutionPayloadBid
payloadAttestations []*eth.PayloadAttestation
}
var _ interfaces.ReadOnlyBeaconBlockBody = &BeaconBlockBody{}

View File

@@ -69,6 +69,8 @@ type ReadOnlyBeaconBlockBody interface {
BLSToExecutionChanges() ([]*ethpb.SignedBLSToExecutionChange, error)
BlobKzgCommitments() ([][]byte, error)
ExecutionRequests() (*enginev1.ExecutionRequests, error)
PayloadAttestations() ([]*ethpb.PayloadAttestation, error)
SignedExecutionPayloadBid() (*ethpb.SignedExecutionPayloadBid, error)
}
type SignedBeaconBlock interface {
@@ -91,6 +93,8 @@ type SignedBeaconBlock interface {
SetSlot(slot primitives.Slot)
SetSignature(sig []byte)
SetExecutionRequests(er *enginev1.ExecutionRequests) error
SetPayloadAttestations(pa []*ethpb.PayloadAttestation) error
SetSignedExecutionPayloadBid(header *ethpb.SignedExecutionPayloadBid) error
Unblind(e ExecutionData) error
}

View File

@@ -276,6 +276,14 @@ func (b *BeaconBlockBody) ExecutionRequests() (*enginev1.ExecutionRequests, erro
panic("implement me")
}
func (b *BeaconBlockBody) PayloadAttestations() ([]*eth.PayloadAttestation, error) {
panic("implement me")
}
func (b *BeaconBlockBody) SignedExecutionPayloadBid() (*eth.SignedExecutionPayloadBid, error) {
panic("implement me")
}
func (b *BeaconBlockBody) Attestations() []eth.Att {
panic("implement me")
}

View File

@@ -172,6 +172,18 @@ func (cf *VersionedUnmarshaler) UnmarshalBeaconState(marshaled []byte) (s state.
if err != nil {
return nil, errors.Wrapf(err, "failed to init state trie from state, detected fork=%s", forkName)
}
case version.Gloas:
st := &ethpb.BeaconStateGloas{}
err = st.UnmarshalSSZ(marshaled)
if err != nil {
return nil, errors.Wrapf(err, "failed to unmarshal state, detected fork=%s", forkName)
}
s, err = state_native.InitializeFromProtoUnsafeGloas(st)
if err != nil {
return nil, errors.Wrapf(err, "failed to init state trie from state, detected fork=%s", forkName)
}
default:
return nil, fmt.Errorf("unable to initialize BeaconState for fork version=%s", forkName)
}

View File

@@ -26,8 +26,21 @@ func TestLifecycle(t *testing.T) {
port := 1000 + rand.Intn(1000)
prometheusService := NewService(t.Context(), fmt.Sprintf(":%d", port), nil)
prometheusService.Start()
// Give service time to start.
time.Sleep(time.Second)
// Actively wait until the service responds on /metrics (faster and less flaky than a fixed sleep)
deadline := time.Now().Add(3 * time.Second)
for {
if time.Now().After(deadline) {
t.Fatalf("metrics endpoint not ready within timeout")
}
resp, err := http.Get(fmt.Sprintf("http://localhost:%d/metrics", port))
if err == nil {
_ = resp.Body.Close()
if resp.StatusCode == http.StatusOK {
break
}
}
time.Sleep(50 * time.Millisecond)
}
// Query the service to ensure it really started.
resp, err := http.Get(fmt.Sprintf("http://localhost:%d/metrics", port))
@@ -36,8 +49,18 @@ func TestLifecycle(t *testing.T) {
err = prometheusService.Stop()
require.NoError(t, err)
// Give service time to stop.
time.Sleep(time.Second)
// Actively wait until the service stops responding on /metrics
deadline = time.Now().Add(3 * time.Second)
for {
if time.Now().After(deadline) {
t.Fatalf("metrics endpoint still reachable after timeout")
}
_, err = http.Get(fmt.Sprintf("http://localhost:%d/metrics", port))
if err != nil {
break
}
time.Sleep(50 * time.Millisecond)
}
// Query the service to ensure it really stopped.
_, err = http.Get(fmt.Sprintf("http://localhost:%d/metrics", port))

288
proto/engine/v1/gloas.pb.go generated Executable file
View File

@@ -0,0 +1,288 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.36.3
// protoc v3.21.7
// source: proto/engine/v1/gloas.proto
package enginev1
import (
reflect "reflect"
sync "sync"
github_com_OffchainLabs_prysm_v6_consensus_types_primitives "github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
_ "github.com/OffchainLabs/prysm/v7/proto/eth/ext"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type ExecutionPayloadEnvelope struct {
state protoimpl.MessageState `protogen:"open.v1"`
Payload *ExecutionPayloadDeneb `protobuf:"bytes,1,opt,name=payload,proto3" json:"payload,omitempty"`
ExecutionRequests *ExecutionRequests `protobuf:"bytes,2,opt,name=execution_requests,json=executionRequests,proto3" json:"execution_requests,omitempty"`
BuilderIndex github_com_OffchainLabs_prysm_v6_consensus_types_primitives.ValidatorIndex `protobuf:"varint,3,opt,name=builder_index,json=builderIndex,proto3" json:"builder_index,omitempty" cast-type:"github.com/OffchainLabs/prysm/v7/consensus-types/primitives.ValidatorIndex"`
BeaconBlockRoot []byte `protobuf:"bytes,4,opt,name=beacon_block_root,json=beaconBlockRoot,proto3" json:"beacon_block_root,omitempty" ssz-size:"32"`
Slot github_com_OffchainLabs_prysm_v6_consensus_types_primitives.Slot `protobuf:"varint,5,opt,name=slot,proto3" json:"slot,omitempty" cast-type:"github.com/OffchainLabs/prysm/v7/consensus-types/primitives.Slot"`
BlobKzgCommitments [][]byte `protobuf:"bytes,6,rep,name=blob_kzg_commitments,json=blobKzgCommitments,proto3" json:"blob_kzg_commitments,omitempty" ssz-max:"4096" ssz-size:"?,48"`
StateRoot []byte `protobuf:"bytes,7,opt,name=state_root,json=stateRoot,proto3" json:"state_root,omitempty" ssz-size:"32"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *ExecutionPayloadEnvelope) Reset() {
*x = ExecutionPayloadEnvelope{}
mi := &file_proto_engine_v1_gloas_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *ExecutionPayloadEnvelope) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*ExecutionPayloadEnvelope) ProtoMessage() {}
func (x *ExecutionPayloadEnvelope) ProtoReflect() protoreflect.Message {
mi := &file_proto_engine_v1_gloas_proto_msgTypes[0]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use ExecutionPayloadEnvelope.ProtoReflect.Descriptor instead.
func (*ExecutionPayloadEnvelope) Descriptor() ([]byte, []int) {
return file_proto_engine_v1_gloas_proto_rawDescGZIP(), []int{0}
}
func (x *ExecutionPayloadEnvelope) GetPayload() *ExecutionPayloadDeneb {
if x != nil {
return x.Payload
}
return nil
}
func (x *ExecutionPayloadEnvelope) GetExecutionRequests() *ExecutionRequests {
if x != nil {
return x.ExecutionRequests
}
return nil
}
func (x *ExecutionPayloadEnvelope) GetBuilderIndex() github_com_OffchainLabs_prysm_v6_consensus_types_primitives.ValidatorIndex {
if x != nil {
return x.BuilderIndex
}
return github_com_OffchainLabs_prysm_v6_consensus_types_primitives.ValidatorIndex(0)
}
func (x *ExecutionPayloadEnvelope) GetBeaconBlockRoot() []byte {
if x != nil {
return x.BeaconBlockRoot
}
return nil
}
func (x *ExecutionPayloadEnvelope) GetSlot() github_com_OffchainLabs_prysm_v6_consensus_types_primitives.Slot {
if x != nil {
return x.Slot
}
return github_com_OffchainLabs_prysm_v6_consensus_types_primitives.Slot(0)
}
func (x *ExecutionPayloadEnvelope) GetBlobKzgCommitments() [][]byte {
if x != nil {
return x.BlobKzgCommitments
}
return nil
}
func (x *ExecutionPayloadEnvelope) GetStateRoot() []byte {
if x != nil {
return x.StateRoot
}
return nil
}
type SignedExecutionPayloadEnvelope struct {
state protoimpl.MessageState `protogen:"open.v1"`
Message *ExecutionPayloadEnvelope `protobuf:"bytes,1,opt,name=message,proto3" json:"message,omitempty"`
Signature []byte `protobuf:"bytes,2,opt,name=signature,proto3" json:"signature,omitempty" ssz-size:"96"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
func (x *SignedExecutionPayloadEnvelope) Reset() {
*x = SignedExecutionPayloadEnvelope{}
mi := &file_proto_engine_v1_gloas_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
func (x *SignedExecutionPayloadEnvelope) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*SignedExecutionPayloadEnvelope) ProtoMessage() {}
func (x *SignedExecutionPayloadEnvelope) ProtoReflect() protoreflect.Message {
mi := &file_proto_engine_v1_gloas_proto_msgTypes[1]
if x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use SignedExecutionPayloadEnvelope.ProtoReflect.Descriptor instead.
func (*SignedExecutionPayloadEnvelope) Descriptor() ([]byte, []int) {
return file_proto_engine_v1_gloas_proto_rawDescGZIP(), []int{1}
}
func (x *SignedExecutionPayloadEnvelope) GetMessage() *ExecutionPayloadEnvelope {
if x != nil {
return x.Message
}
return nil
}
func (x *SignedExecutionPayloadEnvelope) GetSignature() []byte {
if x != nil {
return x.Signature
}
return nil
}
var File_proto_engine_v1_gloas_proto protoreflect.FileDescriptor
var file_proto_engine_v1_gloas_proto_rawDesc = []byte{
0x0a, 0x1b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2f, 0x76,
0x31, 0x2f, 0x67, 0x6c, 0x6f, 0x61, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x12, 0x65,
0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x76,
0x31, 0x1a, 0x26, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2f,
0x76, 0x31, 0x2f, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x65, 0x6e, 0x67,
0x69, 0x6e, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1d, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x2f, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x6c, 0x65, 0x63, 0x74,
0x72, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f,
0x65, 0x74, 0x68, 0x2f, 0x65, 0x78, 0x74, 0x2f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xa3, 0x04, 0x0a, 0x18, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74,
0x69, 0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f,
0x70, 0x65, 0x12, 0x43, 0x0a, 0x07, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x01, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x29, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65,
0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69,
0x6f, 0x6e, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x44, 0x65, 0x6e, 0x65, 0x62, 0x52, 0x07,
0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x54, 0x0a, 0x12, 0x65, 0x78, 0x65, 0x63, 0x75,
0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x18, 0x02, 0x20,
0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65,
0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69,
0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x52, 0x11, 0x65, 0x78, 0x65, 0x63,
0x75, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x73, 0x12, 0x73, 0x0a,
0x0d, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x5f, 0x69, 0x6e, 0x64, 0x65, 0x78, 0x18, 0x03,
0x20, 0x01, 0x28, 0x04, 0x42, 0x4e, 0x82, 0xb5, 0x18, 0x4a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62,
0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x36, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65,
0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69,
0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x56, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x6f, 0x72, 0x49,
0x6e, 0x64, 0x65, 0x78, 0x52, 0x0c, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x65, 0x72, 0x49, 0x6e, 0x64,
0x65, 0x78, 0x12, 0x32, 0x0a, 0x11, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x5f, 0x62, 0x6c, 0x6f,
0x63, 0x6b, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a,
0xb5, 0x18, 0x02, 0x33, 0x32, 0x52, 0x0f, 0x62, 0x65, 0x61, 0x63, 0x6f, 0x6e, 0x42, 0x6c, 0x6f,
0x63, 0x6b, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x58, 0x0a, 0x04, 0x73, 0x6c, 0x6f, 0x74, 0x18, 0x05,
0x20, 0x01, 0x28, 0x04, 0x42, 0x44, 0x82, 0xb5, 0x18, 0x40, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62,
0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x36, 0x2f, 0x63, 0x6f, 0x6e, 0x73, 0x65,
0x6e, 0x73, 0x75, 0x73, 0x2d, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x69, 0x6d, 0x69,
0x74, 0x69, 0x76, 0x65, 0x73, 0x2e, 0x53, 0x6c, 0x6f, 0x74, 0x52, 0x04, 0x73, 0x6c, 0x6f, 0x74,
0x12, 0x42, 0x0a, 0x14, 0x62, 0x6c, 0x6f, 0x62, 0x5f, 0x6b, 0x7a, 0x67, 0x5f, 0x63, 0x6f, 0x6d,
0x6d, 0x69, 0x74, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x06, 0x20, 0x03, 0x28, 0x0c, 0x42, 0x10,
0x8a, 0xb5, 0x18, 0x04, 0x3f, 0x2c, 0x34, 0x38, 0x92, 0xb5, 0x18, 0x04, 0x34, 0x30, 0x39, 0x36,
0x52, 0x12, 0x62, 0x6c, 0x6f, 0x62, 0x4b, 0x7a, 0x67, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x6d,
0x65, 0x6e, 0x74, 0x73, 0x12, 0x25, 0x0a, 0x0a, 0x73, 0x74, 0x61, 0x74, 0x65, 0x5f, 0x72, 0x6f,
0x6f, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x33, 0x32,
0x52, 0x09, 0x73, 0x74, 0x61, 0x74, 0x65, 0x52, 0x6f, 0x6f, 0x74, 0x22, 0x8e, 0x01, 0x0a, 0x1e,
0x53, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50,
0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x12, 0x46,
0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x2c, 0x2e, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x2e, 0x65, 0x6e, 0x67, 0x69, 0x6e,
0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x65, 0x63, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x50, 0x61,
0x79, 0x6c, 0x6f, 0x61, 0x64, 0x45, 0x6e, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x65, 0x52, 0x07, 0x6d,
0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x24, 0x0a, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74,
0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x42, 0x06, 0x8a, 0xb5, 0x18, 0x02, 0x39,
0x36, 0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x42, 0x3b, 0x5a, 0x39,
0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x4f, 0x66, 0x66, 0x63, 0x68,
0x61, 0x69, 0x6e, 0x4c, 0x61, 0x62, 0x73, 0x2f, 0x70, 0x72, 0x79, 0x73, 0x6d, 0x2f, 0x76, 0x36,
0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x2f, 0x76, 0x31,
0x3b, 0x65, 0x6e, 0x67, 0x69, 0x6e, 0x65, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x33,
}
var (
file_proto_engine_v1_gloas_proto_rawDescOnce sync.Once
file_proto_engine_v1_gloas_proto_rawDescData = file_proto_engine_v1_gloas_proto_rawDesc
)
func file_proto_engine_v1_gloas_proto_rawDescGZIP() []byte {
file_proto_engine_v1_gloas_proto_rawDescOnce.Do(func() {
file_proto_engine_v1_gloas_proto_rawDescData = protoimpl.X.CompressGZIP(file_proto_engine_v1_gloas_proto_rawDescData)
})
return file_proto_engine_v1_gloas_proto_rawDescData
}
var file_proto_engine_v1_gloas_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
var file_proto_engine_v1_gloas_proto_goTypes = []any{
(*ExecutionPayloadEnvelope)(nil), // 0: ethereum.engine.v1.ExecutionPayloadEnvelope
(*SignedExecutionPayloadEnvelope)(nil), // 1: ethereum.engine.v1.SignedExecutionPayloadEnvelope
(*ExecutionPayloadDeneb)(nil), // 2: ethereum.engine.v1.ExecutionPayloadDeneb
(*ExecutionRequests)(nil), // 3: ethereum.engine.v1.ExecutionRequests
}
var file_proto_engine_v1_gloas_proto_depIdxs = []int32{
2, // 0: ethereum.engine.v1.ExecutionPayloadEnvelope.payload:type_name -> ethereum.engine.v1.ExecutionPayloadDeneb
3, // 1: ethereum.engine.v1.ExecutionPayloadEnvelope.execution_requests:type_name -> ethereum.engine.v1.ExecutionRequests
0, // 2: ethereum.engine.v1.SignedExecutionPayloadEnvelope.message:type_name -> ethereum.engine.v1.ExecutionPayloadEnvelope
3, // [3:3] is the sub-list for method output_type
3, // [3:3] is the sub-list for method input_type
3, // [3:3] is the sub-list for extension type_name
3, // [3:3] is the sub-list for extension extendee
0, // [0:3] is the sub-list for field type_name
}
func init() { file_proto_engine_v1_gloas_proto_init() }
func file_proto_engine_v1_gloas_proto_init() {
if File_proto_engine_v1_gloas_proto != nil {
return
}
file_proto_engine_v1_execution_engine_proto_init()
file_proto_engine_v1_electra_proto_init()
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_proto_engine_v1_gloas_proto_rawDesc,
NumEnums: 0,
NumMessages: 2,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_proto_engine_v1_gloas_proto_goTypes,
DependencyIndexes: file_proto_engine_v1_gloas_proto_depIdxs,
MessageInfos: file_proto_engine_v1_gloas_proto_msgTypes,
}.Build()
File_proto_engine_v1_gloas_proto = out.File
file_proto_engine_v1_gloas_proto_rawDesc = nil
file_proto_engine_v1_gloas_proto_goTypes = nil
file_proto_engine_v1_gloas_proto_depIdxs = nil
}

View File

@@ -371,6 +371,7 @@ go_library(
"beacon_block.go",
"cloners.go",
"eip_7521.go",
"gloas.go",
"log.go",
"sync_committee_mainnet.go",
"sync_committee_minimal.go", # keep

View File

@@ -61,3 +61,116 @@ func CopySyncCommitteeContribution(c *SyncCommitteeContribution) *SyncCommitteeC
Signature: bytesutil.SafeCopyBytes(c.Signature),
}
}
// CopySignedBeaconBlockGloas copies the provided signed beacon block Gloas object.
func CopySignedBeaconBlockGloas(sb *SignedBeaconBlockGloas) *SignedBeaconBlockGloas {
if sb == nil {
return nil
}
return &SignedBeaconBlockGloas{
Block: copyBeaconBlockGloas(sb.Block),
Signature: bytesutil.SafeCopyBytes(sb.Signature),
}
}
// copyBeaconBlockGloas copies the provided beacon block Gloas object.
func copyBeaconBlockGloas(b *BeaconBlockGloas) *BeaconBlockGloas {
if b == nil {
return nil
}
return &BeaconBlockGloas{
Slot: b.Slot,
ProposerIndex: b.ProposerIndex,
ParentRoot: bytesutil.SafeCopyBytes(b.ParentRoot),
StateRoot: bytesutil.SafeCopyBytes(b.StateRoot),
Body: copyBeaconBlockBodyGloas(b.Body),
}
}
// copyPayloadAttestation copies the provided payload attestation object.
func copyPayloadAttestation(pa *PayloadAttestation) *PayloadAttestation {
if pa == nil {
return nil
}
copied := &PayloadAttestation{
AggregationBits: pa.AggregationBits,
Signature: bytesutil.SafeCopyBytes(pa.Signature),
}
if pa.Data != nil {
copied.Data = &PayloadAttestationData{
BeaconBlockRoot: bytesutil.SafeCopyBytes(pa.Data.BeaconBlockRoot),
Slot: pa.Data.Slot,
PayloadPresent: pa.Data.PayloadPresent,
BlobDataAvailable: pa.Data.BlobDataAvailable,
}
}
return copied
}
// copyPayloadAttestations copies a slice of payload attestations.
func copyPayloadAttestations(pas []*PayloadAttestation) []*PayloadAttestation {
if len(pas) == 0 {
return nil
}
copied := make([]*PayloadAttestation, len(pas))
for i, pa := range pas {
copied[i] = copyPayloadAttestation(pa)
}
return copied
}
// copySignedExecutionPayloadBid copies the provided signed execution payload header.
func copySignedExecutionPayloadBid(header *SignedExecutionPayloadBid) *SignedExecutionPayloadBid {
if header == nil {
return nil
}
copied := &SignedExecutionPayloadBid{
Signature: bytesutil.SafeCopyBytes(header.Signature),
}
if header.Message != nil {
copied.Message = &ExecutionPayloadBid{
ParentBlockHash: bytesutil.SafeCopyBytes(header.Message.ParentBlockHash),
ParentBlockRoot: bytesutil.SafeCopyBytes(header.Message.ParentBlockRoot),
BlockHash: bytesutil.SafeCopyBytes(header.Message.BlockHash),
FeeRecipient: bytesutil.SafeCopyBytes(header.Message.FeeRecipient),
GasLimit: header.Message.GasLimit,
BuilderIndex: header.Message.BuilderIndex,
Slot: header.Message.Slot,
Value: header.Message.Value,
BlobKzgCommitmentsRoot: bytesutil.SafeCopyBytes(header.Message.BlobKzgCommitmentsRoot),
}
}
return copied
}
// copyBeaconBlockBodyGloas copies the provided beacon block body Gloas object.
func copyBeaconBlockBodyGloas(body *BeaconBlockBodyGloas) *BeaconBlockBodyGloas {
if body == nil {
return nil
}
copied := &BeaconBlockBodyGloas{
RandaoReveal: bytesutil.SafeCopyBytes(body.RandaoReveal),
Graffiti: bytesutil.SafeCopyBytes(body.Graffiti),
}
if body.Eth1Data != nil {
copied.Eth1Data = body.Eth1Data.Copy()
}
if body.SyncAggregate != nil {
copied.SyncAggregate = body.SyncAggregate.Copy()
}
copied.ProposerSlashings = CopySlice(body.ProposerSlashings)
copied.AttesterSlashings = CopySlice(body.AttesterSlashings)
copied.Attestations = CopySlice(body.Attestations)
copied.Deposits = CopySlice(body.Deposits)
copied.VoluntaryExits = CopySlice(body.VoluntaryExits)
copied.BlsToExecutionChanges = CopySlice(body.BlsToExecutionChanges)
copied.SignedExecutionPayloadBid = copySignedExecutionPayloadBid(body.SignedExecutionPayloadBid)
copied.PayloadAttestations = copyPayloadAttestations(body.PayloadAttestations)
return copied
}

View File

@@ -5,9 +5,12 @@ import (
"reflect"
"testing"
bitfield "github.com/OffchainLabs/go-bitfield"
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
enginev1 "github.com/OffchainLabs/prysm/v7/proto/engine/v1"
v1alpha1 "github.com/OffchainLabs/prysm/v7/proto/prysm/v1alpha1"
"github.com/OffchainLabs/prysm/v7/testing/assert"
"google.golang.org/protobuf/proto"
)
func TestCopySignedBeaconBlock(t *testing.T) {
@@ -100,6 +103,49 @@ func TestCopySyncCommitteeContribution(t *testing.T) {
assert.NotEmpty(t, got, "Copied sync committee contribution has empty fields")
}
func TestCopySignedBeaconBlockGloasNil(t *testing.T) {
if got := v1alpha1.CopySignedBeaconBlockGloas(nil); got != nil {
t.Fatalf("CopySignedBeaconBlockGloas(nil) = %v, want nil", got)
}
}
func TestCopySignedBeaconBlockGloasDeepCopy(t *testing.T) {
original := genSignedBeaconBlockGloas()
copied := v1alpha1.CopySignedBeaconBlockGloas(original)
if copied == original {
t.Fatalf("CopySignedBeaconBlockGloas returned the original pointer")
}
if !reflect.DeepEqual(copied, original) {
t.Fatalf("CopySignedBeaconBlockGloas() = %v, want %v", copied, original)
}
want := proto.Clone(copied).(*v1alpha1.SignedBeaconBlockGloas)
original.Signature[0] ^= 0xFF
original.Block.ParentRoot[0] ^= 0xFF
original.Block.StateRoot[0] ^= 0xFF
original.Block.Body.RandaoReveal[0] ^= 0xFF
original.Block.Body.Graffiti[0] ^= 0xFF
original.Block.Body.SyncAggregate.SyncCommitteeSignature[0] ^= 0xFF
original.Block.Body.SignedExecutionPayloadBid.Signature[0] ^= 0xFF
original.Block.Body.SignedExecutionPayloadBid.Message.BlockHash[0] ^= 0xFF
original.Block.Body.PayloadAttestations[0].Signature[0] ^= 0xFF
original.Block.Body.PayloadAttestations[0].Data.BeaconBlockRoot[0] ^= 0xFF
original.Block.Body.PayloadAttestations = append(original.Block.Body.PayloadAttestations, &v1alpha1.PayloadAttestation{})
original.Block.Body.BlsToExecutionChanges[0].Message.ValidatorIndex++
if !reflect.DeepEqual(want, copied) {
t.Fatalf("copy mutated after modifying source: got %v, want %v", copied, want)
}
if copied.Block == original.Block {
t.Fatal("expected copied block pointer to differ from original")
}
if copied.Block.Body == original.Block.Body {
t.Fatal("expected copied block body pointer to differ from original")
}
}
func TestCopyPendingAttestationSlice(t *testing.T) {
tests := []struct {
name string
@@ -1125,3 +1171,78 @@ func genConsolidationRequest() *enginev1.ConsolidationRequest {
TargetPubkey: bytes(48),
}
}
func genSignedBeaconBlockGloas() *v1alpha1.SignedBeaconBlockGloas {
return &v1alpha1.SignedBeaconBlockGloas{
Block: genBeaconBlockGloas(),
Signature: bytes(96),
}
}
func genBeaconBlockGloas() *v1alpha1.BeaconBlockGloas {
return &v1alpha1.BeaconBlockGloas{
Slot: primitives.Slot(rand.Uint64()),
ProposerIndex: primitives.ValidatorIndex(rand.Uint64()),
ParentRoot: bytes(32),
StateRoot: bytes(32),
Body: genBeaconBlockBodyGloas(),
}
}
func genBeaconBlockBodyGloas() *v1alpha1.BeaconBlockBodyGloas {
return &v1alpha1.BeaconBlockBodyGloas{
RandaoReveal: bytes(96),
Eth1Data: genEth1Data(),
Graffiti: bytes(32),
ProposerSlashings: genProposerSlashings(3),
AttesterSlashings: genAttesterSlashingsElectra(3),
Attestations: genAttestationsElectra(3),
Deposits: genDeposits(3),
VoluntaryExits: genSignedVoluntaryExits(3),
SyncAggregate: genSyncAggregate(),
BlsToExecutionChanges: genBLSToExecutionChanges(2),
SignedExecutionPayloadBid: genSignedExecutionPayloadBidGloas(),
PayloadAttestations: genPayloadAttestations(2),
}
}
func genSignedExecutionPayloadBidGloas() *v1alpha1.SignedExecutionPayloadBid {
return &v1alpha1.SignedExecutionPayloadBid{
Message: genExecutionPayloadBidGloas(),
Signature: bytes(96),
}
}
func genExecutionPayloadBidGloas() *v1alpha1.ExecutionPayloadBid {
return &v1alpha1.ExecutionPayloadBid{
ParentBlockHash: bytes(32),
ParentBlockRoot: bytes(32),
BlockHash: bytes(32),
FeeRecipient: bytes(20),
GasLimit: rand.Uint64(),
BuilderIndex: primitives.ValidatorIndex(rand.Uint64()),
Slot: primitives.Slot(rand.Uint64()),
Value: primitives.Gwei(rand.Uint64()),
BlobKzgCommitmentsRoot: bytes(32),
}
}
func genPayloadAttestations(num int) []*v1alpha1.PayloadAttestation {
pas := make([]*v1alpha1.PayloadAttestation, num)
for i := range pas {
bits := bitfield.NewBitvector512()
bits.SetBitAt(uint64(i%512), true)
pas[i] = &v1alpha1.PayloadAttestation{
AggregationBits: bits,
Data: &v1alpha1.PayloadAttestationData{
BeaconBlockRoot: bytes(32),
Slot: primitives.Slot(rand.Uint64()),
PayloadPresent: i%2 == 0,
BlobDataAvailable: i%2 == 1,
},
Signature: bytes(96),
}
}
return pas
}

View File

@@ -0,0 +1,47 @@
package eth
import (
"github.com/OffchainLabs/prysm/v7/encoding/bytesutil"
)
// Copy creates a deep copy of ExecutionPayloadBid.
func (header *ExecutionPayloadBid) Copy() *ExecutionPayloadBid {
if header == nil {
return nil
}
return &ExecutionPayloadBid{
ParentBlockHash: bytesutil.SafeCopyBytes(header.ParentBlockHash),
ParentBlockRoot: bytesutil.SafeCopyBytes(header.ParentBlockRoot),
BlockHash: bytesutil.SafeCopyBytes(header.BlockHash),
FeeRecipient: bytesutil.SafeCopyBytes(header.FeeRecipient),
GasLimit: header.GasLimit,
BuilderIndex: header.BuilderIndex,
Slot: header.Slot,
Value: header.Value,
BlobKzgCommitmentsRoot: bytesutil.SafeCopyBytes(header.BlobKzgCommitmentsRoot),
}
}
// Copy creates a deep copy of BuilderPendingWithdrawal.
func (withdrawal *BuilderPendingWithdrawal) Copy() *BuilderPendingWithdrawal {
if withdrawal == nil {
return nil
}
return &BuilderPendingWithdrawal{
FeeRecipient: bytesutil.SafeCopyBytes(withdrawal.FeeRecipient),
Amount: withdrawal.Amount,
BuilderIndex: withdrawal.BuilderIndex,
WithdrawableEpoch: withdrawal.WithdrawableEpoch,
}
}
// Copy creates a deep copy of BuilderPendingPayment.
func (payment *BuilderPendingPayment) Copy() *BuilderPendingPayment {
if payment == nil {
return nil
}
return &BuilderPendingPayment{
Weight: payment.Weight,
Withdrawal: payment.Withdrawal.Copy(),
}
}

View File

@@ -0,0 +1,168 @@
package eth
import (
"reflect"
"testing"
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
)
func TestExecutionPayloadBid_Copy(t *testing.T) {
tests := []struct {
name string
bid *ExecutionPayloadBid
}{
{
name: "nil bid",
bid: nil,
},
{
name: "empty bid",
bid: &ExecutionPayloadBid{},
},
{
name: "fully populated bid",
bid: &ExecutionPayloadBid{
ParentBlockHash: []byte("parent_block_hash_32_bytes_long!"),
ParentBlockRoot: []byte("parent_block_root_32_bytes_long!"),
BlockHash: []byte("block_hash_32_bytes_are_long!!"),
FeeRecipient: []byte("fee_recipient_20_byt"),
GasLimit: 15000000,
BuilderIndex: primitives.ValidatorIndex(42),
Slot: primitives.Slot(12345),
Value: 1000000000000000000,
BlobKzgCommitmentsRoot: []byte("blob_kzg_commitments_32_bytes!!"),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
copied := tt.bid.Copy()
if tt.bid == nil {
if copied != nil {
t.Errorf("Copy() of nil should return nil, got %v", copied)
}
return
}
if !reflect.DeepEqual(tt.bid, copied) {
t.Errorf("Copy() = %v, want %v", copied, tt.bid)
}
if len(tt.bid.ParentBlockHash) > 0 {
tt.bid.ParentBlockHash[0] = 0xFF
if copied.ParentBlockHash[0] == 0xFF {
t.Error("Copy() did not create deep copy of ParentBlockHash")
}
}
})
}
}
func TestBuilderPendingWithdrawal_Copy(t *testing.T) {
tests := []struct {
name string
withdrawal *BuilderPendingWithdrawal
}{
{
name: "nil withdrawal",
withdrawal: nil,
},
{
name: "empty withdrawal",
withdrawal: &BuilderPendingWithdrawal{},
},
{
name: "fully populated withdrawal",
withdrawal: &BuilderPendingWithdrawal{
FeeRecipient: []byte("fee_recipient_20_byt"),
Amount: primitives.Gwei(5000000000),
BuilderIndex: primitives.ValidatorIndex(123),
WithdrawableEpoch: primitives.Epoch(456),
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
copied := tt.withdrawal.Copy()
if tt.withdrawal == nil {
if copied != nil {
t.Errorf("Copy() of nil should return nil, got %v", copied)
}
return
}
if !reflect.DeepEqual(tt.withdrawal, copied) {
t.Errorf("Copy() = %v, want %v", copied, tt.withdrawal)
}
// Verify deep copy by modifying original
if len(tt.withdrawal.FeeRecipient) > 0 {
tt.withdrawal.FeeRecipient[0] = 0xFF
if copied.FeeRecipient[0] == 0xFF {
t.Error("Copy() did not create deep copy of FeeRecipient")
}
}
})
}
}
func TestBuilderPendingPayment_Copy(t *testing.T) {
tests := []struct {
name string
payment *BuilderPendingPayment
}{
{
name: "nil payment",
payment: nil,
},
{
name: "empty payment",
payment: &BuilderPendingPayment{},
},
{
name: "payment with nil withdrawal",
payment: &BuilderPendingPayment{
Weight: primitives.Gwei(1000),
Withdrawal: nil,
},
},
{
name: "fully populated payment",
payment: &BuilderPendingPayment{
Weight: primitives.Gwei(2500),
Withdrawal: &BuilderPendingWithdrawal{
FeeRecipient: []byte("test_recipient_20byt"),
Amount: primitives.Gwei(10000),
BuilderIndex: primitives.ValidatorIndex(789),
WithdrawableEpoch: primitives.Epoch(999),
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
copied := tt.payment.Copy()
if tt.payment == nil {
if copied != nil {
t.Errorf("Copy() of nil should return nil, got %v", copied)
}
return
}
if !reflect.DeepEqual(tt.payment, copied) {
t.Errorf("Copy() = %v, want %v", copied, tt.payment)
}
if tt.payment.Withdrawal != nil && len(tt.payment.Withdrawal.FeeRecipient) > 0 {
tt.payment.Withdrawal.FeeRecipient[0] = 0xFF
if copied.Withdrawal != nil && len(copied.Withdrawal.FeeRecipient) > 0 && copied.Withdrawal.FeeRecipient[0] == 0xFF {
t.Error("Copy() did not create deep copy of nested Withdrawal.FeeRecipient")
}
}
})
}
}

View File

@@ -25,6 +25,11 @@ go_library(
go_test(
name = "go_default_test",
srcs = ["fork_test.go"],
embed = [":go_default_library"],
deps = ["@com_github_stretchr_testify//assert:go_default_library"],
deps = [
":go_default_library",
"//config/params:go_default_library",
"//consensus-types/primitives:go_default_library",
"@com_github_stretchr_testify//assert:go_default_library",
"@com_github_stretchr_testify//require:go_default_library",
],
)

View File

@@ -14,6 +14,7 @@ const (
Deneb
Electra
Fulu
Gloas
)
var versionToString = map[int]string{
@@ -24,11 +25,19 @@ var versionToString = map[int]string{
Deneb: "deneb",
Electra: "electra",
Fulu: "fulu",
Gloas: "gloas",
}
// stringToVersion and allVersions are populated in init()
var stringToVersion = map[string]int{}
var allVersions []int
var supportedVersions []int
// unsupportedVersions contains fork versions that exist in the enums but are not yet
// enabled on any supported network. These versions are removed from All().
var unsupportedVersions = map[int]struct{}{
Gloas: {},
}
// ErrUnrecognizedVersionName means a string does not match the list of canonical version names.
var ErrUnrecognizedVersionName = errors.New("version name doesn't map to a known value in the enum")
@@ -52,9 +61,15 @@ func String(version int) string {
return name
}
// All returns a list of all known fork versions.
// All returns a list of all supported fork versions.
func All() []int {
return allVersions
return supportedVersions
}
// IsUnsupported reports whether the provided version is currently gate-kept.
func IsUnsupported(version int) bool {
_, ok := unsupportedVersions[version]
return ok
}
func init() {
@@ -66,4 +81,12 @@ func init() {
i++
}
sort.Ints(allVersions)
supportedVersions = make([]int, 0, len(allVersions))
for _, v := range allVersions {
if _, skip := unsupportedVersions[v]; skip {
continue
}
supportedVersions = append(supportedVersions, v)
}
}

View File

@@ -1,11 +1,15 @@
package version
package version_test
import (
"slices"
"sort"
"testing"
"github.com/OffchainLabs/prysm/v7/config/params"
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
"github.com/OffchainLabs/prysm/v7/runtime/version"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestVersionString(t *testing.T) {
@@ -16,18 +20,18 @@ func TestVersionString(t *testing.T) {
}{
{
name: "phase0",
version: Phase0,
version: version.Phase0,
want: "phase0",
},
{
name: "altair",
version: Altair,
version: version.Altair,
want: "altair",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := String(tt.version); got != tt.want {
if got := version.String(tt.version); got != tt.want {
t.Errorf("String() = %v, want %v", got, tt.want)
}
})
@@ -35,7 +39,7 @@ func TestVersionString(t *testing.T) {
}
func TestVersionSorting(t *testing.T) {
versions := All()
versions := version.All()
expected := slices.Clone(versions)
sort.Ints(expected)
tests := []struct {
@@ -54,3 +58,74 @@ func TestVersionSorting(t *testing.T) {
})
}
}
func TestUnsupportedVersionsExcludedFromAll(t *testing.T) {
for _, v := range unsupportedVersions() {
assert.NotContains(t, version.All(), v, "unsupported version %s should not be returned by version.All()", version.String(v))
}
}
func TestUnsupportedVersionsAreNotScheduledOnTestnets(t *testing.T) {
unsupported := unsupportedVersions()
if len(unsupported) == 0 {
t.Skip("no unsupported versions defined")
}
testnetConfigs := []*params.BeaconChainConfig{
params.HoleskyConfig(),
params.SepoliaConfig(),
params.HoodiConfig(),
}
for _, v := range unsupported {
for _, cfg := range testnetConfigs {
epoch := forkEpochForVersion(cfg, v)
require.Equalf(
t,
cfg.FarFutureEpoch,
epoch,
"unsupported version %s should not be scheduled on %s (epoch=%d)",
version.String(v),
cfg.ConfigName,
epoch,
)
}
}
}
func forkEpochForVersion(cfg *params.BeaconChainConfig, v int) primitives.Epoch {
switch v {
case version.Phase0:
return cfg.GenesisEpoch
case version.Altair:
return cfg.AltairForkEpoch
case version.Bellatrix:
return cfg.BellatrixForkEpoch
case version.Capella:
return cfg.CapellaForkEpoch
case version.Deneb:
return cfg.DenebForkEpoch
case version.Electra:
return cfg.ElectraForkEpoch
case version.Fulu:
return cfg.FuluForkEpoch
default:
if version.IsUnsupported(v) {
return cfg.FarFutureEpoch
}
panic("forkEpochForVersion missing version " + version.String(v))
}
}
func unsupportedVersions() []int {
var unsupportedVersions []int
for v := 0; ; v++ {
if version.String(v) == "unknown version" {
break
}
if version.IsUnsupported(v) {
unsupportedVersions = append(unsupportedVersions, v)
}
}
return unsupportedVersions
}

View File

@@ -261,11 +261,15 @@ func (c *componentHandler) printPIDs(logger func(string, ...any)) {
msg += "This test PID: " + strconv.Itoa(os.Getpid()) + " (parent=" + strconv.Itoa(os.Getppid()) + ")\n"
// Beacon chain nodes
msg += fmt.Sprintf("Beacon chain nodes: %v\n", PIDsFromMultiComponentRunner(c.beaconNodes))
// Validator nodes
msg += fmt.Sprintf("Prysm beacon chain nodes: %v\n", PIDsFromMultiComponentRunner(c.beaconNodes))
msg += fmt.Sprintf("Prysm validators: %v\n", PIDsFromMultiComponentRunner(c.validatorNodes))
if c.lighthouseBeaconNodes != nil {
msg += fmt.Sprintf("Lighthouse beacon chain nodes: %v\n", PIDsFromMultiComponentRunner(c.lighthouseBeaconNodes))
}
if c.lighthouseValidatorNodes != nil {
msg += fmt.Sprintf("Lighthouse validators: %v\n", PIDsFromMultiComponentRunner(c.lighthouseValidatorNodes))
}
msg += fmt.Sprintf("Validators: %v\n", PIDsFromMultiComponentRunner(c.validatorNodes))
// ETH1 nodes
msg += fmt.Sprintf("ETH1 nodes: %v\n", PIDsFromMultiComponentRunner(c.eth1Nodes))
logger(msg)

View File

@@ -249,6 +249,10 @@ func (node *LighthouseBeaconNode) Stop() error {
return node.cmd.Process.Kill()
}
func (node *LighthouseBeaconNode) UnderlyingProcess() *os.Process {
return node.cmd.Process
}
func (node *LighthouseBeaconNode) createTestnetDir(ctx context.Context, index int) (string, error) {
testNetDir := e2e.TestParams.TestPath + fmt.Sprintf("/lighthouse-testnet-%d", index)
configPath := filepath.Join(testNetDir, "config.yaml")

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