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 processing for Attestation #6772

Merged
merged 8 commits into from
Feb 3, 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
120 changes: 115 additions & 5 deletions cmd/erigon-cl/core/state/accessors.go
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,20 @@ func (b *BeaconState) ComputeShuffledIndex(ind, ind_count uint64, seed [32]byte)
return ind, nil
}

func (b *BeaconState) ComputeCommittee(indicies []uint64, seed libcommon.Hash, index, count uint64) ([]uint64, error) {
ret := []uint64{}
lenIndicies := uint64(len(indicies))
for i := (lenIndicies * index) / count; i < (lenIndicies*(index+1))/count; i++ {
index, err := b.ComputeShuffledIndex(i, lenIndicies, seed)
if err != nil {
return nil, err
}
ret = append(ret, indicies[index])
}
return ret, nil
//return [indices[compute_shuffled_index(uint64(i), uint64(len(indices)), seed)] for i in range(start, end)]
}

func (b *BeaconState) ComputeProposerIndex(indices []uint64, seed [32]byte) (uint64, error) {
if len(indices) == 0 {
return 0, fmt.Errorf("must have >0 indices")
Expand Down Expand Up @@ -214,7 +228,7 @@ func (b *BeaconState) GetBeaconProposerIndex() (uint64, error) {
binary.LittleEndian.PutUint64(slotByteArray, b.Slot())

// Add slot to the end of the input.
inputWithSlot := append(input, slotByteArray...)
inputWithSlot := append(input[:], slotByteArray...)

// Calculate the hash.
hash.Write(inputWithSlot)
Expand All @@ -229,22 +243,29 @@ func (b *BeaconState) GetBeaconProposerIndex() (uint64, error) {
return b.ComputeProposerIndex(indices, seedArray)
}

func (b *BeaconState) GetSeed(epoch uint64, domain [4]byte) []byte {
func (b *BeaconState) GetSeed(epoch uint64, domain [4]byte) libcommon.Hash {
mix := b.GetRandaoMixes(epoch + b.beaconConfig.EpochsPerHistoricalVector - b.beaconConfig.MinSeedLookahead - 1)
epochByteArray := make([]byte, 8)
binary.LittleEndian.PutUint64(epochByteArray, epoch)
input := append(domain[:], epochByteArray...)
input = append(input, mix[:]...)
hash := sha256.New()
hash.Write(input)
return hash.Sum(nil)
return utils.Keccak256(input)
}

// BaseRewardPerIncrement return base rewards for processing sync committee and duties.
func (b *BeaconState) baseRewardPerIncrement(totalActiveBalance uint64) uint64 {
return b.beaconConfig.EffectiveBalanceIncrement * b.beaconConfig.BaseRewardFactor / utils.IntegerSquareRoot(totalActiveBalance)
}

// BaseReward return base rewards for processing sync committee and duties.
func (b *BeaconState) BaseReward(totalActiveBalance, index uint64) (uint64, error) {
validator, err := b.ValidatorAt(int(index))
if err != nil {
return 0, err
}
return (validator.EffectiveBalance / b.beaconConfig.EffectiveBalanceIncrement) * b.baseRewardPerIncrement(totalActiveBalance), nil
}

// SyncRewards returns the proposer reward and the sync participant reward given the total active balance in state.
func (b *BeaconState) SyncRewards() (proposerReward, participantReward uint64, err error) {
activeBalance, err := b.GetTotalActiveBalance()
Expand Down Expand Up @@ -277,3 +298,92 @@ func (b *BeaconState) ValidatorFromDeposit(deposit *cltypes.Deposit) *cltypes.Va
EffectiveBalance: effectiveBalance,
}
}

// CommitteeCount returns current number of committee for epoch.
func (b *BeaconState) CommitteeCount(epoch uint64) uint64 {
committeCount := uint64(len(b.GetActiveValidatorsIndices(epoch))) / b.beaconConfig.SlotsPerEpoch / b.beaconConfig.TargetCommitteeSize
if b.beaconConfig.MaxCommitteesPerSlot < committeCount {
committeCount = b.beaconConfig.MaxCommitteesPerSlot
}
if committeCount < 1 {
committeCount = 1
}
return committeCount
}

func (b *BeaconState) GetAttestationParticipationFlagIndicies(data *cltypes.AttestationData, inclusionDelay uint64) ([]uint8, error) {
var justifiedCheckpoint *cltypes.Checkpoint
// get checkpoint from epoch
if data.Target.Epoch == b.Epoch() {
justifiedCheckpoint = b.currentJustifiedCheckpoint
} else {
justifiedCheckpoint = b.previousJustifiedCheckpoint
}
// Matching roots
if *data.Source != *justifiedCheckpoint {
return nil, fmt.Errorf("GetAttestationParticipationFlagIndicies: source does not match")
}
targetRoot, err := b.GetBlockRoot(data.Target.Epoch)
if err != nil {
return nil, err
}
headRoot, err := b.GetBlockRoot(data.Slot)
if err != nil {
return nil, err
}
matchingTarget := data.Target.Root == targetRoot
matchingHead := matchingTarget && data.BeaconBlockHash == headRoot
participationFlagIndicies := []uint8{}
if inclusionDelay <= utils.IntegerSquareRoot(b.beaconConfig.SlotsPerEpoch) {
participationFlagIndicies = append(participationFlagIndicies, b.beaconConfig.TimelySourceFlagIndex)
}
if matchingTarget && inclusionDelay <= b.beaconConfig.SlotsPerEpoch {
participationFlagIndicies = append(participationFlagIndicies, b.beaconConfig.TimelyTargetFlagIndex)
}
if matchingHead && inclusionDelay == b.beaconConfig.MinAttestationInclusionDelay {
participationFlagIndicies = append(participationFlagIndicies, b.beaconConfig.TimelyHeadFlagIndex)
}
return participationFlagIndicies, nil
}

func (b *BeaconState) GetBeaconCommitee(slot, committeeIndex uint64) ([]uint64, error) {
epoch := b.GetEpochAtSlot(slot)
committeesPerSlot := b.CommitteeCount(epoch)
return b.ComputeCommittee(
b.GetActiveValidatorsIndices(epoch),
b.GetSeed(epoch, b.beaconConfig.DomainBeaconAttester),
(slot%b.beaconConfig.SlotsPerEpoch)*committeesPerSlot+committeeIndex,
committeesPerSlot*b.beaconConfig.SlotsPerEpoch,
)
}

func (b *BeaconState) GetIndexedAttestation(attestation *cltypes.Attestation) (*cltypes.IndexedAttestation, error) {
attestingIndicies, err := b.GetAttestingIndicies(attestation.Data, attestation.AggregationBits)
if err != nil {
return nil, err
}
return &cltypes.IndexedAttestation{
AttestingIndices: attestingIndicies,
Data: attestation.Data,
Signature: attestation.Signature,
}, nil
}

func (b *BeaconState) GetAttestingIndicies(attestation *cltypes.AttestationData, aggregationBits []byte) ([]uint64, error) {
committee, err := b.GetBeaconCommitee(attestation.Slot, attestation.Index)
if err != nil {
return nil, err
}
attestingIndices := []uint64{}
for i, member := range committee {
bitIndex := i % 8
sliceIndex := i / 8
if sliceIndex >= len(aggregationBits) {
return nil, fmt.Errorf("GetAttestingIndicies: committee is too big")
}
if (aggregationBits[sliceIndex] & (1 << bitIndex)) > 0 {
attestingIndices = append(attestingIndices, member)
}
}
return attestingIndices, nil
}
109 changes: 109 additions & 0 deletions cmd/erigon-cl/core/state/accessors_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package state_test

import (
"strconv"
"testing"

"github.com/ledgerwatch/erigon-lib/common"
"github.com/ledgerwatch/erigon/cl/clparams"
"github.com/ledgerwatch/erigon/cl/cltypes"
"github.com/ledgerwatch/erigon/cl/utils"
"github.com/ledgerwatch/erigon/cmd/erigon-cl/core/state"
"github.com/stretchr/testify/require"
)
Expand Down Expand Up @@ -241,3 +243,110 @@ func TestSyncReward(t *testing.T) {
require.Equal(t, propReward, uint64(30))
require.Equal(t, partRew, uint64(214))
}

func TestComputeCommittee(t *testing.T) {
// Create 10 committees
committeeCount := uint64(10)
validatorCount := committeeCount * clparams.MainnetBeaconConfig.TargetCommitteeSize
validators := make([]*cltypes.Validator, validatorCount)

for i := 0; i < len(validators); i++ {
var k [48]byte
copy(k[:], strconv.Itoa(i))
validators[i] = &cltypes.Validator{
PublicKey: k,
ExitEpoch: clparams.MainnetBeaconConfig.FarFutureEpoch,
}
}
state := state.GetEmptyBeaconState()
state.SetValidators(validators)
state.SetSlot(200)

epoch := state.Epoch()
indices := state.GetActiveValidatorsIndices(epoch)
seed := state.GetSeed(epoch, clparams.MainnetBeaconConfig.DomainBeaconAttester)
committees, err := state.ComputeCommittee(indices, seed, 0, 1)
require.NoError(t, err, "Could not compute committee")

// Test shuffled indices are correct for index 5 committee
index := uint64(5)
committee5, err := state.ComputeCommittee(indices, seed, index, committeeCount)
require.NoError(t, err, "Could not compute committee")
start := (validatorCount * index) / committeeCount
end := (validatorCount * (index + 1)) / committeeCount
require.Equal(t, committee5, committees[start:end], "Committee has different shuffled indices")
}

func TestAttestationParticipationFlagIndices(t *testing.T) {
beaconState := state.GetEmptyBeaconState()
//beaconState, _ := util.DeterministicGenesisStateAltair(t, params.BeaconConfig().MaxValidatorsPerCommittee)
beaconState.SetSlot(1)
cfg := clparams.MainnetBeaconConfig

tests := []struct {
name string
inputState state.BeaconState
inputData *cltypes.AttestationData
inputDelay uint64
participationIndices []uint8
}{
{
name: "none",
inputState: func() state.BeaconState {
return *beaconState
}(),
inputData: &cltypes.AttestationData{
Source: &cltypes.Checkpoint{},
Target: &cltypes.Checkpoint{
Root: [32]byte{2},
},
},
inputDelay: cfg.SlotsPerEpoch,
participationIndices: []uint8{},
},
{
name: "participated source",
inputState: func() state.BeaconState {
return *beaconState
}(),
inputData: &cltypes.AttestationData{
Source: &cltypes.Checkpoint{},
Target: &cltypes.Checkpoint{
Root: [32]byte{2},
},
},
inputDelay: utils.IntegerSquareRoot(cfg.SlotsPerEpoch) - 1,
participationIndices: []uint8{cfg.TimelySourceFlagIndex},
},
{
name: "participated source and target",
inputState: func() state.BeaconState {
return *beaconState
}(),
inputData: &cltypes.AttestationData{
Source: &cltypes.Checkpoint{},
Target: &cltypes.Checkpoint{},
},
inputDelay: utils.IntegerSquareRoot(cfg.SlotsPerEpoch) - 1,
participationIndices: []uint8{cfg.TimelySourceFlagIndex, cfg.TimelyTargetFlagIndex},
},
{
name: "participated source and target and head",
inputState: func() state.BeaconState {
return *beaconState
}(),
inputData: &cltypes.AttestationData{
Source: &cltypes.Checkpoint{},
Target: &cltypes.Checkpoint{},
},
inputDelay: 1,
participationIndices: []uint8{cfg.TimelySourceFlagIndex, cfg.TimelyTargetFlagIndex, cfg.TimelyHeadFlagIndex},
},
}

for _, test := range tests {
flagIndices, err := test.inputState.GetAttestationParticipationFlagIndicies(test.inputData, test.inputDelay)
require.NoError(t, err, test.name)
require.Equal(t, test.participationIndices, flagIndices, test.name)
}
}
Loading