mirror of
https://gitlab.com/pulsechaincom/erigon-pulse.git
synced 2025-01-05 10:32:19 +00:00
175 lines
5.5 KiB
Go
175 lines
5.5 KiB
Go
package forkchoice
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
|
|
"github.com/ledgerwatch/erigon/cl/cltypes/solid"
|
|
"github.com/ledgerwatch/erigon/cl/freezer"
|
|
state2 "github.com/ledgerwatch/erigon/cl/phase1/core/state"
|
|
"github.com/ledgerwatch/erigon/cl/phase1/execution_client"
|
|
"github.com/ledgerwatch/erigon/cl/phase1/forkchoice/fork_graph"
|
|
"github.com/ledgerwatch/erigon/cl/pool"
|
|
|
|
lru "github.com/hashicorp/golang-lru/v2"
|
|
libcommon "github.com/ledgerwatch/erigon-lib/common"
|
|
"github.com/ledgerwatch/erigon-lib/common/length"
|
|
)
|
|
|
|
type checkpointComparable string
|
|
|
|
const (
|
|
checkpointsPerCache = 1024
|
|
allowedCachedStates = 8
|
|
)
|
|
|
|
type ForkChoiceStore struct {
|
|
ctx context.Context
|
|
time uint64
|
|
highestSeen uint64
|
|
justifiedCheckpoint solid.Checkpoint
|
|
finalizedCheckpoint solid.Checkpoint
|
|
unrealizedJustifiedCheckpoint solid.Checkpoint
|
|
unrealizedFinalizedCheckpoint solid.Checkpoint
|
|
proposerBoostRoot libcommon.Hash
|
|
headHash libcommon.Hash
|
|
headSlot uint64
|
|
// Use go map because this is actually an unordered set
|
|
equivocatingIndicies map[uint64]struct{}
|
|
forkGraph *fork_graph.ForkGraph
|
|
// I use the cache due to the convenient auto-cleanup feauture.
|
|
checkpointStates map[checkpointComparable]*checkpointState // We keep ssz snappy of it as the full beacon state is full of rendundant data.
|
|
latestMessages map[uint64]*LatestMessage
|
|
anchorPublicKeys []byte
|
|
// We keep track of them so that we can forkchoice with EL.
|
|
eth2Roots *lru.Cache[libcommon.Hash, libcommon.Hash] // ETH2 root -> ETH1 hash
|
|
mu sync.Mutex
|
|
// EL
|
|
engine execution_client.ExecutionEngine
|
|
// freezer
|
|
recorder freezer.Freezer
|
|
// operations pool
|
|
operationsPool pool.OperationsPool
|
|
}
|
|
|
|
type LatestMessage struct {
|
|
Epoch uint64
|
|
Root libcommon.Hash
|
|
}
|
|
|
|
// NewForkChoiceStore initialize a new store from the given anchor state, either genesis or checkpoint sync state.
|
|
func NewForkChoiceStore(ctx context.Context, anchorState *state2.CachingBeaconState, engine execution_client.ExecutionEngine, recorder freezer.Freezer, operationsPool pool.OperationsPool, enabledPruning bool) (*ForkChoiceStore, error) {
|
|
anchorRoot, err := anchorState.BlockRoot()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
anchorCheckpoint := solid.NewCheckpointFromParameters(
|
|
anchorRoot,
|
|
state2.Epoch(anchorState.BeaconState),
|
|
)
|
|
|
|
eth2Roots, err := lru.New[libcommon.Hash, libcommon.Hash](checkpointsPerCache)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
anchorPublicKeys := make([]byte, anchorState.ValidatorLength()*length.Bytes48)
|
|
for idx := 0; idx < anchorState.ValidatorLength(); idx++ {
|
|
pk, err := anchorState.ValidatorPublicKey(idx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
copy(anchorPublicKeys[idx*length.Bytes48:], pk[:])
|
|
}
|
|
|
|
return &ForkChoiceStore{
|
|
ctx: ctx,
|
|
highestSeen: anchorState.Slot(),
|
|
time: anchorState.GenesisTime() + anchorState.BeaconConfig().SecondsPerSlot*anchorState.Slot(),
|
|
justifiedCheckpoint: anchorCheckpoint.Copy(),
|
|
finalizedCheckpoint: anchorCheckpoint.Copy(),
|
|
unrealizedJustifiedCheckpoint: anchorCheckpoint.Copy(),
|
|
unrealizedFinalizedCheckpoint: anchorCheckpoint.Copy(),
|
|
forkGraph: fork_graph.New(anchorState, enabledPruning),
|
|
equivocatingIndicies: map[uint64]struct{}{},
|
|
latestMessages: map[uint64]*LatestMessage{},
|
|
checkpointStates: make(map[checkpointComparable]*checkpointState),
|
|
eth2Roots: eth2Roots,
|
|
engine: engine,
|
|
recorder: recorder,
|
|
operationsPool: operationsPool,
|
|
anchorPublicKeys: anchorPublicKeys,
|
|
}, nil
|
|
}
|
|
|
|
// Highest seen returns highest seen slot
|
|
func (f *ForkChoiceStore) HighestSeen() uint64 {
|
|
f.mu.Lock()
|
|
defer f.mu.Unlock()
|
|
return f.highestSeen
|
|
}
|
|
|
|
// AdvanceHighestSeen advances the highest seen block by n and returns the new slot after the change
|
|
func (f *ForkChoiceStore) AdvanceHighestSeen(n uint64) uint64 {
|
|
f.mu.Lock()
|
|
defer f.mu.Unlock()
|
|
f.highestSeen += n
|
|
return f.highestSeen
|
|
}
|
|
|
|
// Time returns current time
|
|
func (f *ForkChoiceStore) Time() uint64 {
|
|
f.mu.Lock()
|
|
defer f.mu.Unlock()
|
|
return f.time
|
|
}
|
|
|
|
// ProposerBoostRoot returns proposer boost root
|
|
func (f *ForkChoiceStore) ProposerBoostRoot() libcommon.Hash {
|
|
f.mu.Lock()
|
|
defer f.mu.Unlock()
|
|
return f.proposerBoostRoot
|
|
}
|
|
|
|
// JustifiedCheckpoint returns justified checkpoint
|
|
func (f *ForkChoiceStore) JustifiedCheckpoint() solid.Checkpoint {
|
|
f.mu.Lock()
|
|
defer f.mu.Unlock()
|
|
return f.justifiedCheckpoint
|
|
}
|
|
|
|
// FinalizedCheckpoint returns justified checkpoint
|
|
func (f *ForkChoiceStore) FinalizedCheckpoint() solid.Checkpoint {
|
|
f.mu.Lock()
|
|
defer f.mu.Unlock()
|
|
return f.finalizedCheckpoint
|
|
}
|
|
|
|
// FinalizedCheckpoint returns justified checkpoint
|
|
func (f *ForkChoiceStore) FinalizedSlot() uint64 {
|
|
f.mu.Lock()
|
|
defer f.mu.Unlock()
|
|
return f.computeStartSlotAtEpoch(f.finalizedCheckpoint.Epoch())
|
|
}
|
|
|
|
// FinalizedCheckpoint returns justified checkpoint
|
|
func (f *ForkChoiceStore) Engine() execution_client.ExecutionEngine {
|
|
f.mu.Lock()
|
|
defer f.mu.Unlock()
|
|
return f.engine
|
|
}
|
|
|
|
// FinalizedCheckpoint returns justified checkpoint
|
|
func (f *ForkChoiceStore) GetEth1Hash(eth2Root libcommon.Hash) libcommon.Hash {
|
|
f.mu.Lock()
|
|
defer f.mu.Unlock()
|
|
ret, _ := f.eth2Roots.Get(eth2Root)
|
|
return ret
|
|
}
|
|
|
|
// FinalizedCheckpoint returns justified checkpoint
|
|
func (f *ForkChoiceStore) AnchorSlot() uint64 {
|
|
f.mu.Lock()
|
|
defer f.mu.Unlock()
|
|
return f.forkGraph.AnchorSlot()
|
|
}
|