-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor: cleaner routing using gorilla mux regexps (#185)
* refactor: GET routes to use gorilla mux regexp patterns * refactor: server into files routing/handlers/middleware * style: fix lint * refactor: use gorilla mux for POST routes * routing: fix incorrect error msgs for simple commitments * cleanup: old unused function * tests: fix e2e tests * docs: add docstrings to handlers * style: use vars for the gorilla mux routing variables instead of hardcoded strings * logging: better logging msgs in op handlers * metrics: use "unknown" instead of "noCommitment" in metrics middleware when missing meta info * logging(handlers): simplify by moving "processing request" logs to shared handlers only
- Loading branch information
Showing
12 changed files
with
562 additions
and
482 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,209 @@ | ||
package server | ||
|
||
import ( | ||
"context" | ||
"encoding/hex" | ||
"errors" | ||
"fmt" | ||
"io" | ||
"net/http" | ||
|
||
"github.com/Layr-Labs/eigenda-proxy/commitments" | ||
"github.com/Layr-Labs/eigenda-proxy/store" | ||
"github.com/gorilla/mux" | ||
) | ||
|
||
func (svr *Server) handleHealth(w http.ResponseWriter, _ *http.Request) error { | ||
w.WriteHeader(http.StatusOK) | ||
return nil | ||
} | ||
|
||
// ================================================================================================= | ||
// GET ROUTES | ||
// ================================================================================================= | ||
|
||
// handleGetSimpleCommitment handles the GET request for simple commitments. | ||
func (svr *Server) handleGetSimpleCommitment(w http.ResponseWriter, r *http.Request) error { | ||
versionByte, err := parseVersionByte(w, r) | ||
if err != nil { | ||
return fmt.Errorf("error parsing version byte: %w", err) | ||
} | ||
commitmentMeta := commitments.CommitmentMeta{ | ||
Mode: commitments.SimpleCommitmentMode, | ||
CertVersion: versionByte, | ||
} | ||
|
||
rawCommitmentHex, ok := mux.Vars(r)[routingVarNameRawCommitmentHex] | ||
if !ok { | ||
return fmt.Errorf("commitment not found in path: %s", r.URL.Path) | ||
} | ||
commitment, err := hex.DecodeString(rawCommitmentHex) | ||
if err != nil { | ||
return fmt.Errorf("failed to decode commitment %s: %w", rawCommitmentHex, err) | ||
} | ||
|
||
return svr.handleGetShared(r.Context(), w, commitment, commitmentMeta) | ||
} | ||
|
||
// handleGetOPKeccakCommitment handles the GET request for optimism keccak commitments. | ||
func (svr *Server) handleGetOPKeccakCommitment(w http.ResponseWriter, r *http.Request) error { | ||
// TODO: do we use a version byte in OPKeccak commitments? README seems to say so, but server_test didn't | ||
// versionByte, err := parseVersionByte(r) | ||
// if err != nil { | ||
// err = fmt.Errorf("error parsing version byte: %w", err) | ||
// http.Error(w, err.Error(), http.StatusBadRequest) | ||
// return err | ||
// } | ||
commitmentMeta := commitments.CommitmentMeta{ | ||
Mode: commitments.OptimismKeccak, | ||
CertVersion: byte(commitments.CertV0), | ||
} | ||
|
||
rawCommitmentHex, ok := mux.Vars(r)[routingVarNameRawCommitmentHex] | ||
if !ok { | ||
return fmt.Errorf("commitment not found in path: %s", r.URL.Path) | ||
} | ||
commitment, err := hex.DecodeString(rawCommitmentHex) | ||
if err != nil { | ||
return fmt.Errorf("failed to decode commitment %s: %w", rawCommitmentHex, err) | ||
} | ||
|
||
return svr.handleGetShared(r.Context(), w, commitment, commitmentMeta) | ||
} | ||
|
||
// handleGetOPGenericCommitment handles the GET request for optimism generic commitments. | ||
func (svr *Server) handleGetOPGenericCommitment(w http.ResponseWriter, r *http.Request) error { | ||
versionByte, err := parseVersionByte(w, r) | ||
if err != nil { | ||
return fmt.Errorf("error parsing version byte: %w", err) | ||
} | ||
commitmentMeta := commitments.CommitmentMeta{ | ||
Mode: commitments.OptimismGeneric, | ||
CertVersion: versionByte, | ||
} | ||
|
||
rawCommitmentHex, ok := mux.Vars(r)[routingVarNameRawCommitmentHex] | ||
if !ok { | ||
return fmt.Errorf("commitment not found in path: %s", r.URL.Path) | ||
} | ||
commitment, err := hex.DecodeString(rawCommitmentHex) | ||
if err != nil { | ||
return fmt.Errorf("failed to decode commitment %s: %w", rawCommitmentHex, err) | ||
} | ||
|
||
return svr.handleGetShared(r.Context(), w, commitment, commitmentMeta) | ||
} | ||
|
||
func (svr *Server) handleGetShared(ctx context.Context, w http.ResponseWriter, comm []byte, meta commitments.CommitmentMeta) error { | ||
svr.log.Info("Processing GET request", "commitment", hex.EncodeToString(comm), "commitmentMeta", meta) | ||
input, err := svr.router.Get(ctx, comm, meta.Mode) | ||
if err != nil { | ||
err = MetaError{ | ||
Err: fmt.Errorf("get request failed with commitment %v: %w", comm, err), | ||
Meta: meta, | ||
} | ||
if errors.Is(err, ErrNotFound) { | ||
http.Error(w, err.Error(), http.StatusNotFound) | ||
} else { | ||
http.Error(w, err.Error(), http.StatusInternalServerError) | ||
} | ||
return err | ||
} | ||
|
||
svr.writeResponse(w, input) | ||
return nil | ||
} | ||
|
||
// ================================================================================================= | ||
// POST ROUTES | ||
// ================================================================================================= | ||
|
||
// handlePostSimpleCommitment handles the POST request for simple commitments. | ||
func (svr *Server) handlePostSimpleCommitment(w http.ResponseWriter, r *http.Request) error { | ||
commitmentMeta := commitments.CommitmentMeta{ | ||
Mode: commitments.SimpleCommitmentMode, | ||
CertVersion: byte(commitments.CertV0), // TODO: hardcoded for now | ||
} | ||
return svr.handlePostShared(w, r, nil, commitmentMeta) | ||
} | ||
|
||
// handlePostOPKeccakCommitment handles the POST request for optimism keccak commitments. | ||
func (svr *Server) handlePostOPKeccakCommitment(w http.ResponseWriter, r *http.Request) error { | ||
// TODO: do we use a version byte in OPKeccak commitments? README seems to say so, but server_test didn't | ||
// versionByte, err := parseVersionByte(r) | ||
// if err != nil { | ||
// err = fmt.Errorf("error parsing version byte: %w", err) | ||
// http.Error(w, err.Error(), http.StatusBadRequest) | ||
// return err | ||
// } | ||
commitmentMeta := commitments.CommitmentMeta{ | ||
Mode: commitments.OptimismKeccak, | ||
CertVersion: byte(commitments.CertV0), | ||
} | ||
|
||
rawCommitmentHex, ok := mux.Vars(r)[routingVarNameRawCommitmentHex] | ||
if !ok { | ||
return fmt.Errorf("commitment not found in path: %s", r.URL.Path) | ||
} | ||
commitment, err := hex.DecodeString(rawCommitmentHex) | ||
if err != nil { | ||
return fmt.Errorf("failed to decode commitment %s: %w", rawCommitmentHex, err) | ||
} | ||
|
||
return svr.handlePostShared(w, r, commitment, commitmentMeta) | ||
} | ||
|
||
// handlePostOPGenericCommitment handles the POST request for optimism generic commitments. | ||
func (svr *Server) handlePostOPGenericCommitment(w http.ResponseWriter, r *http.Request) error { | ||
commitmentMeta := commitments.CommitmentMeta{ | ||
Mode: commitments.OptimismGeneric, | ||
CertVersion: byte(commitments.CertV0), // TODO: hardcoded for now | ||
} | ||
return svr.handlePostShared(w, r, nil, commitmentMeta) | ||
} | ||
|
||
func (svr *Server) handlePostShared(w http.ResponseWriter, r *http.Request, comm []byte, meta commitments.CommitmentMeta) error { | ||
svr.log.Info("Processing POST request", "commitment", hex.EncodeToString(comm), "commitmentMeta", meta) | ||
input, err := io.ReadAll(r.Body) | ||
if err != nil { | ||
err = MetaError{ | ||
Err: fmt.Errorf("failed to read request body: %w", err), | ||
Meta: meta, | ||
} | ||
http.Error(w, err.Error(), http.StatusBadRequest) | ||
return err | ||
} | ||
|
||
commitment, err := svr.router.Put(r.Context(), meta.Mode, comm, input) | ||
if err != nil { | ||
err = MetaError{ | ||
Err: fmt.Errorf("put request failed with commitment %v (commitment mode %v): %w", comm, meta.Mode, err), | ||
Meta: meta, | ||
} | ||
if errors.Is(err, store.ErrEigenDAOversizedBlob) || errors.Is(err, store.ErrProxyOversizedBlob) { | ||
// we add here any error that should be returned as a 400 instead of a 500. | ||
// currently only includes oversized blob requests | ||
http.Error(w, err.Error(), http.StatusBadRequest) | ||
} else { | ||
http.Error(w, err.Error(), http.StatusInternalServerError) | ||
} | ||
return err | ||
} | ||
|
||
responseCommit, err := commitments.EncodeCommitment(commitment, meta.Mode) | ||
if err != nil { | ||
err = MetaError{ | ||
Err: fmt.Errorf("failed to encode commitment %v (commitment mode %v): %w", commitment, meta.Mode, err), | ||
Meta: meta, | ||
} | ||
http.Error(w, err.Error(), http.StatusInternalServerError) | ||
return err | ||
} | ||
|
||
svr.log.Info(fmt.Sprintf("response commitment: %x\n", responseCommit)) | ||
// write commitment to resp body if not in OptimismKeccak mode | ||
if meta.Mode != commitments.OptimismKeccak { | ||
svr.writeResponse(w, responseCommit) | ||
} | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
package server | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"net/http" | ||
"time" | ||
|
||
"github.com/Layr-Labs/eigenda-proxy/commitments" | ||
"github.com/Layr-Labs/eigenda-proxy/metrics" | ||
"github.com/ethereum/go-ethereum/log" | ||
) | ||
|
||
// withMetrics is a middleware that records metrics for the route path. | ||
func withMetrics( | ||
handleFn func(http.ResponseWriter, *http.Request) error, | ||
m metrics.Metricer, | ||
mode commitments.CommitmentMode, | ||
) func(http.ResponseWriter, *http.Request) error { | ||
return func(w http.ResponseWriter, r *http.Request) error { | ||
recordDur := m.RecordRPCServerRequest(r.Method) | ||
|
||
err := handleFn(w, r) | ||
if err != nil { | ||
var metaErr MetaError | ||
if errors.As(err, &metaErr) { | ||
recordDur(w.Header().Get("status"), string(metaErr.Meta.Mode), string(metaErr.Meta.CertVersion)) | ||
} else { | ||
recordDur(w.Header().Get("status"), string("unknown"), string("unknown")) | ||
} | ||
return err | ||
} | ||
// we assume that every route will set the status header | ||
versionByte, err := parseVersionByte(w, r) | ||
if err != nil { | ||
return fmt.Errorf("metrics middleware: error parsing version byte: %w", err) | ||
} | ||
recordDur(w.Header().Get("status"), string(mode), string(versionByte)) | ||
return nil | ||
} | ||
} | ||
|
||
// withLogging is a middleware that logs information related to each request. | ||
// It does not write anything to the response, that is the job of the handlers. | ||
// Currently we cannot log the status code because go's default ResponseWriter interface does not expose it. | ||
// TODO: implement a ResponseWriter wrapper that saves the status code: see https://github.com/golang/go/issues/18997 | ||
func withLogging( | ||
handleFn func(http.ResponseWriter, *http.Request) error, | ||
log log.Logger, | ||
) func(http.ResponseWriter, *http.Request) { | ||
return func(w http.ResponseWriter, r *http.Request) { | ||
start := time.Now() | ||
err := handleFn(w, r) | ||
var metaErr MetaError | ||
//nolint:gocritic // ifElseChain is not a good replacement with errors.As | ||
if errors.As(err, &metaErr) { | ||
log.Info("request", "method", r.Method, "url", r.URL, "duration", time.Since(start), | ||
"err", err, "status", w.Header().Get("status"), | ||
"commitment_mode", metaErr.Meta.Mode, "cert_version", metaErr.Meta.CertVersion) | ||
} else if err != nil { | ||
log.Info("request", "method", r.Method, "url", r.URL, "duration", time.Since(start), "err", err) | ||
} else { | ||
log.Info("request", "method", r.Method, "url", r.URL, "duration", time.Since(start)) | ||
} | ||
} | ||
} |
Oops, something went wrong.