Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

draft(exporter): expose all messages #2008

Draft
wants to merge 24 commits into
base: stage
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 151 additions & 3 deletions api/handlers/exporter.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,26 @@
"net/http"

"github.com/attestantio/go-eth2-client/spec/phase0"
spectypes "github.com/ssvlabs/ssv-spec/types"
"github.com/ssvlabs/ssv/networkconfig"
qbftstorage "github.com/ssvlabs/ssv/protocol/v2/qbft/storage"

spectypes "github.com/ssvlabs/ssv-spec/types"
"github.com/ssvlabs/ssv/api"
model "github.com/ssvlabs/ssv/exporter/v2"
ibftstorage "github.com/ssvlabs/ssv/ibft/storage"
"github.com/ssvlabs/ssv/networkconfig"
qbftstorage "github.com/ssvlabs/ssv/protocol/v2/qbft/storage"
)

type Exporter struct {
NetworkConfig networkconfig.NetworkConfig
ParticipantStores *ibftstorage.ParticipantStores
TraceStore DutyTraceStore
}

type DutyTraceStore interface {
GetValidatorDuty(role spectypes.BeaconRole, slot phase0.Slot, pubkey spectypes.ValidatorPK) (*model.ValidatorDutyTrace, error)
GetCommitteeDutiesByOperator(indexes []spectypes.OperatorID, slot phase0.Slot) ([]*model.CommitteeDutyTrace, error)
GetCommitteeDuty(slot phase0.Slot, committeeID spectypes.CommitteeID) (*model.CommitteeDutyTrace, error)
GetAllValidatorDuties(role spectypes.BeaconRole, slot phase0.Slot) ([]*model.ValidatorDutyTrace, error)
}

type ParticipantResponse struct {
Expand Down Expand Up @@ -97,3 +106,142 @@

return response
}

func (e *Exporter) OperatorTraces(w http.ResponseWriter, r *http.Request) error {
var request struct {
From uint64 `json:"from"`
To uint64 `json:"to"`
OperatorID []uint64 `json:"operatorID"`
}

if err := api.Bind(r, &request); err != nil {
return api.BadRequestError(err)
}

Check warning on line 119 in api/handlers/exporter.go

View check run for this annotation

Codecov / codecov/patch

api/handlers/exporter.go#L110-L119

Added lines #L110 - L119 were not covered by tests

var indexes []spectypes.OperatorID
indexes = append(indexes, request.OperatorID...)

var duties []*model.CommitteeDutyTrace
for s := request.From; s <= request.To; s++ {
slot := phase0.Slot(s)
dd, err := e.TraceStore.GetCommitteeDutiesByOperator(indexes, slot)
if err != nil {
return api.Error(fmt.Errorf("error getting duties: %w", err))
}
duties = append(duties, dd...)

Check warning on line 131 in api/handlers/exporter.go

View check run for this annotation

Codecov / codecov/patch

api/handlers/exporter.go#L121-L131

Added lines #L121 - L131 were not covered by tests
}

return api.Render(w, r, toCommitteeTraceResponse(duties))

Check warning on line 134 in api/handlers/exporter.go

View check run for this annotation

Codecov / codecov/patch

api/handlers/exporter.go#L134

Added line #L134 was not covered by tests
}

func (e *Exporter) CommitteeTraces(w http.ResponseWriter, r *http.Request) error {
var request struct {
From uint64 `json:"from"`
To uint64 `json:"to"`
CommitteeID api.Hex `json:"committeeID"`
}

if err := api.Bind(r, &request); err != nil {
return api.BadRequestError(err)
}

Check warning on line 146 in api/handlers/exporter.go

View check run for this annotation

Codecov / codecov/patch

api/handlers/exporter.go#L137-L146

Added lines #L137 - L146 were not covered by tests

if request.From > request.To {
return api.BadRequestError(fmt.Errorf("'from' must be less than or equal to 'to'"))
}

Check warning on line 150 in api/handlers/exporter.go

View check run for this annotation

Codecov / codecov/patch

api/handlers/exporter.go#L148-L150

Added lines #L148 - L150 were not covered by tests

var committeeID spectypes.CommitteeID

if len(request.CommitteeID) != len(committeeID) {
return api.BadRequestError(fmt.Errorf("invalid committee ID length"))
}

Check warning on line 156 in api/handlers/exporter.go

View check run for this annotation

Codecov / codecov/patch

api/handlers/exporter.go#L152-L156

Added lines #L152 - L156 were not covered by tests

copy(committeeID[:], request.CommitteeID)

var duties []*model.CommitteeDutyTrace
for s := request.From; s <= request.To; s++ {
slot := phase0.Slot(s)
duty, err := e.TraceStore.GetCommitteeDuty(slot, committeeID)
if err != nil {
return api.Error(fmt.Errorf("error getting duties: %w", err))
}
duties = append(duties, duty)

Check warning on line 167 in api/handlers/exporter.go

View check run for this annotation

Codecov / codecov/patch

api/handlers/exporter.go#L158-L167

Added lines #L158 - L167 were not covered by tests
}

return api.Render(w, r, toCommitteeTraceResponse(duties))

Check warning on line 170 in api/handlers/exporter.go

View check run for this annotation

Codecov / codecov/patch

api/handlers/exporter.go#L170

Added line #L170 was not covered by tests
}

func toCommitteeTraceResponse(duties []*model.CommitteeDutyTrace) *committeeTraceResponse {
r := new(committeeTraceResponse)
for _, t := range duties {
r.Data = append(r.Data, toCommitteeTrace(t))
}
return r

Check warning on line 178 in api/handlers/exporter.go

View check run for this annotation

Codecov / codecov/patch

api/handlers/exporter.go#L173-L178

Added lines #L173 - L178 were not covered by tests
}

func (e *Exporter) ValidatorTraces(w http.ResponseWriter, r *http.Request) error {
var request struct {
From uint64 `json:"from"`
To uint64 `json:"to"`
Roles api.RoleSlice `json:"roles"`
PubKey api.Hex `json:"pubkey"` // optional
}

if err := api.Bind(r, &request); err != nil {
return api.BadRequestError(err)
}

Check warning on line 191 in api/handlers/exporter.go

View check run for this annotation

Codecov / codecov/patch

api/handlers/exporter.go#L181-L191

Added lines #L181 - L191 were not covered by tests

if request.From > request.To {
return api.BadRequestError(fmt.Errorf("'from' must be less than or equal to 'to'"))
}

Check warning on line 195 in api/handlers/exporter.go

View check run for this annotation

Codecov / codecov/patch

api/handlers/exporter.go#L193-L195

Added lines #L193 - L195 were not covered by tests

if len(request.Roles) == 0 {
return api.BadRequestError(fmt.Errorf("at least one role is required"))
}

Check warning on line 199 in api/handlers/exporter.go

View check run for this annotation

Codecov / codecov/patch

api/handlers/exporter.go#L197-L199

Added lines #L197 - L199 were not covered by tests

var results []*model.ValidatorDutyTrace

if len(request.PubKey) == 0 {
for s := request.From; s <= request.To; s++ {
slot := phase0.Slot(s)
for _, r := range request.Roles {
role := spectypes.BeaconRole(r)
duties, err := e.TraceStore.GetAllValidatorDuties(role, slot)
if err != nil {
return api.Error(fmt.Errorf("error getting duties: %w", err))
}
results = append(results, duties...)

Check warning on line 212 in api/handlers/exporter.go

View check run for this annotation

Codecov / codecov/patch

api/handlers/exporter.go#L201-L212

Added lines #L201 - L212 were not covered by tests
}
}
return api.Render(w, r, toValidatorTraceResponse(results))

Check warning on line 215 in api/handlers/exporter.go

View check run for this annotation

Codecov / codecov/patch

api/handlers/exporter.go#L215

Added line #L215 was not covered by tests
}

var pubkey spectypes.ValidatorPK

if len(request.PubKey) != len(pubkey) {
return api.BadRequestError(fmt.Errorf("invalid pubkey length"))
}

Check warning on line 222 in api/handlers/exporter.go

View check run for this annotation

Codecov / codecov/patch

api/handlers/exporter.go#L218-L222

Added lines #L218 - L222 were not covered by tests

copy(pubkey[:], request.PubKey)

for s := request.From; s <= request.To; s++ {
slot := phase0.Slot(s)
for _, r := range request.Roles {
role := spectypes.BeaconRole(r)
duty, err := e.TraceStore.GetValidatorDuty(role, slot, pubkey)
if err != nil {
return api.Error(fmt.Errorf("error getting duties: %w", err))
}
results = append(results, duty)

Check warning on line 234 in api/handlers/exporter.go

View check run for this annotation

Codecov / codecov/patch

api/handlers/exporter.go#L224-L234

Added lines #L224 - L234 were not covered by tests
}
}

return api.Render(w, r, toValidatorTraceResponse(results))

Check warning on line 238 in api/handlers/exporter.go

View check run for this annotation

Codecov / codecov/patch

api/handlers/exporter.go#L238

Added line #L238 was not covered by tests
}

func toValidatorTraceResponse(duties []*model.ValidatorDutyTrace) *validatorTraceResponse {
r := new(validatorTraceResponse)
for _, t := range duties {
r.Data = append(r.Data, toValidatorTrace(t))
}
return r

Check warning on line 246 in api/handlers/exporter.go

View check run for this annotation

Codecov / codecov/patch

api/handlers/exporter.go#L241-L246

Added lines #L241 - L246 were not covered by tests
}
222 changes: 222 additions & 0 deletions api/handlers/model.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
package handlers

import (
"encoding/hex"
"time"

"github.com/attestantio/go-eth2-client/spec/phase0"
spectypes "github.com/ssvlabs/ssv-spec/types"
model "github.com/ssvlabs/ssv/exporter/v2"
qbftmsg "github.com/ssvlabs/ssv/protocol/v2/message"
)

type validatorTraceResponse struct {
Data []validatorTrace `json:"data"`
}

type validatorTrace struct {
Slot phase0.Slot `json:"slot"`
Rounds []round
Decideds []decided
Pre []message `json:"pre"`
Post []message `json:"post"`
Role spectypes.BeaconRole `json:"role"`
Validator phase0.ValidatorIndex `json:"validator"`
}

type decided struct {
Round uint64 `json:"round"`
BeaconRoot phase0.Root `json:"beaconRoot"`
Signers []spectypes.OperatorID `json:"signers"`
ReceivedTime time.Time `json:"time"`
}

type round struct {
ProposalTrace *proposalTrace `json:"proposal"`
Proposer spectypes.OperatorID `json:"proposer"`
Prepares []message `json:"prepares"`
Commits []message `json:"commits"`
RoundChanges []roundChange `json:"roundChanges"`
}

type proposalTrace struct {
Round uint64 `json:"round"`
BeaconRoot phase0.Root `json:"beaconRoot"`
Signer spectypes.OperatorID `json:"signer"`
RoundChanges []roundChange `json:"roundChanges"`
PrepareMessages []message `json:"prepareMessages"`
ReceivedTime time.Time `json:"time"`
}

type roundChange struct {
message
PreparedRound uint64 `json:"preparedRound"`
PrepareMessages []message `json:"prepareMessages"`
}

type message struct {
Round uint64 `json:"round"`
BeaconRoot phase0.Root `json:"beaconRoot"`
Signer spectypes.OperatorID `json:"signer"`
ReceivedTime time.Time `json:"time"`
}

type partialSigMessage struct {
BeaconRoot phase0.Root `json:"beaconRoot"`
Signer spectypes.OperatorID `json:"signer"`
Validator phase0.ValidatorIndex `json:"validator"`
}

func toValidatorTrace(t *model.ValidatorDutyTrace) validatorTrace {
return validatorTrace{
Slot: t.Slot,
Role: t.Role,
Validator: t.Validator,
Pre: toMessageTrace(t.Pre),
Post: toMessageTrace(t.Post),
Rounds: toRounds(t.Rounds),
}

Check warning on line 78 in api/handlers/model.go

View check run for this annotation

Codecov / codecov/patch

api/handlers/model.go#L70-L78

Added lines #L70 - L78 were not covered by tests
}

func toMessageTrace(m []*model.PartialSigTrace) (out []message) {
for _, mt := range m {
out = append(out, message{
BeaconRoot: mt.BeaconRoot,
Signer: mt.Signer,
ReceivedTime: mt.ReceivedTime,
})
}

Check warning on line 88 in api/handlers/model.go

View check run for this annotation

Codecov / codecov/patch

api/handlers/model.go#L81-L88

Added lines #L81 - L88 were not covered by tests

return

Check warning on line 90 in api/handlers/model.go

View check run for this annotation

Codecov / codecov/patch

api/handlers/model.go#L90

Added line #L90 was not covered by tests
}

func toRounds(r []*model.RoundTrace) (out []round) {
for _, rt := range r {
out = append(out, round{
Proposer: rt.Proposer,
ProposalTrace: toProposalTrace(rt.ProposalTrace),
Prepares: toUIMessageTrace(rt.Prepares),
Commits: toUIMessageTrace(rt.Commits),
RoundChanges: toUIRoundChangeTrace(rt.RoundChanges),
})
}

Check warning on line 102 in api/handlers/model.go

View check run for this annotation

Codecov / codecov/patch

api/handlers/model.go#L93-L102

Added lines #L93 - L102 were not covered by tests

return

Check warning on line 104 in api/handlers/model.go

View check run for this annotation

Codecov / codecov/patch

api/handlers/model.go#L104

Added line #L104 was not covered by tests
}

func toProposalTrace(rt *model.ProposalTrace) *proposalTrace {
if rt == nil {
return nil
}
return &proposalTrace{
Round: rt.Round,
BeaconRoot: rt.BeaconRoot,
Signer: rt.Signer,
ReceivedTime: rt.ReceivedTime,
RoundChanges: toUIRoundChangeTrace(rt.RoundChanges),
PrepareMessages: toUIMessageTrace(rt.PrepareMessages),
}

Check warning on line 118 in api/handlers/model.go

View check run for this annotation

Codecov / codecov/patch

api/handlers/model.go#L107-L118

Added lines #L107 - L118 were not covered by tests
}

func toUIMessageTrace(m []*model.QBFTTrace) (out []message) {
for _, mt := range m {
out = append(out, message{
Round: mt.Round,
BeaconRoot: mt.BeaconRoot,
Signer: mt.Signer,
ReceivedTime: mt.ReceivedTime,
})
}

Check warning on line 129 in api/handlers/model.go

View check run for this annotation

Codecov / codecov/patch

api/handlers/model.go#L121-L129

Added lines #L121 - L129 were not covered by tests

return

Check warning on line 131 in api/handlers/model.go

View check run for this annotation

Codecov / codecov/patch

api/handlers/model.go#L131

Added line #L131 was not covered by tests
}

func toUIRoundChangeTrace(m []*model.RoundChangeTrace) (out []roundChange) {
for _, mt := range m {
out = append(out, roundChange{
message: message{
Round: mt.Round,
BeaconRoot: mt.BeaconRoot,
Signer: mt.Signer,
ReceivedTime: mt.ReceivedTime,
},
PreparedRound: mt.PreparedRound,
PrepareMessages: toUIMessageTrace(mt.PrepareMessages),
})
}

Check warning on line 146 in api/handlers/model.go

View check run for this annotation

Codecov / codecov/patch

api/handlers/model.go#L134-L146

Added lines #L134 - L146 were not covered by tests

return

Check warning on line 148 in api/handlers/model.go

View check run for this annotation

Codecov / codecov/patch

api/handlers/model.go#L148

Added line #L148 was not covered by tests
}

// committee

type committeeTraceResponse struct {
Data []committeeTrace `json:"data"`
}

type committeeTrace struct {
Slot phase0.Slot `json:"slot"`
Rounds []round `json:"rounds"`
Decideds []decided `json:"decideds"`
Post []committeeMessage `json:"post"`

CommitteeID string `json:"committeeID"`
OperatorIDs []spectypes.OperatorID `json:"operatorIDs"`
}

type committeeMessage struct {
Type string `json:"type"`
Signer spectypes.OperatorID `json:"signer"`
Messages []partialSigMessage `json:"messages"`
ReceivedTime time.Time `json:"time"`
}

func toCommitteeTrace(t *model.CommitteeDutyTrace) committeeTrace {
return committeeTrace{
// consensus trace
Rounds: toRounds(t.Rounds),
Decideds: toDecidedTrace(t.Decideds),
Slot: t.Slot,
Post: toCommitteePost(t.Post),
CommitteeID: hex.EncodeToString(t.CommitteeID[:]),
OperatorIDs: t.OperatorIDs,
}

Check warning on line 183 in api/handlers/model.go

View check run for this annotation

Codecov / codecov/patch

api/handlers/model.go#L174-L183

Added lines #L174 - L183 were not covered by tests
}

func toDecidedTrace(d []*model.DecidedTrace) (out []decided) {
for _, dt := range d {
out = append(out, decided{
Round: dt.Round,
BeaconRoot: dt.BeaconRoot,
Signers: dt.Signers,
ReceivedTime: dt.ReceivedTime,
})
}

Check warning on line 194 in api/handlers/model.go

View check run for this annotation

Codecov / codecov/patch

api/handlers/model.go#L186-L194

Added lines #L186 - L194 were not covered by tests

return

Check warning on line 196 in api/handlers/model.go

View check run for this annotation

Codecov / codecov/patch

api/handlers/model.go#L196

Added line #L196 was not covered by tests
}

func toCommitteePost(m []*model.CommitteePartialSigMessageTrace) (out []committeeMessage) {
for _, mt := range m {
out = append(out, committeeMessage{
Type: qbftmsg.PartialMsgTypeToString(mt.Type),
Signer: mt.Signer,
Messages: toCommitteePartSigMessage(mt.Messages),
ReceivedTime: mt.ReceivedTime,
})
}

Check warning on line 207 in api/handlers/model.go

View check run for this annotation

Codecov / codecov/patch

api/handlers/model.go#L199-L207

Added lines #L199 - L207 were not covered by tests

return

Check warning on line 209 in api/handlers/model.go

View check run for this annotation

Codecov / codecov/patch

api/handlers/model.go#L209

Added line #L209 was not covered by tests
}

func toCommitteePartSigMessage(m []*model.PartialSigMessage) (out []partialSigMessage) {
for _, mt := range m {
out = append(out, partialSigMessage{
BeaconRoot: mt.BeaconRoot,
Signer: mt.Signer,
Validator: mt.ValidatorIndex,
})
}

Check warning on line 219 in api/handlers/model.go

View check run for this annotation

Codecov / codecov/patch

api/handlers/model.go#L212-L219

Added lines #L212 - L219 were not covered by tests

return

Check warning on line 221 in api/handlers/model.go

View check run for this annotation

Codecov / codecov/patch

api/handlers/model.go#L221

Added line #L221 was not covered by tests
}
Loading