mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 21:38:05 -05:00
Aditya's PR 18 (#11945)
* Aditya's PR 18 This PR implements PR18. There is still a missing piece which is consistency of head with finalized checkpoint. I will think on ways to enforce this. * prune finalized incompatible * don't check finalization on viable for head * unit tests fixes * gazelle * remove finalized epoch from viableForHead * remove finalized epoch from leadsToViableHead * use non-slashed indices * function rename * lint fixes * lint fixes * lint fixes
This commit is contained in:
@@ -39,7 +39,7 @@ type mockBalanceByRooter struct {
|
||||
|
||||
var _ balanceByRooter = &mockBalanceByRooter{}
|
||||
|
||||
func (m mockBalanceByRooter) BalancesByRoot(_ context.Context, _ [32]byte) ([]uint64, error) {
|
||||
func (m mockBalanceByRooter) ActiveNonSlashedBalancesByRoot(_ context.Context, _ [32]byte) ([]uint64, error) {
|
||||
st := m.state
|
||||
if st == nil || st.IsNil() {
|
||||
return nil, errors.New("nil state")
|
||||
|
||||
@@ -16,7 +16,7 @@ type stateBalanceCache struct {
|
||||
}
|
||||
|
||||
type balanceByRooter interface {
|
||||
BalancesByRoot(context.Context, [32]byte) ([]uint64, error)
|
||||
ActiveNonSlashedBalancesByRoot(context.Context, [32]byte) ([]uint64, error)
|
||||
}
|
||||
|
||||
// newStateBalanceCache exists to remind us that stateBalanceCache needs a state gen
|
||||
@@ -36,7 +36,7 @@ func newStateBalanceCache(sg *stategen.State) (*stateBalanceCache, error) {
|
||||
func (c *stateBalanceCache) update(ctx context.Context, justifiedRoot [32]byte) ([]uint64, error) {
|
||||
stateBalanceCacheMiss.Inc()
|
||||
|
||||
justifiedBalances, err := c.stateGen.BalancesByRoot(ctx, justifiedRoot)
|
||||
justifiedBalances, err := c.stateGen.ActiveNonSlashedBalancesByRoot(ctx, justifiedRoot)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not get balances")
|
||||
}
|
||||
|
||||
@@ -43,6 +43,12 @@ func IsActiveValidatorUsingTrie(validator state.ReadOnlyValidator, epoch primiti
|
||||
return checkValidatorActiveStatus(validator.ActivationEpoch(), validator.ExitEpoch(), epoch)
|
||||
}
|
||||
|
||||
// IsActiveNonSlashedValidatorUsingTrie checks if a read only validator is active and not slashed
|
||||
func IsActiveNonSlashedValidatorUsingTrie(validator state.ReadOnlyValidator, epoch primitives.Epoch) bool {
|
||||
active := checkValidatorActiveStatus(validator.ActivationEpoch(), validator.ExitEpoch(), epoch)
|
||||
return active && !validator.Slashed()
|
||||
}
|
||||
|
||||
func checkValidatorActiveStatus(activationEpoch, exitEpoch, epoch primitives.Epoch) bool {
|
||||
return activationEpoch <= epoch && epoch < exitEpoch
|
||||
}
|
||||
|
||||
@@ -56,6 +56,34 @@ func TestIsActiveValidatorUsingTrie_OK(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsActiveNonSlashedValidatorUsingTrie_OK(t *testing.T) {
|
||||
tests := []struct {
|
||||
a primitives.Epoch
|
||||
s bool
|
||||
b bool
|
||||
}{
|
||||
{a: 0, s: false, b: false},
|
||||
{a: 10, s: false, b: true},
|
||||
{a: 100, s: false, b: false},
|
||||
{a: 1000, s: false, b: false},
|
||||
{a: 64, s: false, b: true},
|
||||
{a: 0, s: true, b: false},
|
||||
{a: 10, s: true, b: false},
|
||||
{a: 100, s: true, b: false},
|
||||
{a: 1000, s: true, b: false},
|
||||
{a: 64, s: true, b: false},
|
||||
}
|
||||
for _, test := range tests {
|
||||
val := ðpb.Validator{ActivationEpoch: 10, ExitEpoch: 100}
|
||||
val.Slashed = test.s
|
||||
beaconState, err := state_native.InitializeFromProtoPhase0(ðpb.BeaconState{Validators: []*ethpb.Validator{val}})
|
||||
require.NoError(t, err)
|
||||
readOnlyVal, err := beaconState.ValidatorAtIndexReadOnly(0)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, test.b, IsActiveNonSlashedValidatorUsingTrie(readOnlyVal, test.a), "IsActiveNonSlashedValidatorUsingTrie(%d)", test.a)
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsSlashableValidator_OK(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
|
||||
@@ -168,37 +168,42 @@ func (f *ForkChoice) updateCheckpoints(ctx context.Context, jc, fc *ethpb.Checkp
|
||||
f.store.bestJustifiedCheckpoint = &forkchoicetypes.Checkpoint{Epoch: jc.Epoch,
|
||||
Root: bytesutil.ToBytes32(jc.Root)}
|
||||
}
|
||||
currentSlot := slots.CurrentSlot(f.store.genesisTime)
|
||||
if slots.SinceEpochStarts(currentSlot) < params.BeaconConfig().SafeSlotsToUpdateJustified {
|
||||
f.store.prevJustifiedCheckpoint = f.store.justifiedCheckpoint
|
||||
f.store.justifiedCheckpoint = &forkchoicetypes.Checkpoint{Epoch: jc.Epoch,
|
||||
Root: bytesutil.ToBytes32(jc.Root)}
|
||||
} else {
|
||||
currentJcp := f.store.justifiedCheckpoint
|
||||
currentRoot := currentJcp.Root
|
||||
if currentRoot == params.BeaconConfig().ZeroHash {
|
||||
currentRoot = f.store.originRoot
|
||||
}
|
||||
jSlot, err := slots.EpochStart(currentJcp.Epoch)
|
||||
if err != nil {
|
||||
f.store.checkpointsLock.Unlock()
|
||||
return err
|
||||
}
|
||||
jcRoot := bytesutil.ToBytes32(jc.Root)
|
||||
// Releasing here the checkpoints lock because
|
||||
// AncestorRoot acquires a lock on nodes and that can
|
||||
// cause a double lock.
|
||||
f.store.checkpointsLock.Unlock()
|
||||
root, err := f.AncestorRoot(ctx, jcRoot, jSlot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.store.checkpointsLock.Lock()
|
||||
if root == currentRoot {
|
||||
if !features.Get().EnableDefensivePull {
|
||||
currentSlot := slots.CurrentSlot(f.store.genesisTime)
|
||||
if slots.SinceEpochStarts(currentSlot) < params.BeaconConfig().SafeSlotsToUpdateJustified {
|
||||
f.store.prevJustifiedCheckpoint = f.store.justifiedCheckpoint
|
||||
f.store.justifiedCheckpoint = &forkchoicetypes.Checkpoint{Epoch: jc.Epoch,
|
||||
Root: jcRoot}
|
||||
Root: bytesutil.ToBytes32(jc.Root)}
|
||||
} else {
|
||||
currentJcp := f.store.justifiedCheckpoint
|
||||
currentRoot := currentJcp.Root
|
||||
if currentRoot == params.BeaconConfig().ZeroHash {
|
||||
currentRoot = f.store.originRoot
|
||||
}
|
||||
jSlot, err := slots.EpochStart(currentJcp.Epoch)
|
||||
if err != nil {
|
||||
f.store.checkpointsLock.Unlock()
|
||||
return err
|
||||
}
|
||||
jcRoot := bytesutil.ToBytes32(jc.Root)
|
||||
// Releasing here the checkpoints lock because
|
||||
// AncestorRoot acquires a lock on nodes and that can
|
||||
// cause a double lock.
|
||||
f.store.checkpointsLock.Unlock()
|
||||
root, err := f.AncestorRoot(ctx, jcRoot, jSlot)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.store.checkpointsLock.Lock()
|
||||
if root == currentRoot {
|
||||
f.store.prevJustifiedCheckpoint = f.store.justifiedCheckpoint
|
||||
f.store.justifiedCheckpoint = &forkchoicetypes.Checkpoint{Epoch: jc.Epoch,
|
||||
Root: jcRoot}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
f.store.prevJustifiedCheckpoint = f.store.justifiedCheckpoint
|
||||
f.store.justifiedCheckpoint = &forkchoicetypes.Checkpoint{Epoch: jc.Epoch, Root: bytesutil.ToBytes32(jc.Root)}
|
||||
}
|
||||
}
|
||||
// Update finalization
|
||||
@@ -208,8 +213,10 @@ func (f *ForkChoice) updateCheckpoints(ctx context.Context, jc, fc *ethpb.Checkp
|
||||
}
|
||||
f.store.finalizedCheckpoint = &forkchoicetypes.Checkpoint{Epoch: fc.Epoch,
|
||||
Root: bytesutil.ToBytes32(fc.Root)}
|
||||
f.store.justifiedCheckpoint = &forkchoicetypes.Checkpoint{Epoch: jc.Epoch,
|
||||
Root: bytesutil.ToBytes32(jc.Root)}
|
||||
if !features.Get().EnableDefensivePull {
|
||||
f.store.justifiedCheckpoint = &forkchoicetypes.Checkpoint{Epoch: jc.Epoch,
|
||||
Root: bytesutil.ToBytes32(jc.Root)}
|
||||
}
|
||||
f.store.checkpointsLock.Unlock()
|
||||
return f.store.prune(ctx)
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ func (n *Node) updateBestDescendant(ctx context.Context, justifiedEpoch, finaliz
|
||||
if err := child.updateBestDescendant(ctx, justifiedEpoch, finalizedEpoch, currentEpoch); err != nil {
|
||||
return err
|
||||
}
|
||||
childLeadsToViableHead := child.leadsToViableHead(justifiedEpoch, finalizedEpoch, currentEpoch)
|
||||
childLeadsToViableHead := child.leadsToViableHead(justifiedEpoch, currentEpoch)
|
||||
if childLeadsToViableHead && !hasViableDescendant {
|
||||
// The child leads to a viable head, but the current
|
||||
// parent's best child doesn't.
|
||||
@@ -89,25 +89,21 @@ func (n *Node) updateBestDescendant(ctx context.Context, justifiedEpoch, finaliz
|
||||
// viableForHead returns true if the node is viable to head.
|
||||
// Any node with different finalized or justified epoch than
|
||||
// the ones in fork choice store should not be viable to head.
|
||||
func (n *Node) viableForHead(justifiedEpoch, finalizedEpoch, currentEpoch primitives.Epoch) bool {
|
||||
func (n *Node) viableForHead(justifiedEpoch, currentEpoch primitives.Epoch) bool {
|
||||
justified := justifiedEpoch == n.justifiedEpoch || justifiedEpoch == 0
|
||||
finalized := finalizedEpoch == n.finalizedEpoch || finalizedEpoch == 0
|
||||
if features.Get().EnableDefensivePull && !justified && justifiedEpoch+1 == currentEpoch {
|
||||
if n.unrealizedJustifiedEpoch+1 >= currentEpoch {
|
||||
if n.unrealizedJustifiedEpoch+1 >= currentEpoch && n.justifiedEpoch+2 >= currentEpoch {
|
||||
justified = true
|
||||
}
|
||||
if n.unrealizedFinalizedEpoch >= finalizedEpoch {
|
||||
finalized = true
|
||||
}
|
||||
}
|
||||
return justified && finalized
|
||||
return justified
|
||||
}
|
||||
|
||||
func (n *Node) leadsToViableHead(justifiedEpoch, finalizedEpoch, currentEpoch primitives.Epoch) bool {
|
||||
func (n *Node) leadsToViableHead(justifiedEpoch, currentEpoch primitives.Epoch) bool {
|
||||
if n.bestDescendant == nil {
|
||||
return n.viableForHead(justifiedEpoch, finalizedEpoch, currentEpoch)
|
||||
return n.viableForHead(justifiedEpoch, currentEpoch)
|
||||
}
|
||||
return n.bestDescendant.viableForHead(justifiedEpoch, finalizedEpoch, currentEpoch)
|
||||
return n.bestDescendant.viableForHead(justifiedEpoch, currentEpoch)
|
||||
}
|
||||
|
||||
// setNodeAndParentValidated sets the current node and all the ancestors as validated (i.e. non-optimistic).
|
||||
|
||||
@@ -144,18 +144,16 @@ func TestNode_ViableForHead(t *testing.T) {
|
||||
tests := []struct {
|
||||
n *Node
|
||||
justifiedEpoch primitives.Epoch
|
||||
finalizedEpoch primitives.Epoch
|
||||
want bool
|
||||
}{
|
||||
{&Node{}, 0, 0, true},
|
||||
{&Node{}, 1, 0, false},
|
||||
{&Node{}, 0, 1, false},
|
||||
{&Node{finalizedEpoch: 1, justifiedEpoch: 1}, 1, 1, true},
|
||||
{&Node{finalizedEpoch: 1, justifiedEpoch: 1}, 2, 2, false},
|
||||
{&Node{finalizedEpoch: 3, justifiedEpoch: 4}, 4, 3, true},
|
||||
{&Node{}, 0, true},
|
||||
{&Node{}, 1, false},
|
||||
{&Node{finalizedEpoch: 1, justifiedEpoch: 1}, 1, true},
|
||||
{&Node{finalizedEpoch: 1, justifiedEpoch: 1}, 2, false},
|
||||
{&Node{finalizedEpoch: 3, justifiedEpoch: 4}, 4, true},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
got := tc.n.viableForHead(tc.justifiedEpoch, tc.finalizedEpoch, 5)
|
||||
got := tc.n.viableForHead(tc.justifiedEpoch, 5)
|
||||
assert.Equal(t, tc.want, got)
|
||||
}
|
||||
}
|
||||
@@ -179,10 +177,10 @@ func TestNode_LeadsToViableHead(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||
|
||||
require.Equal(t, true, f.store.treeRootNode.leadsToViableHead(4, 3, 5))
|
||||
require.Equal(t, true, f.store.nodeByRoot[indexToHash(5)].leadsToViableHead(4, 3, 5))
|
||||
require.Equal(t, false, f.store.nodeByRoot[indexToHash(2)].leadsToViableHead(4, 3, 5))
|
||||
require.Equal(t, false, f.store.nodeByRoot[indexToHash(4)].leadsToViableHead(4, 3, 5))
|
||||
require.Equal(t, true, f.store.treeRootNode.leadsToViableHead(4, 5))
|
||||
require.Equal(t, true, f.store.nodeByRoot[indexToHash(5)].leadsToViableHead(4, 5))
|
||||
require.Equal(t, false, f.store.nodeByRoot[indexToHash(2)].leadsToViableHead(4, 5))
|
||||
require.Equal(t, false, f.store.nodeByRoot[indexToHash(4)].leadsToViableHead(4, 5))
|
||||
}
|
||||
|
||||
func TestNode_SetFullyValidated(t *testing.T) {
|
||||
|
||||
@@ -86,7 +86,7 @@ func (s *Store) head(ctx context.Context) ([32]byte, error) {
|
||||
bestDescendant = justifiedNode
|
||||
}
|
||||
currentEpoch := slots.EpochsSinceGenesis(time.Unix(int64(s.genesisTime), 0))
|
||||
if !bestDescendant.viableForHead(s.justifiedCheckpoint.Epoch, s.finalizedCheckpoint.Epoch, currentEpoch) {
|
||||
if !bestDescendant.viableForHead(s.justifiedCheckpoint.Epoch, currentEpoch) {
|
||||
s.allTipsAreInvalid = true
|
||||
return [32]byte{}, fmt.Errorf("head at slot %d with weight %d is not eligible, finalizedEpoch, justified Epoch %d, %d != %d, %d",
|
||||
bestDescendant.slot, bestDescendant.weight/10e9, bestDescendant.finalizedEpoch, bestDescendant.justifiedEpoch, s.finalizedCheckpoint.Epoch, s.justifiedCheckpoint.Epoch)
|
||||
@@ -218,8 +218,8 @@ func (s *Store) prune(ctx context.Context) error {
|
||||
defer s.nodesLock.Unlock()
|
||||
s.checkpointsLock.RLock()
|
||||
finalizedRoot := s.finalizedCheckpoint.Root
|
||||
finalizedEpoch := s.finalizedCheckpoint.Epoch
|
||||
s.checkpointsLock.RUnlock()
|
||||
|
||||
finalizedNode, ok := s.nodeByRoot[finalizedRoot]
|
||||
if !ok || finalizedNode == nil {
|
||||
return errors.WithMessage(errUnknownFinalizedRoot, fmt.Sprintf("%#x", finalizedRoot))
|
||||
@@ -238,6 +238,22 @@ func (s *Store) prune(ctx context.Context) error {
|
||||
s.treeRootNode = finalizedNode
|
||||
|
||||
prunedCount.Inc()
|
||||
// Prune all children of the finalized checkpoint block that are incompatible with it
|
||||
checkpointMaxSlot, err := slots.EpochStart(finalizedEpoch)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not compute epoch start")
|
||||
}
|
||||
if finalizedNode.slot == checkpointMaxSlot {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, child := range finalizedNode.children {
|
||||
if child != nil && child.slot <= checkpointMaxSlot {
|
||||
if err := s.pruneFinalizedNodeByRootMap(ctx, child, finalizedNode); err != nil {
|
||||
return errors.Wrap(err, "could not prune incompatible finalized child")
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/epoch/precompute"
|
||||
forkchoicetypes "github.com/prysmaticlabs/prysm/v3/beacon-chain/forkchoice/types"
|
||||
"github.com/prysmaticlabs/prysm/v3/beacon-chain/state"
|
||||
"github.com/prysmaticlabs/prysm/v3/config/features"
|
||||
"github.com/prysmaticlabs/prysm/v3/config/params"
|
||||
"github.com/prysmaticlabs/prysm/v3/consensus-types/primitives"
|
||||
"github.com/prysmaticlabs/prysm/v3/encoding/bytesutil"
|
||||
@@ -55,12 +56,14 @@ func (f *ForkChoice) updateUnrealizedCheckpoints() {
|
||||
if node.justifiedEpoch > f.store.justifiedCheckpoint.Epoch {
|
||||
f.store.prevJustifiedCheckpoint = f.store.justifiedCheckpoint
|
||||
f.store.justifiedCheckpoint = f.store.unrealizedJustifiedCheckpoint
|
||||
if node.justifiedEpoch > f.store.bestJustifiedCheckpoint.Epoch {
|
||||
if !features.Get().EnableDefensivePull && node.justifiedEpoch > f.store.bestJustifiedCheckpoint.Epoch {
|
||||
f.store.bestJustifiedCheckpoint = f.store.unrealizedJustifiedCheckpoint
|
||||
}
|
||||
}
|
||||
if node.finalizedEpoch > f.store.finalizedCheckpoint.Epoch {
|
||||
f.store.justifiedCheckpoint = f.store.unrealizedJustifiedCheckpoint
|
||||
if !features.Get().EnableDefensivePull {
|
||||
f.store.justifiedCheckpoint = f.store.unrealizedJustifiedCheckpoint
|
||||
}
|
||||
f.store.finalizedCheckpoint = f.store.unrealizedFinalizedCheckpoint
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,7 +187,7 @@ func TestStore_NoDeadLock(t *testing.T) {
|
||||
require.Equal(t, primitives.Epoch(1), f.FinalizedCheckpoint().Epoch)
|
||||
}
|
||||
|
||||
// Epoch 1 | Epoch 2
|
||||
// Epoch 2 | Epoch 3
|
||||
// |
|
||||
// -- D (late)
|
||||
// / |
|
||||
@@ -203,31 +203,31 @@ func TestStore_ForkNextEpoch(t *testing.T) {
|
||||
})
|
||||
defer resetCfg()
|
||||
|
||||
f := setup(0, 0)
|
||||
f := setup(1, 0)
|
||||
ctx := context.Background()
|
||||
|
||||
// Epoch 1 blocks (D does not arrive)
|
||||
state, blkRoot, err := prepareForkchoiceState(ctx, 92, [32]byte{'a'}, params.BeaconConfig().ZeroHash, [32]byte{'A'}, 0, 0)
|
||||
state, blkRoot, err := prepareForkchoiceState(ctx, 92, [32]byte{'a'}, params.BeaconConfig().ZeroHash, [32]byte{'A'}, 1, 0)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||
state, blkRoot, err = prepareForkchoiceState(ctx, 93, [32]byte{'b'}, [32]byte{'a'}, [32]byte{'B'}, 0, 0)
|
||||
state, blkRoot, err = prepareForkchoiceState(ctx, 93, [32]byte{'b'}, [32]byte{'a'}, [32]byte{'B'}, 1, 0)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||
state, blkRoot, err = prepareForkchoiceState(ctx, 94, [32]byte{'c'}, [32]byte{'b'}, [32]byte{'C'}, 0, 0)
|
||||
state, blkRoot, err = prepareForkchoiceState(ctx, 94, [32]byte{'c'}, [32]byte{'b'}, [32]byte{'C'}, 1, 0)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||
|
||||
// Epoch 2 blocks
|
||||
state, blkRoot, err = prepareForkchoiceState(ctx, 96, [32]byte{'e'}, [32]byte{'c'}, [32]byte{'E'}, 0, 0)
|
||||
state, blkRoot, err = prepareForkchoiceState(ctx, 96, [32]byte{'e'}, [32]byte{'c'}, [32]byte{'E'}, 1, 0)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||
state, blkRoot, err = prepareForkchoiceState(ctx, 97, [32]byte{'f'}, [32]byte{'e'}, [32]byte{'F'}, 0, 0)
|
||||
state, blkRoot, err = prepareForkchoiceState(ctx, 97, [32]byte{'f'}, [32]byte{'e'}, [32]byte{'F'}, 1, 0)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||
state, blkRoot, err = prepareForkchoiceState(ctx, 98, [32]byte{'g'}, [32]byte{'f'}, [32]byte{'G'}, 0, 0)
|
||||
state, blkRoot, err = prepareForkchoiceState(ctx, 98, [32]byte{'g'}, [32]byte{'f'}, [32]byte{'G'}, 1, 0)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||
state, blkRoot, err = prepareForkchoiceState(ctx, 99, [32]byte{'h'}, [32]byte{'g'}, [32]byte{'H'}, 0, 0)
|
||||
state, blkRoot, err = prepareForkchoiceState(ctx, 99, [32]byte{'h'}, [32]byte{'g'}, [32]byte{'H'}, 1, 0)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||
|
||||
@@ -236,10 +236,10 @@ func TestStore_ForkNextEpoch(t *testing.T) {
|
||||
headRoot, err := f.Head(ctx, []uint64{100})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, [32]byte{'h'}, headRoot)
|
||||
require.Equal(t, primitives.Epoch(0), f.JustifiedCheckpoint().Epoch)
|
||||
require.Equal(t, primitives.Epoch(1), f.JustifiedCheckpoint().Epoch)
|
||||
|
||||
// D arrives late, D is head
|
||||
state, blkRoot, err = prepareForkchoiceState(ctx, 95, [32]byte{'d'}, [32]byte{'c'}, [32]byte{'D'}, 0, 0)
|
||||
state, blkRoot, err = prepareForkchoiceState(ctx, 95, [32]byte{'d'}, [32]byte{'c'}, [32]byte{'D'}, 1, 0)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||
require.NoError(t, f.store.setUnrealizedJustifiedEpoch([32]byte{'d'}, 2))
|
||||
|
||||
@@ -123,7 +123,9 @@ func TestVotes_CanFindHead(t *testing.T) {
|
||||
// 4
|
||||
// /
|
||||
// 5 <- head, justified epoch = 2
|
||||
state, blkRoot, err = prepareForkchoiceState(context.Background(), 0, indexToHash(5), indexToHash(4), params.BeaconConfig().ZeroHash, 2, 2)
|
||||
//
|
||||
// We set this node's slot to be 64 so that when prunning below we do not prune its child
|
||||
state, blkRoot, err = prepareForkchoiceState(context.Background(), 2*params.BeaconConfig().SlotsPerEpoch, indexToHash(5), indexToHash(4), params.BeaconConfig().ZeroHash, 2, 2)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, f.InsertNode(ctx, state, blkRoot))
|
||||
|
||||
|
||||
@@ -65,9 +65,9 @@ func (s *State) StateByRoot(ctx context.Context, blockRoot [32]byte) (state.Beac
|
||||
return s.loadStateByRoot(ctx, blockRoot)
|
||||
}
|
||||
|
||||
// BalancesByRoot retrieves the effective balances of all validators at the
|
||||
// ActiveNonSlashedBalancesByRoot retrieves the effective balances of all active and non-slashed validators at the
|
||||
// state with a given root
|
||||
func (s *State) BalancesByRoot(ctx context.Context, blockRoot [32]byte) ([]uint64, error) {
|
||||
func (s *State) ActiveNonSlashedBalancesByRoot(ctx context.Context, blockRoot [32]byte) ([]uint64, error) {
|
||||
st, err := s.StateByRoot(ctx, blockRoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -79,7 +79,7 @@ func (s *State) BalancesByRoot(ctx context.Context, blockRoot [32]byte) ([]uint6
|
||||
|
||||
balances := make([]uint64, st.NumValidators())
|
||||
var balanceAccretor = func(idx int, val state.ReadOnlyValidator) error {
|
||||
if helpers.IsActiveValidatorUsingTrie(val, epoch) {
|
||||
if helpers.IsActiveNonSlashedValidatorUsingTrie(val, epoch) {
|
||||
balances[idx] = val.EffectiveBalance()
|
||||
} else {
|
||||
balances[idx] = 0
|
||||
|
||||
@@ -48,6 +48,14 @@ func TestStateByRoot_ColdState(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
beaconState, _ := util.DeterministicGenesisState(t, 32)
|
||||
require.NoError(t, beaconState.SetSlot(1))
|
||||
val, err := beaconState.ValidatorAtIndex(0)
|
||||
require.NoError(t, err)
|
||||
val.Slashed = true
|
||||
require.NoError(t, beaconState.UpdateValidatorAtIndex(0, val))
|
||||
roval, err := beaconState.ValidatorAtIndexReadOnly(0)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, true, roval.Slashed())
|
||||
|
||||
require.NoError(t, service.beaconDB.SaveState(ctx, beaconState, bRoot))
|
||||
util.SaveBlock(t, ctx, service.beaconDB, b)
|
||||
require.NoError(t, service.beaconDB.SaveGenesisBlockRoot(ctx, bRoot))
|
||||
@@ -55,12 +63,13 @@ func TestStateByRoot_ColdState(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.DeepSSZEqual(t, loadedState.ToProtoUnsafe(), beaconState.ToProtoUnsafe())
|
||||
|
||||
bal, err := service.BalancesByRoot(ctx, bRoot)
|
||||
bal, err := service.ActiveNonSlashedBalancesByRoot(ctx, bRoot)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 32, len(bal))
|
||||
for i := range bal {
|
||||
require.Equal(t, params.BeaconConfig().MaxEffectiveBalance, bal[i])
|
||||
for _, balance := range bal[1:] {
|
||||
require.Equal(t, params.BeaconConfig().MaxEffectiveBalance, balance)
|
||||
}
|
||||
require.Equal(t, uint64(0), bal[0])
|
||||
}
|
||||
|
||||
func TestStateByRootIfCachedNoCopy_HotState(t *testing.T) {
|
||||
|
||||
@@ -52,7 +52,7 @@ func (m *MockStateManager) StateByRoot(_ context.Context, blockRoot [32]byte) (s
|
||||
}
|
||||
|
||||
// BalancesByRoot --
|
||||
func (*MockStateManager) BalancesByRoot(_ context.Context, _ [32]byte) ([]uint64, error) {
|
||||
func (*MockStateManager) ActiveNonSlashedBalancesByRoot(_ context.Context, _ [32]byte) ([]uint64, error) {
|
||||
return []uint64{}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ type StateManager interface {
|
||||
SaveFinalizedState(fSlot primitives.Slot, fRoot [32]byte, fState state.BeaconState)
|
||||
MigrateToCold(ctx context.Context, fRoot [32]byte) error
|
||||
StateByRoot(ctx context.Context, blockRoot [32]byte) (state.BeaconState, error)
|
||||
BalancesByRoot(context.Context, [32]byte) ([]uint64, error)
|
||||
ActiveNonSlashedBalancesByRoot(context.Context, [32]byte) ([]uint64, error)
|
||||
StateByRootIfCachedNoCopy(blockRoot [32]byte) state.BeaconState
|
||||
StateByRootInitialSync(ctx context.Context, blockRoot [32]byte) (state.BeaconState, error)
|
||||
}
|
||||
@@ -96,8 +96,7 @@ func New(beaconDB db.NoHeadAccessDatabase, fc forkchoice.ForkChoicer, opts ...St
|
||||
for _, o := range opts {
|
||||
o(s)
|
||||
}
|
||||
fc.SetBalancesByRooter(s.BalancesByRoot)
|
||||
|
||||
fc.SetBalancesByRooter(s.ActiveNonSlashedBalancesByRoot)
|
||||
return s
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user