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

Store and serve validation errors #13

Merged
merged 1 commit into from
Sep 20, 2023
Merged
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
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,7 @@ The callback can then use the slot number to determine whether to throw an error

- `DELETE` `/mock/invalid/payload`: Disables any modification to payload built
- `POST` `/mock/invalid/payload/{type}`: Enables specified [type](#payload-invalidation-types) of modification to payload built
- `POST` `/mock/invalid/payload/{type}/<slot|epoch>/{slot/epoch number}`: Enables specified [type](#payload-invalidation-types) of modification to payload built starting at the slot or epoch specified
- `POST` `/mock/invalid/payload/{type}/<slot|epoch>/{slot/epoch number}`: Enables specified [type](#payload-invalidation-types) of modification to payload built starting at the slot or epoch specified

# Statistics
- `GET` `/mock/stats/validation_errors`: Returns a JSON containing all the errors encountered when validating the submitted signed blinded responses from the consensus client (e.g. Invalid signature on submitted blinded block, invalid signature on submitted blinded blob sidecar)
74 changes: 73 additions & 1 deletion mock/mock_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ type MockBuilder struct {
receivedSignedBeaconBlocksMutex sync.Mutex
signedBeaconBlock map[tree.Root]bool
signedBeaconBlockMutex sync.Mutex
validationErrors map[beacon.Slot]error
validationErrorsMutex sync.Mutex

// Configuration object
cfg *config
Expand Down Expand Up @@ -105,6 +107,7 @@ func NewMockBuilder(
map[beacon.Slot]common.SignedBeaconResponse,
),
signedBeaconBlock: make(map[tree.Root]bool),
validationErrors: make(map[beacon.Slot]error),

cfg: &config{
host: DEFAULT_BUILDER_HOST,
Expand Down Expand Up @@ -215,6 +218,9 @@ func NewMockBuilder(
m.HandleMockEnableInvalidatePayload,
).Methods("POST")

// Statistics Handlers
router.HandleFunc("/mock/stats/validation_errors", m.HandleValidationErrors).Methods("GET")

m.srv = &http.Server{
Handler: router,
Addr: fmt.Sprintf("%s:%d", m.cfg.host, m.cfg.port),
Expand Down Expand Up @@ -339,6 +345,13 @@ func (m *MockBuilder) GetModifiedPayloads() map[beacon.Slot]common.ExecutionPayl
return mapCopy
}

func (m *MockBuilder) GetSignedBeaconBlock(slot beacon.Slot) (common.SignedBeaconResponse, bool) {
m.receivedSignedBeaconBlocksMutex.Lock()
defer m.receivedSignedBeaconBlocksMutex.Unlock()
signedBeaconResponse, ok := m.receivedSignedBeaconBlocks[slot]
return signedBeaconResponse, ok
}

func (m *MockBuilder) GetSignedBeaconBlocks() map[beacon.Slot]common.SignedBeaconResponse {
m.receivedSignedBeaconBlocksMutex.Lock()
defer m.receivedSignedBeaconBlocksMutex.Unlock()
Expand All @@ -349,6 +362,20 @@ func (m *MockBuilder) GetSignedBeaconBlocks() map[beacon.Slot]common.SignedBeaco
return mapCopy
}

func (m *MockBuilder) GetValidationErrors() map[beacon.Slot]error {
m.validationErrorsMutex.Lock()
defer m.validationErrorsMutex.Unlock()
mapCopy := make(map[beacon.Slot]error)
for k, v := range m.validationErrors {
mapCopy[k] = v
}
return mapCopy
}

func (m *MockBuilder) GetValidationErrorsCount() int {
return len(m.validationErrors)
}

func (m *MockBuilder) HandleValidators(
w http.ResponseWriter,
req *http.Request,
Expand Down Expand Up @@ -604,6 +631,19 @@ func (m *MockBuilder) HandleGetExecutionPayloadHeader(
}

blockHead, err = m.cl.BlockV2(context.Background(), blockId)
if err != nil {
logrus.WithFields(logrus.Fields{
"builder_id": m.cfg.id,
"slot": slot,
"err": err,
}).Error("Error getting block from CL")
http.Error(
w,
"Unable to respond to header request",
http.StatusInternalServerError,
)
return
}

if randaoMix == nil || (withdrawalsRequired && withdrawals == nil) {
// We are missing information from the CL, request the full state to reproduce
Expand Down Expand Up @@ -1110,7 +1150,6 @@ func (m *MockBuilder) HandleSubmitBlindedBlock(
return
}


// First try to find out the slot to get the version of the block
var slot beacon.Slot
{
Expand Down Expand Up @@ -1154,6 +1193,7 @@ func (m *MockBuilder) HandleSubmitBlindedBlock(
logrus.WithFields(logrus.Fields{
"builder_id": m.cfg.id,
"err": err,
"request": string(requestBytes),
}).Error("Unable to parse request body")
http.Error(w, "Unable to parse request body", http.StatusBadRequest)
return
Expand All @@ -1165,6 +1205,7 @@ func (m *MockBuilder) HandleSubmitBlindedBlock(
logrus.WithFields(logrus.Fields{
"builder_id": m.cfg.id,
"slot": slot,
"request": string(requestBytes),
}).Error("Could not find payload in history")
http.Error(w, "Unable to get payload", http.StatusInternalServerError)
return
Expand Down Expand Up @@ -1211,9 +1252,13 @@ func (m *MockBuilder) HandleSubmitBlindedBlock(
pk, signedBeaconResponse, m.cfg.spec, slot, m.cl.Config.GenesisValidatorsRoot,
)
if err != nil {
m.validationErrorsMutex.Lock()
m.validationErrors[signedBeaconResponse.Slot()] = err
m.validationErrorsMutex.Unlock()
logrus.WithFields(logrus.Fields{
"builder_id": m.cfg.id,
"slot": slot,
"request": string(requestBytes),
"err": err,
}).Error("Error validating signed beacon response")
http.Error(
Expand Down Expand Up @@ -1606,6 +1651,33 @@ func (m *MockBuilder) HandleMockEnableInvalidatePayload(
w.WriteHeader(http.StatusOK)
}

// Stats handlers

func (m *MockBuilder) HandleValidationErrors(
w http.ResponseWriter, req *http.Request,
) {
// Return the map of validation errors
m.validationErrorsMutex.Lock()
defer m.validationErrorsMutex.Unlock()
validationErrors := make(map[string]string)
for k, v := range m.validationErrors {
validationErrors[k.String()] = v.Error()
}
if err := serveJSON(w, validationErrors); err != nil {
logrus.WithFields(logrus.Fields{
"builder_id": m.cfg.id,
"err": err,
}).Error("Error writing JSON response to CL")
http.Error(
w,
"Unable to respond to header request",
http.StatusInternalServerError,
)
return
}
w.WriteHeader(http.StatusOK)
}

// helpers

func serveJSON(w http.ResponseWriter, value interface{}) error {
Expand Down
Loading