Skip to content

Commit

Permalink
Merge branch 'devel' into eof
Browse files Browse the repository at this point in the history
  • Loading branch information
racytech committed Nov 21, 2023
2 parents e46ba9d + 0aa06af commit 1292572
Show file tree
Hide file tree
Showing 100 changed files with 3,157 additions and 819 deletions.
120 changes: 120 additions & 0 deletions cl/beacon/handler/duties_proposer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package handler

import (
"crypto/sha256"
"encoding/binary"
"fmt"
"net/http"
"sync"

"github.com/ledgerwatch/erigon/cl/phase1/core/state/lru"
shuffling2 "github.com/ledgerwatch/erigon/cl/phase1/core/state/shuffling"

libcommon "github.com/ledgerwatch/erigon-lib/common"
"github.com/ledgerwatch/erigon/cl/clparams"
)

type proposerDuties struct {
Pubkey libcommon.Bytes48 `json:"pubkey"`
ValidatorIndex uint64 `json:"validator_index"`
Slot uint64 `json:"slot"`
}

// The proposer knight respects its duties and hands over which proposer should be proposing in which slot.
type proposerKnight struct {
// The proposer knight's duties.
dutiesCache *lru.Cache[uint64, []proposerDuties]
}

func (a *ApiHandler) getDutiesProposer(r *http.Request) (data any, finalized *bool, version *clparams.StateVersion, httpStatus int, err error) {
if a.dutiesCache == nil {
a.dutiesCache, err = lru.New[uint64, []proposerDuties]("proposerKnight", 32)
}
if err != nil {
httpStatus = http.StatusInternalServerError
return
}
var epoch uint64

epoch, err = epochFromRequest(r)
if err != nil {
httpStatus = http.StatusBadRequest
return
}

if epoch < a.forkchoiceStore.FinalizedCheckpoint().Epoch() {
err = fmt.Errorf("invalid epoch")
httpStatus = http.StatusBadRequest
return
}

// We need to compute our duties
state, cancel := a.syncedData.HeadState()
defer cancel()
if state == nil {
err = fmt.Errorf("node is syncing")
httpStatus = http.StatusInternalServerError
return
}

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 {
httpStatus = http.StatusInternalServerError
return
}
var pk libcommon.Bytes48
pk, err = state.ValidatorPublicKey(int(proposerIndex))
if err != nil {
httpStatus = http.StatusInternalServerError
return
}
duties[i] = proposerDuties{
Pubkey: pk,
ValidatorIndex: proposerIndex,
Slot: slot,
}
}(slot-expectedSlot, slot, indices, seedArray)
}
wg.Wait()
a.dutiesCache.Add(epoch, duties)
data = duties
finalized = new(bool)
*finalized = false
httpStatus = http.StatusAccepted
return

}
24 changes: 23 additions & 1 deletion cl/beacon/handler/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"regexp"
"strconv"
"strings"
"time"

"github.com/go-chi/chi/v5"
libcommon "github.com/ledgerwatch/erigon-lib/common"
Expand All @@ -30,6 +31,11 @@ func beaconHandlerWrapper(fn beaconHandlerFn, supportSSZ bool) func(w http.Respo
return func(w http.ResponseWriter, r *http.Request) {
accept := r.Header.Get("Accept")
isSSZ := !strings.Contains(accept, "application/json") && strings.Contains(accept, "application/stream-octect")
start := time.Now()
defer func() {
log.Debug("[Beacon API] finished", "method", r.Method, "path", r.URL.Path, "duration", time.Since(start))
}()

data, finalized, version, httpStatus, err := fn(r)
if err != nil {
w.WriteHeader(httpStatus)
Expand Down Expand Up @@ -64,7 +70,9 @@ func beaconHandlerWrapper(fn beaconHandlerFn, supportSSZ bool) func(w http.Respo
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(httpStatus)
json.NewEncoder(w).Encode(resp)
if err := json.NewEncoder(w).Encode(resp); err != nil {
log.Warn("[Beacon API] failed", "method", r.Method, "err", err, "ssz", isSSZ)
}
}
}

Expand Down Expand Up @@ -108,6 +116,20 @@ func (c *segmentID) getRoot() *libcommon.Hash {
return c.root
}

func epochFromRequest(r *http.Request) (uint64, error) {
// Must only be a number
regex := regexp.MustCompile(`^\d+$`)
epoch := chi.URLParam(r, "epoch")
if !regex.MatchString(epoch) {
return 0, fmt.Errorf("invalid path variable: {epoch}")
}
epochMaybe, err := strconv.ParseUint(epoch, 10, 64)
if err != nil {
return 0, err
}
return epochMaybe, nil
}

func blockIdFromRequest(r *http.Request) (*segmentID, error) {
regex := regexp.MustCompile(`^(?:0x[0-9a-fA-F]{64}|head|finalized|genesis|\d+)$`)

Expand Down
20 changes: 14 additions & 6 deletions cl/beacon/handler/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

"github.com/go-chi/chi/v5"
"github.com/ledgerwatch/erigon-lib/kv"
"github.com/ledgerwatch/erigon/cl/beacon/synced_data"
"github.com/ledgerwatch/erigon/cl/clparams"
"github.com/ledgerwatch/erigon/cl/persistence"
"github.com/ledgerwatch/erigon/cl/phase1/forkchoice"
Expand All @@ -23,10 +24,13 @@ type ApiHandler struct {
beaconChainCfg *clparams.BeaconChainConfig
forkchoiceStore forkchoice.ForkChoiceStorage
operationsPool pool.OperationsPool
syncedData *synced_data.SyncedDataManager

proposerKnight
}

func NewApiHandler(genesisConfig *clparams.GenesisConfig, beaconChainConfig *clparams.BeaconChainConfig, source persistence.RawBeaconBlockChain, indiciesDB kv.RoDB, forkchoiceStore forkchoice.ForkChoiceStorage, operationsPool pool.OperationsPool, rcsn freezeblocks.BeaconSnapshotReader) *ApiHandler {
return &ApiHandler{o: sync.Once{}, genesisCfg: genesisConfig, beaconChainCfg: beaconChainConfig, indiciesDB: indiciesDB, forkchoiceStore: forkchoiceStore, operationsPool: operationsPool, blockReader: rcsn}
func NewApiHandler(genesisConfig *clparams.GenesisConfig, beaconChainConfig *clparams.BeaconChainConfig, source persistence.RawBeaconBlockChain, indiciesDB kv.RoDB, forkchoiceStore forkchoice.ForkChoiceStorage, operationsPool pool.OperationsPool, rcsn freezeblocks.BeaconSnapshotReader, syncedData *synced_data.SyncedDataManager) *ApiHandler {
return &ApiHandler{o: sync.Once{}, genesisCfg: genesisConfig, beaconChainCfg: beaconChainConfig, indiciesDB: indiciesDB, forkchoiceStore: forkchoiceStore, operationsPool: operationsPool, blockReader: rcsn, syncedData: syncedData}
}

func (a *ApiHandler) init() {
Expand Down Expand Up @@ -65,12 +69,11 @@ func (a *ApiHandler) init() {
r.Post("/sync_committees", nil)
})
r.Get("/node/syncing", nil)

r.Route("/states", func(r chi.Router) {
r.Get("/head/validators/{index}", nil) // otterscan
r.Get("/head/committees", nil) // otterscan
r.Route("/{state_id}", func(r chi.Router) {
r.Get("/validators", nil)
r.Get("/validators", beaconHandlerWrapper(a.getValidators, false))
r.Get("/root", beaconHandlerWrapper(a.getStateRoot, false))
r.Get("/fork", beaconHandlerWrapper(a.getStateFork, false))
r.Get("/validators/{id}", nil)
Expand All @@ -80,7 +83,7 @@ func (a *ApiHandler) init() {
r.Route("/validator", func(r chi.Router) {
r.Route("/duties", func(r chi.Router) {
r.Post("/attester/{epoch}", nil)
r.Get("/proposer/{epoch}", nil)
r.Get("/proposer/{epoch}", beaconHandlerWrapper(a.getDutiesProposer, false))
r.Post("/sync/{epoch}", nil)
})
r.Get("/blinded_blocks/{slot}", nil)
Expand All @@ -95,8 +98,13 @@ func (a *ApiHandler) init() {
})
})
r.Route("/v2", func(r chi.Router) {
r.Route("/debug", func(r chi.Router) {
r.Route("/beacon", func(r chi.Router) {
r.Get("/states/{state_id}", beaconHandlerWrapper(a.getFullState, true))
})
})
r.Route("/beacon", func(r chi.Router) {
r.Post("/blocks/{slot}", nil) //otterscan
r.Get("/blocks/{block_id}", beaconHandlerWrapper(a.getBlock, true)) //otterscan
})
r.Route("/validator", func(r chi.Router) {
r.Post("/blocks/{slot}", nil)
Expand Down
93 changes: 92 additions & 1 deletion cl/beacon/handler/states.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/ledgerwatch/erigon/cl/clparams"
"github.com/ledgerwatch/erigon/cl/cltypes"
"github.com/ledgerwatch/erigon/cl/persistence/beacon_indicies"
"github.com/ledgerwatch/erigon/cl/phase1/core/state"
"github.com/ledgerwatch/erigon/cl/utils"
)

Expand Down Expand Up @@ -52,7 +53,7 @@ func (a *ApiHandler) rootFromStateId(ctx context.Context, tx kv.Tx, stateId *seg
return libcommon.Hash{}, http.StatusInternalServerError, err
}
if root == (libcommon.Hash{}) {
return libcommon.Hash{}, http.StatusNotFound, fmt.Errorf("block not found %d", *stateId.getSlot())
return libcommon.Hash{}, http.StatusNotFound, fmt.Errorf("block not found")
}
return
}
Expand Down Expand Up @@ -168,3 +169,93 @@ func (a *ApiHandler) getStateRoot(r *http.Request) (data any, finalized *bool, v
httpStatus = http.StatusAccepted
return
}

func (a *ApiHandler) getFullState(r *http.Request) (data any, finalized *bool, version *clparams.StateVersion, httpStatus int, err error) {
var (
tx kv.Tx
blockId *segmentID
root libcommon.Hash
)

ctx := r.Context()

tx, err = a.indiciesDB.BeginRo(ctx)
if err != nil {
httpStatus = http.StatusInternalServerError
return
}
defer tx.Rollback()

blockId, err = stateIdFromRequest(r)
if err != nil {
httpStatus = http.StatusBadRequest
return
}
root, httpStatus, err = a.rootFromStateId(ctx, tx, blockId)
if err != nil {
return
}

blockRoot, err := beacon_indicies.ReadBlockRootByStateRoot(tx, root)
if err != nil {
httpStatus = http.StatusInternalServerError
return
}

data, err = a.forkchoiceStore.GetFullState(blockRoot, true)
if err != nil {
httpStatus = http.StatusBadRequest
return
}

finalized = new(bool)
*finalized = false
httpStatus = http.StatusAccepted
return
}

func (a *ApiHandler) getValidators(r *http.Request) (data any, finalized *bool, version *clparams.StateVersion, httpStatus int, err error) {
var (
tx kv.Tx
blockId *segmentID
root libcommon.Hash
)

ctx := r.Context()

tx, err = a.indiciesDB.BeginRo(ctx)
if err != nil {
httpStatus = http.StatusInternalServerError
return
}
defer tx.Rollback()

blockId, err = stateIdFromRequest(r)
if err != nil {
httpStatus = http.StatusBadRequest
return
}
root, httpStatus, err = a.rootFromStateId(ctx, tx, blockId)
if err != nil {
return
}

blockRoot, err := beacon_indicies.ReadBlockRootByStateRoot(tx, root)
if err != nil {
httpStatus = http.StatusInternalServerError
return
}

var state *state.CachingBeaconState
state, err = a.forkchoiceStore.GetFullState(blockRoot, true)
if err != nil {
httpStatus = http.StatusBadRequest
return
}

data = state.Validators()
finalized = new(bool)
*finalized = false
httpStatus = http.StatusAccepted
return
}
Loading

0 comments on commit 1292572

Please sign in to comment.