Skip to content

Commit 631d822

Browse files
authored
Merge pull request #786 from jshufro/jms/electrarewards
Process committees correctly post-electra
2 parents c3b7b9d + 8926467 commit 631d822

File tree

8 files changed

+168
-103
lines changed

8 files changed

+168
-103
lines changed

shared/services/beacon/client.go

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package beacon
22

33
import (
44
"math/big"
5+
"sort"
56

67
"github.com/ethereum/go-ethereum/common"
78
"github.com/prysmaticlabs/go-bitfield"
@@ -82,6 +83,9 @@ type Committees interface {
8283
// Validators returns the list of validators of the committee at
8384
// the provided offset
8485
Validators(int) []string
86+
// ValidatorCount returns the number of validators in the committee at
87+
// the provided offset
88+
ValidatorCount(int) int
8589
// Count returns the number of committees in the response
8690
Count() int
8791
// Release returns the reused validators slice buffer to the pool for
@@ -93,7 +97,27 @@ type Committees interface {
9397
type AttestationInfo struct {
9498
AggregationBits bitfield.Bitlist
9599
SlotIndex uint64
96-
CommitteeIndex uint64
100+
// Committees represented by AggregationBits
101+
Committees bitfield.Bitvector64
102+
}
103+
104+
func (a *AttestationInfo) CommitteeIndices() []int {
105+
out := a.Committees.BitIndices()
106+
sort.Ints(out)
107+
return out
108+
}
109+
110+
func (a AttestationInfo) ValidatorAttested(committeeIndex int, position int, committeeSizes map[uint64]int) bool {
111+
// Calculate the offset in aggregation_bits
112+
committeeOffset := 0
113+
for _, c := range a.CommitteeIndices() {
114+
if c >= committeeIndex {
115+
break
116+
}
117+
committeeOffset += committeeSizes[uint64(c)]
118+
}
119+
offset := committeeOffset + position
120+
return a.AggregationBits.BitAt(uint64(offset))
97121
}
98122

99123
// Beacon client type

shared/services/beacon/client/committee.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ func (c *CommitteesResponse) Validators(idx int) []string {
6565
return c.Data[idx].Validators
6666
}
6767

68+
func (c *CommitteesResponse) ValidatorCount(idx int) int {
69+
return len(c.Data[idx].Validators)
70+
}
71+
6872
func (c *CommitteesResponse) Release() {
6973
for _, committee := range c.Data {
7074
// Reset the slice length to 0 (capacity stays the same)

shared/services/beacon/client/std-http-client.go

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import (
1616

1717
"github.com/ethereum/go-ethereum/common"
1818
"github.com/goccy/go-json"
19+
"github.com/prysmaticlabs/go-bitfield"
1920
"github.com/prysmaticlabs/prysm/v5/crypto/bls"
2021
"github.com/rocket-pool/rocketpool-go/types"
2122
eth2types "github.com/wealdtech/go-eth2-types/v2"
@@ -616,11 +617,19 @@ func (c *StandardHttpClient) GetAttestations(blockId string) ([]beacon.Attestati
616617
for i, attestation := range attestations.Data {
617618
bitString := hexutil.RemovePrefix(attestation.AggregationBits)
618619
attestationInfo[i].SlotIndex = uint64(attestation.Data.Slot)
619-
attestationInfo[i].CommitteeIndex = uint64(attestation.Data.Index)
620620
attestationInfo[i].AggregationBits, err = hex.DecodeString(bitString)
621621
if err != nil {
622622
return nil, false, fmt.Errorf("Error decoding aggregation bits for attestation %d of block %s: %w", i, blockId, err)
623623
}
624+
if attestation.CommitteeBits != "" && attestation.CommitteeBits != "0x" {
625+
attestationInfo[i].Committees, err = hex.DecodeString(hexutil.RemovePrefix(attestation.CommitteeBits))
626+
if err != nil {
627+
return nil, false, fmt.Errorf("Error decoding committee bits for attestation %d of block %s: %w", i, blockId, err)
628+
}
629+
} else {
630+
attestationInfo[i].Committees = bitfield.NewBitvector64()
631+
attestationInfo[i].Committees.SetBitAt(uint64(attestation.Data.Index), true)
632+
}
624633
}
625634

626635
return attestationInfo, true, nil
@@ -654,13 +663,21 @@ func (c *StandardHttpClient) GetBeaconBlock(blockId string) (beacon.BeaconBlock,
654663
for i, attestation := range block.Data.Message.Body.Attestations {
655664
bitString := hexutil.RemovePrefix(attestation.AggregationBits)
656665
info := beacon.AttestationInfo{
657-
SlotIndex: uint64(attestation.Data.Slot),
658-
CommitteeIndex: uint64(attestation.Data.Index),
666+
SlotIndex: uint64(attestation.Data.Slot),
659667
}
660668
info.AggregationBits, err = hex.DecodeString(bitString)
661669
if err != nil {
662670
return beacon.BeaconBlock{}, false, fmt.Errorf("Error decoding aggregation bits for attestation %d of block %s: %w", i, blockId, err)
663671
}
672+
if attestation.CommitteeBits != "" && attestation.CommitteeBits != "0x" {
673+
info.Committees, err = hex.DecodeString(hexutil.RemovePrefix(attestation.CommitteeBits))
674+
if err != nil {
675+
return beacon.BeaconBlock{}, false, fmt.Errorf("Error decoding committee bits for attestation %d of block %s: %w", i, blockId, err)
676+
}
677+
} else {
678+
info.Committees = bitfield.NewBitvector64()
679+
info.Committees.SetBitAt(uint64(attestation.Data.Index), true)
680+
}
664681
beaconBlock.Attestations = append(beaconBlock.Attestations, info)
665682
}
666683

shared/services/beacon/client/types.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ type Attestation struct {
166166
Slot uinteger `json:"slot"`
167167
Index uinteger `json:"index"`
168168
} `json:"data"`
169+
CommitteeBits string `json:"committee_bits"`
169170
}
170171

171172
type Withdrawal struct {

shared/services/rewards/generator-impl-v8.go

Lines changed: 53 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -852,50 +852,52 @@ func (r *treeGeneratorImpl_v8) checkDutiesForSlot(attestations []beacon.Attestat
852852
if inclusionSlot-attestation.SlotIndex > r.beaconConfig.SlotsPerEpoch {
853853
continue
854854
}
855-
rpCommittee, exists := slotInfo.Committees[attestation.CommitteeIndex]
856-
if !exists {
857-
continue
858-
}
859-
blockTime := r.genesisTime.Add(time.Second * time.Duration(r.networkState.BeaconConfig.SecondsPerSlot*attestation.SlotIndex))
860-
861-
// Check if each RP validator attested successfully
862-
for position, validator := range rpCommittee.Positions {
863-
if !attestation.AggregationBits.BitAt(uint64(position)) {
855+
for _, committeeIndex := range attestation.CommitteeIndices() {
856+
rpCommittee, exists := slotInfo.Committees[uint64(committeeIndex)]
857+
if !exists {
864858
continue
865859
}
860+
blockTime := r.genesisTime.Add(time.Second * time.Duration(r.networkState.BeaconConfig.SecondsPerSlot*attestation.SlotIndex))
866861

867-
// This was seen, so remove it from the missing attestations and add it to the completed ones
868-
delete(rpCommittee.Positions, position)
869-
if len(rpCommittee.Positions) == 0 {
870-
delete(slotInfo.Committees, attestation.CommitteeIndex)
871-
}
872-
if len(slotInfo.Committees) == 0 {
873-
delete(r.intervalDutiesInfo.Slots, attestation.SlotIndex)
874-
}
875-
delete(validator.MissingAttestationSlots, attestation.SlotIndex)
862+
// Check if each RP validator attested successfully
863+
for position, validator := range rpCommittee.Positions {
864+
if !attestation.ValidatorAttested(committeeIndex, position, slotInfo.CommitteeSizes) {
865+
continue
866+
}
876867

877-
// Check if this minipool was opted into the SP for this block
878-
nodeDetails := r.nodeDetails[validator.NodeIndex]
879-
if blockTime.Sub(nodeDetails.OptInTime) < 0 || nodeDetails.OptOutTime.Sub(blockTime) < 0 {
880-
// Not opted in
881-
continue
882-
}
868+
// This was seen, so remove it from the missing attestations and add it to the completed ones
869+
delete(rpCommittee.Positions, position)
870+
if len(rpCommittee.Positions) == 0 {
871+
delete(slotInfo.Committees, uint64(committeeIndex))
872+
}
873+
if len(slotInfo.Committees) == 0 {
874+
delete(r.intervalDutiesInfo.Slots, attestation.SlotIndex)
875+
}
876+
delete(validator.MissingAttestationSlots, attestation.SlotIndex)
877+
878+
// Check if this minipool was opted into the SP for this block
879+
nodeDetails := r.nodeDetails[validator.NodeIndex]
880+
if blockTime.Sub(nodeDetails.OptInTime) < 0 || nodeDetails.OptOutTime.Sub(blockTime) < 0 {
881+
// Not opted in
882+
continue
883+
}
883884

884-
// Mark this duty as completed
885-
validator.CompletedAttestations[attestation.SlotIndex] = true
886-
887-
// Get the pseudoscore for this attestation
888-
details := r.networkState.MinipoolDetailsByAddress[validator.Address]
889-
bond, fee := r.getMinipoolBondAndNodeFee(details, blockTime)
890-
minipoolScore := big.NewInt(0).Sub(one, fee) // 1 - fee
891-
minipoolScore.Mul(minipoolScore, bond) // Multiply by bond
892-
minipoolScore.Div(minipoolScore, validatorReq) // Divide by 32 to get the bond as a fraction of a total validator
893-
minipoolScore.Add(minipoolScore, fee) // Total = fee + (bond/32)(1 - fee)
894-
895-
// Add it to the minipool's score and the total score
896-
validator.AttestationScore.Add(&validator.AttestationScore.Int, minipoolScore)
897-
r.totalAttestationScore.Add(r.totalAttestationScore, minipoolScore)
898-
r.successfulAttestations++
885+
// Mark this duty as completed
886+
validator.CompletedAttestations[attestation.SlotIndex] = true
887+
888+
// Get the pseudoscore for this attestation
889+
details := r.networkState.MinipoolDetailsByAddress[validator.Address]
890+
bond, fee := r.getMinipoolBondAndNodeFee(details, blockTime)
891+
minipoolScore := big.NewInt(0).Sub(one, fee) // 1 - fee
892+
minipoolScore.Mul(minipoolScore, bond) // Multiply by bond
893+
minipoolScore.Div(minipoolScore, validatorReq) // Divide by 32 to get the bond as a fraction of a total validator
894+
minipoolScore.Add(minipoolScore, fee) // Total = fee + (bond/32)(1 - fee)
895+
896+
// Add it to the minipool's score and the total score
897+
validator.AttestationScore.Add(&validator.AttestationScore.Int, minipoolScore)
898+
r.totalAttestationScore.Add(r.totalAttestationScore, minipoolScore)
899+
r.successfulAttestations++
900+
}
899901
}
900902
}
901903

@@ -916,6 +918,18 @@ func (r *treeGeneratorImpl_v8) getDutiesForEpoch(committees beacon.Committees) e
916918
blockTime := r.genesisTime.Add(time.Second * time.Duration(r.beaconConfig.SecondsPerSlot*slotIndex))
917919
committeeIndex := committees.Index(idx)
918920

921+
// Add the committee size to the list, for calculating offset in post-electra aggregation_bits
922+
slotInfo, exists := r.intervalDutiesInfo.Slots[slotIndex]
923+
if !exists {
924+
slotInfo = &SlotInfo{
925+
Index: slotIndex,
926+
Committees: map[uint64]*CommitteeInfo{},
927+
CommitteeSizes: map[uint64]int{},
928+
}
929+
r.intervalDutiesInfo.Slots[slotIndex] = slotInfo
930+
}
931+
slotInfo.CommitteeSizes[committeeIndex] = committees.ValidatorCount(idx)
932+
919933
// Check if there are any RP validators in this committee
920934
rpValidators := map[int]*MinipoolInfo{}
921935
for position, validator := range committees.Validators(idx) {
@@ -948,14 +962,6 @@ func (r *treeGeneratorImpl_v8) getDutiesForEpoch(committees beacon.Committees) e
948962

949963
// If there are some RP validators, add this committee to the map
950964
if len(rpValidators) > 0 {
951-
slotInfo, exists := r.intervalDutiesInfo.Slots[slotIndex]
952-
if !exists {
953-
slotInfo = &SlotInfo{
954-
Index: slotIndex,
955-
Committees: map[uint64]*CommitteeInfo{},
956-
}
957-
r.intervalDutiesInfo.Slots[slotIndex] = slotInfo
958-
}
959965
slotInfo.Committees[committeeIndex] = &CommitteeInfo{
960966
Index: committeeIndex,
961967
Positions: rpValidators,

shared/services/rewards/generator-impl-v9-v10.go

Lines changed: 56 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -948,58 +948,61 @@ func (r *treeGeneratorImpl_v9_v10) checkAttestations(attestations []beacon.Attes
948948
if inclusionSlot-attestation.SlotIndex > r.beaconConfig.SlotsPerEpoch {
949949
continue
950950
}
951-
rpCommittee, exists := slotInfo.Committees[attestation.CommitteeIndex]
952-
if !exists {
953-
continue
954-
}
955-
blockTime := r.genesisTime.Add(time.Second * time.Duration(r.networkState.BeaconConfig.SecondsPerSlot*attestation.SlotIndex))
956951

957-
// Check if each RP validator attested successfully
958-
for position, validator := range rpCommittee.Positions {
959-
if !attestation.AggregationBits.BitAt(uint64(position)) {
952+
for _, committeeIndex := range attestation.CommitteeIndices() {
953+
rpCommittee, exists := slotInfo.Committees[uint64(committeeIndex)]
954+
if !exists {
960955
continue
961956
}
957+
blockTime := r.genesisTime.Add(time.Second * time.Duration(r.networkState.BeaconConfig.SecondsPerSlot*attestation.SlotIndex))
962958

963-
// This was seen, so remove it from the missing attestations and add it to the completed ones
964-
delete(rpCommittee.Positions, position)
965-
if len(rpCommittee.Positions) == 0 {
966-
delete(slotInfo.Committees, attestation.CommitteeIndex)
967-
}
968-
if len(slotInfo.Committees) == 0 {
969-
delete(r.intervalDutiesInfo.Slots, attestation.SlotIndex)
970-
}
971-
delete(validator.MissingAttestationSlots, attestation.SlotIndex)
959+
// Check if each RP validator attested successfully
960+
for position, validator := range rpCommittee.Positions {
961+
if !attestation.ValidatorAttested(committeeIndex, position, slotInfo.CommitteeSizes) {
962+
continue
963+
}
972964

973-
// Check if this minipool was opted into the SP for this block
974-
nodeDetails := r.nodeDetails[validator.NodeIndex]
975-
if blockTime.Before(nodeDetails.OptInTime) || blockTime.After(nodeDetails.OptOutTime) {
976-
// Not opted in
977-
continue
978-
}
965+
// This was seen, so remove it from the missing attestations and add it to the completed ones
966+
delete(rpCommittee.Positions, position)
967+
if len(rpCommittee.Positions) == 0 {
968+
delete(slotInfo.Committees, uint64(committeeIndex))
969+
}
970+
if len(slotInfo.Committees) == 0 {
971+
delete(r.intervalDutiesInfo.Slots, attestation.SlotIndex)
972+
}
973+
delete(validator.MissingAttestationSlots, attestation.SlotIndex)
979974

980-
eligibleBorrowedEth := nodeDetails.EligibleBorrowedEth
981-
_, percentOfBorrowedEth := r.networkState.GetStakedRplValueInEthAndPercentOfBorrowedEth(eligibleBorrowedEth, nodeDetails.RplStake)
975+
// Check if this minipool was opted into the SP for this block
976+
nodeDetails := r.nodeDetails[validator.NodeIndex]
977+
if blockTime.Before(nodeDetails.OptInTime) || blockTime.After(nodeDetails.OptOutTime) {
978+
// Not opted in
979+
continue
980+
}
982981

983-
// Mark this duty as completed
984-
validator.CompletedAttestations[attestation.SlotIndex] = true
982+
eligibleBorrowedEth := nodeDetails.EligibleBorrowedEth
983+
_, percentOfBorrowedEth := r.networkState.GetStakedRplValueInEthAndPercentOfBorrowedEth(eligibleBorrowedEth, nodeDetails.RplStake)
985984

986-
// Get the pseudoscore for this attestation
987-
details := r.networkState.MinipoolDetailsByAddress[validator.Address]
988-
bond, fee := details.GetMinipoolBondAndNodeFee(blockTime)
985+
// Mark this duty as completed
986+
validator.CompletedAttestations[attestation.SlotIndex] = true
989987

990-
if r.rewardsFile.RulesetVersion >= 10 {
991-
fee = fees.GetMinipoolFeeWithBonus(bond, fee, percentOfBorrowedEth)
992-
}
988+
// Get the pseudoscore for this attestation
989+
details := r.networkState.MinipoolDetailsByAddress[validator.Address]
990+
bond, fee := details.GetMinipoolBondAndNodeFee(blockTime)
993991

994-
minipoolScore := big.NewInt(0).Sub(oneEth, fee) // 1 - fee
995-
minipoolScore.Mul(minipoolScore, bond) // Multiply by bond
996-
minipoolScore.Div(minipoolScore, thirtyTwoEth) // Divide by 32 to get the bond as a fraction of a total validator
997-
minipoolScore.Add(minipoolScore, fee) // Total = fee + (bond/32)(1 - fee)
992+
if r.rewardsFile.RulesetVersion >= 10 {
993+
fee = fees.GetMinipoolFeeWithBonus(bond, fee, percentOfBorrowedEth)
994+
}
998995

999-
// Add it to the minipool's score and the total score
1000-
validator.AttestationScore.Add(&validator.AttestationScore.Int, minipoolScore)
1001-
r.totalAttestationScore.Add(r.totalAttestationScore, minipoolScore)
1002-
r.successfulAttestations++
996+
minipoolScore := big.NewInt(0).Sub(oneEth, fee) // 1 - fee
997+
minipoolScore.Mul(minipoolScore, bond) // Multiply by bond
998+
minipoolScore.Div(minipoolScore, thirtyTwoEth) // Divide by 32 to get the bond as a fraction of a total validator
999+
minipoolScore.Add(minipoolScore, fee) // Total = fee + (bond/32)(1 - fee)
1000+
1001+
// Add it to the minipool's score and the total score
1002+
validator.AttestationScore.Add(&validator.AttestationScore.Int, minipoolScore)
1003+
r.totalAttestationScore.Add(r.totalAttestationScore, minipoolScore)
1004+
r.successfulAttestations++
1005+
}
10031006
}
10041007
}
10051008

@@ -1020,6 +1023,18 @@ func (r *treeGeneratorImpl_v9_v10) getDutiesForEpoch(committees beacon.Committee
10201023
blockTime := r.genesisTime.Add(time.Second * time.Duration(r.beaconConfig.SecondsPerSlot*slotIndex))
10211024
committeeIndex := committees.Index(idx)
10221025

1026+
// Add the committee size to the list, for calculating offset in post-electra aggregation_bits
1027+
slotInfo, exists := r.intervalDutiesInfo.Slots[slotIndex]
1028+
if !exists {
1029+
slotInfo = &SlotInfo{
1030+
Index: slotIndex,
1031+
Committees: map[uint64]*CommitteeInfo{},
1032+
CommitteeSizes: map[uint64]int{},
1033+
}
1034+
r.intervalDutiesInfo.Slots[slotIndex] = slotInfo
1035+
}
1036+
slotInfo.CommitteeSizes[committeeIndex] = committees.ValidatorCount(idx)
1037+
10231038
// Check if there are any RP validators in this committee
10241039
rpValidators := map[int]*MinipoolInfo{}
10251040
for position, validator := range committees.Validators(idx) {
@@ -1052,14 +1067,6 @@ func (r *treeGeneratorImpl_v9_v10) getDutiesForEpoch(committees beacon.Committee
10521067

10531068
// If there are some RP validators, add this committee to the map
10541069
if len(rpValidators) > 0 {
1055-
slotInfo, exists := r.intervalDutiesInfo.Slots[slotIndex]
1056-
if !exists {
1057-
slotInfo = &SlotInfo{
1058-
Index: slotIndex,
1059-
Committees: map[uint64]*CommitteeInfo{},
1060-
}
1061-
r.intervalDutiesInfo.Slots[slotIndex] = slotInfo
1062-
}
10631070
slotInfo.Committees[committeeIndex] = &CommitteeInfo{
10641071
Index: committeeIndex,
10651072
Positions: rpValidators,

0 commit comments

Comments
 (0)