2024-01-01 21:18:11 +00:00
|
|
|
package handler
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"net/http"
|
|
|
|
"sort"
|
|
|
|
"strconv"
|
|
|
|
|
|
|
|
libcommon "github.com/ledgerwatch/erigon-lib/common"
|
|
|
|
"github.com/ledgerwatch/erigon/cl/beacon/beaconhttp"
|
|
|
|
"github.com/ledgerwatch/erigon/cl/cltypes/solid"
|
|
|
|
"github.com/ledgerwatch/erigon/cl/persistence/beacon_indicies"
|
|
|
|
state_accessors "github.com/ledgerwatch/erigon/cl/persistence/state"
|
|
|
|
)
|
|
|
|
|
|
|
|
type syncDutyResponse struct {
|
|
|
|
Pubkey libcommon.Bytes48 `json:"pubkey"`
|
|
|
|
ValidatorIndex uint64 `json:"validator_index,string"`
|
|
|
|
ValidatorSyncCommitteeIndicies []string `json:"validator_sync_committee_indicies"`
|
|
|
|
}
|
|
|
|
|
2024-01-15 05:22:34 +00:00
|
|
|
func (a *ApiHandler) getSyncDuties(w http.ResponseWriter, r *http.Request) (*beaconhttp.BeaconResponse, error) {
|
|
|
|
epoch, err := beaconhttp.EpochFromRequest(r)
|
2024-01-01 21:18:11 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// compute the sync committee period
|
|
|
|
period := epoch / a.beaconChainCfg.EpochsPerSyncCommitteePeriod
|
|
|
|
|
|
|
|
var idxsStr []string
|
|
|
|
if err := json.NewDecoder(r.Body).Decode(&idxsStr); err != nil {
|
|
|
|
return nil, beaconhttp.NewEndpointError(http.StatusBadRequest, fmt.Errorf("could not decode request body: %w. request body is required.", err).Error())
|
|
|
|
}
|
|
|
|
if len(idxsStr) == 0 {
|
2024-01-15 05:22:34 +00:00
|
|
|
return newBeaconResponse([]string{}).WithOptimistic(false), nil
|
2024-01-01 21:18:11 +00:00
|
|
|
}
|
|
|
|
duplicates := map[int]struct{}{}
|
|
|
|
// convert the request to uint64
|
|
|
|
idxs := make([]uint64, 0, len(idxsStr))
|
|
|
|
for _, idxStr := range idxsStr {
|
|
|
|
|
|
|
|
idx, err := strconv.ParseUint(idxStr, 10, 64)
|
|
|
|
if err != nil {
|
|
|
|
return nil, beaconhttp.NewEndpointError(http.StatusBadRequest, fmt.Errorf("could not parse validator index: %w", err).Error())
|
|
|
|
}
|
|
|
|
if _, ok := duplicates[int(idx)]; ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
idxs = append(idxs, idx)
|
|
|
|
duplicates[int(idx)] = struct{}{}
|
|
|
|
}
|
|
|
|
|
|
|
|
tx, err := a.indiciesDB.BeginRo(r.Context())
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer tx.Rollback()
|
|
|
|
|
|
|
|
// Try to find a slot in the epoch or close to it
|
|
|
|
referenceSlot := ((epoch + 1) * a.beaconChainCfg.SlotsPerEpoch) - 1
|
|
|
|
|
|
|
|
// Find the first slot in the epoch (or close enough that have a sync committee)
|
|
|
|
var referenceRoot libcommon.Hash
|
|
|
|
for ; referenceRoot != (libcommon.Hash{}); referenceSlot-- {
|
|
|
|
referenceRoot, err = beacon_indicies.ReadCanonicalBlockRoot(tx, referenceSlot)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
referencePeriod := (referenceSlot / a.beaconChainCfg.SlotsPerEpoch) / a.beaconChainCfg.EpochsPerSyncCommitteePeriod
|
|
|
|
// Now try reading the sync committee
|
|
|
|
currentSyncCommittee, nextSyncCommittee, ok := a.forkchoiceStore.GetSyncCommittees(referenceRoot)
|
|
|
|
if !ok {
|
|
|
|
roundedSlotToPeriod := a.beaconChainCfg.RoundSlotToSyncCommitteePeriod(referenceSlot)
|
|
|
|
switch {
|
|
|
|
case referencePeriod == period:
|
|
|
|
currentSyncCommittee, err = state_accessors.ReadCurrentSyncCommittee(tx, roundedSlotToPeriod)
|
|
|
|
case referencePeriod+1 == period:
|
|
|
|
nextSyncCommittee, err = state_accessors.ReadNextSyncCommittee(tx, roundedSlotToPeriod)
|
|
|
|
default:
|
|
|
|
return nil, beaconhttp.NewEndpointError(http.StatusNotFound, fmt.Sprintf("could not find sync committee for epoch %d", epoch))
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
var syncCommittee *solid.SyncCommittee
|
|
|
|
// Determine which one to use. TODO(Giulio2002): Make this less rendundant.
|
|
|
|
switch {
|
|
|
|
case referencePeriod == period:
|
|
|
|
syncCommittee = currentSyncCommittee
|
|
|
|
case referencePeriod+1 == period:
|
|
|
|
syncCommittee = nextSyncCommittee
|
|
|
|
default:
|
|
|
|
return nil, beaconhttp.NewEndpointError(http.StatusNotFound, fmt.Sprintf("could not find sync committee for epoch %d", epoch))
|
|
|
|
}
|
|
|
|
if syncCommittee == nil {
|
|
|
|
return nil, beaconhttp.NewEndpointError(http.StatusNotFound, fmt.Sprintf("could not find sync committee for epoch %d", epoch))
|
|
|
|
}
|
|
|
|
// Now we have the sync committee, we can initialize our response set
|
|
|
|
dutiesSet := map[uint64]*syncDutyResponse{}
|
|
|
|
for _, idx := range idxs {
|
|
|
|
publicKey, err := state_accessors.ReadPublicKeyByIndex(tx, idx)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if publicKey == (libcommon.Bytes48{}) {
|
|
|
|
return nil, beaconhttp.NewEndpointError(http.StatusNotFound, fmt.Sprintf("could not find validator with index %d", idx))
|
|
|
|
}
|
|
|
|
dutiesSet[idx] = &syncDutyResponse{
|
|
|
|
Pubkey: publicKey,
|
|
|
|
ValidatorIndex: idx,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Now we can iterate over the sync committee and fill the response
|
|
|
|
for idx, committeePartecipantPublicKey := range syncCommittee.GetCommittee() {
|
|
|
|
committeePartecipantIndex, ok, err := state_accessors.ReadValidatorIndexByPublicKey(tx, committeePartecipantPublicKey)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
if !ok {
|
|
|
|
return nil, beaconhttp.NewEndpointError(http.StatusNotFound, fmt.Sprintf("could not find validator with public key %x", committeePartecipantPublicKey))
|
|
|
|
}
|
|
|
|
if _, ok := dutiesSet[committeePartecipantIndex]; !ok {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
dutiesSet[committeePartecipantIndex].ValidatorSyncCommitteeIndicies = append(
|
|
|
|
dutiesSet[committeePartecipantIndex].ValidatorSyncCommitteeIndicies,
|
|
|
|
strconv.FormatUint(uint64(idx), 10))
|
|
|
|
}
|
|
|
|
// Now we can convert the map to a slice
|
|
|
|
duties := make([]*syncDutyResponse, 0, len(dutiesSet))
|
|
|
|
for _, duty := range dutiesSet {
|
|
|
|
if len(duty.ValidatorSyncCommitteeIndicies) == 0 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
duties = append(duties, duty)
|
|
|
|
}
|
|
|
|
sort.Slice(duties, func(i, j int) bool {
|
|
|
|
return duties[i].ValidatorIndex < duties[j].ValidatorIndex
|
|
|
|
})
|
|
|
|
|
2024-01-15 05:22:34 +00:00
|
|
|
return newBeaconResponse(duties).WithOptimistic(false), nil
|
2024-01-01 21:18:11 +00:00
|
|
|
}
|