Skip to content

Commit

Permalink
fix log and change rpc in response to json rpc error (Layr-Labs#474)
Browse files Browse the repository at this point in the history
Co-authored-by: Bowen Xue <[email protected]>
  • Loading branch information
bxue-l2 and Bowen Xue authored Apr 13, 2024
1 parent b215782 commit 66a9a6f
Show file tree
Hide file tree
Showing 4 changed files with 131 additions and 98 deletions.
30 changes: 24 additions & 6 deletions common/geth/failover.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package geth

import (
"net/url"
"sync"

"github.com/Layr-Labs/eigensdk-go/logging"
Expand All @@ -9,27 +10,44 @@ import (
type FailoverController struct {
mu *sync.RWMutex
numberRpcFault uint64
UrlDomains []string

Logger logging.Logger
}

func NewFailoverController(logger logging.Logger) *FailoverController {
return &FailoverController{
Logger: logger.With("component", "FailoverController"),
mu: &sync.RWMutex{},
func NewFailoverController(logger logging.Logger, rpcUrls []string) (*FailoverController, error) {
urlDomains := make([]string, len(rpcUrls))
for i := 0; i < len(urlDomains); i++ {
url, err := url.Parse(rpcUrls[i])
if err != nil {
return nil, err
}
urlDomains[i] = url.Hostname()
}
return &FailoverController{
Logger: logger.With("component", "FailoverController"),
mu: &sync.RWMutex{},
UrlDomains: urlDomains,
}, nil
}

// ProcessError attributes the error and updates total number of fault for RPC
// It returns if RPC should immediately give up
func (f *FailoverController) ProcessError(err error) bool {
func (f *FailoverController) ProcessError(err error, rpcIndex int) bool {
f.mu.Lock()
defer f.mu.Unlock()
if err == nil {
return false
}

nextEndpoint, action := f.handleError(err)
urlDomain := ""
if rpcIndex >= len(f.UrlDomains) || rpcIndex < 0 {
f.Logger.Error("[FailoverController]", "err", "rpc index is outside of known url")
} else {
urlDomain = f.UrlDomains[rpcIndex]
}

nextEndpoint, action := f.handleError(err, urlDomain)

if nextEndpoint == NewRPC {
f.numberRpcFault += 1
Expand Down
18 changes: 8 additions & 10 deletions common/geth/handle_error.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ const (

// handleHttpError returns a boolean indicating if the current RPC should be rotated
// the second boolean indicating if should giveup immediately
func (f *FailoverController) handleHttpError(httpRespError rpc.HTTPError) (NextEndpoint, ImmediateAction) {
func (f *FailoverController) handleHttpError(httpRespError rpc.HTTPError, urlDomain string) (NextEndpoint, ImmediateAction) {
sc := httpRespError.StatusCode
// Default to rotation the current RPC, because it allows a higher chance to get the query completed.
f.Logger.Info("[HTTP Response Error]", "Status Code", sc, "Error", httpRespError)
f.Logger.Info("[HTTP Response Error]", "urlDomain", urlDomain, "statusCode", sc, "err", httpRespError)

if sc >= 200 && sc < 300 {
// 2xx error, however it should not be reachable
Expand All @@ -51,30 +51,28 @@ func (f *FailoverController) handleHttpError(httpRespError rpc.HTTPError) (NextE
// If the error is http, non2xx error would generate HTTP error, https://github.com/ethereum/go-ethereum/blob/master/rpc/http.go#L233
// but a 2xx http response could contain JSON RPC error, https://github.com/ethereum/go-ethereum/blob/master/rpc/http.go#L181
// If the error is Websocket or IPC, we only look for JSON error, https://github.com/ethereum/go-ethereum/blob/master/rpc/json.go#L67

func (f *FailoverController) handleError(err error) (NextEndpoint, ImmediateAction) {
func (f *FailoverController) handleError(err error, urlDomain string) (NextEndpoint, ImmediateAction) {

var httpRespError rpc.HTTPError
if errors.As(err, &httpRespError) {
// if error is http error, i.e. non 2xx error, it is handled here
// if it is 2xx error, the error message is nil, https://github.com/ethereum/go-ethereum/blob/master/rpc/http.go,
// execution does not enter here.
return f.handleHttpError(httpRespError)
return f.handleHttpError(httpRespError, urlDomain)
} else {
// it might be http2xx error, websocket error or ipc error. Parse json error code
var rpcError rpc.Error
if errors.As(err, &rpcError) {
ec := rpcError.ErrorCode()
f.Logger.Info("[JSON RPC Response Error]", "Error Code", ec, "Error", rpcError)
// we always attribute JSON RPC error as sender's fault, i.e no connection rotation
return CurrentRPC, Return
f.Logger.Warn("[JSON RPC Response Error]", "urlDomain", urlDomain, "errorCode", ec, "err", rpcError)
// we always attribute JSON RPC error as receiver's fault, i.e new connection rotation
return NewRPC, Return
}

// If no http response or no rpc response is returned, it is a connection issue,
// since we can't accurately attribute the network issue to neither sender nor receiver
// side. Optimistically, switch rpc client
f.Logger.Info("[Default Response Error]", err)
f.Logger.Warn("[Default Response Error]", "urlDomain", urlDomain, "err", err)
return NewRPC, Retry
}

}
Loading

0 comments on commit 66a9a6f

Please sign in to comment.