Skip to content

Commit

Permalink
Added phase0 support to Erigon-CL. (erigontech#7066)
Browse files Browse the repository at this point in the history
Added phase 0 support.
  • Loading branch information
Giulio2002 authored and calmbeing committed Mar 16, 2023
1 parent e25809c commit 089a1d4
Show file tree
Hide file tree
Showing 16 changed files with 510 additions and 116 deletions.
32 changes: 31 additions & 1 deletion cl/cltypes/validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -294,8 +294,38 @@ type Validator struct {
ActivationEpoch uint64
ExitEpoch uint64
WithdrawableEpoch uint64
// This is all stuff used by phase0 state transition. It makes many operations faster.
// Source attesters
IsCurrentMatchingSourceAttester bool
IsPreviousMatchingSourceAttester bool
// Target Attesters
IsCurrentMatchingTargetAttester bool
IsPreviousMatchingTargetAttester bool
// Head attesters
IsCurrentMatchingHeadAttester bool
IsPreviousMatchingHeadAttester bool
// MinInclusionDelay
MinCurrentInclusionDelayAttestation *PendingAttestation
MinPreviousInclusionDelayAttestation *PendingAttestation
}

// DutiesAttested returns how many of its duties the validator attested and missed
func (v *Validator) DutiesAttested() (attested, missed uint64) {
if v.Slashed {
return 0, 3
}
if v.IsPreviousMatchingSourceAttester {
attested++
}
if v.IsPreviousMatchingTargetAttester {
attested++
}
if v.IsPreviousMatchingHeadAttester {
attested++
}
missed = 3 - attested
return
}

func (v *Validator) IsSlashable(epoch uint64) bool {
return !v.Slashed && (v.ActivationEpoch <= epoch) && (epoch < v.WithdrawableEpoch)
}
Expand Down
21 changes: 21 additions & 0 deletions cl/utils/bytes.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ package utils

import (
"encoding/binary"
"math/bits"

"github.com/golang/snappy"
"github.com/ledgerwatch/erigon/cl/cltypes/ssz_utils"
Expand Down Expand Up @@ -105,3 +106,23 @@ func IsSliceSortedSet(vals []uint64) bool {
}
return true
}

// getBitlistLength return the amount of bits in given bitlist.
func GetBitlistLength(b []byte) int {
if len(b) == 0 {
return 0
}
// The most significant bit is present in the last byte in the array.
last := b[len(b)-1]

// Determine the position of the most significant bit.
msb := bits.Len8(last)
if msb == 0 {
return 0
}

// The absolute position of the most significant bit will be the number of
// bits in the preceding bytes plus the position of the most significant
// bit. Subtract this value by 1 to determine the length of the bitlist.
return 8*(len(b)-1) + msb - 1
}
6 changes: 4 additions & 2 deletions cmd/ef-tests-cl/consensus_tests/consensus_tester.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
"golang.org/x/exp/slices"
)

var supportedVersions = []string{"altair", "bellatrix", "capella"}
var supportedVersions = []string{"phase0", "altair", "bellatrix", "capella"}

type ConsensusTester struct {
// parameters
Expand Down Expand Up @@ -70,7 +70,7 @@ func (c *ConsensusTester) iterateOverTests(dir, p string, depth int) {
// Depth 1 means that we are setting the version
if depth == 1 {
if !slices.Contains(supportedVersions, childName) {
return
continue
}
c.context.version = stringToClVersion(childName)
}
Expand All @@ -81,12 +81,14 @@ func (c *ConsensusTester) iterateOverTests(dir, p string, depth int) {
// depth 3 we find the specific
c.context.caseName = childName
}

// If we found a non-directory then it is a test folder.
if !childDir.IsDir() {
// Check if it matches case specified.
if *c.pattern != "" && !strings.Contains(p, *c.pattern) {
return
}

// If yes execute it.
if implemented, err := c.executeTest(p); err != nil {
log.Warn("Test Failed", "err", err, "test", p)
Expand Down
9 changes: 6 additions & 3 deletions cmd/ef-tests-cl/consensus_tests/epoch_processing.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,18 +29,16 @@ func getTestEpochProcessing(f func(s *state.BeaconState) error) testFunc {
expectedState, err := decodeStateFromFile(context, "post.ssz_snappy")
if os.IsNotExist(err) {
isErrExpected = true
} else {
} else if err != nil {
return err
}

// Make up state transistor
if err := f(testState); err != nil {
if isErrExpected {
return nil
}
return err
}

if isErrExpected && err == nil {
return fmt.Errorf("expected an error got none")
}
Expand Down Expand Up @@ -107,3 +105,8 @@ var slashingsResetTest = getTestEpochProcessing(func(s *state.BeaconState) error
transition.ProcessSlashingsReset(s)
return nil
})

var recordsResetTest = getTestEpochProcessing(func(s *state.BeaconState) error {
transition.ProcessParticipationRecordUpdates(s)
return nil
})
2 changes: 2 additions & 0 deletions cmd/ef-tests-cl/consensus_tests/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ var (
caseRewardsAndPenalties = "rewards_and_penalties"
caseSlashings = "slashings"
caseSlashingsReset = "slashings_reset"
caseParticipationRecords = "participation_record_updates"
)

// Operations cases
Expand Down Expand Up @@ -69,6 +70,7 @@ var handlers map[string]testFunc = map[string]testFunc{
path.Join(epochProcessingDivision, caseRewardsAndPenalties): rewardsAndPenaltiesTest,
path.Join(epochProcessingDivision, caseSlashings): slashingsTest,
path.Join(epochProcessingDivision, caseSlashingsReset): slashingsResetTest,
path.Join(epochProcessingDivision, caseParticipationRecords): recordsResetTest,
path.Join(operationsDivision, caseAttestation): operationAttestationHandler,
path.Join(operationsDivision, caseAttesterSlashing): operationAttesterSlashingHandler,
path.Join(operationsDivision, caseProposerSlashing): operationProposerSlashingHandler,
Expand Down
8 changes: 6 additions & 2 deletions cmd/ef-tests-cl/consensus_tests/sanity.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"os"

"github.com/ledgerwatch/erigon/cl/cltypes"
"github.com/ledgerwatch/erigon/cmd/erigon-cl/core/transition"
)

Expand All @@ -12,6 +13,7 @@ func testSanityFunction(context testContext) error {
if err != nil {
return err
}
testState.HashSSZ()
var expectedError bool
expectedState, err := decodeStateFromFile(context, "post.ssz_snappy")
if os.IsNotExist(err) {
Expand All @@ -25,7 +27,9 @@ func testSanityFunction(context testContext) error {
if err != nil {
return err
}
for _, block := range blocks {
startSlot := testState.Slot()
var block *cltypes.SignedBeaconBlock
for _, block = range blocks {
err = transition.TransitionState(testState, block, true)
if err != nil {
break
Expand All @@ -39,7 +43,7 @@ func testSanityFunction(context testContext) error {
if expectedError {
return nil
}
return err
return fmt.Errorf("cannot transition state: %s. slot=%d. start_slot=%d", err, block.Block.Slot, startSlot)
}
expectedRoot, err := expectedState.HashSSZ()
if err != nil {
Expand Down
52 changes: 25 additions & 27 deletions cmd/erigon-cl/core/state/accessors.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package state

import (
"encoding/binary"
"errors"
"fmt"
"math/bits"
"sort"

libcommon "github.com/ledgerwatch/erigon-lib/common"
Expand All @@ -17,6 +17,10 @@ import (

const PreAllocatedRewardsAndPenalties = 8192

var (
ErrGetBlockRootAtSlotFuture = errors.New("GetBlockRootAtSlot: slot in the future")
)

// GetActiveValidatorsIndices returns the list of validator indices active for the given epoch.
func (b *BeaconState) GetActiveValidatorsIndices(epoch uint64) (indicies []uint64) {
if cachedIndicies, ok := b.activeValidatorsCache.Get(epoch); ok {
Expand Down Expand Up @@ -119,7 +123,7 @@ func (b *BeaconState) GetBlockRoot(epoch uint64) (libcommon.Hash, error) {
// GetBlockRootAtSlot returns the block root at a given slot
func (b *BeaconState) GetBlockRootAtSlot(slot uint64) (libcommon.Hash, error) {
if slot >= b.slot {
return libcommon.Hash{}, fmt.Errorf("GetBlockRootAtSlot: slot in the future")
return libcommon.Hash{}, ErrGetBlockRootAtSlotFuture
}
if b.slot > slot+b.beaconConfig.SlotsPerHistoricalRoot {
return libcommon.Hash{}, fmt.Errorf("GetBlockRootAtSlot: slot too much far behind")
Expand Down Expand Up @@ -268,7 +272,13 @@ func (b *BeaconState) BaseReward(index uint64) (uint64, error) {
if index >= uint64(len(b.validators)) {
return 0, ErrInvalidValidatorIndex
}
return (b.validators[index].EffectiveBalance / b.beaconConfig.EffectiveBalanceIncrement) * b.BaseRewardPerIncrement(), nil
if b.totalActiveBalanceCache == nil {
b._refreshActiveBalances()
}
if b.version != clparams.Phase0Version {
return (b.validators[index].EffectiveBalance / b.beaconConfig.EffectiveBalanceIncrement) * b.BaseRewardPerIncrement(), nil
}
return b.validators[index].EffectiveBalance * b.beaconConfig.BaseRewardFactor / b.totalActiveBalanceRootCache / b.beaconConfig.BaseRewardsPerEpoch, nil
}

// SyncRewards returns the proposer reward and the sync participant reward given the total active balance in state.
Expand Down Expand Up @@ -385,33 +395,16 @@ func (b *BeaconState) GetIndexedAttestation(attestation *cltypes.Attestation, at
}, nil
}

// getBitlistLength return the amount of bits in given bitlist.
func getBitlistLength(b []byte) int {
if len(b) == 0 {
return 0
}
// The most significant bit is present in the last byte in the array.
last := b[len(b)-1]

// Determine the position of the most significant bit.
msb := bits.Len8(last)
if msb == 0 {
return 0
}

// The absolute position of the most significant bit will be the number of
// bits in the preceding bytes plus the position of the most significant
// bit. Subtract this value by 1 to determine the length of the bitlist.
return 8*(len(b)-1) + msb - 1
}

func (b *BeaconState) GetAttestingIndicies(attestation *cltypes.AttestationData, aggregationBits []byte) ([]uint64, error) {
// GetAttestingIndicies retrieves attesting indicies for a specific attestation. however some tests will not expect the aggregation bits check.
// thus, it is a flag now.
func (b *BeaconState) GetAttestingIndicies(attestation *cltypes.AttestationData, aggregationBits []byte, checkBitsLength bool) ([]uint64, error) {
committee, err := b.GetBeaconCommitee(attestation.Slot, attestation.Index)
if err != nil {
return nil, err
}
if getBitlistLength(aggregationBits) != len(committee) {
return nil, fmt.Errorf("GetAttestingIndicies: invalid aggregation bits")
aggregationBitsLen := utils.GetBitlistLength(aggregationBits)
if checkBitsLength && utils.GetBitlistLength(aggregationBits) != len(committee) {
return nil, fmt.Errorf("GetAttestingIndicies: invalid aggregation bits. agg bits size: %d, expect: %d", aggregationBitsLen, len(committee))
}
attestingIndices := []uint64{}
for i, member := range committee {
Expand Down Expand Up @@ -440,9 +433,14 @@ func (b *BeaconState) EligibleValidatorsIndicies() (eligibleValidators []uint64)
return
}

// FinalityDelay determines by how many epochs we are late on finality.
func (b *BeaconState) FinalityDelay() uint64 {
return b.PreviousEpoch() - b.finalizedCheckpoint.Epoch
}

// Implementation of is_in_inactivity_leak. tells us if network is in danger pretty much. defined in ETH 2.0 specs.
func (b *BeaconState) InactivityLeaking() bool {
return (b.PreviousEpoch() - b.finalizedCheckpoint.Epoch) > b.beaconConfig.MinEpochsToInactivityPenalty
return b.FinalityDelay() > b.beaconConfig.MinEpochsToInactivityPenalty
}

func (b *BeaconState) IsUnslashedParticipatingIndex(epoch, index uint64, flagIdx int) bool {
Expand Down
4 changes: 4 additions & 0 deletions cmd/erigon-cl/core/state/getters.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,10 @@ func (b *BeaconState) NextWithdrawalIndex() uint64 {
return b.nextWithdrawalIndex
}

func (b *BeaconState) CurrentEpochAttestations() []*cltypes.PendingAttestation {
return b.currentEpochAttestations
}

func (b *BeaconState) NextWithdrawalValidatorIndex() uint64 {
return b.nextWithdrawalValidatorIndex
}
Expand Down
39 changes: 39 additions & 0 deletions cmd/erigon-cl/core/state/setters.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package state
import (
libcommon "github.com/ledgerwatch/erigon-lib/common"

"github.com/ledgerwatch/erigon/cl/clparams"
"github.com/ledgerwatch/erigon/cl/cltypes"
)

Expand Down Expand Up @@ -210,11 +211,49 @@ func (b *BeaconState) SetValidatorInactivityScore(index int, score uint64) error
}

func (b *BeaconState) AddCurrentEpochParticipationFlags(flags cltypes.ParticipationFlags) {
if b.version == clparams.Phase0Version {
panic("cannot call AddCurrentEpochParticipationFlags on phase0")
}
b.touchedLeaves[CurrentEpochParticipationLeafIndex] = true
b.currentEpochParticipation = append(b.currentEpochParticipation, flags)
}

func (b *BeaconState) AddPreviousEpochParticipationFlags(flags cltypes.ParticipationFlags) {
if b.version == clparams.Phase0Version {
panic("cannot call AddPreviousEpochParticipationFlags on phase0")
}
b.touchedLeaves[PreviousEpochParticipationLeafIndex] = true
b.previousEpochParticipation = append(b.previousEpochParticipation, flags)
}

func (b *BeaconState) AddCurrentEpochAtteastation(attestation *cltypes.PendingAttestation) {
if b.version != clparams.Phase0Version {
panic("can call AddCurrentEpochAtteastation only on phase0")
}
b.touchedLeaves[CurrentEpochParticipationLeafIndex] = true
b.currentEpochAttestations = append(b.currentEpochAttestations, attestation)
}

func (b *BeaconState) AddPreviousEpochAtteastation(attestation *cltypes.PendingAttestation) {
if b.version != clparams.Phase0Version {
panic("can call AddPreviousEpochAtteastation only on phase0")
}
b.touchedLeaves[PreviousEpochParticipationLeafIndex] = true
b.previousEpochAttestations = append(b.previousEpochAttestations, attestation)
}

func (b *BeaconState) SetCurrentEpochAtteastations(attestations []*cltypes.PendingAttestation) {
if b.version != clparams.Phase0Version {
panic("can call SetCurrentEpochAtteastations only on phase0")
}
b.touchedLeaves[CurrentEpochParticipationLeafIndex] = true
b.currentEpochAttestations = attestations
}

func (b *BeaconState) SetPreviousEpochAtteastations(attestations []*cltypes.PendingAttestation) {
if b.version != clparams.Phase0Version {
panic("can call SetPreviousEpochAtteastations only on phase0")
}
b.touchedLeaves[PreviousEpochParticipationLeafIndex] = true
b.previousEpochAttestations = attestations
}
Loading

0 comments on commit 089a1d4

Please sign in to comment.