Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions api/server/structs/endpoints_debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,41 @@ type ForkChoiceNodeExtraData struct {
Target string `json:"target"`
}

type GetForkChoiceDumpV2Response struct {
JustifiedCheckpoint *Checkpoint `json:"justified_checkpoint"`
FinalizedCheckpoint *Checkpoint `json:"finalized_checkpoint"`
ForkChoiceNodes []*ForkChoiceNodeV2 `json:"fork_choice_nodes"`
ExtraData *ForkChoiceDumpExtraData `json:"extra_data"`
}

type ForkChoiceNodeV2 struct {
PayloadStatus string `json:"payload_status"`
Slot string `json:"slot"`
BlockRoot string `json:"block_root"`
ParentRoot string `json:"parent_root"`
Weight string `json:"weight"`
Validity string `json:"validity"`
ExecutionBlockHash string `json:"execution_block_hash"`
ExtraData *ForkChoiceNodeV2ExtraData `json:"extra_data"`
}

type ForkChoiceNodeV2ExtraData struct {
Balance string `json:"balance"`
ExecutionOptimistic bool `json:"execution_optimistic"`
TimeStamp string `json:"timestamp"`

Target string `json:"target,omitempty"`
JustifiedEpoch string `json:"justified_epoch,omitempty"`
FinalizedEpoch string `json:"finalized_epoch,omitempty"`
UnrealizedJustifiedEpoch string `json:"unrealized_justified_epoch,omitempty"`
UnrealizedFinalizedEpoch string `json:"unrealized_finalized_epoch,omitempty"`
PayloadAttesterCount string `json:"payload_attester_count,omitempty"`
PayloadAvailabilityYesCount string `json:"payload_availability_yes_count,omitempty"`
PayloadDataAvailabilityYesCount string `json:"payload_data_availability_yes_count,omitempty"`

GasLimit string `json:"gas_limit,omitempty"`
}

type GetDebugDataColumnSidecarsResponse struct {
Version string `json:"version"`
ExecutionOptimistic bool `json:"execution_optimistic"`
Expand Down
1 change: 1 addition & 0 deletions beacon-chain/blockchain/chain_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ type ForkchoiceFetcher interface {
InsertNode(context.Context, state.BeaconState, consensus_blocks.ROBlock) error
InsertPayload(interfaces.ROExecutionPayloadEnvelope) error
ForkChoiceDump(context.Context) (*forkchoice.Dump, error)
ForkChoiceDumpV2(context.Context) (*forkchoice.DumpV2, error)
NewSlot(context.Context, primitives.Slot) error
ProposerBoost() [32]byte
RecentBlockSlot(root [32]byte) (primitives.Slot, error)
Expand Down
7 changes: 7 additions & 0 deletions beacon-chain/blockchain/chain_info_forkchoice.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,13 @@ func (s *Service) ForkChoiceDump(ctx context.Context) (*forkchoice.Dump, error)
return s.cfg.ForkChoiceStore.ForkChoiceDump(ctx)
}

// ForkChoiceDumpV2 returns the corresponding value from forkchoice
func (s *Service) ForkChoiceDumpV2(ctx context.Context) (*forkchoice.DumpV2, error) {
s.cfg.ForkChoiceStore.RLock()
defer s.cfg.ForkChoiceStore.RUnlock()
return s.cfg.ForkChoiceStore.ForkChoiceDumpV2(ctx)
}

// NewSlot returns the corresponding value from forkchoice
func (s *Service) NewSlot(ctx context.Context, slot primitives.Slot) error {
s.cfg.ForkChoiceStore.Lock()
Expand Down
8 changes: 8 additions & 0 deletions beacon-chain/blockchain/testing/mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -848,6 +848,14 @@ func (s *ChainService) ForkChoiceDump(ctx context.Context) (*forkchoice2.Dump, e
return nil, nil
}

// ForkChoiceDumpV2 mocks the same method in the chain service
func (s *ChainService) ForkChoiceDumpV2(ctx context.Context) (*forkchoice2.DumpV2, error) {
if s.ForkChoiceStore != nil {
return s.ForkChoiceStore.ForkChoiceDumpV2(ctx)
}
return nil, nil
}

// NewSlot mocks the same method in the chain service
func (s *ChainService) NewSlot(ctx context.Context, slot primitives.Slot) error {
if s.ForkChoiceStore != nil {
Expand Down
42 changes: 42 additions & 0 deletions beacon-chain/forkchoice/doubly-linked-tree/forkchoice.go
Original file line number Diff line number Diff line change
Expand Up @@ -664,6 +664,48 @@ func (f *ForkChoice) ForkChoiceDump(ctx context.Context) (*forkchoice2.Dump, err
return resp, nil
}

// ForkChoiceDumpV2 returns a Gloas-aware dump of forkchoice, emitting one entry per (root, payload_status) tuple.
func (f *ForkChoice) ForkChoiceDumpV2(ctx context.Context) (*forkchoice2.DumpV2, error) {
jc := &ethpb.Checkpoint{
Epoch: f.store.justifiedCheckpoint.Epoch,
Root: f.store.justifiedCheckpoint.Root[:],
}
ujc := &ethpb.Checkpoint{
Epoch: f.store.unrealizedJustifiedCheckpoint.Epoch,
Root: f.store.unrealizedJustifiedCheckpoint.Root[:],
}
fc := &ethpb.Checkpoint{
Epoch: f.store.finalizedCheckpoint.Epoch,
Root: f.store.finalizedCheckpoint.Root[:],
}
ufc := &ethpb.Checkpoint{
Epoch: f.store.unrealizedFinalizedCheckpoint.Epoch,
Root: f.store.unrealizedFinalizedCheckpoint.Root[:],
}
nodes := make([]*forkchoice2.NodeV2, 0, f.NodeCount())
var err error
if f.store.treeRootNode != nil {
nodes, err = f.store.nodeTreeDumpV2(ctx, f.store.treeRootNode, nodes)
if err != nil {
return nil, err
}
}
var headRoot [32]byte
if f.store.headNode != nil {
headRoot = f.store.headNode.root
}
return &forkchoice2.DumpV2{
JustifiedCheckpoint: jc,
UnrealizedJustifiedCheckpoint: ujc,
FinalizedCheckpoint: fc,
UnrealizedFinalizedCheckpoint: ufc,
ProposerBoostRoot: f.store.proposerBoostRoot[:],
PreviousProposerBoostRoot: f.store.previousProposerBoostRoot[:],
HeadRoot: headRoot[:],
ForkChoiceNodes: nodes,
}, nil
}

// SetBalancesByRooter sets the balanceByRoot handler in forkchoice
func (f *ForkChoice) SetBalancesByRooter(handler forkchoice.BalancesByRooter) {
f.balancesByRoot = handler
Expand Down
117 changes: 94 additions & 23 deletions beacon-chain/forkchoice/doubly-linked-tree/gloas.go
Original file line number Diff line number Diff line change
Expand Up @@ -354,6 +354,96 @@ func (s *Store) nodeTreeDump(ctx context.Context, n *Node, nodes []*forkchoice2.
return nodes, nil
}

// nodeTreeDumpV2 appends to the given list one entry per (root, payload_status) tuple descending from n.
func (s *Store) nodeTreeDumpV2(ctx context.Context, n *Node, nodes []*forkchoice2.NodeV2) ([]*forkchoice2.NodeV2, error) {
if ctx.Err() != nil {
return nil, ctx.Err()
}
var parentRoot [32]byte
if n.parent != nil {
parentRoot = n.parent.node.root
}
target := [32]byte{}
if n.target != nil {
target = n.target.root
}
en := s.emptyNodeByRoot[n.root]
fn := s.fullNodeByRoot[n.root]
optimistic := false
if n.parent != nil {
optimistic = n.parent.optimistic
}
if fn != nil {
optimistic = fn.optimistic
}

pending := &forkchoice2.NodeV2{
PayloadStatus: forkchoice2.PayloadStatusPending,
BlockRoot: n.root[:],
ParentRoot: parentRoot[:],
Slot: n.slot,
Weight: n.weight,
Balance: n.balance,
ExecutionOptimistic: optimistic,
Timestamp: en.timestamp,
ExecutionBlockHash: n.blockHash[:],
Target: target[:],
JustifiedEpoch: n.justifiedEpoch,
FinalizedEpoch: n.finalizedEpoch,
UnrealizedJustifiedEpoch: n.unrealizedJustifiedEpoch,
UnrealizedFinalizedEpoch: n.unrealizedFinalizedEpoch,
PayloadAttesterCount: n.payloadAttesters.Count(),
PayloadAvailabilityYesCount: n.payloadAvailabilityVote.Count(),
PayloadDataAvailabilityYesCount: n.payloadDataAvailabilityVote.Count(),
}
if optimistic {
pending.Validity = forkchoice2.Optimistic
} else {
pending.Validity = forkchoice2.Valid
}
nodes = append(nodes, pending)

emptyEntry := &forkchoice2.NodeV2{
PayloadStatus: forkchoice2.PayloadStatusEmpty,
BlockRoot: n.root[:],
ParentRoot: parentRoot[:],
Slot: n.slot,
Weight: en.weight,
Balance: en.balance,
Validity: pending.Validity,
ExecutionOptimistic: en.optimistic,
Timestamp: en.timestamp,
ExecutionBlockHash: n.blockHash[:],
}
nodes = append(nodes, emptyEntry)

if fn != nil {
fullEntry := &forkchoice2.NodeV2{
PayloadStatus: forkchoice2.PayloadStatusFull,
BlockRoot: n.root[:],
ParentRoot: parentRoot[:],
Slot: n.slot,
Weight: fn.weight,
Balance: fn.balance,
Validity: pending.Validity,
ExecutionOptimistic: fn.optimistic,
Timestamp: fn.timestamp,
ExecutionBlockHash: n.blockHash[:],
GasLimit: fn.gasLimit,
}
nodes = append(nodes, fullEntry)
}

var err error
for _, child := range s.allConsensusChildren(n) {
nodes, err = s.nodeTreeDumpV2(ctx, child, nodes)
if err != nil {
return nil, err
}
}
return nodes, nil
}

// MarkFullNode creates a full payload node for an existing empty node at the
// given beacon block root. This is used during forkchoice tree reconstruction on
// startup to mark blocks whose execution payload was delivered. The caller must
Expand Down Expand Up @@ -419,35 +509,16 @@ func (f *ForkChoice) updateNewFullNodeWeight(fn *PayloadNode) {
fn.weight = fn.balance
}

// SetPTCVote sets the PTC vote bits on the consensus node identified by root.
// SetPTCVote records ptcIdx's vote on root, overwriting any previous vote from the same index.
func (f *ForkChoice) SetPTCVote(root [32]byte, ptcIdx uint64, payloadPresent, blobDataAvailable bool) {
n := f.store.emptyNodeByRoot[root]
if n == nil {
return
}
ptcVoteCount.Inc()
if payloadPresent {
n.node.setPayloadAvailabilityVote(ptcIdx)
}
if blobDataAvailable {
n.node.setPayloadDataAvailabilityVote(ptcIdx)
}
}

func (n *Node) setPayloadAvailabilityVote(idx uint64) {
n.payloadAvailabilityVote.SetBitAt(idx, true)
}

func (n *Node) setPayloadDataAvailabilityVote(idx uint64) {
n.payloadDataAvailabilityVote.SetBitAt(idx, true)
}

func (n *Node) payloadAvailabilityVoteCount() uint64 {
return n.payloadAvailabilityVote.Count()
}

func (n *Node) payloadDataAvailabilityVoteCount() uint64 {
return n.payloadDataAvailabilityVote.Count()
n.node.payloadAttesters.SetBitAt(ptcIdx, true)
n.node.payloadAvailabilityVote.SetBitAt(ptcIdx, payloadPresent)
n.node.payloadDataAvailabilityVote.SetBitAt(ptcIdx, blobDataAvailable)
}

// resolveVoteNode returns the node that should receive the balance of a vote. It returns always a PayloadNode, but the boolean indicates
Expand Down
Loading
Loading