more endpoints (#9109)

This commit is contained in:
a 2024-01-01 14:11:31 -06:00 committed by GitHub
parent a959387aae
commit bab123c07a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 762 additions and 131 deletions

View File

@ -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()
}

View 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))
}

View 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"`
}

View 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
}

View File

@ -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)

View File

@ -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 {

View File

@ -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")

View File

@ -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 {

View File

@ -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())

View File

@ -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")
}

View File

@ -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 {

View File

@ -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
}

View File

@ -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)

View File

@ -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)

View File

@ -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)

View File

@ -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
}
}

View File

@ -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
}

View File

@ -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)
})
})

View 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
}

View 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")
}

View File

@ -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
View 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[:])
}

View 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[:])
}