Report voted fractions for a given root (#11421)

* Report voted fractions for a given root

* unused parameters

* comment update

* change test

Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
This commit is contained in:
Potuz
2022-09-11 16:04:40 -03:00
committed by GitHub
parent 99fbf5d3d8
commit 6cf4f3c260
9 changed files with 82 additions and 9 deletions

View File

@@ -14,25 +14,27 @@ import (
var errNilState = errors.New("nil state")
// UnrealizedCheckpoints returns the justification and finalization checkpoints of the
// given state as if it was progressed with empty slots until the next epoch.
func UnrealizedCheckpoints(st state.BeaconState) (*ethpb.Checkpoint, *ethpb.Checkpoint, error) {
// given state as if it was progressed with empty slots until the next epoch. It
// also returns the total active balance during the epoch.
func UnrealizedCheckpoints(st state.BeaconState) (uint64, *ethpb.Checkpoint, *ethpb.Checkpoint, error) {
if st == nil || st.IsNil() {
return nil, nil, errNilState
return 0, nil, nil, errNilState
}
if slots.ToEpoch(st.Slot()) <= params.BeaconConfig().GenesisEpoch+1 {
jc := st.CurrentJustifiedCheckpoint()
fc := st.FinalizedCheckpoint()
return jc, fc, nil
return 0, jc, fc, nil
}
activeBalance, prevTarget, currentTarget, err := st.UnrealizedCheckpointBalances()
if err != nil {
return nil, nil, err
return 0, nil, nil, err
}
justification := processJustificationBits(st, activeBalance, prevTarget, currentTarget)
return computeCheckpoints(st, justification)
jc, fc, err := computeCheckpoints(st, justification)
return activeBalance, jc, fc, err
}
// ProcessJustificationAndFinalizationPreCompute processes justification and finalization during

View File

@@ -243,10 +243,12 @@ func TestUnrealizedCheckpoints(t *testing.T) {
_, _, err = altair.InitializePrecomputeValidators(context.Background(), state)
require.NoError(t, err)
jc, fc, err := precompute.UnrealizedCheckpoints(state)
ab, jc, fc, err := precompute.UnrealizedCheckpoints(state)
require.NoError(t, err)
require.DeepEqual(t, test.expectedJustified, jc.Epoch)
require.DeepEqual(t, test.expectedFinalized, fc.Epoch)
eb := params.BeaconConfig().MinGenesisActiveValidatorCount * params.BeaconConfig().MaxEffectiveBalance
require.Equal(t, eb, ab)
})
}
}

View File

@@ -161,3 +161,21 @@ func (n *Node) nodeTreeDump(ctx context.Context, nodes []*v1.ForkChoiceNode) ([]
}
return nodes, nil
}
// VotedFraction returns the fraction of the committee that voted directly for
// this node.
func (f *ForkChoice) VotedFraction(root [32]byte) (uint64, error) {
f.store.nodesLock.RLock()
defer f.store.nodesLock.RUnlock()
// Avoid division by zero before a block is inserted.
if f.store.committeeBalance == 0 {
return 0, nil
}
node, ok := f.store.nodeByRoot[root]
if !ok || node == nil {
return 0, ErrNilNode
}
return node.balance * 100 / f.store.committeeBalance, nil
}

View File

@@ -261,3 +261,45 @@ func TestNode_SetFullyValidated(t *testing.T) {
require.Equal(t, storeNodes[i].timestamp, respNode.Timestamp)
}
}
func TestStore_VotedFraction(t *testing.T) {
f := setup(1, 1)
ctx := context.Background()
state, blkRoot, err := prepareForkchoiceState(ctx, 100, [32]byte{'a'}, params.BeaconConfig().ZeroHash, [32]byte{'A'}, 1, 1)
require.NoError(t, err)
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
state, blkRoot, err = prepareForkchoiceState(ctx, 101, [32]byte{'b'}, [32]byte{'a'}, [32]byte{'B'}, 1, 1)
require.NoError(t, err)
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
// No division by zero error
vote, err := f.VotedFraction([32]byte{'b'})
require.NoError(t, err)
require.Equal(t, uint64(0), vote)
// Zero balance in the node
f.store.committeeBalance = 100 * params.BeaconConfig().MaxEffectiveBalance
vote, err = f.VotedFraction([32]byte{'b'})
require.NoError(t, err)
require.Equal(t, uint64(0), vote)
// Attestations are not counted until we process Head
balances := []uint64{params.BeaconConfig().MaxEffectiveBalance, params.BeaconConfig().MaxEffectiveBalance}
_, err = f.Head(context.Background(), balances)
require.NoError(t, err)
f.ProcessAttestation(context.Background(), []uint64{0, 1}, [32]byte{'b'}, 2)
vote, err = f.VotedFraction([32]byte{'b'})
require.NoError(t, err)
require.Equal(t, uint64(0), vote)
// After we call head the voted fraction is obtained.
_, err = f.Head(context.Background(), balances)
require.NoError(t, err)
vote, err = f.VotedFraction([32]byte{'b'})
require.NoError(t, err)
require.Equal(t, uint64(2), vote)
// Check for non-existent root
_, err = f.VotedFraction([32]byte{'c'})
require.ErrorIs(t, err, ErrNilNode)
}

View File

@@ -40,6 +40,7 @@ type Store struct {
highestReceivedSlot types.Slot // The highest received slot in the chain.
receivedBlocksLastEpoch [fieldparams.SlotsPerEpoch]types.Slot // Using `highestReceivedSlot`. The slot of blocks received in the last epoch.
allTipsAreInvalid bool // tracks if all tips are not viable for head
committeeBalance uint64 // tracks the total active validator balance divided by slots per epoch. Requires a lock on nodes to read/write
}
// Node defines the individual block which includes its block parent, ancestor and how much weight accounted for it.

View File

@@ -90,12 +90,14 @@ func (s *Store) pullTips(state state.BeaconState, node *Node, jc, fc *ethpb.Chec
return jc, fc
}
uj, uf, err := precompute.UnrealizedCheckpoints(state)
ab, uj, uf, err := precompute.UnrealizedCheckpoints(state)
if err != nil {
log.WithError(err).Debug("could not compute unrealized checkpoints")
uj, uf = jc, fc
}
s.committeeBalance = ab / uint64(params.BeaconConfig().SlotsPerEpoch)
// Update store's unrealized checkpoints.
if uj.Epoch > s.unrealizedJustifiedCheckpoint.Epoch {
s.unrealizedJustifiedCheckpoint = &forkchoicetypes.Checkpoint{

View File

@@ -64,6 +64,7 @@ type Getter interface {
HighestReceivedBlockSlot() types.Slot
ReceivedBlocksLastEpoch() (uint64, error)
ForkChoiceDump(context.Context) (*v1.ForkChoiceResponse, error)
VotedFraction(root [32]byte) (uint64, error)
}
// Setter allows to set forkchoice information

View File

@@ -43,3 +43,8 @@ func (n *Node) BestChild() uint64 {
func (n *Node) BestDescendant() uint64 {
return n.bestDescendant
}
// VotedFraction is not implemented for protoarray
func (*ForkChoice) VotedFraction(_ [32]byte) (uint64, error) {
return 0, nil
}

View File

@@ -100,7 +100,7 @@ func (s *Store) pullTips(state state.BeaconState, node *Node, jc, fc *ethpb.Chec
return jc, fc
}
uj, uf, err := precompute.UnrealizedCheckpoints(state)
_, uj, uf, err := precompute.UnrealizedCheckpoints(state)
if err != nil {
log.WithError(err).Debug("could not compute unrealized checkpoints")
uj, uf = jc, fc