mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-13 15:28:07 -05:00
Compare commits
2 Commits
micro-opt-
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2e43d50364 | ||
|
|
71e7b526d2 |
@@ -72,7 +72,7 @@ Do NOT add new `go_repository` to the WORKSPACE file. All dependencies should li
|
||||
|
||||
To enable conditional compilation and custom configuration for tests (where compiled code has more
|
||||
debug info, while not being completely optimized), we rely on Go's build tags/constraints mechanism
|
||||
(see official docs on [build constraints](https://golang.org/pkg/go/build/#hdr-Build_Constraints)).
|
||||
(see official docs on [build constraints](https://pkg.go.dev/go/build#hdr-Build_Constraints)).
|
||||
Therefore, whenever using `go test`, do not forget to pass in extra build tag, eg:
|
||||
|
||||
```bash
|
||||
|
||||
@@ -9,7 +9,7 @@ This README details how to setup Prysm for interop testing for usage with other
|
||||
|
||||
## Installation & Setup
|
||||
|
||||
1. Install [Bazel](https://docs.bazel.build/versions/master/install.html) **(Recommended)**
|
||||
1. Install [Bazel](https://bazel.build/install) **(Recommended)**
|
||||
2. `git clone https://github.com/OffchainLabs/prysm && cd prysm`
|
||||
3. `bazel build //cmd/...`
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
## 📖 Overview
|
||||
|
||||
This is the core repository for Prysm, a [Golang](https://golang.org/) implementation of the [Ethereum Consensus](https://ethereum.org/en/developers/docs/consensus-mechanisms/#proof-of-stake) [specification](https://github.com/ethereum/consensus-specs), developed by [Offchain Labs](https://www.offchainlabs.com).
|
||||
This is the core repository for Prysm, a [Golang](https://go.dev/) implementation of the [Ethereum Consensus](https://ethereum.org/en/developers/docs/consensus-mechanisms/#proof-of-stake) [specification](https://github.com/ethereum/consensus-specs), developed by [Offchain Labs](https://www.offchainlabs.com).
|
||||
|
||||
See the [Changelog](https://github.com/OffchainLabs/prysm/releases) for details of the latest releases and upcoming breaking changes.
|
||||
|
||||
@@ -23,7 +23,7 @@ See the [Changelog](https://github.com/OffchainLabs/prysm/releases) for details
|
||||
|
||||
## 🚀 Getting Started
|
||||
|
||||
A detailed set of installation and usage instructions as well as breakdowns of each individual component are available in the **[official documentation portal](https://docs.prylabs.network)**.
|
||||
A detailed set of installation and usage instructions as well as breakdowns of each individual component are available in the **[official documentation portal](https://prysm.offchainlabs.com/docs/)**.
|
||||
|
||||
💬 **Need help?** Join our **[Discord Community](https://discord.gg/prysm)** for support.
|
||||
|
||||
@@ -51,7 +51,7 @@ Prysm maintains two permanent branches:
|
||||
|
||||
### 🛠 Contribution Guide
|
||||
|
||||
Want to get involved? Check out our **[Contribution Guide](https://docs.prylabs.network/docs/contribute/contribution-guidelines/)** to learn more!
|
||||
Want to get involved? Check out our **[Contribution Guide](https://prysm.offchainlabs.com/docs/contribute/contribution-guidelines/)** to learn more!
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
3
changelog/ninastef-fixes-broken-links.md
Normal file
3
changelog/ninastef-fixes-broken-links.md
Normal file
@@ -0,0 +1,3 @@
|
||||
### Fixed
|
||||
|
||||
- fixed broken and old links to actual
|
||||
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.
|
||||
Reference in New Issue
Block a user