-
Notifications
You must be signed in to change notification settings - Fork 175
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: Ubuntu <[email protected]>
- Loading branch information
Showing
20 changed files
with
1,224 additions
and
35 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package geth | ||
|
||
import ( | ||
"sync" | ||
|
||
"github.com/Layr-Labs/eigensdk-go/logging" | ||
) | ||
|
||
type FailoverController struct { | ||
mu *sync.RWMutex | ||
numberRpcFault uint64 | ||
|
||
Logger logging.Logger | ||
} | ||
|
||
func NewFailoverController(logger logging.Logger) *FailoverController { | ||
return &FailoverController{ | ||
Logger: logger, | ||
mu: &sync.RWMutex{}, | ||
} | ||
} | ||
|
||
// 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 { | ||
f.mu.Lock() | ||
defer f.mu.Unlock() | ||
if err == nil { | ||
return false | ||
} | ||
|
||
nextEndpoint, action := f.handleError(err) | ||
|
||
if nextEndpoint == NewRPC { | ||
f.numberRpcFault += 1 | ||
} | ||
|
||
return action == Return | ||
} | ||
|
||
func (f *FailoverController) GetTotalNumberRpcFault() uint64 { | ||
f.mu.RLock() | ||
defer f.mu.RUnlock() | ||
return f.numberRpcFault | ||
} |
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,80 @@ | ||
package geth | ||
|
||
import ( | ||
"errors" | ||
|
||
"github.com/ethereum/go-ethereum/rpc" | ||
) | ||
|
||
type ImmediateAction int | ||
|
||
const ( | ||
Return ImmediateAction = iota | ||
Retry | ||
) | ||
|
||
type NextEndpoint int | ||
|
||
const ( | ||
NewRPC = iota | ||
CurrentRPC | ||
) | ||
|
||
// 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) { | ||
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) | ||
|
||
if sc >= 200 && sc < 300 { | ||
// 2xx error, however it should not be reachable | ||
return CurrentRPC, Return | ||
} | ||
|
||
if sc >= 400 && sc < 500 { | ||
// 403 Forbidden, 429 Too many Requests. We should rotate | ||
if sc == 403 || sc == 429 { | ||
return NewRPC, Retry | ||
} | ||
return CurrentRPC, Retry | ||
} | ||
|
||
// 500 | ||
return NewRPC, Retry | ||
} | ||
|
||
// handleError returns a boolean indicating if the current connection should be rotated. | ||
// Because library of the sender uses geth, which supports only 3 types of connections, | ||
// we can categorize the error as HTTP error, Websocket error and IPC error. | ||
// | ||
// 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) { | ||
|
||
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) | ||
} 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 | ||
} | ||
|
||
// 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) | ||
return NewRPC, Retry | ||
} | ||
|
||
} |
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
Oops, something went wrong.