mirror of
https://gitlab.com/pulsechaincom/erigon-pulse.git
synced 2024-12-22 03:30:37 +00:00
more endpoints (#9109)
This commit is contained in:
parent
a959387aae
commit
bab123c07a
@ -54,13 +54,13 @@ func (e *EndpointError) WriteTo(w http.ResponseWriter) {
|
||||
}
|
||||
|
||||
type EndpointHandler[T any] interface {
|
||||
Handle(r *http.Request) (T, error)
|
||||
Handle(w http.ResponseWriter, r *http.Request) (T, error)
|
||||
}
|
||||
|
||||
type EndpointHandlerFunc[T any] func(r *http.Request) (T, error)
|
||||
type EndpointHandlerFunc[T any] func(w http.ResponseWriter, r *http.Request) (T, error)
|
||||
|
||||
func (e EndpointHandlerFunc[T]) Handle(r *http.Request) (T, error) {
|
||||
return e(r)
|
||||
func (e EndpointHandlerFunc[T]) Handle(w http.ResponseWriter, r *http.Request) (T, error) {
|
||||
return e(w, r)
|
||||
}
|
||||
|
||||
func HandleEndpointFunc[T any](h EndpointHandlerFunc[T]) http.HandlerFunc {
|
||||
@ -70,7 +70,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(r)
|
||||
ans, err := h.Handle(w, r)
|
||||
log.Debug("beacon api request", "endpoint", r.URL.Path, "duration", time.Since(start))
|
||||
if err != nil {
|
||||
log.Error("beacon api request error", "err", err)
|
||||
@ -83,7 +83,6 @@ func HandleEndpoint[T any](h EndpointHandler[T]) http.HandlerFunc {
|
||||
endpointError.WriteTo(w)
|
||||
return
|
||||
}
|
||||
// TODO: ssz handler
|
||||
// TODO: potentially add a context option to buffer these
|
||||
contentType := r.Header.Get("Accept")
|
||||
contentTypes := strings.Split(contentType, ",")
|
||||
@ -102,15 +101,31 @@ func HandleEndpoint[T any](h EndpointHandler[T]) http.HandlerFunc {
|
||||
}
|
||||
w.Write(encoded)
|
||||
case contentType == "*/*", contentType == "", slices.Contains(contentTypes, "text/html"), slices.Contains(contentTypes, "application/json"):
|
||||
w.Header().Add("content-type", "application/json")
|
||||
err := json.NewEncoder(w).Encode(ans)
|
||||
if err != nil {
|
||||
// this error is fatal, log to console
|
||||
log.Error("beaconapi failed to encode json", "type", reflect.TypeOf(ans), "err", err)
|
||||
if !isNil(ans) {
|
||||
w.Header().Add("content-type", "application/json")
|
||||
err := json.NewEncoder(w).Encode(ans)
|
||||
if err != nil {
|
||||
// this error is fatal, log to console
|
||||
log.Error("beaconapi failed to encode json", "type", reflect.TypeOf(ans), "err", err)
|
||||
}
|
||||
} else {
|
||||
w.WriteHeader(200)
|
||||
}
|
||||
default:
|
||||
http.Error(w, "content type must be application/json or application/octet-stream", http.StatusBadRequest)
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func isNil[T any](t T) bool {
|
||||
v := reflect.ValueOf(t)
|
||||
kind := v.Kind()
|
||||
// Must be one of these types to be nillable
|
||||
return (kind == reflect.Ptr ||
|
||||
kind == reflect.Interface ||
|
||||
kind == reflect.Slice ||
|
||||
kind == reflect.Map ||
|
||||
kind == reflect.Chan ||
|
||||
kind == reflect.Func) &&
|
||||
v.IsNil()
|
||||
}
|
||||
|
28
cl/beacon/beaconhttp/types.go
Normal file
28
cl/beacon/beaconhttp/types.go
Normal file
@ -0,0 +1,28 @@
|
||||
package beaconhttp
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type IntStr int
|
||||
|
||||
func (i IntStr) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(strconv.FormatInt(int64(i), 10))
|
||||
}
|
||||
|
||||
func (i *IntStr) UnmarshalJSON(b []byte) error {
|
||||
// Try string first
|
||||
var s string
|
||||
if err := json.Unmarshal(b, &s); err == nil {
|
||||
value, err := strconv.ParseInt(s, 10, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*i = IntStr(value)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Fallback to number
|
||||
return json.Unmarshal(b, (*int)(i))
|
||||
}
|
25
cl/beacon/building/endpoints.go
Normal file
25
cl/beacon/building/endpoints.go
Normal file
@ -0,0 +1,25 @@
|
||||
package building
|
||||
|
||||
import (
|
||||
"github.com/ledgerwatch/erigon-lib/common"
|
||||
"github.com/ledgerwatch/erigon/cl/beacon/beaconhttp"
|
||||
)
|
||||
|
||||
type BeaconCommitteeSubscription struct {
|
||||
ValidatorIndex int `json:"validator_index,string"`
|
||||
CommitteeIndex int `json:"committee_index,string"`
|
||||
CommitteesAtSlot int `json:"committees_at_slot,string"`
|
||||
Slot int `json:"slot,string"`
|
||||
IsAggregator bool `json:"is_aggregator"`
|
||||
}
|
||||
|
||||
type SyncCommitteeSubscription struct {
|
||||
ValidatorIndex int `json:"validator_index,string"`
|
||||
SyncCommitteeIndices []beaconhttp.IntStr `json:"sync_committee_indices"`
|
||||
UntilEpoch int `json:"until_epoch,string"`
|
||||
}
|
||||
|
||||
type PrepareBeaconProposer struct {
|
||||
ValidatorIndex int `json:"validator_index,string"`
|
||||
FeeRecipient common.Address `json:"fee_recipient"`
|
||||
}
|
25
cl/beacon/building/state.go
Normal file
25
cl/beacon/building/state.go
Normal file
@ -0,0 +1,25 @@
|
||||
package building
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/ledgerwatch/erigon-lib/common"
|
||||
)
|
||||
|
||||
type State struct {
|
||||
feeRecipients map[int]common.Address
|
||||
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
func NewState() *State {
|
||||
return &State{
|
||||
feeRecipients: map[int]common.Address{},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *State) SetFeeRecipient(idx int, address common.Address) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.feeRecipients[idx] = address
|
||||
}
|
@ -40,7 +40,7 @@ type attestationsRewardsResponse struct {
|
||||
TotalRewards []TotalReward `json:"total_rewards"`
|
||||
}
|
||||
|
||||
func (a *ApiHandler) getAttestationsRewards(r *http.Request) (*beaconResponse, error) {
|
||||
func (a *ApiHandler) getAttestationsRewards(w http.ResponseWriter, r *http.Request) (*beaconResponse, error) {
|
||||
ctx := r.Context()
|
||||
|
||||
tx, err := a.indiciesDB.BeginRo(ctx)
|
||||
|
@ -59,7 +59,7 @@ func (a *ApiHandler) rootFromBlockId(ctx context.Context, tx kv.Tx, blockId *seg
|
||||
return
|
||||
}
|
||||
|
||||
func (a *ApiHandler) getBlock(r *http.Request) (*beaconResponse, error) {
|
||||
func (a *ApiHandler) getBlock(w http.ResponseWriter, r *http.Request) (*beaconResponse, error) {
|
||||
ctx := r.Context()
|
||||
tx, err := a.indiciesDB.BeginRo(ctx)
|
||||
if err != nil {
|
||||
@ -94,7 +94,7 @@ func (a *ApiHandler) getBlock(r *http.Request) (*beaconResponse, error) {
|
||||
withVersion(blk.Version()), nil
|
||||
}
|
||||
|
||||
func (a *ApiHandler) getBlindedBlock(r *http.Request) (*beaconResponse, error) {
|
||||
func (a *ApiHandler) getBlindedBlock(w http.ResponseWriter, r *http.Request) (*beaconResponse, error) {
|
||||
ctx := r.Context()
|
||||
tx, err := a.indiciesDB.BeginRo(ctx)
|
||||
if err != nil {
|
||||
@ -133,7 +133,7 @@ func (a *ApiHandler) getBlindedBlock(r *http.Request) (*beaconResponse, error) {
|
||||
withVersion(blk.Version()), nil
|
||||
}
|
||||
|
||||
func (a *ApiHandler) getBlockAttestations(r *http.Request) (*beaconResponse, error) {
|
||||
func (a *ApiHandler) getBlockAttestations(w http.ResponseWriter, r *http.Request) (*beaconResponse, error) {
|
||||
ctx := r.Context()
|
||||
tx, err := a.indiciesDB.BeginRo(ctx)
|
||||
if err != nil {
|
||||
@ -164,7 +164,7 @@ func (a *ApiHandler) getBlockAttestations(r *http.Request) (*beaconResponse, err
|
||||
withVersion(blk.Version()), nil
|
||||
}
|
||||
|
||||
func (a *ApiHandler) getBlockRoot(r *http.Request) (*beaconResponse, error) {
|
||||
func (a *ApiHandler) getBlockRoot(w http.ResponseWriter, r *http.Request) (*beaconResponse, error) {
|
||||
ctx := r.Context()
|
||||
tx, err := a.indiciesDB.BeginRo(ctx)
|
||||
if err != nil {
|
||||
|
@ -17,7 +17,7 @@ type committeeResponse struct {
|
||||
Validators []string `json:"validators"` // do string directly but it is still a base10 number
|
||||
}
|
||||
|
||||
func (a *ApiHandler) getCommittees(r *http.Request) (*beaconResponse, error) {
|
||||
func (a *ApiHandler) getCommittees(w http.ResponseWriter, r *http.Request) (*beaconResponse, error) {
|
||||
ctx := r.Context()
|
||||
|
||||
epochReq, err := uint64FromQueryParams(r, "epoch")
|
||||
|
@ -9,11 +9,11 @@ import (
|
||||
"github.com/ledgerwatch/erigon/cl/cltypes"
|
||||
)
|
||||
|
||||
func (a *ApiHandler) getSpec(r *http.Request) (*beaconResponse, error) {
|
||||
func (a *ApiHandler) getSpec(w http.ResponseWriter, r *http.Request) (*beaconResponse, error) {
|
||||
return newBeaconResponse(a.beaconChainCfg), nil
|
||||
}
|
||||
|
||||
func (a *ApiHandler) getDepositContract(r *http.Request) (*beaconResponse, error) {
|
||||
func (a *ApiHandler) getDepositContract(w http.ResponseWriter, r *http.Request) (*beaconResponse, error) {
|
||||
return newBeaconResponse(struct {
|
||||
ChainId uint64 `json:"chain_id,string"`
|
||||
DepositContract string `json:"address"`
|
||||
@ -21,7 +21,7 @@ func (a *ApiHandler) getDepositContract(r *http.Request) (*beaconResponse, error
|
||||
|
||||
}
|
||||
|
||||
func (a *ApiHandler) getForkSchedule(r *http.Request) (*beaconResponse, error) {
|
||||
func (a *ApiHandler) getForkSchedule(w http.ResponseWriter, r *http.Request) (*beaconResponse, error) {
|
||||
response := []cltypes.Fork{}
|
||||
// create first response (unordered and incomplete)
|
||||
for currentVersion, epoch := range a.beaconChainCfg.ForkVersionSchedule {
|
||||
|
@ -21,7 +21,7 @@ type proposerDuties struct {
|
||||
Slot uint64 `json:"slot,string"`
|
||||
}
|
||||
|
||||
func (a *ApiHandler) getDutiesProposer(r *http.Request) (*beaconResponse, error) {
|
||||
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())
|
||||
|
@ -15,7 +15,7 @@ type genesisResponse struct {
|
||||
GenesisForkVersion libcommon.Bytes4 `json:"genesis_fork_version,omitempty"`
|
||||
}
|
||||
|
||||
func (a *ApiHandler) getGenesis(r *http.Request) (*beaconResponse, error) {
|
||||
func (a *ApiHandler) getGenesis(w http.ResponseWriter, r *http.Request) (*beaconResponse, error) {
|
||||
if a.genesisCfg == nil {
|
||||
return nil, beaconhttp.NewEndpointError(http.StatusNotFound, "Genesis Config is missing")
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ import (
|
||||
"github.com/ledgerwatch/erigon/cl/persistence/beacon_indicies"
|
||||
)
|
||||
|
||||
func (a *ApiHandler) getHeaders(r *http.Request) (*beaconResponse, error) {
|
||||
func (a *ApiHandler) getHeaders(w http.ResponseWriter, r *http.Request) (*beaconResponse, error) {
|
||||
ctx := r.Context()
|
||||
|
||||
querySlot, err := uint64FromQueryParams(r, "slot")
|
||||
@ -89,7 +89,7 @@ func (a *ApiHandler) getHeaders(r *http.Request) (*beaconResponse, error) {
|
||||
return newBeaconResponse(headers), nil
|
||||
}
|
||||
|
||||
func (a *ApiHandler) getHeader(r *http.Request) (*beaconResponse, error) {
|
||||
func (a *ApiHandler) getHeader(w http.ResponseWriter, r *http.Request) (*beaconResponse, error) {
|
||||
ctx := r.Context()
|
||||
tx, err := a.indiciesDB.BeginRo(ctx)
|
||||
if err != nil {
|
||||
|
@ -4,22 +4,22 @@ import (
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func (a *ApiHandler) poolVoluntaryExits(r *http.Request) (*beaconResponse, error) {
|
||||
func (a *ApiHandler) poolVoluntaryExits(w http.ResponseWriter, r *http.Request) (*beaconResponse, error) {
|
||||
return newBeaconResponse(a.operationsPool.VoluntaryExistsPool.Raw()), nil
|
||||
}
|
||||
|
||||
func (a *ApiHandler) poolAttesterSlashings(r *http.Request) (*beaconResponse, error) {
|
||||
func (a *ApiHandler) poolAttesterSlashings(w http.ResponseWriter, r *http.Request) (*beaconResponse, error) {
|
||||
return newBeaconResponse(a.operationsPool.AttesterSlashingsPool.Raw()), nil
|
||||
}
|
||||
|
||||
func (a *ApiHandler) poolProposerSlashings(r *http.Request) (*beaconResponse, error) {
|
||||
func (a *ApiHandler) poolProposerSlashings(w http.ResponseWriter, r *http.Request) (*beaconResponse, error) {
|
||||
return newBeaconResponse(a.operationsPool.ProposerSlashingsPool.Raw()), nil
|
||||
}
|
||||
|
||||
func (a *ApiHandler) poolBlsToExecutionChanges(r *http.Request) (*beaconResponse, error) {
|
||||
func (a *ApiHandler) poolBlsToExecutionChanges(w http.ResponseWriter, r *http.Request) (*beaconResponse, error) {
|
||||
return newBeaconResponse(a.operationsPool.BLSToExecutionChangesPool.Raw()), nil
|
||||
}
|
||||
|
||||
func (a *ApiHandler) poolAttestations(r *http.Request) (*beaconResponse, error) {
|
||||
func (a *ApiHandler) poolAttestations(w http.ResponseWriter, r *http.Request) (*beaconResponse, error) {
|
||||
return newBeaconResponse(a.operationsPool.AttestationsPool.Raw()), nil
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ type blockRewardsResponse struct {
|
||||
Total uint64 `json:"total,string"`
|
||||
}
|
||||
|
||||
func (a *ApiHandler) getBlockRewards(r *http.Request) (*beaconResponse, error) {
|
||||
func (a *ApiHandler) getBlockRewards(w http.ResponseWriter, r *http.Request) (*beaconResponse, error) {
|
||||
ctx := r.Context()
|
||||
tx, err := a.indiciesDB.BeginRo(ctx)
|
||||
if err != nil {
|
||||
@ -84,7 +84,7 @@ type syncCommitteeReward struct {
|
||||
Reward int64 `json:"reward,string"`
|
||||
}
|
||||
|
||||
func (a *ApiHandler) getSyncCommitteesRewards(r *http.Request) (*beaconResponse, error) {
|
||||
func (a *ApiHandler) getSyncCommitteesRewards(w http.ResponseWriter, r *http.Request) (*beaconResponse, error) {
|
||||
ctx := r.Context()
|
||||
|
||||
tx, err := a.indiciesDB.BeginRo(ctx)
|
||||
|
@ -71,7 +71,7 @@ func previousVersion(v clparams.StateVersion) clparams.StateVersion {
|
||||
return v - 1
|
||||
}
|
||||
|
||||
func (a *ApiHandler) getStateFork(r *http.Request) (*beaconResponse, error) {
|
||||
func (a *ApiHandler) getStateFork(w http.ResponseWriter, r *http.Request) (*beaconResponse, error) {
|
||||
ctx := r.Context()
|
||||
|
||||
tx, err := a.indiciesDB.BeginRo(ctx)
|
||||
@ -110,7 +110,7 @@ func (a *ApiHandler) getStateFork(r *http.Request) (*beaconResponse, error) {
|
||||
}), nil
|
||||
}
|
||||
|
||||
func (a *ApiHandler) getStateRoot(r *http.Request) (*beaconResponse, error) {
|
||||
func (a *ApiHandler) getStateRoot(w http.ResponseWriter, r *http.Request) (*beaconResponse, error) {
|
||||
ctx := r.Context()
|
||||
|
||||
tx, err := a.indiciesDB.BeginRo(ctx)
|
||||
@ -152,7 +152,7 @@ func (a *ApiHandler) getStateRoot(r *http.Request) (*beaconResponse, error) {
|
||||
withFinalized(canonicalRoot == root && *slot <= a.forkchoiceStore.FinalizedSlot()), nil
|
||||
}
|
||||
|
||||
func (a *ApiHandler) getFullState(r *http.Request) (*beaconResponse, error) {
|
||||
func (a *ApiHandler) getFullState(w http.ResponseWriter, r *http.Request) (*beaconResponse, error) {
|
||||
ctx := r.Context()
|
||||
|
||||
tx, err := a.indiciesDB.BeginRo(ctx)
|
||||
@ -210,7 +210,7 @@ type finalityCheckpointsResponse struct {
|
||||
PreviousJustifiedCheckpoint solid.Checkpoint `json:"previous_justified_checkpoint"`
|
||||
}
|
||||
|
||||
func (a *ApiHandler) getFinalityCheckpoints(r *http.Request) (*beaconResponse, error) {
|
||||
func (a *ApiHandler) getFinalityCheckpoints(w http.ResponseWriter, r *http.Request) (*beaconResponse, error) {
|
||||
ctx := r.Context()
|
||||
|
||||
tx, err := a.indiciesDB.BeginRo(ctx)
|
||||
@ -267,7 +267,7 @@ type syncCommitteesResponse struct {
|
||||
ValidatorAggregates [][]string `json:"validator_aggregates"`
|
||||
}
|
||||
|
||||
func (a *ApiHandler) getSyncCommittees(r *http.Request) (*beaconResponse, error) {
|
||||
func (a *ApiHandler) getSyncCommittees(w http.ResponseWriter, r *http.Request) (*beaconResponse, error) {
|
||||
ctx := r.Context()
|
||||
|
||||
tx, err := a.indiciesDB.BeginRo(ctx)
|
||||
|
@ -184,7 +184,7 @@ func checkValidValidatorId(s string) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (a *ApiHandler) getAllValidators(r *http.Request) (*beaconResponse, error) {
|
||||
func (a *ApiHandler) getAllValidators(w http.ResponseWriter, r *http.Request) (*beaconResponse, error) {
|
||||
ctx := r.Context()
|
||||
|
||||
tx, err := a.indiciesDB.BeginRo(ctx)
|
||||
@ -308,7 +308,7 @@ func parseQueryValidatorIndicies(tx kv.Tx, ids []string) ([]uint64, error) {
|
||||
return filterIndicies, nil
|
||||
}
|
||||
|
||||
func (a *ApiHandler) getSingleValidator(r *http.Request) (*beaconResponse, error) {
|
||||
func (a *ApiHandler) getSingleValidator(w http.ResponseWriter, r *http.Request) (*beaconResponse, error) {
|
||||
ctx := r.Context()
|
||||
|
||||
tx, err := a.indiciesDB.BeginRo(ctx)
|
||||
@ -375,7 +375,7 @@ func (a *ApiHandler) getSingleValidator(r *http.Request) (*beaconResponse, error
|
||||
return responseValidator(validatorIndex, stateEpoch, state.Balances(), state.Validators(), *slot <= a.forkchoiceStore.FinalizedSlot())
|
||||
}
|
||||
|
||||
func (a *ApiHandler) getAllValidatorsBalances(r *http.Request) (*beaconResponse, error) {
|
||||
func (a *ApiHandler) getAllValidatorsBalances(w http.ResponseWriter, r *http.Request) (*beaconResponse, error) {
|
||||
ctx := r.Context()
|
||||
|
||||
tx, err := a.indiciesDB.BeginRo(ctx)
|
||||
|
@ -1,23 +0,0 @@
|
||||
package validatorapi
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gfx-labs/sse"
|
||||
)
|
||||
|
||||
func (v *ValidatorApiHandler) GetEthV1Events(w http.ResponseWriter, r *http.Request) {
|
||||
sink, err := sse.DefaultUpgrader.Upgrade(w, r)
|
||||
if err != nil {
|
||||
// OK to ignore this error.
|
||||
return
|
||||
}
|
||||
topics := r.URL.Query()["topics"]
|
||||
for _, topic := range topics {
|
||||
sink.Encode(&sse.Event{
|
||||
Event: []byte(topic),
|
||||
Data: nil,
|
||||
})
|
||||
// OK to ignore this error. maybe should log it later
|
||||
}
|
||||
}
|
@ -5,10 +5,11 @@ import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/gfx-labs/sse"
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/ledgerwatch/erigon-lib/common"
|
||||
"github.com/ledgerwatch/erigon-lib/common/hexutil"
|
||||
"github.com/ledgerwatch/erigon-lib/common/hexutility"
|
||||
"github.com/ledgerwatch/erigon/cl/beacon/beaconhttp"
|
||||
"github.com/ledgerwatch/erigon/cl/clparams"
|
||||
@ -17,7 +18,7 @@ import (
|
||||
"github.com/ledgerwatch/erigon/cl/utils"
|
||||
)
|
||||
|
||||
func (v *ValidatorApiHandler) GetEthV1NodeSyncing(r *http.Request) (any, error) {
|
||||
func (v *ValidatorApiHandler) GetEthV1NodeSyncing(w http.ResponseWriter, r *http.Request) (any, error) {
|
||||
_, slot, err := v.FC.GetHead()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -50,17 +51,14 @@ func (v *ValidatorApiHandler) GetEthV1NodeSyncing(r *http.Request) (any, error)
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (v *ValidatorApiHandler) EventSourceGetV1Events(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
func (v *ValidatorApiHandler) GetEthV1ConfigSpec(r *http.Request) (*clparams.BeaconChainConfig, error) {
|
||||
func (v *ValidatorApiHandler) GetEthV1ConfigSpec(w http.ResponseWriter, r *http.Request) (*clparams.BeaconChainConfig, error) {
|
||||
if v.BeaconChainCfg == nil {
|
||||
return nil, beaconhttp.NewEndpointError(http.StatusNotFound, "beacon config not found")
|
||||
}
|
||||
return v.BeaconChainCfg, nil
|
||||
}
|
||||
|
||||
func (v *ValidatorApiHandler) GetEthV1BeaconGenesis(r *http.Request) (any, error) {
|
||||
func (v *ValidatorApiHandler) GetEthV1BeaconGenesis(w http.ResponseWriter, r *http.Request) (any, error) {
|
||||
if v.GenesisCfg == nil {
|
||||
return nil, beaconhttp.NewEndpointError(http.StatusNotFound, "genesis config not found")
|
||||
}
|
||||
@ -75,7 +73,7 @@ func (v *ValidatorApiHandler) GetEthV1BeaconGenesis(r *http.Request) (any, error
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (v *ValidatorApiHandler) GetEthV1BeaconStatesStateIdFork(r *http.Request) (any, error) {
|
||||
func (v *ValidatorApiHandler) GetEthV1BeaconStatesStateIdFork(w http.ResponseWriter, r *http.Request) (any, error) {
|
||||
stateId := chi.URLParam(r, "state_id")
|
||||
state, err := v.privateGetStateFromStateId(stateId)
|
||||
if err != nil {
|
||||
@ -95,7 +93,7 @@ func (v *ValidatorApiHandler) GetEthV1BeaconStatesStateIdFork(r *http.Request) (
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
func (v *ValidatorApiHandler) GetEthV1BeaconStatesStateIdValidatorsValidatorId(r *http.Request) (any, error) {
|
||||
func (v *ValidatorApiHandler) GetEthV1BeaconStatesStateIdValidatorsValidatorId(w http.ResponseWriter, r *http.Request) (any, error) {
|
||||
stateId := chi.URLParam(r, "state_id")
|
||||
// grab the correct state for the given state id
|
||||
beaconState, err := v.privateGetStateFromStateId(stateId)
|
||||
@ -206,44 +204,59 @@ func (v *ValidatorApiHandler) GetEthV1BeaconStatesStateIdValidatorsValidatorId(r
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (v *ValidatorApiHandler) privateGetStateFromStateId(stateId string) (*state.CachingBeaconState, error) {
|
||||
switch {
|
||||
case stateId == "head":
|
||||
// Now check the head
|
||||
headRoot, _, err := v.FC.GetHead()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return v.FC.GetStateAtBlockRoot(headRoot, true)
|
||||
case stateId == "genesis":
|
||||
// not supported
|
||||
return nil, beaconhttp.NewEndpointError(http.StatusNotFound, "genesis block not found")
|
||||
case stateId == "finalized":
|
||||
return v.FC.GetStateAtBlockRoot(v.FC.FinalizedCheckpoint().BlockRoot(), true)
|
||||
case stateId == "justified":
|
||||
return v.FC.GetStateAtBlockRoot(v.FC.JustifiedCheckpoint().BlockRoot(), true)
|
||||
case strings.HasPrefix(stateId, "0x"):
|
||||
// assume is hex has, so try to parse
|
||||
hsh := common.Hash{}
|
||||
err := hsh.UnmarshalText([]byte(stateId))
|
||||
if err != nil {
|
||||
return nil, beaconhttp.NewEndpointError(http.StatusBadRequest, fmt.Sprintf("Invalid state ID: %s", stateId))
|
||||
}
|
||||
return v.FC.GetStateAtStateRoot(hsh, true)
|
||||
case isInt(stateId):
|
||||
// ignore the error bc isInt check succeeded. yes this doesn't protect for overflow, they will request slot 0 and it will fail. good
|
||||
val, _ := strconv.ParseUint(stateId, 10, 64)
|
||||
return v.FC.GetStateAtSlot(val, true)
|
||||
default:
|
||||
return nil, beaconhttp.NewEndpointError(http.StatusBadRequest, fmt.Sprintf("Invalid state ID: %s", stateId))
|
||||
func (v *ValidatorApiHandler) GetEthV1EthNodeSyncing(w http.ResponseWriter, r *http.Request) (any, error) {
|
||||
// TODO: populate this map
|
||||
o := map[string]any{
|
||||
"data": map[string]any{},
|
||||
}
|
||||
return o, nil
|
||||
}
|
||||
func (v *ValidatorApiHandler) GetEthV3ValidatorBlocksSlot(w http.ResponseWriter, r *http.Request) (any, error) {
|
||||
// TODO: populate this map
|
||||
o := map[string]any{
|
||||
"data": map[string]any{},
|
||||
}
|
||||
|
||||
slotString := chi.URLParam(r, "slot")
|
||||
slot, err := strconv.ParseUint(slotString, 10, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fail to parse slot: %w", err)
|
||||
}
|
||||
randaoRevealString := r.URL.Query().Get("randao_reveal")
|
||||
randaoReveal, err := hexutil.Decode(randaoRevealString)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fail to parse randao_reveal: %w", err)
|
||||
}
|
||||
graffitiString := r.URL.Query().Get("randao_reveal")
|
||||
if graffitiString == "" {
|
||||
graffitiString = "0x"
|
||||
}
|
||||
graffiti, err := hexutil.Decode(graffitiString)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("fail to parse graffiti: %w", err)
|
||||
}
|
||||
skip_randao_verification := r.URL.Query().Has("skip_randao_verification")
|
||||
//if skip_randao_verification {
|
||||
// if isInfinity(randaoReveal) {
|
||||
// return nil, beaconhttp.NewEndpointError(400, "randao reveal must be set to infinity if skip randao verification is set")
|
||||
// }
|
||||
//}
|
||||
_, _, _, _ = slot, graffiti, randaoReveal, skip_randao_verification
|
||||
return o, nil
|
||||
}
|
||||
|
||||
func isInt(s string) bool {
|
||||
for _, c := range s {
|
||||
if !unicode.IsDigit(c) {
|
||||
return false
|
||||
}
|
||||
func (v *ValidatorApiHandler) EventSourceGetV1Events(w http.ResponseWriter, r *http.Request) {
|
||||
sink, err := sse.DefaultUpgrader.Upgrade(w, r)
|
||||
if err != nil {
|
||||
// OK to ignore this error.
|
||||
return
|
||||
}
|
||||
topics := r.URL.Query()["topics"]
|
||||
for _, topic := range topics {
|
||||
sink.Encode(&sse.Event{
|
||||
Event: []byte(topic),
|
||||
Data: nil,
|
||||
})
|
||||
// OK to ignore this error. maybe should log it later
|
||||
}
|
||||
return true
|
||||
}
|
@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
"github.com/ledgerwatch/erigon/cl/beacon/beaconhttp"
|
||||
"github.com/ledgerwatch/erigon/cl/beacon/building"
|
||||
"github.com/ledgerwatch/erigon/cl/clparams"
|
||||
"github.com/ledgerwatch/erigon/cl/phase1/forkchoice"
|
||||
)
|
||||
@ -16,6 +17,8 @@ type ValidatorApiHandler struct {
|
||||
BeaconChainCfg *clparams.BeaconChainConfig
|
||||
GenesisCfg *clparams.GenesisConfig
|
||||
|
||||
state *building.State
|
||||
|
||||
o sync.Once
|
||||
mux *chi.Mux
|
||||
}
|
||||
@ -23,6 +26,7 @@ type ValidatorApiHandler struct {
|
||||
func (v *ValidatorApiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
v.o.Do(func() {
|
||||
v.mux = chi.NewRouter()
|
||||
v.state = building.NewState()
|
||||
v.Route(v.mux)
|
||||
})
|
||||
v.mux.ServeHTTP(w, r)
|
||||
@ -39,31 +43,32 @@ func (v *ValidatorApiHandler) Route(r chi.Router) {
|
||||
r.Get("/validators/{validator_id}", beaconhttp.HandleEndpointFunc(v.GetEthV1BeaconStatesStateIdValidatorsValidatorId))
|
||||
})
|
||||
})
|
||||
r.Post("/binded_blocks", http.NotFound)
|
||||
r.Post("/blocks", http.NotFound)
|
||||
r.Post("/blocks", beaconhttp.HandleEndpointFunc(v.PostEthV1BeaconBlocks))
|
||||
r.Post("/blinded_blocks", beaconhttp.HandleEndpointFunc(v.PostEthV1BeaconBlindedBlocks))
|
||||
r.Route("/pool", func(r chi.Router) {
|
||||
r.Post("/attestations", http.NotFound)
|
||||
r.Post("/sync_committees", http.NotFound)
|
||||
r.Post("/attestations", beaconhttp.HandleEndpointFunc(v.PostEthV1BeaconPoolAttestations))
|
||||
r.Post("/sync_committees", beaconhttp.HandleEndpointFunc(v.PostEthV1BeaconPoolAttestations))
|
||||
})
|
||||
r.Get("/node/syncing", beaconhttp.HandleEndpointFunc(v.GetEthV1NodeSyncing))
|
||||
})
|
||||
r.Get("/config/spec", beaconhttp.HandleEndpointFunc(v.GetEthV1ConfigSpec))
|
||||
r.Get("/events", v.GetEthV1Events)
|
||||
r.Get("/events", v.EventSourceGetV1Events)
|
||||
r.Route("/validator", func(r chi.Router) {
|
||||
r.Route("/duties", func(r chi.Router) {
|
||||
r.Post("/attester/{epoch}", http.NotFound)
|
||||
r.Get("/proposer/{epoch}", http.NotFound)
|
||||
r.Post("/sync/{epoch}", http.NotFound)
|
||||
})
|
||||
// implemented by archive api (for now)
|
||||
// r.Route("/duties", func(r chi.Router) {
|
||||
// r.Post("/attester/{epoch}", http.NotFound)
|
||||
// r.Post("/sync/{epoch}", http.NotFound)
|
||||
// r.Get("/proposer/{epoch}", http.NotFound)
|
||||
// })
|
||||
// r.Get("/blinded_blocks/{slot}", http.NotFound) - deprecated
|
||||
r.Get("/attestation_data", http.NotFound)
|
||||
r.Get("/aggregate_attestation", http.NotFound)
|
||||
r.Post("/aggregate_and_proofs", http.NotFound)
|
||||
r.Post("/beacon_committee_subscriptions", http.NotFound)
|
||||
r.Post("/sync_committee_subscriptions", http.NotFound)
|
||||
r.Post("/aggregate_and_proofs", beaconhttp.HandleEndpointFunc(v.PostEthV1ValidatorAggregateAndProofs))
|
||||
r.Post("/beacon_committee_subscriptions", beaconhttp.HandleEndpointFunc(v.PostEthV1ValidatorBeaconCommitteeSubscriptions))
|
||||
r.Post("/sync_committee_subscriptions", beaconhttp.HandleEndpointFunc(v.PostEthV1ValidatorSyncCommitteeSubscriptions))
|
||||
r.Get("/sync_committee_contribution", http.NotFound)
|
||||
r.Post("/contribution_and_proofs", http.NotFound)
|
||||
r.Post("/prepare_beacon_proposer", http.NotFound)
|
||||
r.Post("/contribution_and_proofs", beaconhttp.HandleEndpointFunc(v.PostEthV1ValidatorContributionAndProofs))
|
||||
r.Post("/prepare_beacon_proposer", beaconhttp.HandleEndpointFunc(v.PostEthV1ValidatorPrepareBeaconProposer))
|
||||
})
|
||||
})
|
||||
r.Route("/v2", func(r chi.Router) {
|
||||
@ -73,14 +78,15 @@ func (v *ValidatorApiHandler) Route(r chi.Router) {
|
||||
})
|
||||
})
|
||||
r.Route("/beacon", func(r chi.Router) {
|
||||
r.Post("/blocks/{block_id}", http.NotFound)
|
||||
r.Post("/blocks", beaconhttp.HandleEndpointFunc(v.PostEthV2BeaconBlocks))
|
||||
r.Post("/blinded_blocks", beaconhttp.HandleEndpointFunc(v.PostEthV2BeaconBlindedBlocks))
|
||||
})
|
||||
r.Route("/validator", func(r chi.Router) {
|
||||
r.Post("/blocks/{slot}", http.NotFound)
|
||||
r.Post("/blocks/{slot}", beaconhttp.HandleEndpointFunc(v.GetEthV3ValidatorBlocksSlot))
|
||||
})
|
||||
})
|
||||
r.Route("/v3", func(r chi.Router) {
|
||||
r.Route("/beacon", func(r chi.Router) {
|
||||
r.Route("/validator", func(r chi.Router) {
|
||||
r.Get("/blocks/{block_id}", http.NotFound)
|
||||
})
|
||||
})
|
||||
|
55
cl/beacon/validatorapi/helpers.go
Normal file
55
cl/beacon/validatorapi/helpers.go
Normal file
@ -0,0 +1,55 @@
|
||||
package validatorapi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/ledgerwatch/erigon-lib/common"
|
||||
"github.com/ledgerwatch/erigon/cl/beacon/beaconhttp"
|
||||
"github.com/ledgerwatch/erigon/cl/phase1/core/state"
|
||||
)
|
||||
|
||||
func (v *ValidatorApiHandler) privateGetStateFromStateId(stateId string) (*state.CachingBeaconState, error) {
|
||||
switch {
|
||||
case stateId == "head":
|
||||
// Now check the head
|
||||
headRoot, _, err := v.FC.GetHead()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return v.FC.GetStateAtBlockRoot(headRoot, true)
|
||||
case stateId == "genesis":
|
||||
// not supported
|
||||
return nil, beaconhttp.NewEndpointError(http.StatusNotFound, "genesis block not found")
|
||||
case stateId == "finalized":
|
||||
return v.FC.GetStateAtBlockRoot(v.FC.FinalizedCheckpoint().BlockRoot(), true)
|
||||
case stateId == "justified":
|
||||
return v.FC.GetStateAtBlockRoot(v.FC.JustifiedCheckpoint().BlockRoot(), true)
|
||||
case strings.HasPrefix(stateId, "0x"):
|
||||
// assume is hex has, so try to parse
|
||||
hsh := common.Hash{}
|
||||
err := hsh.UnmarshalText([]byte(stateId))
|
||||
if err != nil {
|
||||
return nil, beaconhttp.NewEndpointError(http.StatusBadRequest, fmt.Sprintf("Invalid state ID: %s", stateId))
|
||||
}
|
||||
return v.FC.GetStateAtStateRoot(hsh, true)
|
||||
case isInt(stateId):
|
||||
// ignore the error bc isInt check succeeded. yes this doesn't protect for overflow, they will request slot 0 and it will fail. good
|
||||
val, _ := strconv.ParseUint(stateId, 10, 64)
|
||||
return v.FC.GetStateAtSlot(val, true)
|
||||
default:
|
||||
return nil, beaconhttp.NewEndpointError(http.StatusBadRequest, fmt.Sprintf("Invalid state ID: %s", stateId))
|
||||
}
|
||||
}
|
||||
|
||||
func isInt(s string) bool {
|
||||
for _, c := range s {
|
||||
if !unicode.IsDigit(c) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
145
cl/beacon/validatorapi/post.go
Normal file
145
cl/beacon/validatorapi/post.go
Normal file
@ -0,0 +1,145 @@
|
||||
package validatorapi
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/ledgerwatch/erigon/cl/beacon/beaconhttp"
|
||||
"github.com/ledgerwatch/erigon/cl/beacon/building"
|
||||
"github.com/ledgerwatch/erigon/cl/cltypes"
|
||||
"github.com/ledgerwatch/erigon/cl/cltypes/solid"
|
||||
)
|
||||
|
||||
func (v *ValidatorApiHandler) PostEthV1ValidatorPrepareBeaconProposer(w http.ResponseWriter, r *http.Request) (*int, error) {
|
||||
var req []building.PrepareBeaconProposer
|
||||
err := json.NewDecoder(r.Body).Decode(&req)
|
||||
if err != nil {
|
||||
return nil, beaconhttp.NewEndpointError(400, "invalid request: "+err.Error())
|
||||
}
|
||||
for _, x := range req {
|
||||
v.state.SetFeeRecipient(x.ValidatorIndex, x.FeeRecipient)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (v *ValidatorApiHandler) PostEthV1ValidatorContributionAndProofs(w http.ResponseWriter, r *http.Request) (*int, error) {
|
||||
var req []*cltypes.ContributionAndProof
|
||||
err := json.NewDecoder(r.Body).Decode(&req)
|
||||
if err != nil {
|
||||
return nil, beaconhttp.NewEndpointError(400, "invalid request: "+err.Error())
|
||||
}
|
||||
// TODO: this endpoint
|
||||
return nil, beaconhttp.NewEndpointError(404, "not implemented")
|
||||
}
|
||||
|
||||
func (v *ValidatorApiHandler) PostEthV1ValidatorSyncCommitteeSubscriptions(w http.ResponseWriter, r *http.Request) (*int, error) {
|
||||
var req []building.SyncCommitteeSubscription
|
||||
err := json.NewDecoder(r.Body).Decode(&req)
|
||||
if err != nil {
|
||||
return nil, beaconhttp.NewEndpointError(400, "invalid request: "+err.Error())
|
||||
}
|
||||
// TODO: this endpoint
|
||||
return nil, beaconhttp.NewEndpointError(404, "not implemented")
|
||||
}
|
||||
|
||||
func (v *ValidatorApiHandler) PostEthV1ValidatorBeaconCommitteeSubscriptions(w http.ResponseWriter, r *http.Request) (*int, error) {
|
||||
var req []building.BeaconCommitteeSubscription
|
||||
err := json.NewDecoder(r.Body).Decode(&req)
|
||||
if err != nil {
|
||||
return nil, beaconhttp.NewEndpointError(400, "invalid request: "+err.Error())
|
||||
}
|
||||
// TODO: this endpoint
|
||||
return nil, beaconhttp.NewEndpointError(404, "not implemented")
|
||||
}
|
||||
|
||||
func (v *ValidatorApiHandler) PostEthV1ValidatorAggregateAndProofs(w http.ResponseWriter, r *http.Request) (*int, error) {
|
||||
var req []cltypes.SignedAggregateAndProof
|
||||
err := json.NewDecoder(r.Body).Decode(&req)
|
||||
if err != nil {
|
||||
return nil, beaconhttp.NewEndpointError(400, "invalid request: "+err.Error())
|
||||
}
|
||||
// TODO: this endpoint
|
||||
return nil, beaconhttp.NewEndpointError(404, "not implemented")
|
||||
}
|
||||
|
||||
func (v *ValidatorApiHandler) PostEthV1BeaconPoolSyncCommittees(w http.ResponseWriter, r *http.Request) (*int, error) {
|
||||
var req []*solid.SyncCommittee
|
||||
err := json.NewDecoder(r.Body).Decode(&req)
|
||||
if err != nil {
|
||||
return nil, beaconhttp.NewEndpointError(400, "invalid request: "+err.Error())
|
||||
}
|
||||
// TODO: this endpoint
|
||||
return nil, beaconhttp.NewEndpointError(404, "not implemented")
|
||||
}
|
||||
|
||||
func (v *ValidatorApiHandler) PostEthV1BeaconPoolAttestations(w http.ResponseWriter, r *http.Request) (*int, error) {
|
||||
var req []*solid.Attestation
|
||||
err := json.NewDecoder(r.Body).Decode(&req)
|
||||
if err != nil {
|
||||
return nil, beaconhttp.NewEndpointError(400, "invalid request: "+err.Error())
|
||||
}
|
||||
// TODO: this endpoint
|
||||
return nil, beaconhttp.NewEndpointError(404, "not implemented")
|
||||
}
|
||||
|
||||
func (v *ValidatorApiHandler) PostEthV1BeaconBlocks(w http.ResponseWriter, r *http.Request) (*int, error) {
|
||||
ethConsensusVersion := r.Header.Get("Eth-Consensus-Version")
|
||||
var req cltypes.SignedBeaconBlock
|
||||
err := json.NewDecoder(r.Body).Decode(&req)
|
||||
if err != nil {
|
||||
return nil, beaconhttp.NewEndpointError(400, "invalid request: "+err.Error())
|
||||
}
|
||||
// TODO: this endpoint
|
||||
_ = ethConsensusVersion
|
||||
return nil, beaconhttp.NewEndpointError(404, "not implemented")
|
||||
}
|
||||
|
||||
func (v *ValidatorApiHandler) PostEthV2BeaconBlocks(w http.ResponseWriter, r *http.Request) (*int, error) {
|
||||
broadcastValidation := r.URL.Query().Get("broadcast_validation")
|
||||
if broadcastValidation == "" {
|
||||
broadcastValidation = "gossip"
|
||||
}
|
||||
ethConsensusVersion := r.Header.Get("Eth-Consensus-Version")
|
||||
if ethConsensusVersion == "" {
|
||||
return nil, beaconhttp.NewEndpointError(400, "no eth consensus version set")
|
||||
}
|
||||
var req cltypes.SignedBeaconBlock
|
||||
err := json.NewDecoder(r.Body).Decode(&req)
|
||||
if err != nil {
|
||||
return nil, beaconhttp.NewEndpointError(400, "invalid request: "+err.Error())
|
||||
}
|
||||
// TODO: this endpoint
|
||||
_, _ = broadcastValidation, ethConsensusVersion
|
||||
return nil, beaconhttp.NewEndpointError(404, "not implemented")
|
||||
}
|
||||
|
||||
func (v *ValidatorApiHandler) PostEthV1BeaconBlindedBlocks(w http.ResponseWriter, r *http.Request) (*int, error) {
|
||||
ethConsensusVersion := r.Header.Get("Eth-Consensus-Version")
|
||||
var req cltypes.SignedBlindedBeaconBlock
|
||||
err := json.NewDecoder(r.Body).Decode(&req)
|
||||
if err != nil {
|
||||
return nil, beaconhttp.NewEndpointError(400, "invalid request: "+err.Error())
|
||||
}
|
||||
// TODO: this endpoint
|
||||
_ = ethConsensusVersion
|
||||
return nil, beaconhttp.NewEndpointError(404, "not implemented")
|
||||
}
|
||||
|
||||
func (v *ValidatorApiHandler) PostEthV2BeaconBlindedBlocks(w http.ResponseWriter, r *http.Request) (*int, error) {
|
||||
broadcastValidation := r.URL.Query().Get("broadcast_validation")
|
||||
if broadcastValidation == "" {
|
||||
broadcastValidation = "gossip"
|
||||
}
|
||||
ethConsensusVersion := r.Header.Get("Eth-Consensus-Version")
|
||||
if ethConsensusVersion == "" {
|
||||
return nil, beaconhttp.NewEndpointError(400, "no eth consensus version set")
|
||||
}
|
||||
var req cltypes.SignedBlindedBeaconBlock
|
||||
err := json.NewDecoder(r.Body).Decode(&req)
|
||||
if err != nil {
|
||||
return nil, beaconhttp.NewEndpointError(400, "invalid request: "+err.Error())
|
||||
}
|
||||
// TODO: this endpoint
|
||||
_, _ = broadcastValidation, ethConsensusVersion
|
||||
return nil, beaconhttp.NewEndpointError(404, "not implemented")
|
||||
}
|
@ -12,9 +12,9 @@ import (
|
||||
* to be aggregated and the BLS signature of the attestation.
|
||||
*/
|
||||
type AggregateAndProof struct {
|
||||
AggregatorIndex uint64
|
||||
Aggregate *solid.Attestation
|
||||
SelectionProof libcommon.Bytes96
|
||||
AggregatorIndex uint64 `json:"aggregator_index,string"`
|
||||
Aggregate *solid.Attestation `json:"aggregate"`
|
||||
SelectionProof libcommon.Bytes96 `json:"selection_proof"`
|
||||
}
|
||||
|
||||
func (a *AggregateAndProof) EncodeSSZ(dst []byte) ([]byte, error) {
|
||||
@ -39,8 +39,8 @@ func (a *AggregateAndProof) HashSSZ() ([32]byte, error) {
|
||||
}
|
||||
|
||||
type SignedAggregateAndProof struct {
|
||||
Message *AggregateAndProof
|
||||
Signature libcommon.Bytes96
|
||||
Message *AggregateAndProof `json:"message"`
|
||||
Signature libcommon.Bytes96 `json:"signature"`
|
||||
}
|
||||
|
||||
func (a *SignedAggregateAndProof) EncodeSSZ(dst []byte) ([]byte, error) {
|
||||
|
111
cl/cltypes/contribution.go
Normal file
111
cl/cltypes/contribution.go
Normal file
@ -0,0 +1,111 @@
|
||||
package cltypes
|
||||
|
||||
import (
|
||||
libcommon "github.com/ledgerwatch/erigon-lib/common"
|
||||
"github.com/ledgerwatch/erigon/cl/cltypes/solid"
|
||||
"github.com/ledgerwatch/erigon/cl/merkle_tree"
|
||||
ssz2 "github.com/ledgerwatch/erigon/cl/ssz"
|
||||
)
|
||||
|
||||
/*
|
||||
* ContributionAndProof contains the index of the aggregator, the attestation
|
||||
* to be aggregated and the BLS signature of the attestation.
|
||||
*/
|
||||
type ContributionAndProof struct {
|
||||
AggregatorIndex uint64 `json:"aggregator_index,string"`
|
||||
SelectionProof libcommon.Bytes96 `json:"selection_proof"`
|
||||
Contribution *solid.Contribution `json:"contribution"`
|
||||
}
|
||||
|
||||
func (a *ContributionAndProof) EncodeSSZ(dst []byte) ([]byte, error) {
|
||||
return ssz2.MarshalSSZ(dst, a.AggregatorIndex, a.Contribution, a.SelectionProof[:])
|
||||
}
|
||||
|
||||
func (a *ContributionAndProof) Static() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (a *ContributionAndProof) DecodeSSZ(buf []byte, version int) error {
|
||||
a.Contribution = new(solid.Contribution)
|
||||
return ssz2.UnmarshalSSZ(buf, version, &a.AggregatorIndex, a.Contribution, a.SelectionProof[:])
|
||||
}
|
||||
|
||||
func (a *ContributionAndProof) EncodingSizeSSZ() int {
|
||||
return 108 + a.Contribution.EncodingSizeSSZ()
|
||||
}
|
||||
|
||||
func (a *ContributionAndProof) HashSSZ() ([32]byte, error) {
|
||||
return merkle_tree.HashTreeRoot(a.AggregatorIndex, a.Contribution, a.SelectionProof[:])
|
||||
}
|
||||
|
||||
type SignedContributionAndProof struct {
|
||||
Message *ContributionAndProof `json:"message"`
|
||||
Signature libcommon.Bytes96 `json:"signature"`
|
||||
}
|
||||
|
||||
func (a *SignedContributionAndProof) EncodeSSZ(dst []byte) ([]byte, error) {
|
||||
return ssz2.MarshalSSZ(dst, a.Message, a.Signature[:])
|
||||
}
|
||||
|
||||
func (a *SignedContributionAndProof) DecodeSSZ(buf []byte, version int) error {
|
||||
a.Message = new(ContributionAndProof)
|
||||
return ssz2.UnmarshalSSZ(buf, version, a.Message, a.Signature[:])
|
||||
}
|
||||
|
||||
func (a *SignedContributionAndProof) EncodingSizeSSZ() int {
|
||||
return 100 + a.Message.EncodingSizeSSZ()
|
||||
}
|
||||
|
||||
func (a *SignedContributionAndProof) HashSSZ() ([32]byte, error) {
|
||||
return merkle_tree.HashTreeRoot(a.Message, a.Signature[:])
|
||||
}
|
||||
|
||||
/*
|
||||
* SyncContribution, Determines successfull committee, bits shows active participants,
|
||||
* and signature is the aggregate BLS signature of the committee.
|
||||
*/
|
||||
type SyncContribution struct {
|
||||
SyncCommiteeBits libcommon.Bytes64 `json:"sync_commitee_bits"`
|
||||
SyncCommiteeSignature libcommon.Bytes96 `json:"signature"`
|
||||
}
|
||||
|
||||
// return sum of the committee bits
|
||||
func (agg *SyncContribution) Sum() int {
|
||||
ret := 0
|
||||
for i := range agg.SyncCommiteeBits {
|
||||
for bit := 1; bit <= 128; bit *= 2 {
|
||||
if agg.SyncCommiteeBits[i]&byte(bit) > 0 {
|
||||
ret++
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (agg *SyncContribution) IsSet(idx uint64) bool {
|
||||
if idx >= 2048 {
|
||||
return false
|
||||
}
|
||||
return agg.SyncCommiteeBits[idx/8]&(1<<(idx%8)) > 0
|
||||
}
|
||||
|
||||
func (agg *SyncContribution) EncodeSSZ(buf []byte) ([]byte, error) {
|
||||
return append(buf, append(agg.SyncCommiteeBits[:], agg.SyncCommiteeSignature[:]...)...), nil
|
||||
}
|
||||
|
||||
func (*SyncContribution) Static() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (agg *SyncContribution) DecodeSSZ(buf []byte, version int) error {
|
||||
return ssz2.UnmarshalSSZ(buf, version, agg.SyncCommiteeBits[:], agg.SyncCommiteeSignature[:])
|
||||
}
|
||||
|
||||
func (agg *SyncContribution) EncodingSizeSSZ() int {
|
||||
return 160
|
||||
}
|
||||
|
||||
func (agg *SyncContribution) HashSSZ() ([32]byte, error) {
|
||||
return merkle_tree.HashTreeRoot(agg.SyncCommiteeBits[:], agg.SyncCommiteeSignature[:])
|
||||
|
||||
}
|
231
cl/cltypes/solid/contribution.go
Normal file
231
cl/cltypes/solid/contribution.go
Normal file
@ -0,0 +1,231 @@
|
||||
package solid
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/ledgerwatch/erigon-lib/common"
|
||||
libcommon "github.com/ledgerwatch/erigon-lib/common"
|
||||
"github.com/ledgerwatch/erigon-lib/common/hexutility"
|
||||
"github.com/ledgerwatch/erigon-lib/common/length"
|
||||
"github.com/ledgerwatch/erigon-lib/types/clonable"
|
||||
"github.com/ledgerwatch/erigon-lib/types/ssz"
|
||||
"github.com/ledgerwatch/erigon/cl/merkle_tree"
|
||||
ssz2 "github.com/ledgerwatch/erigon/cl/ssz"
|
||||
)
|
||||
|
||||
const (
|
||||
// slot: 8 bytes // 0
|
||||
// beaconBlockHash: 32 bytes // 8
|
||||
// subcommitteeIndex: 8 bytes // 40
|
||||
// aggregationbits: 16 bytes // 48
|
||||
// signature: 96 bytes // 64
|
||||
// total = 160
|
||||
contributionStaticBufferSize = 8 + 32 + 8 + 16 + 96
|
||||
)
|
||||
|
||||
// Contribution type represents a statement or confirmation of some occurrence or phenomenon.
|
||||
type Contribution [160]byte
|
||||
|
||||
// Static returns whether the contribution is static or not. For Contribution, it's always false.
|
||||
func (*Contribution) Static() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// NewAttestionFromParameters creates a new Contribution instance using provided parameters
|
||||
func NewContributionFromParameters(
|
||||
slot uint64,
|
||||
beaconBlockRoot libcommon.Hash,
|
||||
subcommitteeIndex uint64,
|
||||
aggregationBits [16]byte,
|
||||
signature libcommon.Bytes96,
|
||||
) *Contribution {
|
||||
a := &Contribution{}
|
||||
a.SetSlot(slot)
|
||||
a.SetBeaconBlockRoot(beaconBlockRoot)
|
||||
a.SetSubcommitteeIndex(subcommitteeIndex)
|
||||
a.SetAggregationBits(aggregationBits)
|
||||
a.SetSignature(signature)
|
||||
return a
|
||||
}
|
||||
|
||||
func (a Contribution) MarshalJSON() ([]byte, error) {
|
||||
ab := a.AggregationBits()
|
||||
return json.Marshal(struct {
|
||||
Slot uint64 `json:"slot,string"`
|
||||
BeaconBlockRoot libcommon.Hash `json:"beacon_block_root"`
|
||||
SubcommitteeIndex uint64 `json:"subcommittee_index,string"`
|
||||
AggregationBits hexutility.Bytes `json:"aggregation_bits"`
|
||||
Signature libcommon.Bytes96 `json:"signature"`
|
||||
}{
|
||||
Slot: a.Slot(),
|
||||
BeaconBlockRoot: a.BeaconBlockRoot(),
|
||||
SubcommitteeIndex: a.SubcommitteeIndex(),
|
||||
AggregationBits: hexutility.Bytes(ab[:]),
|
||||
Signature: a.Signature(),
|
||||
})
|
||||
}
|
||||
|
||||
func (a *Contribution) UnmarshalJSON(buf []byte) error {
|
||||
var tmp struct {
|
||||
Slot uint64 `json:"slot,string"`
|
||||
BeaconBlockRoot libcommon.Hash `json:"beacon_block_root"`
|
||||
SubcommitteeIndex uint64 `json:"subcommittee_index,string"`
|
||||
AggregationBits hexutility.Bytes `json:"aggregation_bits"`
|
||||
Signature libcommon.Bytes96 `json:"signature"`
|
||||
}
|
||||
if err := json.Unmarshal(buf, &tmp); err != nil {
|
||||
return err
|
||||
}
|
||||
a.SetSlot(tmp.Slot)
|
||||
a.SetBeaconBlockRoot(tmp.BeaconBlockRoot)
|
||||
a.SetSubcommitteeIndex(tmp.SubcommitteeIndex)
|
||||
o := [16]byte{}
|
||||
copy(o[:], tmp.AggregationBits)
|
||||
a.SetAggregationBits(o)
|
||||
a.SetSignature(tmp.Signature)
|
||||
return nil
|
||||
}
|
||||
func (a Contribution) Slot() uint64 {
|
||||
return binary.LittleEndian.Uint64(a[:8])
|
||||
}
|
||||
func (a Contribution) BeaconBlockRoot() (o libcommon.Hash) {
|
||||
copy(o[:], a[16:40])
|
||||
return
|
||||
}
|
||||
func (a Contribution) SubcommitteeIndex() uint64 {
|
||||
return binary.LittleEndian.Uint64(a[40:48])
|
||||
}
|
||||
func (a Contribution) AggregationBits() (o [16]byte) {
|
||||
copy(o[:], a[48:64])
|
||||
return
|
||||
}
|
||||
func (a Contribution) Signature() (o libcommon.Bytes96) {
|
||||
copy(o[:], a[96:160])
|
||||
return
|
||||
}
|
||||
|
||||
func (a Contribution) SetSlot(slot uint64) {
|
||||
binary.LittleEndian.PutUint64(a[:8], slot)
|
||||
}
|
||||
|
||||
func (a Contribution) SetBeaconBlockRoot(hsh common.Hash) {
|
||||
copy(a[40:48], hsh[:])
|
||||
}
|
||||
|
||||
func (a Contribution) SetSubcommitteeIndex(validatorIndex uint64) {
|
||||
binary.LittleEndian.PutUint64(a[40:48], validatorIndex)
|
||||
}
|
||||
|
||||
func (a Contribution) SetAggregationBits(xs [16]byte) {
|
||||
copy(a[48:64], xs[:])
|
||||
}
|
||||
|
||||
// SetSignature sets the signature of the Contribution instance.
|
||||
func (a Contribution) SetSignature(signature [96]byte) {
|
||||
copy(a[64:], signature[:])
|
||||
}
|
||||
|
||||
// EncodingSizeSSZ returns the size of the Contribution instance when encoded in SSZ format.
|
||||
func (a *Contribution) EncodingSizeSSZ() (size int) {
|
||||
return 160
|
||||
}
|
||||
|
||||
// DecodeSSZ decodes the provided buffer into the Contribution instance.
|
||||
func (a *Contribution) DecodeSSZ(buf []byte, _ int) error {
|
||||
if len(buf) < contributionStaticBufferSize {
|
||||
return ssz.ErrLowBufferSize
|
||||
}
|
||||
copy((*a)[:], buf)
|
||||
return nil
|
||||
}
|
||||
|
||||
// EncodeSSZ encodes the Contribution instance into the provided buffer.
|
||||
func (a *Contribution) EncodeSSZ(dst []byte) ([]byte, error) {
|
||||
buf := dst
|
||||
buf = append(buf, (*a)[:]...)
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// CopyHashBufferTo copies the hash buffer of the Contribution instance to the provided byte slice.
|
||||
func (a *Contribution) CopyHashBufferTo(o []byte) error {
|
||||
for i := 0; i < 160; i++ {
|
||||
o[i] = 0
|
||||
}
|
||||
|
||||
// hash signature first
|
||||
copy(o[:128], a[64:160])
|
||||
if err := merkle_tree.InPlaceRoot(o); err != nil {
|
||||
return err
|
||||
}
|
||||
copy(o[:128:160], o[:32])
|
||||
|
||||
copy(o[:32], a[:8])
|
||||
copy(o[32:64], a[8:40])
|
||||
copy(o[64:96], a[40:48])
|
||||
copy(o[96:128], a[48:64])
|
||||
return nil
|
||||
}
|
||||
|
||||
// HashSSZ hashes the Contribution instance using SSZ.
|
||||
// It creates a byte slice `leaves` with a size based on length.Hash,
|
||||
// then fills this slice with the values from the Contribution's hash buffer.
|
||||
func (a *Contribution) HashSSZ() (o [32]byte, err error) {
|
||||
leaves := make([]byte, length.Hash*5)
|
||||
if err = a.CopyHashBufferTo(leaves); err != nil {
|
||||
return
|
||||
}
|
||||
err = merkle_tree.MerkleRootFromFlatLeaves(leaves, o[:])
|
||||
return
|
||||
}
|
||||
|
||||
// Clone creates a new clone of the Contribution instance.
|
||||
// This can be useful for creating copies without changing the original object.
|
||||
func (*Contribution) Clone() clonable.Clonable {
|
||||
return &Contribution{}
|
||||
}
|
||||
|
||||
type ContributionAndProof struct {
|
||||
AggregatorIndex uint64 `json:"aggregator_index,string"`
|
||||
Message *Contribution `json:"message"`
|
||||
Signature libcommon.Bytes96 `json:"selection_proof"`
|
||||
}
|
||||
|
||||
func (a *ContributionAndProof) EncodeSSZ(dst []byte) ([]byte, error) {
|
||||
return ssz2.MarshalSSZ(dst, a.Message, a.Signature[:])
|
||||
}
|
||||
|
||||
func (a *ContributionAndProof) DecodeSSZ(buf []byte, version int) error {
|
||||
a.Message = new(Contribution)
|
||||
return ssz2.UnmarshalSSZ(buf, version, a.Message, a.Signature[:])
|
||||
}
|
||||
|
||||
func (a *ContributionAndProof) EncodingSizeSSZ() int {
|
||||
return 100 + a.Message.EncodingSizeSSZ()
|
||||
}
|
||||
|
||||
func (a *ContributionAndProof) HashSSZ() ([32]byte, error) {
|
||||
return merkle_tree.HashTreeRoot(a.Message, a.Signature[:])
|
||||
}
|
||||
|
||||
type SignedContributionAndProof struct {
|
||||
Message *ContributionAndProof `json:"message"`
|
||||
Signature libcommon.Bytes96 `json:"signature"`
|
||||
}
|
||||
|
||||
func (a *SignedContributionAndProof) EncodeSSZ(dst []byte) ([]byte, error) {
|
||||
return ssz2.MarshalSSZ(dst, a.Message, a.Signature[:])
|
||||
}
|
||||
|
||||
func (a *SignedContributionAndProof) DecodeSSZ(buf []byte, version int) error {
|
||||
a.Message = new(ContributionAndProof)
|
||||
return ssz2.UnmarshalSSZ(buf, version, a.Message, a.Signature[:])
|
||||
}
|
||||
|
||||
func (a *SignedContributionAndProof) EncodingSizeSSZ() int {
|
||||
return 100 + a.Message.EncodingSizeSSZ()
|
||||
}
|
||||
|
||||
func (a *SignedContributionAndProof) HashSSZ() ([32]byte, error) {
|
||||
return merkle_tree.HashTreeRoot(a.Message, a.Signature[:])
|
||||
}
|
Loading…
Reference in New Issue
Block a user