mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-12 14:58:03 -05:00
Compare commits
3 Commits
process-ex
...
dependent_
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0df4d2cf82 | ||
|
|
c81283ece0 | ||
|
|
76420d9428 |
10
WORKSPACE
10
WORKSPACE
@@ -273,16 +273,16 @@ filegroup(
|
||||
url = "https://github.com/ethereum/EIPs/archive/5480440fe51742ed23342b68cf106cefd427e39d.tar.gz",
|
||||
)
|
||||
|
||||
consensus_spec_version = "v1.7.0-alpha.0"
|
||||
consensus_spec_version = "v1.7.0-alpha.1"
|
||||
|
||||
load("@prysm//tools:download_spectests.bzl", "consensus_spec_tests")
|
||||
|
||||
consensus_spec_tests(
|
||||
name = "consensus_spec_tests",
|
||||
flavors = {
|
||||
"general": "sha256-b+rJOuVqq+Dy53quPcNYcQwPFoMU7Wp7tdUVe7n0g8w=",
|
||||
"minimal": "sha256-qxRIxtjPxVsVCY90WsBJKhk0027XDSmhjnRvRN14V1c=",
|
||||
"mainnet": "sha256-NsuOQG3LzeiEE1TrWuvQ6vu6BboHv7h7f/RTS0pWkCs=",
|
||||
"general": "sha256-j5R3jA7Oo4OSDMTvpMuD+8RomaCByeFSwtfkq6fL0Zg=",
|
||||
"minimal": "sha256-tdTqByoyswOS4r6OxFmo70y2BP7w1TgEok+gf4cbxB0=",
|
||||
"mainnet": "sha256-5gB4dt6SnSDKzdBc06VedId3NkgvSYyv9n9FRxWKwYI=",
|
||||
},
|
||||
version = consensus_spec_version,
|
||||
)
|
||||
@@ -298,7 +298,7 @@ filegroup(
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
""",
|
||||
integrity = "sha256-hwNdUBgdBrkk6pWIpNYbzbwswUuOu6AMD2exN8uv+QQ=",
|
||||
integrity = "sha256-J+43DrK1pF658kTXTwMS6zGf4KDjvas++m8w2a8swpg=",
|
||||
strip_prefix = "consensus-specs-" + consensus_spec_version[1:],
|
||||
url = "https://github.com/ethereum/consensus-specs/archive/refs/tags/%s.tar.gz" % consensus_spec_version,
|
||||
)
|
||||
|
||||
@@ -26,7 +26,6 @@ go_library(
|
||||
"//beacon-chain/core/time:go_default_library",
|
||||
"//beacon-chain/core/validators:go_default_library",
|
||||
"//beacon-chain/state:go_default_library",
|
||||
"//config/features:go_default_library",
|
||||
"//config/fieldparams:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//consensus-types:go_default_library",
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/helpers"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/signing"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/state"
|
||||
"github.com/OffchainLabs/prysm/v7/config/features"
|
||||
fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams"
|
||||
"github.com/OffchainLabs/prysm/v7/config/params"
|
||||
"github.com/OffchainLabs/prysm/v7/consensus-types/interfaces"
|
||||
@@ -213,12 +212,7 @@ func ProcessWithdrawals(st state.BeaconState, executionData interfaces.Execution
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get next withdrawal validator index")
|
||||
}
|
||||
if features.Get().LowValcountSweep {
|
||||
bound := min(uint64(st.NumValidators()), params.BeaconConfig().MaxValidatorsPerWithdrawalsSweep)
|
||||
nextValidatorIndex += primitives.ValidatorIndex(bound)
|
||||
} else {
|
||||
nextValidatorIndex += primitives.ValidatorIndex(params.BeaconConfig().MaxValidatorsPerWithdrawalsSweep)
|
||||
}
|
||||
nextValidatorIndex += primitives.ValidatorIndex(params.BeaconConfig().MaxValidatorsPerWithdrawalsSweep)
|
||||
nextValidatorIndex = nextValidatorIndex % primitives.ValidatorIndex(st.NumValidators())
|
||||
} else {
|
||||
nextValidatorIndex = expectedWithdrawals[len(expectedWithdrawals)-1].ValidatorIndex + 1
|
||||
|
||||
@@ -588,6 +588,12 @@ func fcReturnsTargetRoot(root [32]byte) func([32]byte, primitives.Epoch) ([32]by
|
||||
}
|
||||
}
|
||||
|
||||
func fcReturnsDependentRoot() func([32]byte, primitives.Epoch) ([32]byte, error) {
|
||||
return func(root [32]byte, epoch primitives.Epoch) ([32]byte, error) {
|
||||
return root, nil
|
||||
}
|
||||
}
|
||||
|
||||
type mockSignatureCache struct {
|
||||
svCalledForSig map[signatureData]bool
|
||||
svcb func(sig signatureData) (bool, error)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package verification
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
@@ -19,6 +18,7 @@ import (
|
||||
"github.com/OffchainLabs/prysm/v7/runtime/logging"
|
||||
"github.com/OffchainLabs/prysm/v7/time/slots"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -293,55 +293,57 @@ func (dv *RODataColumnsVerifier) ValidProposerSignature(ctx context.Context) (er
|
||||
// The returned state is guaranteed to be at the same epoch as the data column's epoch, and have the same randao mix and active
|
||||
// validator indices as the data column's parent state advanced to the data column's slot.
|
||||
func (dv *RODataColumnsVerifier) getVerifyingState(ctx context.Context, dataColumn blocks.RODataColumn) (state.ReadOnlyBeaconState, error) {
|
||||
dataColumnSlot := dataColumn.Slot()
|
||||
dataColumnEpoch := slots.ToEpoch(dataColumnSlot)
|
||||
if dataColumnEpoch == 0 {
|
||||
return dv.hsp.HeadStateReadOnly(ctx)
|
||||
}
|
||||
parentRoot := dataColumn.ParentRoot()
|
||||
dcDependentRoot, err := dv.fc.DependentRootForEpoch(parentRoot, dataColumnEpoch-1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
headRoot, err := dv.hsp.HeadRoot(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
parentRoot := dataColumn.ParentRoot()
|
||||
dataColumnSlot := dataColumn.Slot()
|
||||
dataColumnEpoch := slots.ToEpoch(dataColumnSlot)
|
||||
headSlot := dv.hsp.HeadSlot()
|
||||
headEpoch := slots.ToEpoch(headSlot)
|
||||
|
||||
// Use head if it's the parent
|
||||
if bytes.Equal(parentRoot[:], headRoot) {
|
||||
// If they are in the same epoch, then we can return the head state directly
|
||||
if dataColumnEpoch == headEpoch {
|
||||
return dv.hsp.HeadStateReadOnly(ctx)
|
||||
}
|
||||
// Otherwise, we need to process the head state to the data column's slot
|
||||
headState, err := dv.hsp.HeadState(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return transition.ProcessSlotsUsingNextSlotCache(ctx, headState, headRoot, dataColumnSlot)
|
||||
}
|
||||
|
||||
// If head and data column are in the same epoch and head is compatible with the parent's depdendent root, then use head
|
||||
if dataColumnEpoch == headEpoch {
|
||||
headDependent, err := dv.fc.DependentRootForEpoch(bytesutil.ToBytes32(headRoot), dataColumnEpoch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
parentDependent, err := dv.fc.DependentRootForEpoch(parentRoot, dataColumnEpoch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if bytes.Equal(headDependent[:], parentDependent[:]) {
|
||||
return dv.hsp.HeadStateReadOnly(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise retrieve the parent state and advance it to the data column's slot
|
||||
parentState, err := dv.sr.StateByRoot(ctx, parentRoot)
|
||||
headDependentRoot, err := dv.fc.DependentRootForEpoch(bytesutil.ToBytes32(headRoot), dataColumnEpoch-1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
parentEpoch := slots.ToEpoch(parentState.Slot())
|
||||
if dataColumnEpoch == parentEpoch {
|
||||
return parentState, nil
|
||||
if dcDependentRoot == headDependentRoot {
|
||||
headSlot := dv.hsp.HeadSlot()
|
||||
headEpoch := slots.ToEpoch(headSlot)
|
||||
if headEpoch == dataColumnEpoch || headEpoch == dataColumnEpoch-1 {
|
||||
return dv.hsp.HeadStateReadOnly(ctx)
|
||||
}
|
||||
if headEpoch+1 < dataColumnEpoch {
|
||||
headState, err := dv.hsp.HeadState(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return transition.ProcessSlotsUsingNextSlotCache(ctx, headState, headRoot, dataColumnSlot)
|
||||
}
|
||||
}
|
||||
return transition.ProcessSlotsUsingNextSlotCache(ctx, parentState, parentRoot[:], dataColumnSlot)
|
||||
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"slot": dataColumnSlot,
|
||||
"parentRoot": fmt.Sprintf("%#x", parentRoot),
|
||||
"headRoot": fmt.Sprintf("%#x", headRoot),
|
||||
}).Debug("Replying state for data column verification")
|
||||
targetRoot, err := dv.fc.TargetRootForEpoch(parentRoot, dataColumnEpoch)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
targetState, err := dv.sr.StateByRoot(ctx, targetRoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
targetEpoch := slots.ToEpoch(targetState.Slot())
|
||||
if targetEpoch == dataColumnEpoch || targetEpoch == dataColumnEpoch-1 {
|
||||
return targetState, nil
|
||||
}
|
||||
return transition.ProcessSlotsUsingNextSlotCache(ctx, targetState, parentRoot[:], dataColumnSlot)
|
||||
}
|
||||
|
||||
func (dv *RODataColumnsVerifier) SidecarParentSeen(parentSeen func([fieldparams.RootLength]byte) bool) (err error) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package verification
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -9,6 +10,7 @@ import (
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/peerdas"
|
||||
forkchoicetypes "github.com/OffchainLabs/prysm/v7/beacon-chain/forkchoice/types"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/startup"
|
||||
"github.com/OffchainLabs/prysm/v7/beacon-chain/state"
|
||||
fieldparams "github.com/OffchainLabs/prysm/v7/config/fieldparams"
|
||||
"github.com/OffchainLabs/prysm/v7/config/params"
|
||||
"github.com/OffchainLabs/prysm/v7/consensus-types/blocks"
|
||||
@@ -281,7 +283,7 @@ func TestColumnSlotAboveFinalized(t *testing.T) {
|
||||
|
||||
func TestValidProposerSignature(t *testing.T) {
|
||||
const (
|
||||
columnSlot = 0
|
||||
columnSlot = 97
|
||||
blobCount = 1
|
||||
)
|
||||
|
||||
@@ -294,59 +296,83 @@ func TestValidProposerSignature(t *testing.T) {
|
||||
// The signature data does not depend on the data column itself, so we can use the first one.
|
||||
expectedSignatureData := columnToSignatureData(firstColumn)
|
||||
|
||||
// Create a proper Fulu state for verification.
|
||||
// We need enough validators to cover the proposer index.
|
||||
numValidators := max(uint64(firstColumn.ProposerIndex()+1), 64)
|
||||
fuluState, _ := util.DeterministicGenesisStateFulu(t, numValidators)
|
||||
|
||||
// Head state provider that returns the fuluState via HeadStateReadOnly path.
|
||||
headStateWithState := &mockHeadStateProvider{
|
||||
headRoot: parentRoot[:],
|
||||
headSlot: columnSlot,
|
||||
headStateReadOnly: fuluState,
|
||||
}
|
||||
|
||||
// Head state provider that will fail (headStateReadOnly is nil).
|
||||
headStateNotFound := &mockHeadStateProvider{
|
||||
headRoot: parentRoot[:],
|
||||
headSlot: columnSlot,
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
isError bool
|
||||
vscbShouldError bool
|
||||
svcbReturn bool
|
||||
stateByRooter StateByRooter
|
||||
vscbError error
|
||||
svcbError error
|
||||
name string
|
||||
isError bool
|
||||
vscbShouldError bool
|
||||
svcbReturn bool
|
||||
stateByRooter StateByRooter
|
||||
headStateProvider *mockHeadStateProvider
|
||||
vscbError error
|
||||
svcbError error
|
||||
name string
|
||||
}{
|
||||
{
|
||||
name: "cache hit - success",
|
||||
svcbReturn: true,
|
||||
svcbError: nil,
|
||||
vscbShouldError: true,
|
||||
vscbError: nil,
|
||||
stateByRooter: &mockStateByRooter{sbr: sbrErrorIfCalled(t)},
|
||||
isError: false,
|
||||
name: "cache hit - success",
|
||||
svcbReturn: true,
|
||||
svcbError: nil,
|
||||
vscbShouldError: true,
|
||||
vscbError: nil,
|
||||
stateByRooter: &mockStateByRooter{sbr: sbrErrorIfCalled(t)},
|
||||
headStateProvider: headStateWithState,
|
||||
isError: false,
|
||||
},
|
||||
{
|
||||
name: "cache hit - error",
|
||||
svcbReturn: true,
|
||||
svcbError: errors.New("derp"),
|
||||
vscbShouldError: true,
|
||||
vscbError: nil,
|
||||
stateByRooter: &mockStateByRooter{sbr: sbrErrorIfCalled(t)},
|
||||
isError: true,
|
||||
name: "cache hit - error",
|
||||
svcbReturn: true,
|
||||
svcbError: errors.New("derp"),
|
||||
vscbShouldError: true,
|
||||
vscbError: nil,
|
||||
stateByRooter: &mockStateByRooter{sbr: sbrErrorIfCalled(t)},
|
||||
headStateProvider: headStateWithState,
|
||||
isError: true,
|
||||
},
|
||||
{
|
||||
name: "cache miss - success",
|
||||
svcbReturn: false,
|
||||
svcbError: nil,
|
||||
vscbShouldError: false,
|
||||
vscbError: nil,
|
||||
stateByRooter: sbrForValOverrideWithT(t, firstColumn.ProposerIndex(), validator),
|
||||
isError: false,
|
||||
name: "cache miss - success",
|
||||
svcbReturn: false,
|
||||
svcbError: nil,
|
||||
vscbShouldError: false,
|
||||
vscbError: nil,
|
||||
stateByRooter: sbrForValOverrideWithT(t, firstColumn.ProposerIndex(), validator),
|
||||
headStateProvider: headStateWithState,
|
||||
isError: false,
|
||||
},
|
||||
{
|
||||
name: "cache miss - state not found",
|
||||
svcbReturn: false,
|
||||
svcbError: nil,
|
||||
vscbShouldError: false,
|
||||
vscbError: nil,
|
||||
stateByRooter: sbrNotFound(t, expectedSignatureData.Parent),
|
||||
isError: true,
|
||||
name: "cache miss - state not found",
|
||||
svcbReturn: false,
|
||||
svcbError: nil,
|
||||
vscbShouldError: false,
|
||||
vscbError: nil,
|
||||
stateByRooter: sbrNotFound(t, expectedSignatureData.Parent),
|
||||
headStateProvider: headStateNotFound,
|
||||
isError: true,
|
||||
},
|
||||
{
|
||||
name: "cache miss - signature failure",
|
||||
svcbReturn: false,
|
||||
svcbError: nil,
|
||||
vscbShouldError: false,
|
||||
vscbError: errors.New("signature, not so good!"),
|
||||
stateByRooter: sbrForValOverrideWithT(t, firstColumn.ProposerIndex(), validator),
|
||||
isError: true,
|
||||
name: "cache miss - signature failure",
|
||||
svcbReturn: false,
|
||||
svcbError: nil,
|
||||
vscbShouldError: false,
|
||||
vscbError: errors.New("signature, not so good!"),
|
||||
stateByRooter: sbrForValOverrideWithT(t, firstColumn.ProposerIndex(), validator),
|
||||
headStateProvider: headStateWithState,
|
||||
isError: true,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -377,9 +403,10 @@ func TestValidProposerSignature(t *testing.T) {
|
||||
shared: &sharedResources{
|
||||
sc: signatureCache,
|
||||
sr: tc.stateByRooter,
|
||||
hsp: &mockHeadStateProvider{},
|
||||
hsp: tc.headStateProvider,
|
||||
fc: &mockForkchoicer{
|
||||
TargetRootForEpochCB: fcReturnsTargetRoot([fieldparams.RootLength]byte{}),
|
||||
DependentRootForEpochCB: fcReturnsDependentRoot(),
|
||||
TargetRootForEpochCB: fcReturnsTargetRoot([fieldparams.RootLength]byte{}),
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -405,7 +432,7 @@ func TestValidProposerSignature(t *testing.T) {
|
||||
|
||||
func TestDataColumnsSidecarParentSeen(t *testing.T) {
|
||||
const (
|
||||
columnSlot = 0
|
||||
columnSlot = 97
|
||||
blobCount = 1
|
||||
)
|
||||
|
||||
@@ -509,7 +536,7 @@ func TestDataColumnsSidecarParentValid(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
const (
|
||||
columnSlot = 0
|
||||
columnSlot = 97
|
||||
blobCount = 1
|
||||
)
|
||||
|
||||
@@ -630,7 +657,7 @@ func TestDataColumnsSidecarDescendsFromFinalized(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
const (
|
||||
columnSlot = 0
|
||||
columnSlot = 97
|
||||
blobCount = 1
|
||||
)
|
||||
|
||||
@@ -693,7 +720,7 @@ func TestDataColumnsSidecarInclusionProven(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
const (
|
||||
columnSlot = 0
|
||||
columnSlot = 97
|
||||
blobCount = 1
|
||||
)
|
||||
|
||||
@@ -748,7 +775,7 @@ func TestDataColumnsSidecarKzgProofVerified(t *testing.T) {
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
const (
|
||||
columnSlot = 0
|
||||
columnSlot = 97
|
||||
blobCount = 1
|
||||
)
|
||||
|
||||
@@ -924,3 +951,135 @@ func TestColumnRequirementSatisfaction(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestGetVerifyingStateEdgeCases(t *testing.T) {
|
||||
const (
|
||||
columnSlot = 97 // epoch 3
|
||||
blobCount = 1
|
||||
)
|
||||
|
||||
parentRoot := [fieldparams.RootLength]byte{}
|
||||
columns := GenerateTestDataColumns(t, parentRoot, columnSlot, blobCount)
|
||||
|
||||
// Create a proper Fulu state for verification.
|
||||
numValidators := max(uint64(columns[0].ProposerIndex()+1), 64)
|
||||
fuluState, _ := util.DeterministicGenesisStateFulu(t, numValidators)
|
||||
|
||||
t.Run("different dependent roots - uses StateByRoot path", func(t *testing.T) {
|
||||
// Parent and head are on different forks with different dependent roots.
|
||||
// This forces the code to use TargetRootForEpoch -> StateByRoot path.
|
||||
signatureCache := &mockSignatureCache{
|
||||
svcb: func(signatureData signatureData) (bool, error) {
|
||||
return false, nil // Cache miss
|
||||
},
|
||||
vscb: func(signatureData signatureData, _ validatorAtIndexer) (err error) {
|
||||
return nil // Signature valid
|
||||
},
|
||||
}
|
||||
|
||||
// StateByRoot will be called because dependent roots differ
|
||||
stateByRootCalled := false
|
||||
stateByRooter := &mockStateByRooter{
|
||||
sbr: func(_ context.Context, root [32]byte) (state.BeaconState, error) {
|
||||
stateByRootCalled = true
|
||||
return fuluState, nil
|
||||
},
|
||||
}
|
||||
|
||||
initializer := Initializer{
|
||||
shared: &sharedResources{
|
||||
sc: signatureCache,
|
||||
sr: stateByRooter,
|
||||
hsp: &mockHeadStateProvider{
|
||||
headRoot: []byte{0xff}, // Different from parentRoot
|
||||
headSlot: columnSlot,
|
||||
},
|
||||
fc: &mockForkchoicer{
|
||||
// Return different roots for parent vs head to simulate different forks
|
||||
DependentRootForEpochCB: func(root [32]byte, epoch primitives.Epoch) ([32]byte, error) {
|
||||
return root, nil // Returns input, so parent [0...] != head [0xff...]
|
||||
},
|
||||
TargetRootForEpochCB: fcReturnsTargetRoot([fieldparams.RootLength]byte{}),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
verifier := initializer.NewDataColumnsVerifier(columns, GossipDataColumnSidecarRequirements)
|
||||
err := verifier.ValidProposerSignature(t.Context())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, stateByRootCalled, "StateByRoot should be called when dependent roots differ")
|
||||
})
|
||||
|
||||
t.Run("same dependent root head far ahead - uses head state with ProcessSlots", func(t *testing.T) {
|
||||
// Parent is ancestor of head on same chain, but head is in epoch 1 while column is in epoch 3.
|
||||
// headEpoch (1) + 1 < dataColumnEpoch (3), so ProcessSlots is called on head state.
|
||||
signatureCache := &mockSignatureCache{
|
||||
svcb: func(signatureData signatureData) (bool, error) {
|
||||
return false, nil // Cache miss
|
||||
},
|
||||
vscb: func(signatureData signatureData, _ validatorAtIndexer) (err error) {
|
||||
return nil // Signature valid
|
||||
},
|
||||
}
|
||||
|
||||
headStateCalled := false
|
||||
initializer := Initializer{
|
||||
shared: &sharedResources{
|
||||
sc: signatureCache,
|
||||
sr: &mockStateByRooter{sbr: sbrErrorIfCalled(t)}, // Should not be called
|
||||
hsp: &mockHeadStateProvider{
|
||||
headRoot: parentRoot[:], // Same as parent
|
||||
headSlot: 32, // Epoch 1
|
||||
headState: fuluState.Copy(), // HeadState (not ReadOnly) for ProcessSlots
|
||||
headStateReadOnly: nil, // Should not use ReadOnly path
|
||||
},
|
||||
fc: &mockForkchoicer{
|
||||
// Return same root for both to simulate same chain
|
||||
DependentRootForEpochCB: func(root [32]byte, epoch primitives.Epoch) ([32]byte, error) {
|
||||
return [32]byte{0xaa}, nil // Same for all inputs
|
||||
},
|
||||
TargetRootForEpochCB: fcReturnsTargetRoot([fieldparams.RootLength]byte{}),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Wrap to detect HeadState call
|
||||
originalHsp := initializer.shared.hsp.(*mockHeadStateProvider)
|
||||
wrappedHsp := &mockHeadStateProvider{
|
||||
headRoot: originalHsp.headRoot,
|
||||
headSlot: originalHsp.headSlot,
|
||||
headState: originalHsp.headState,
|
||||
}
|
||||
initializer.shared.hsp = &headStateCallTracker{
|
||||
mockHeadStateProvider: wrappedHsp,
|
||||
headStateCalled: &headStateCalled,
|
||||
}
|
||||
|
||||
verifier := initializer.NewDataColumnsVerifier(columns, GossipDataColumnSidecarRequirements)
|
||||
err := verifier.ValidProposerSignature(t.Context())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, headStateCalled, "HeadState should be called when head is far ahead")
|
||||
})
|
||||
}
|
||||
|
||||
// headStateCallTracker wraps mockHeadStateProvider to track HeadState calls.
|
||||
type headStateCallTracker struct {
|
||||
*mockHeadStateProvider
|
||||
headStateCalled *bool
|
||||
}
|
||||
|
||||
func (h *headStateCallTracker) HeadState(ctx context.Context) (state.BeaconState, error) {
|
||||
*h.headStateCalled = true
|
||||
return h.mockHeadStateProvider.HeadState(ctx)
|
||||
}
|
||||
|
||||
func (h *headStateCallTracker) HeadRoot(ctx context.Context) ([]byte, error) {
|
||||
return h.mockHeadStateProvider.HeadRoot(ctx)
|
||||
}
|
||||
|
||||
func (h *headStateCallTracker) HeadSlot() primitives.Slot {
|
||||
return h.mockHeadStateProvider.HeadSlot()
|
||||
}
|
||||
|
||||
func (h *headStateCallTracker) HeadStateReadOnly(ctx context.Context) (state.ReadOnlyBeaconState, error) {
|
||||
return h.mockHeadStateProvider.HeadStateReadOnly(ctx)
|
||||
}
|
||||
|
||||
2
changelog/potuz_dc_deproot.md
Normal file
2
changelog/potuz_dc_deproot.md
Normal file
@@ -0,0 +1,2 @@
|
||||
### Changed
|
||||
- Use dependent root and target root to verify data column proposer index.
|
||||
2
changelog/potuz_update_spectests_2.md
Normal file
2
changelog/potuz_update_spectests_2.md
Normal file
@@ -0,0 +1,2 @@
|
||||
### Added
|
||||
- Update spectests to v1.7.0-alpha-1.
|
||||
@@ -81,7 +81,6 @@ type Flags struct {
|
||||
SaveInvalidBlob bool // SaveInvalidBlob saves invalid blob to temp.
|
||||
|
||||
EnableDiscoveryReboot bool // EnableDiscoveryReboot allows the node to have its local listener to be rebooted in the event of discovery issues.
|
||||
LowValcountSweep bool // LowValcountSweep bounds withdrawal sweep by validator count.
|
||||
|
||||
// KeystoreImportDebounceInterval specifies the time duration the validator waits to reload new keys if they have
|
||||
// changed on disk. This feature is for advanced use cases only.
|
||||
@@ -290,11 +289,6 @@ func ConfigureBeaconChain(ctx *cli.Context) error {
|
||||
logEnabled(ignoreUnviableAttestations)
|
||||
cfg.IgnoreUnviableAttestations = true
|
||||
}
|
||||
if ctx.Bool(lowValcountSweep.Name) {
|
||||
logEnabled(lowValcountSweep)
|
||||
cfg.LowValcountSweep = true
|
||||
}
|
||||
|
||||
if ctx.IsSet(EnableStateDiff.Name) {
|
||||
logEnabled(EnableStateDiff)
|
||||
cfg.EnableStateDiff = true
|
||||
|
||||
@@ -216,13 +216,6 @@ var (
|
||||
Name: "ignore-unviable-attestations",
|
||||
Usage: "Ignores attestations whose target state is not viable with respect to the current head (avoid expensive state replay from lagging attesters).",
|
||||
}
|
||||
// lowValcountSweep bounds withdrawal sweep by validator count.
|
||||
lowValcountSweep = &cli.BoolFlag{
|
||||
Name: "low-valcount-sweep",
|
||||
Usage: "Uses validator count bound for withdrawal sweep when validator count is less than MaxValidatorsPerWithdrawalsSweep.",
|
||||
Value: false,
|
||||
Hidden: true,
|
||||
}
|
||||
)
|
||||
|
||||
// devModeFlags holds list of flags that are set when development mode is on.
|
||||
@@ -285,7 +278,6 @@ var BeaconChainFlags = combinedFlags([]cli.Flag{
|
||||
enableExperimentalAttestationPool,
|
||||
forceHeadFlag,
|
||||
blacklistRoots,
|
||||
lowValcountSweep,
|
||||
}, deprecatedBeaconFlags, deprecatedFlags, upcomingDeprecation)
|
||||
|
||||
func combinedFlags(flags ...[]cli.Flag) []cli.Flag {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
version: v1.7.0-alpha.0
|
||||
version: v1.7.0-alpha.1
|
||||
style: full
|
||||
|
||||
specrefs:
|
||||
@@ -117,6 +117,8 @@ exceptions:
|
||||
dataclasses:
|
||||
# Not implemented
|
||||
- BlobParameters#fulu
|
||||
- ExpectedWithdrawals#capella
|
||||
- ExpectedWithdrawals#electra
|
||||
- LatestMessage#phase0
|
||||
- LightClientStore#altair
|
||||
- OptimisticStore#bellatrix
|
||||
@@ -126,6 +128,7 @@ exceptions:
|
||||
- LightClientStore#capella
|
||||
|
||||
# Not implemented: gloas (future fork)
|
||||
- ExpectedWithdrawals#gloas
|
||||
- LatestMessage#gloas
|
||||
- Store#gloas
|
||||
|
||||
|
||||
@@ -34,6 +34,26 @@
|
||||
blobs: List[Blob, MAX_BLOB_COMMITMENTS_PER_BLOCK]
|
||||
</spec>
|
||||
|
||||
- name: ExpectedWithdrawals#capella
|
||||
sources: []
|
||||
spec: |
|
||||
<spec dataclass="ExpectedWithdrawals" fork="capella" hash="73becb4c">
|
||||
class ExpectedWithdrawals(object):
|
||||
withdrawals: Sequence[Withdrawal]
|
||||
processed_sweep_withdrawals_count: uint64
|
||||
</spec>
|
||||
|
||||
- name: ExpectedWithdrawals#electra
|
||||
sources: []
|
||||
spec: |
|
||||
<spec dataclass="ExpectedWithdrawals" fork="electra" hash="a3827f01">
|
||||
class ExpectedWithdrawals(object):
|
||||
withdrawals: Sequence[Withdrawal]
|
||||
# [New in Electra:EIP7251]
|
||||
processed_partial_withdrawals_count: uint64
|
||||
processed_sweep_withdrawals_count: uint64
|
||||
</spec>
|
||||
|
||||
- name: GetPayloadResponse#bellatrix
|
||||
sources:
|
||||
- file: consensus-types/blocks/get_payload.go
|
||||
|
||||
@@ -2366,8 +2366,8 @@
|
||||
- file: beacon-chain/state/state-native/getters_withdrawal.go
|
||||
search: func (b *BeaconState) ExpectedWithdrawals(
|
||||
spec: |
|
||||
<spec fn="get_expected_withdrawals" fork="capella" hash="d6a98c14">
|
||||
def get_expected_withdrawals(state: BeaconState) -> Tuple[Sequence[Withdrawal], uint64]:
|
||||
<spec fn="get_expected_withdrawals" fork="capella" hash="12088347">
|
||||
def get_expected_withdrawals(state: BeaconState) -> ExpectedWithdrawals:
|
||||
withdrawal_index = state.next_withdrawal_index
|
||||
withdrawals: List[Withdrawal] = []
|
||||
|
||||
@@ -2377,7 +2377,10 @@
|
||||
)
|
||||
withdrawals.extend(validators_sweep_withdrawals)
|
||||
|
||||
return withdrawals, processed_validators_sweep_count
|
||||
return ExpectedWithdrawals(
|
||||
withdrawals,
|
||||
processed_validators_sweep_count,
|
||||
)
|
||||
</spec>
|
||||
|
||||
- name: get_expected_withdrawals#electra
|
||||
@@ -2385,8 +2388,8 @@
|
||||
- file: beacon-chain/state/state-native/getters_withdrawal.go
|
||||
search: func (b *BeaconState) ExpectedWithdrawals(
|
||||
spec: |
|
||||
<spec fn="get_expected_withdrawals" fork="electra" hash="cfce862b">
|
||||
def get_expected_withdrawals(state: BeaconState) -> Tuple[Sequence[Withdrawal], uint64, uint64]:
|
||||
<spec fn="get_expected_withdrawals" fork="electra" hash="254541e3">
|
||||
def get_expected_withdrawals(state: BeaconState) -> ExpectedWithdrawals:
|
||||
withdrawal_index = state.next_withdrawal_index
|
||||
withdrawals: List[Withdrawal] = []
|
||||
|
||||
@@ -2403,8 +2406,12 @@
|
||||
)
|
||||
withdrawals.extend(validators_sweep_withdrawals)
|
||||
|
||||
# [Modified in Electra:EIP7251]
|
||||
return withdrawals, processed_partial_withdrawals_count, processed_validators_sweep_count
|
||||
return ExpectedWithdrawals(
|
||||
withdrawals,
|
||||
# [New in Electra:EIP7251]
|
||||
processed_partial_withdrawals_count,
|
||||
processed_validators_sweep_count,
|
||||
)
|
||||
</spec>
|
||||
|
||||
- name: get_filtered_block_tree
|
||||
@@ -4932,7 +4939,7 @@
|
||||
- name: prepare_execution_payload#capella
|
||||
sources: []
|
||||
spec: |
|
||||
<spec fn="prepare_execution_payload" fork="capella" hash="c258893e">
|
||||
<spec fn="prepare_execution_payload" fork="capella" hash="998e8b92">
|
||||
def prepare_execution_payload(
|
||||
state: BeaconState,
|
||||
safe_block_hash: Hash32,
|
||||
@@ -4945,15 +4952,12 @@
|
||||
parent_hash = state.latest_execution_payload_header.block_hash
|
||||
|
||||
# Set the forkchoice head and initiate the payload build process
|
||||
# [New in Capella]
|
||||
withdrawals, _ = get_expected_withdrawals(state)
|
||||
|
||||
payload_attributes = PayloadAttributes(
|
||||
timestamp=compute_time_at_slot(state, state.slot),
|
||||
prev_randao=get_randao_mix(state, get_current_epoch(state)),
|
||||
suggested_fee_recipient=suggested_fee_recipient,
|
||||
# [New in Capella]
|
||||
withdrawals=withdrawals,
|
||||
withdrawals=get_expected_withdrawals(state).withdrawals,
|
||||
)
|
||||
return execution_engine.notify_forkchoice_updated(
|
||||
head_block_hash=parent_hash,
|
||||
@@ -4966,7 +4970,7 @@
|
||||
- name: prepare_execution_payload#deneb
|
||||
sources: []
|
||||
spec: |
|
||||
<spec fn="prepare_execution_payload" fork="deneb" hash="59f61f3a">
|
||||
<spec fn="prepare_execution_payload" fork="deneb" hash="c617aa1b">
|
||||
def prepare_execution_payload(
|
||||
state: BeaconState,
|
||||
safe_block_hash: Hash32,
|
||||
@@ -4978,13 +4982,11 @@
|
||||
parent_hash = state.latest_execution_payload_header.block_hash
|
||||
|
||||
# Set the forkchoice head and initiate the payload build process
|
||||
withdrawals, _ = get_expected_withdrawals(state)
|
||||
|
||||
payload_attributes = PayloadAttributes(
|
||||
timestamp=compute_time_at_slot(state, state.slot),
|
||||
prev_randao=get_randao_mix(state, get_current_epoch(state)),
|
||||
suggested_fee_recipient=suggested_fee_recipient,
|
||||
withdrawals=withdrawals,
|
||||
withdrawals=get_expected_withdrawals(state).withdrawals,
|
||||
# [New in Deneb:EIP4788]
|
||||
parent_beacon_block_root=hash_tree_root(state.latest_block_header),
|
||||
)
|
||||
@@ -4999,7 +5001,7 @@
|
||||
- name: prepare_execution_payload#electra
|
||||
sources: []
|
||||
spec: |
|
||||
<spec fn="prepare_execution_payload" fork="electra" hash="5414b883">
|
||||
<spec fn="prepare_execution_payload" fork="electra" hash="c617aa1b">
|
||||
def prepare_execution_payload(
|
||||
state: BeaconState,
|
||||
safe_block_hash: Hash32,
|
||||
@@ -5010,15 +5012,13 @@
|
||||
# Verify consistency of the parent hash with respect to the previous execution payload header
|
||||
parent_hash = state.latest_execution_payload_header.block_hash
|
||||
|
||||
# [Modified in EIP7251]
|
||||
# Set the forkchoice head and initiate the payload build process
|
||||
withdrawals, _, _ = get_expected_withdrawals(state)
|
||||
|
||||
payload_attributes = PayloadAttributes(
|
||||
timestamp=compute_time_at_slot(state, state.slot),
|
||||
prev_randao=get_randao_mix(state, get_current_epoch(state)),
|
||||
suggested_fee_recipient=suggested_fee_recipient,
|
||||
withdrawals=withdrawals,
|
||||
withdrawals=get_expected_withdrawals(state).withdrawals,
|
||||
# [New in Deneb:EIP4788]
|
||||
parent_beacon_block_root=hash_tree_root(state.latest_block_header),
|
||||
)
|
||||
return execution_engine.notify_forkchoice_updated(
|
||||
@@ -7071,18 +7071,18 @@
|
||||
- file: beacon-chain/core/blocks/withdrawals.go
|
||||
search: func ProcessWithdrawals(
|
||||
spec: |
|
||||
<spec fn="process_withdrawals" fork="capella" hash="901f9fc4">
|
||||
<spec fn="process_withdrawals" fork="capella" hash="47327ef6">
|
||||
def process_withdrawals(state: BeaconState, payload: ExecutionPayload) -> None:
|
||||
# Get expected withdrawals
|
||||
withdrawals, processed_validators_sweep_count = get_expected_withdrawals(state)
|
||||
assert payload.withdrawals == withdrawals
|
||||
expected = get_expected_withdrawals(state)
|
||||
assert payload.withdrawals == expected.withdrawals
|
||||
|
||||
# Apply expected withdrawals
|
||||
apply_withdrawals(state, withdrawals)
|
||||
apply_withdrawals(state, expected.withdrawals)
|
||||
|
||||
# Update withdrawals fields in the state
|
||||
update_next_withdrawal_index(state, withdrawals)
|
||||
update_next_withdrawal_validator_index(state, processed_validators_sweep_count)
|
||||
update_next_withdrawal_index(state, expected.withdrawals)
|
||||
update_next_withdrawal_validator_index(state, expected.withdrawals)
|
||||
</spec>
|
||||
|
||||
- name: process_withdrawals#electra
|
||||
@@ -7090,23 +7090,20 @@
|
||||
- file: beacon-chain/core/blocks/withdrawals.go
|
||||
search: func ProcessWithdrawals(
|
||||
spec: |
|
||||
<spec fn="process_withdrawals" fork="electra" hash="67870972">
|
||||
<spec fn="process_withdrawals" fork="electra" hash="24a50daa">
|
||||
def process_withdrawals(state: BeaconState, payload: ExecutionPayload) -> None:
|
||||
# [Modified in Electra:EIP7251]
|
||||
# Get expected withdrawals
|
||||
withdrawals, processed_partial_withdrawals_count, processed_validators_sweep_count = (
|
||||
get_expected_withdrawals(state)
|
||||
)
|
||||
assert payload.withdrawals == withdrawals
|
||||
expected = get_expected_withdrawals(state)
|
||||
assert payload.withdrawals == expected.withdrawals
|
||||
|
||||
# Apply expected withdrawals
|
||||
apply_withdrawals(state, withdrawals)
|
||||
apply_withdrawals(state, expected.withdrawals)
|
||||
|
||||
# Update withdrawals fields in the state
|
||||
update_next_withdrawal_index(state, withdrawals)
|
||||
update_next_withdrawal_index(state, expected.withdrawals)
|
||||
# [New in Electra:EIP7251]
|
||||
update_pending_partial_withdrawals(state, processed_partial_withdrawals_count)
|
||||
update_next_withdrawal_validator_index(state, processed_validators_sweep_count)
|
||||
update_pending_partial_withdrawals(state, expected.processed_partial_withdrawals_count)
|
||||
update_next_withdrawal_validator_index(state, expected.withdrawals)
|
||||
</spec>
|
||||
|
||||
- name: queue_excess_active_balance
|
||||
|
||||
@@ -10,7 +10,6 @@ go_library(
|
||||
importpath = "github.com/OffchainLabs/prysm/v7/testing/spectest/utils",
|
||||
visibility = ["//testing/spectest:__subpackages__"],
|
||||
deps = [
|
||||
"//config/features:go_default_library",
|
||||
"//config/params:go_default_library",
|
||||
"//io/file:go_default_library",
|
||||
"//testing/require:go_default_library",
|
||||
|
||||
@@ -6,20 +6,12 @@ import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/OffchainLabs/prysm/v7/config/features"
|
||||
"github.com/OffchainLabs/prysm/v7/config/params"
|
||||
)
|
||||
|
||||
// SetConfig sets the global params for spec tests depending on the option chosen.
|
||||
// Provides reset function allowing to get back to the previous configuration at the end of a test.
|
||||
func SetConfig(t testing.TB, config string) error {
|
||||
params.SetupTestConfigCleanup(t)
|
||||
|
||||
resetFeatures := features.InitWithReset(&features.Flags{
|
||||
LowValcountSweep: true,
|
||||
})
|
||||
t.Cleanup(resetFeatures)
|
||||
|
||||
switch config {
|
||||
case "minimal":
|
||||
params.OverrideBeaconConfig(params.MinimalSpecConfig().Copy())
|
||||
|
||||
Reference in New Issue
Block a user