Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added phase0 support to Erigon-CL. #7066

Merged
merged 7 commits into from
Mar 11, 2023
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
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