Skip to content

Commit

Permalink
Made caplin endpoints flaggable and added documentation to the README. (
Browse files Browse the repository at this point in the history
erigontech#9458)

* Fixed small hiccup in attestations deltas
* Added flags to caplin to determine which endpoint to expose.
* Fused ValidatorAPI <-> APIHandler
  • Loading branch information
Giulio2002 authored Feb 16, 2024
1 parent e66842d commit dd1c521
Show file tree
Hide file tree
Showing 25 changed files with 508 additions and 656 deletions.
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,15 @@ An accessible and complete version of the documentation is available at **[erigo
+ [GoDoc](https://godoc.org/github.com/ledgerwatch/erigon)
+ [Beacon Chain](#beacon-chain-consensus-layer)
+ [Dev Chain](#dev-chain)
+ [Caplin (Internal Consensus Layer)](#caplin)

- [Key features](#key-features)
+ [More Efficient State Storage](#more-efficient-state-storage)
+ [Faster Initial Sync](#faster-initial-sync)
+ [JSON-RPC daemon](#json-rpc-daemon)
+ [Run all components by docker-compose](#run-all-components-by-docker-compose)
+ [Grafana dashboard](#grafana-dashboard)
+ [Internal Consensus Layer](#caplin)
- [Documentation](#documentation)
- [FAQ](#faq)
- [Getting in touch](#getting-in-touch)
Expand Down Expand Up @@ -306,6 +308,26 @@ Once Erigon is running, you need to point your CL client to `<erigon address>:85
where `<erigon address>` is either `localhost` or the IP address of the device running Erigon, and also point to the JWT
secret path created by Erigon.

### Caplin

Caplin is a full-fledged validating Consensus Client like Prysm, Lighthouse, Teku, Nimbus and Lodestar. Its goal is:

* provide better stability
* Validation of the chain
* Stay in sync
* keep the execution of blocks on chain tip
* serve the Beacon API using a fast and compact data model alongside low CPU and memory usage.

The main reason why developed a new Consensus Layer is to experiment with the possible benefits that could come with it. For example, The Engine API does not work well with Erigon. The Engine API sends data one block at a time, which does not suit how Erigon works. Erigon is designed to handle many blocks simultaneously and needs to sort and process data efficiently. Therefore, it would be better for Erigon to handle the blocks independently instead of relying on the Engine API.

#### Caplin's Usage.

Caplin can be enabled through the `--internalcl` flag. from that point on, an external Consensus Layer will not be need anymore.

Caplin also has an archivial mode for historical states and blocks. it can be enabled through the `--caplin.archive` flag.
In order to enable the caplin's Beacon API, the flag `--beacon.api=<namespaces>` must be added.
e.g: `--beacon.api=beacon,builder,config,debug,events,node,validator,rewards,lighthouse` will enable all endpoints. **NOTE: Caplin is not staking-ready so aggregation endpoints are still to be implemented. Additionally enabling the Beacon API will lead to a 6 GB higher RAM usage.

### Multiple Instances / One Machine

Define 6 flags to avoid conflicts: `--datadir --port --http.port --authrpc.port --torrent.port --private.api.addr`.
Expand Down
53 changes: 52 additions & 1 deletion cl/beacon/beacon_router_configuration/cfg.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package beacon_router_configuration

import "time"
import (
"fmt"
"strings"
"time"
)

type RouterConfiguration struct {
Active bool
Expand All @@ -14,4 +18,51 @@ type RouterConfiguration struct {
ReadTimeTimeout time.Duration
IdleTimeout time.Duration
WriteTimeout time.Duration

Beacon bool
Builder bool
Config bool
Debug bool
Events bool
Node bool
Validator bool
Lighthouse bool
}

func (r *RouterConfiguration) UnwrapEndpointsList(l []string) error {
r.Active = len(l) > 0
for _, v := range l {
// convert v to lowercase
v = strings.ToLower(v)
switch v {
case "beacon":
r.Beacon = true
case "builder":
r.Builder = true
case "config":
r.Config = true
case "debug":
r.Debug = true
case "events":
r.Events = true
case "node":
r.Node = true
case "validator":
r.Validator = true
case "lighthouse":
r.Lighthouse = true
default:
r.Active = false
r.Beacon = false
r.Builder = false
r.Config = false
r.Debug = false
r.Events = false
r.Node = false
r.Validator = false
r.Lighthouse = false
return fmt.Errorf("unknown endpoint for beacon.api: %s. known endpoints: beacon, builder, config, debug, events, node, validator, rewards, lighthouse", v)
}
}
return nil
}
3 changes: 0 additions & 3 deletions cl/beacon/beaconhttp/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"net/http"
"reflect"
"strings"
"time"

"github.com/ledgerwatch/erigon-lib/types/ssz"
"github.com/ledgerwatch/erigon/cl/phase1/forkchoice/fork_graph"
Expand Down Expand Up @@ -85,9 +84,7 @@ func HandleEndpointFunc[T any](h EndpointHandlerFunc[T]) http.HandlerFunc {

func HandleEndpoint[T any](h EndpointHandler[T]) http.HandlerFunc {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
ans, err := h.Handle(w, r)
log.Debug("beacon api request", "endpoint", r.URL.Path, "duration", time.Since(start))
if err != nil {
var endpointError *EndpointError
if e, ok := err.(*EndpointError); ok {
Expand Down
17 changes: 8 additions & 9 deletions cl/beacon/handler/attestation_rewards.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ func (a *ApiHandler) PostEthV1BeaconRewardsAttestations(w http.ResponseWriter, r
version := a.beaconChainCfg.GetCurrentStateVersion(epoch)

// finalized data
if epoch > a.forkchoiceStore.LowestAvaiableSlot() {
if epoch > a.forkchoiceStore.LowestAvaiableSlot()/a.beaconChainCfg.SlotsPerEpoch {
minRange := epoch * a.beaconChainCfg.SlotsPerEpoch
maxRange := (epoch + 1) * a.beaconChainCfg.SlotsPerEpoch
var blockRoot libcommon.Hash
Expand All @@ -101,14 +101,6 @@ func (a *ApiHandler) PostEthV1BeaconRewardsAttestations(w http.ResponseWriter, r
if version == clparams.Phase0Version {
return nil, beaconhttp.NewEndpointError(http.StatusHTTPVersionNotSupported, fmt.Errorf("phase0 state is not supported when there is no antiquation"))
}
validatorSet, err := a.forkchoiceStore.GetValidatorSet(blockRoot)
if err != nil {
return nil, err
}
if validatorSet == nil {
return nil, beaconhttp.NewEndpointError(http.StatusNotFound, fmt.Errorf("no validator set found for this epoch"))
}

inactivityScores, err := a.forkchoiceStore.GetInactivitiesScores(blockRoot)
if err != nil {
return nil, err
Expand All @@ -124,6 +116,13 @@ func (a *ApiHandler) PostEthV1BeaconRewardsAttestations(w http.ResponseWriter, r
if prevPartecipation == nil {
return nil, beaconhttp.NewEndpointError(http.StatusNotFound, fmt.Errorf("no previous partecipation found for this epoch"))
}
validatorSet, err := a.forkchoiceStore.GetValidatorSet(blockRoot)
if err != nil {
return nil, err
}
if validatorSet == nil {
return nil, beaconhttp.NewEndpointError(http.StatusNotFound, fmt.Errorf("no validator set found for this epoch"))
}

ok, finalizedCheckpoint, _, _ := a.forkchoiceStore.GetFinalityCheckpoints(blockRoot)
if !ok {
Expand Down
66 changes: 66 additions & 0 deletions cl/beacon/handler/events.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package handler

import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"sync"

"github.com/gfx-labs/sse"
"github.com/ledgerwatch/log/v3"
)

var validTopics = map[string]struct{}{
"head": {},
"block": {},
"attestation": {},
"voluntary_exit": {},
"bls_to_execution_change": {},
"finalized_checkpoint": {},
"chain_reorg": {},
"contribution_and_proof": {},
"light_client_finality_update": {},
"light_client_optimistic_update": {},
"payload_attributes": {},
"*": {},
}

func (a *ApiHandler) EventSourceGetV1Events(w http.ResponseWriter, r *http.Request) {
sink, err := sse.DefaultUpgrader.Upgrade(w, r)
if err != nil {
http.Error(w, "failed to upgrade", http.StatusInternalServerError)
}
topics := r.URL.Query()["topics"]
for _, v := range topics {
if _, ok := validTopics[v]; !ok {
http.Error(w, fmt.Sprintf("invalid Topic: %s", v), http.StatusBadRequest)
}
}
var mu sync.Mutex
closer, err := a.emitters.Subscribe(topics, func(topic string, item any) {
buf := &bytes.Buffer{}
err := json.NewEncoder(buf).Encode(item)
if err != nil {
// return early
return
}
mu.Lock()
err = sink.Encode(&sse.Event{
Event: []byte(topic),
Data: buf,
})
mu.Unlock()
if err != nil {
log.Error("failed to encode data", "topic", topic, "err", err)
}
// OK to ignore this error. maybe should log it later?
})
if err != nil {
http.Error(w, "failed to subscribe", http.StatusInternalServerError)
return
}
defer closer()
<-r.Context().Done()

}
Loading

0 comments on commit dd1c521

Please sign in to comment.