erigon-pulse/cl/beacon/handler/duties_proposer.go
2024-01-01 21:11:31 +01:00

123 lines
3.9 KiB
Go

package handler
import (
"crypto/sha256"
"encoding/binary"
"net/http"
"sync"
"github.com/ledgerwatch/erigon/cl/beacon/beaconhttp"
"github.com/ledgerwatch/erigon/cl/persistence/base_encoding"
state_accessors "github.com/ledgerwatch/erigon/cl/persistence/state"
shuffling2 "github.com/ledgerwatch/erigon/cl/phase1/core/state/shuffling"
libcommon "github.com/ledgerwatch/erigon-lib/common"
"github.com/ledgerwatch/erigon-lib/kv"
)
type proposerDuties struct {
Pubkey libcommon.Bytes48 `json:"pubkey"`
ValidatorIndex uint64 `json:"validator_index,string"`
Slot uint64 `json:"slot,string"`
}
func (a *ApiHandler) getDutiesProposer(w http.ResponseWriter, r *http.Request) (*beaconResponse, error) {
epoch, err := epochFromRequest(r)
if err != nil {
return nil, beaconhttp.NewEndpointError(http.StatusBadRequest, err.Error())
}
if epoch < a.forkchoiceStore.FinalizedCheckpoint().Epoch() {
tx, err := a.indiciesDB.BeginRo(r.Context())
if err != nil {
return nil, err
}
defer tx.Rollback()
key := base_encoding.Encode64ToBytes4(epoch)
indiciesBytes, err := tx.GetOne(kv.Proposers, key)
if err != nil {
return nil, err
}
if len(indiciesBytes) != int(a.beaconChainCfg.SlotsPerEpoch*4) {
return nil, beaconhttp.NewEndpointError(http.StatusInternalServerError, "proposer duties is corrupted")
}
duties := make([]proposerDuties, a.beaconChainCfg.SlotsPerEpoch)
for i := uint64(0); i < a.beaconChainCfg.SlotsPerEpoch; i++ {
validatorIndex := binary.BigEndian.Uint32(indiciesBytes[i*4 : i*4+4])
var pk libcommon.Bytes48
pk, err := state_accessors.ReadPublicKeyByIndex(tx, uint64(validatorIndex))
if err != nil {
return nil, err
}
duties[i] = proposerDuties{
Pubkey: pk,
ValidatorIndex: uint64(validatorIndex),
Slot: epoch*a.beaconChainCfg.SlotsPerEpoch + i,
}
}
return newBeaconResponse(duties).withFinalized(true).withVersion(a.beaconChainCfg.GetCurrentStateVersion(epoch)), nil
}
// We need to compute our duties
state, cancel := a.syncedData.HeadState()
defer cancel()
if state == nil {
return nil, beaconhttp.NewEndpointError(http.StatusServiceUnavailable, "beacon node is syncing")
}
expectedSlot := epoch * a.beaconChainCfg.SlotsPerEpoch
duties := make([]proposerDuties, a.beaconChainCfg.SlotsPerEpoch)
wg := sync.WaitGroup{}
for slot := expectedSlot; slot < expectedSlot+a.beaconChainCfg.SlotsPerEpoch; slot++ {
var proposerIndex uint64
// Lets do proposer index computation
mixPosition := (epoch + a.beaconChainCfg.EpochsPerHistoricalVector - a.beaconChainCfg.MinSeedLookahead - 1) %
a.beaconChainCfg.EpochsPerHistoricalVector
// Input for the seed hash.
mix := state.GetRandaoMix(int(mixPosition))
input := shuffling2.GetSeed(a.beaconChainCfg, mix, epoch, a.beaconChainCfg.DomainBeaconProposer)
slotByteArray := make([]byte, 8)
binary.LittleEndian.PutUint64(slotByteArray, slot)
// Add slot to the end of the input.
inputWithSlot := append(input[:], slotByteArray...)
hash := sha256.New()
// Calculate the hash.
hash.Write(inputWithSlot)
seed := hash.Sum(nil)
indices := state.GetActiveValidatorsIndices(epoch)
// Write the seed to an array.
seedArray := [32]byte{}
copy(seedArray[:], seed)
wg.Add(1)
// Do it in parallel
go func(i, slot uint64, indicies []uint64, seedArray [32]byte) {
defer wg.Done()
proposerIndex, err = shuffling2.ComputeProposerIndex(state.BeaconState, indices, seedArray)
if err != nil {
panic(err)
}
var pk libcommon.Bytes48
pk, err = state.ValidatorPublicKey(int(proposerIndex))
if err != nil {
panic(err)
}
duties[i] = proposerDuties{
Pubkey: pk,
ValidatorIndex: proposerIndex,
Slot: slot,
}
}(slot-expectedSlot, slot, indices, seedArray)
}
wg.Wait()
return newBeaconResponse(duties).withFinalized(false).withVersion(a.beaconChainCfg.GetCurrentStateVersion(epoch)), nil
}