Skip to content

Commit

Permalink
Added processing for Attestation (#6772)
Browse files Browse the repository at this point in the history
  • Loading branch information
Giulio2002 authored Feb 3, 2023
1 parent 2975ca5 commit 03f737c
Show file tree
Hide file tree
Showing 12 changed files with 470 additions and 121 deletions.
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

0 comments on commit 03f737c

Please sign in to comment.