mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2025-01-10 19:51:20 +00:00
0a87210514
* write locks * fix forkchoice tests * blockchain locks * lock on IsOptimistic * use forkchoice instead of chaininfo within savehead * Use forkchoice HasNode to check if a block is consistent with finality * interface fix * Use forkchoice HasNode to check if a block is consistent with finality * interface fix * fix tests * remove VerifyFinalizedBlkDescendant * don't write lock wrappers * fix validateBeaconBlock * Terence's review and more missing locks * add lock for InForkChoice * lock head on fillMissingBlockPayload * fix lock on IsOptimisticForRoot * fix lock in fillMissingBlockPayloadId * extra comments * lock proposerBoost on spectests * nishant's review --------- Co-authored-by: Nishant Das <nishdas93@gmail.com>
162 lines
5.5 KiB
Go
162 lines
5.5 KiB
Go
package forkchoice
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/prysmaticlabs/prysm/v3/beacon-chain/blockchain"
|
|
"github.com/prysmaticlabs/prysm/v3/beacon-chain/execution"
|
|
"github.com/prysmaticlabs/prysm/v3/beacon-chain/state"
|
|
"github.com/prysmaticlabs/prysm/v3/config/params"
|
|
"github.com/prysmaticlabs/prysm/v3/consensus-types/interfaces"
|
|
"github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
|
|
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
|
|
ethpb "github.com/prysmaticlabs/prysm/v3/proto/prysm/v1alpha1"
|
|
"github.com/prysmaticlabs/prysm/v3/testing/require"
|
|
)
|
|
|
|
type Builder struct {
|
|
service *blockchain.Service
|
|
lastTick int64
|
|
execMock *engineMock
|
|
}
|
|
|
|
func NewBuilder(t testing.TB, initialState state.BeaconState, initialBlock interfaces.ReadOnlySignedBeaconBlock) *Builder {
|
|
execMock := &engineMock{
|
|
powBlocks: make(map[[32]byte]*ethpb.PowBlock),
|
|
}
|
|
service := startChainService(t, initialState, initialBlock, execMock)
|
|
return &Builder{
|
|
service: service,
|
|
execMock: execMock,
|
|
}
|
|
}
|
|
|
|
// Tick resets the genesis time to now()-tick and adjusts the slot to the appropriate value.
|
|
func (bb *Builder) Tick(t testing.TB, tick int64) {
|
|
bb.service.SetGenesisTime(time.Unix(time.Now().Unix()-tick, 0))
|
|
lastSlot := uint64(bb.lastTick) / params.BeaconConfig().SecondsPerSlot
|
|
currentSlot := uint64(tick) / params.BeaconConfig().SecondsPerSlot
|
|
for lastSlot < currentSlot {
|
|
lastSlot++
|
|
bb.service.ForkChoicer().SetGenesisTime(uint64(time.Now().Unix() - int64(params.BeaconConfig().SecondsPerSlot*lastSlot)))
|
|
require.NoError(t, bb.service.ForkChoicer().NewSlot(context.TODO(), primitives.Slot(lastSlot)))
|
|
}
|
|
if tick > int64(params.BeaconConfig().SecondsPerSlot*lastSlot) {
|
|
bb.service.ForkChoicer().SetGenesisTime(uint64(time.Now().Unix() - tick))
|
|
}
|
|
bb.lastTick = tick
|
|
}
|
|
|
|
// SetPayloadStatus sets the payload status that the engine will return
|
|
func (bb *Builder) SetPayloadStatus(resp *MockEngineResp) error {
|
|
if resp == nil {
|
|
return errors.New("invalid nil payload status")
|
|
}
|
|
if resp.LatestValidHash == nil {
|
|
bb.execMock.latestValidHash = common.FromHex("0x0000000000000000000000000000000000000000000000000000000000000000")
|
|
} else {
|
|
bb.execMock.latestValidHash = common.FromHex(*resp.LatestValidHash)
|
|
}
|
|
if resp.Status == nil {
|
|
return errors.New("invalid nil status")
|
|
}
|
|
switch *resp.Status {
|
|
case "SYNCING":
|
|
bb.execMock.payloadStatus = execution.ErrAcceptedSyncingPayloadStatus
|
|
case "VALID":
|
|
bb.execMock.payloadStatus = nil
|
|
case "INVALID":
|
|
bb.execMock.payloadStatus = execution.ErrInvalidPayloadStatus
|
|
default:
|
|
return errors.New("unknown payload status")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// block returns the block root.
|
|
func (bb *Builder) block(t testing.TB, b interfaces.ReadOnlySignedBeaconBlock) [32]byte {
|
|
r, err := b.Block().HashTreeRoot()
|
|
require.NoError(t, err)
|
|
return r
|
|
}
|
|
|
|
// InvalidBlock receives the invalid block and notifies forkchoice.
|
|
func (bb *Builder) InvalidBlock(t testing.TB, b interfaces.ReadOnlySignedBeaconBlock) {
|
|
r := bb.block(t, b)
|
|
require.Equal(t, true, bb.service.ReceiveBlock(context.TODO(), b, r) != nil)
|
|
}
|
|
|
|
// ValidBlock receives the valid block and notifies forkchoice.
|
|
func (bb *Builder) ValidBlock(t testing.TB, b interfaces.ReadOnlySignedBeaconBlock) {
|
|
r := bb.block(t, b)
|
|
require.NoError(t, bb.service.ReceiveBlock(context.TODO(), b, r))
|
|
}
|
|
|
|
// PoWBlock receives the block and notifies a mocked execution engine.
|
|
func (bb *Builder) PoWBlock(pb *ethpb.PowBlock) {
|
|
bb.execMock.powBlocks[bytesutil.ToBytes32(pb.BlockHash)] = pb
|
|
}
|
|
|
|
// Attestation receives the attestation and updates forkchoice.
|
|
func (bb *Builder) Attestation(t testing.TB, a *ethpb.Attestation) {
|
|
require.NoError(t, bb.service.OnAttestation(context.TODO(), a))
|
|
}
|
|
|
|
// AttesterSlashing receives an attester slashing and feeds it to forkchoice.
|
|
func (bb *Builder) AttesterSlashing(s *ethpb.AttesterSlashing) {
|
|
slashings := []*ethpb.AttesterSlashing{s}
|
|
bb.service.InsertSlashingsToForkChoiceStore(context.TODO(), slashings)
|
|
}
|
|
|
|
// Check evaluates the fork choice results and compares them to the expected values.
|
|
func (bb *Builder) Check(t testing.TB, c *Check) {
|
|
if c == nil {
|
|
return
|
|
}
|
|
ctx := context.TODO()
|
|
require.NoError(t, bb.service.UpdateAndSaveHeadWithBalances(ctx))
|
|
if c.Head != nil {
|
|
r, err := bb.service.HeadRoot(ctx)
|
|
require.NoError(t, err)
|
|
require.DeepEqual(t, common.FromHex(c.Head.Root), r)
|
|
require.Equal(t, primitives.Slot(c.Head.Slot), bb.service.HeadSlot())
|
|
}
|
|
if c.JustifiedCheckPoint != nil {
|
|
cp := ðpb.Checkpoint{
|
|
Epoch: primitives.Epoch(c.JustifiedCheckPoint.Epoch),
|
|
Root: common.FromHex(c.JustifiedCheckPoint.Root),
|
|
}
|
|
got := bb.service.CurrentJustifiedCheckpt()
|
|
require.DeepEqual(t, cp, got)
|
|
}
|
|
if c.BestJustifiedCheckPoint != nil {
|
|
cp := ðpb.Checkpoint{
|
|
Epoch: primitives.Epoch(c.BestJustifiedCheckPoint.Epoch),
|
|
Root: common.FromHex(c.BestJustifiedCheckPoint.Root),
|
|
}
|
|
got := bb.service.BestJustifiedCheckpt()
|
|
require.DeepEqual(t, cp, got)
|
|
}
|
|
if c.FinalizedCheckPoint != nil {
|
|
cp := ðpb.Checkpoint{
|
|
Epoch: primitives.Epoch(c.FinalizedCheckPoint.Epoch),
|
|
Root: common.FromHex(c.FinalizedCheckPoint.Root),
|
|
}
|
|
got := bb.service.FinalizedCheckpt()
|
|
require.DeepSSZEqual(t, cp, got)
|
|
}
|
|
if c.ProposerBoostRoot != nil {
|
|
want := fmt.Sprintf("%#x", common.FromHex(*c.ProposerBoostRoot))
|
|
bb.service.ForkChoiceStore().RLock()
|
|
got := fmt.Sprintf("%#x", bb.service.ForkChoiceStore().ProposerBoost())
|
|
bb.service.ForkChoiceStore().RUnlock()
|
|
require.DeepEqual(t, want, got)
|
|
}
|
|
|
|
}
|