Commit 46b56d89 authored by Mark Tyneway's avatar Mark Tyneway Committed by GitHub

Merge pull request #1754 from ethereum-optimism/develop

Develop -> Master PR
parents a63c653b 27c37f15
---
'@eth-optimism/proxyd': major
---
Make endpoints match Geth, better logging
---
'@eth-optimism/contracts': patch
---
Add AddressDictator validation script
...@@ -170,7 +170,7 @@ jobs: ...@@ -170,7 +170,7 @@ jobs:
name: Publish proxyd Version ${{ needs.release.outputs.proxyd }} name: Publish proxyd Version ${{ needs.release.outputs.proxyd }}
needs: release needs: release
if: needs.release.outputs.proxyd != '' if: needs.release.outputs.proxyd != ''
runs-on: ubuntu:latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
......
...@@ -163,7 +163,12 @@ func (b *Backend) Forward(ctx context.Context, req *RPCReq) (*RPCRes, error) { ...@@ -163,7 +163,12 @@ func (b *Backend) Forward(ctx context.Context, req *RPCReq) (*RPCRes, error) {
res, err := b.doForward(req) res, err := b.doForward(req)
if err != nil { if err != nil {
lastError = err lastError = err
log.Warn("backend request failed, trying again", "err", err, "name", b.Name) log.Warn(
"backend request failed, trying again",
"name", b.Name,
"req_id", GetReqID(ctx),
"err", err,
)
respTimer.ObserveDuration() respTimer.ObserveDuration()
RecordRPCError(ctx, b.Name, req.Method, err) RecordRPCError(ctx, b.Name, req.Method, err)
time.Sleep(calcBackoff(i)) time.Sleep(calcBackoff(i))
...@@ -172,6 +177,20 @@ func (b *Backend) Forward(ctx context.Context, req *RPCReq) (*RPCRes, error) { ...@@ -172,6 +177,20 @@ func (b *Backend) Forward(ctx context.Context, req *RPCReq) (*RPCRes, error) {
respTimer.ObserveDuration() respTimer.ObserveDuration()
if res.IsError() { if res.IsError() {
RecordRPCError(ctx, b.Name, req.Method, res.Error) RecordRPCError(ctx, b.Name, req.Method, res.Error)
log.Info(
"backend responded with RPC error",
"code", res.Error.Code,
"msg", res.Error.Message,
"req_id", GetReqID(ctx),
"source", "rpc",
"auth", GetAuthCtx(ctx),
)
} else {
log.Info("forwarded RPC request",
"method", req.Method,
"auth", GetAuthCtx(ctx),
"req_id", GetReqID(ctx),
)
} }
return res, nil return res, nil
} }
...@@ -313,15 +332,31 @@ func (b *BackendGroup) Forward(ctx context.Context, rpcReq *RPCReq) (*RPCRes, er ...@@ -313,15 +332,31 @@ func (b *BackendGroup) Forward(ctx context.Context, rpcReq *RPCReq) (*RPCRes, er
return nil, err return nil, err
} }
if errors.Is(err, ErrBackendOffline) { if errors.Is(err, ErrBackendOffline) {
log.Debug("skipping offline backend", "name", back.Name) log.Warn(
"skipping offline backend",
"name", back.Name,
"auth", GetAuthCtx(ctx),
"req_id", GetReqID(ctx),
)
continue continue
} }
if errors.Is(err, ErrBackendOverCapacity) { if errors.Is(err, ErrBackendOverCapacity) {
log.Debug("skipping over-capacity backend", "name", back.Name) log.Warn(
"skipping over-capacity backend",
"name", back.Name,
"auth", GetAuthCtx(ctx),
"req_id", GetReqID(ctx),
)
continue continue
} }
if err != nil { if err != nil {
log.Error("error forwarding request to backend", "err", err, "name", b.Name) log.Error(
"error forwarding request to backend",
"name", b.Name,
"req_id", GetReqID(ctx),
"auth", GetAuthCtx(ctx),
"err", err,
)
continue continue
} }
return res, nil return res, nil
...@@ -331,19 +366,35 @@ func (b *BackendGroup) Forward(ctx context.Context, rpcReq *RPCReq) (*RPCRes, er ...@@ -331,19 +366,35 @@ func (b *BackendGroup) Forward(ctx context.Context, rpcReq *RPCReq) (*RPCRes, er
return nil, ErrNoBackends return nil, ErrNoBackends
} }
func (b *BackendGroup) ProxyWS(clientConn *websocket.Conn, methodWhitelist *StringSet) (*WSProxier, error) { func (b *BackendGroup) ProxyWS(ctx context.Context, clientConn *websocket.Conn, methodWhitelist *StringSet) (*WSProxier, error) {
for _, back := range b.Backends { for _, back := range b.Backends {
proxier, err := back.ProxyWS(clientConn, methodWhitelist) proxier, err := back.ProxyWS(clientConn, methodWhitelist)
if errors.Is(err, ErrBackendOffline) { if errors.Is(err, ErrBackendOffline) {
log.Debug("skipping offline backend", "name", back.Name) log.Warn(
"skipping offline backend",
"name", back.Name,
"req_id", GetReqID(ctx),
"auth", GetAuthCtx(ctx),
)
continue continue
} }
if errors.Is(err, ErrBackendOverCapacity) { if errors.Is(err, ErrBackendOverCapacity) {
log.Debug("skipping over-capacity backend", "name", back.Name) log.Warn(
"skipping over-capacity backend",
"name", back.Name,
"req_id", GetReqID(ctx),
"auth", GetAuthCtx(ctx),
)
continue continue
} }
if err != nil { if err != nil {
log.Warn("error dialing ws backend", "name", back.Name, "err", err) log.Warn(
"error dialing ws backend",
"name", back.Name,
"req_id", GetReqID(ctx),
"auth", GetAuthCtx(ctx),
"err", err,
)
continue continue
} }
return proxier, nil return proxier, nil
...@@ -411,7 +462,7 @@ func (w *WSProxier) clientPump(ctx context.Context, errC chan error) { ...@@ -411,7 +462,7 @@ func (w *WSProxier) clientPump(ctx context.Context, errC chan error) {
// Don't bother sending invalid requests to the backend, // Don't bother sending invalid requests to the backend,
// just handle them here. // just handle them here.
req, err := w.parseClientMsg(msg) req, err := w.prepareClientMsg(msg)
if err != nil { if err != nil {
var id *int var id *int
method := MethodUnknown method := MethodUnknown
...@@ -419,11 +470,23 @@ func (w *WSProxier) clientPump(ctx context.Context, errC chan error) { ...@@ -419,11 +470,23 @@ func (w *WSProxier) clientPump(ctx context.Context, errC chan error) {
id = req.ID id = req.ID
method = req.Method method = req.Method
} }
log.Info(
"error preparing client message",
"auth", GetAuthCtx(ctx),
"req_id", GetReqID(ctx),
"err", err,
)
outConn = w.clientConn outConn = w.clientConn
msg = mustMarshalJSON(NewRPCErrorRes(id, err)) msg = mustMarshalJSON(NewRPCErrorRes(id, err))
RecordRPCError(ctx, BackendProxyd, method, err) RecordRPCError(ctx, BackendProxyd, method, err)
} else { } else {
RecordRPCForward(ctx, w.backend.Name, req.Method, RPCRequestSourceWS) RecordRPCForward(ctx, w.backend.Name, req.Method, RPCRequestSourceWS)
log.Info(
"forwarded WS message to backend",
"method", req.Method,
"auth", GetAuthCtx(ctx),
"req_id", GetReqID(ctx),
)
} }
err = outConn.WriteMessage(msgType, msg) err = outConn.WriteMessage(msgType, msg)
...@@ -465,7 +528,21 @@ func (w *WSProxier) backendPump(ctx context.Context, errC chan error) { ...@@ -465,7 +528,21 @@ func (w *WSProxier) backendPump(ctx context.Context, errC chan error) {
msg = mustMarshalJSON(NewRPCErrorRes(id, err)) msg = mustMarshalJSON(NewRPCErrorRes(id, err))
} }
if res.IsError() { if res.IsError() {
log.Info(
"backend responded with RPC error",
"code", res.Error.Code,
"msg", res.Error.Message,
"source", "ws",
"auth", GetAuthCtx(ctx),
"req_id", GetReqID(ctx),
)
RecordRPCError(ctx, w.backend.Name, MethodUnknown, res.Error) RecordRPCError(ctx, w.backend.Name, MethodUnknown, res.Error)
} else {
log.Info(
"forwarded WS message to client",
"auth", GetAuthCtx(ctx),
"req_id", GetReqID(ctx),
)
} }
err = w.clientConn.WriteMessage(msgType, msg) err = w.clientConn.WriteMessage(msgType, msg)
...@@ -485,15 +562,13 @@ func (w *WSProxier) close() { ...@@ -485,15 +562,13 @@ func (w *WSProxier) close() {
activeBackendWsConnsGauge.WithLabelValues(w.backend.Name).Dec() activeBackendWsConnsGauge.WithLabelValues(w.backend.Name).Dec()
} }
func (w *WSProxier) parseClientMsg(msg []byte) (*RPCReq, error) { func (w *WSProxier) prepareClientMsg(msg []byte) (*RPCReq, error) {
req, err := ParseRPCReq(bytes.NewReader(msg)) req, err := ParseRPCReq(bytes.NewReader(msg))
if err != nil { if err != nil {
log.Warn("error parsing RPC request", "source", "ws", "err", err)
return nil, err return nil, err
} }
if !w.methodWhitelist.Has(req.Method) { if !w.methodWhitelist.Has(req.Method) {
log.Info("blocked request for non-whitelisted method", "source", "ws", "method", req.Method)
return req, ErrMethodNotWhitelisted return req, ErrMethodNotWhitelisted
} }
......
package proxyd package proxyd
type ServerConfig struct { type ServerConfig struct {
Host string `toml:"host"` RPCHost string `toml:"rpc_host"`
Port int `toml:"port"` RPCPort int `toml:"rpc_port"`
WSHost string `toml:"ws_host"`
WSPort int `toml:"ws_port"`
MaxBodySizeBytes int64 `toml:"max_body_size_bytes"` MaxBodySizeBytes int64 `toml:"max_body_size_bytes"`
} }
...@@ -36,7 +38,6 @@ type BackendsConfig map[string]*BackendConfig ...@@ -36,7 +38,6 @@ type BackendsConfig map[string]*BackendConfig
type BackendGroupConfig struct { type BackendGroupConfig struct {
Backends []string `toml:"backends"` Backends []string `toml:"backends"`
WSEnabled bool `toml:"ws_enabled"`
} }
type BackendGroupsConfig map[string]*BackendGroupConfig type BackendGroupsConfig map[string]*BackendGroupConfig
...@@ -44,6 +45,7 @@ type BackendGroupsConfig map[string]*BackendGroupConfig ...@@ -44,6 +45,7 @@ type BackendGroupsConfig map[string]*BackendGroupConfig
type MethodMappingsConfig map[string]string type MethodMappingsConfig map[string]string
type Config struct { type Config struct {
WSBackendGroup string `toml:"ws_backend_group"`
Server *ServerConfig `toml:"server"` Server *ServerConfig `toml:"server"`
Redis *RedisConfig `toml:"redis"` Redis *RedisConfig `toml:"redis"`
Metrics *MetricsConfig `toml:"metrics"` Metrics *MetricsConfig `toml:"metrics"`
......
...@@ -4,12 +4,18 @@ ws_method_whitelist = [ ...@@ -4,12 +4,18 @@ ws_method_whitelist = [
"eth_call", "eth_call",
"eth_chainId" "eth_chainId"
] ]
# Enable WS on this backend group. There can only be one WS-enabled backend group.
ws_backend_group = "main"
[server] [server]
# Host for the proxyd server to listen on. # Host for the proxyd RPC server to listen on.
host = "0.0.0.0" rpc_host = "0.0.0.0"
# Port for the above. # Port for the above.
port = 8080 rpc_port = 8080
# Host for the proxyd WS server to listen on.
ws_host = "0.0.0.0"
# Port for the above
ws_port = 8085
# Maximum client body size, in bytes, that the server will accept. # Maximum client body size, in bytes, that the server will accept.
max_body_size_bytes = 10485760 max_body_size_bytes = 10485760
...@@ -59,8 +65,6 @@ max_ws_conns = 1 ...@@ -59,8 +65,6 @@ max_ws_conns = 1
[backend_groups] [backend_groups]
[backend_groups.main] [backend_groups.main]
backends = ["infura"] backends = ["infura"]
# Enable WS on this backend group. There can only be one WS-enabled backend group.
ws_enabled = true
[backend_groups.alchemy] [backend_groups.alchemy]
backends = ["alchemy"] backends = ["alchemy"]
......
...@@ -16,12 +16,12 @@ func Start(config *Config) error { ...@@ -16,12 +16,12 @@ func Start(config *Config) error {
if len(config.Backends) == 0 { if len(config.Backends) == 0 {
return errors.New("must define at least one backend") return errors.New("must define at least one backend")
} }
if len(config.BackendGroups) == 0 { if len(config.BackendGroups) == 0 {
return errors.New("must define at least one backend group") return errors.New("must define at least one backend group")
} }
if len(config.RPCMethodMappings) == 0 { if len(config.RPCMethodMappings) == 0 {
return errors.New("must define at least one RPC method mapping") return errors.New("must define at least one RPC method mapping")
} }
for authKey := range config.Authentication { for authKey := range config.Authentication {
if authKey == "none" { if authKey == "none" {
...@@ -35,7 +35,7 @@ func Start(config *Config) error { ...@@ -35,7 +35,7 @@ func Start(config *Config) error {
} }
backendNames := make([]string, 0) backendNames := make([]string, 0)
backendsByName := make(map[string]*Backend) backendsByName := make(map[string]*Backend)
for name, cfg := range config.Backends { for name, cfg := range config.Backends {
opts := make([]BackendOpt, 0) opts := make([]BackendOpt, 0)
...@@ -70,44 +70,49 @@ func Start(config *Config) error { ...@@ -70,44 +70,49 @@ func Start(config *Config) error {
} }
back := NewBackend(name, cfg.RPCURL, cfg.WSURL, redis, opts...) back := NewBackend(name, cfg.RPCURL, cfg.WSURL, redis, opts...)
backendNames = append(backendNames, name) backendNames = append(backendNames, name)
backendsByName[name] = back backendsByName[name] = back
log.Info("configured backend", "name", name, "rpc_url", cfg.RPCURL, "ws_url", cfg.WSURL) log.Info("configured backend", "name", name, "rpc_url", cfg.RPCURL, "ws_url", cfg.WSURL)
} }
backendGroups := make(map[string]*BackendGroup) backendGroups := make(map[string]*BackendGroup)
for bgName, bg := range config.BackendGroups {
backends := make([]*Backend, 0)
for _, bName := range bg.Backends {
if backendsByName[bName] == nil {
return fmt.Errorf("backend %s is not defined", bName)
}
backends = append(backends, backendsByName[bName])
}
group := &BackendGroup{
Name: bgName,
Backends: backends,
}
backendGroups[bgName] = group
}
var wsBackendGroup *BackendGroup var wsBackendGroup *BackendGroup
for bgName, bg := range config.BackendGroups { if config.WSBackendGroup != "" {
backends := make([]*Backend, 0) wsBackendGroup = backendGroups[config.WSBackendGroup]
for _, bName := range bg.Backends { if wsBackendGroup == nil {
if backendsByName[bName] == nil { return fmt.Errorf("ws backend group %s does not exist", config.WSBackendGroup)
return fmt.Errorf("backend %s is not defined", bName)
}
backends = append(backends, backendsByName[bName])
}
group := &BackendGroup{
Name: bgName,
Backends: backends,
}
backendGroups[bgName] = group
if bg.WSEnabled {
if wsBackendGroup != nil {
return fmt.Errorf("cannot define more than one WS-enabled backend group")
}
wsBackendGroup = group
} }
} }
for _, bg := range config.RPCMethodMappings { if wsBackendGroup == nil && config.Server.WSPort != 0 {
if backendGroups[bg] == nil { return fmt.Errorf("a ws port was defined, but no ws group was defined")
return fmt.Errorf("undefined backend group %s", bg)
}
} }
for _, bg := range config.RPCMethodMappings {
if backendGroups[bg] == nil {
return fmt.Errorf("undefined backend group %s", bg)
}
}
srv := NewServer( srv := NewServer(
backendGroups, backendGroups,
wsBackendGroup, wsBackendGroup,
NewStringSetFromStrings(config.WSMethodWhitelist), NewStringSetFromStrings(config.WSMethodWhitelist),
config.RPCMethodMappings, config.RPCMethodMappings,
config.Server.MaxBodySizeBytes, config.Server.MaxBodySizeBytes,
config.Authentication, config.Authentication,
) )
...@@ -118,15 +123,29 @@ func Start(config *Config) error { ...@@ -118,15 +123,29 @@ func Start(config *Config) error {
go http.ListenAndServe(addr, promhttp.Handler()) go http.ListenAndServe(addr, promhttp.Handler())
} }
go func() { if config.Server.RPCPort != 0 {
if err := srv.ListenAndServe(config.Server.Host, config.Server.Port); err != nil { go func() {
if errors.Is(err, http.ErrServerClosed) { if err := srv.RPCListenAndServe(config.Server.RPCHost, config.Server.RPCPort); err != nil {
log.Info("server shut down") if errors.Is(err, http.ErrServerClosed) {
return log.Info("RPC server shut down")
return
}
log.Crit("error starting RPC server", "err", err)
} }
log.Crit("error starting server", "err", err) }()
} }
}()
if config.Server.WSPort != 0 {
go func() {
if err := srv.WSListenAndServe(config.Server.WSHost, config.Server.WSPort); err != nil {
if errors.Is(err, http.ErrServerClosed) {
log.Info("WS server shut down")
return
}
log.Crit("error starting WS server", "err", err)
}
}()
}
sig := make(chan os.Signal, 1) sig := make(chan os.Signal, 1)
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM) signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
......
...@@ -16,7 +16,8 @@ import ( ...@@ -16,7 +16,8 @@ import (
) )
const ( const (
ContextKeyAuth = "authorization" ContextKeyAuth = "authorization"
ContextKeyReqID = "req_id"
) )
type Server struct { type Server struct {
...@@ -27,7 +28,8 @@ type Server struct { ...@@ -27,7 +28,8 @@ type Server struct {
maxBodySize int64 maxBodySize int64
authenticatedPaths map[string]string authenticatedPaths map[string]string
upgrader *websocket.Upgrader upgrader *websocket.Upgrader
server *http.Server rpcServer *http.Server
wsServer *http.Server
} }
func NewServer( func NewServer(
...@@ -51,27 +53,46 @@ func NewServer( ...@@ -51,27 +53,46 @@ func NewServer(
} }
} }
func (s *Server) ListenAndServe(host string, port int) error { func (s *Server) RPCListenAndServe(host string, port int) error {
hdlr := mux.NewRouter() hdlr := mux.NewRouter()
hdlr.HandleFunc("/healthz", s.HandleHealthz).Methods("GET") hdlr.HandleFunc("/healthz", s.HandleHealthz).Methods("GET")
hdlr.HandleFunc("/api/v1/rpc", s.HandleRPC).Methods("POST") hdlr.HandleFunc("/", s.HandleRPC).Methods("POST")
hdlr.HandleFunc("/api/v1/{authorization}/rpc", s.HandleRPC).Methods("POST") hdlr.HandleFunc("/{authorization}", s.HandleRPC).Methods("POST")
hdlr.HandleFunc("/api/v1/ws", s.HandleWS)
hdlr.HandleFunc("/api/v1/{authorization}/ws", s.HandleWS)
c := cors.New(cors.Options{ c := cors.New(cors.Options{
AllowedOrigins: []string{"*"}, AllowedOrigins: []string{"*"},
}) })
addr := fmt.Sprintf("%s:%d", host, port) addr := fmt.Sprintf("%s:%d", host, port)
s.server = &http.Server{ s.rpcServer = &http.Server{
Handler: instrumentedHdlr(c.Handler(hdlr)), Handler: instrumentedHdlr(c.Handler(hdlr)),
Addr: addr, Addr: addr,
} }
log.Info("starting HTTP server", "addr", addr) log.Info("starting HTTP server", "addr", addr)
return s.server.ListenAndServe() return s.rpcServer.ListenAndServe()
}
func (s *Server) WSListenAndServe(host string, port int) error {
hdlr := mux.NewRouter()
hdlr.HandleFunc("/", s.HandleWS)
hdlr.HandleFunc("/{authorization}", s.HandleWS)
c := cors.New(cors.Options{
AllowedOrigins: []string{"*"},
})
addr := fmt.Sprintf("%s:%d", host, port)
s.wsServer = &http.Server{
Handler: instrumentedHdlr(c.Handler(hdlr)),
Addr: addr,
}
log.Info("starting WS server", "addr", addr)
return s.wsServer.ListenAndServe()
} }
func (s *Server) Shutdown() { func (s *Server) Shutdown() {
s.server.Shutdown(context.Background()) if s.rpcServer != nil {
s.rpcServer.Shutdown(context.Background())
}
if s.wsServer != nil {
s.wsServer.Shutdown(context.Background())
}
} }
func (s *Server) HandleHealthz(w http.ResponseWriter, r *http.Request) { func (s *Server) HandleHealthz(w http.ResponseWriter, r *http.Request) {
...@@ -79,11 +100,13 @@ func (s *Server) HandleHealthz(w http.ResponseWriter, r *http.Request) { ...@@ -79,11 +100,13 @@ func (s *Server) HandleHealthz(w http.ResponseWriter, r *http.Request) {
} }
func (s *Server) HandleRPC(w http.ResponseWriter, r *http.Request) { func (s *Server) HandleRPC(w http.ResponseWriter, r *http.Request) {
ctx := s.authenticate(w, r) ctx := s.populateContext(w, r)
if ctx == nil { if ctx == nil {
return return
} }
log.Info("received RPC request", "req_id", GetReqID(ctx), "auth", GetAuthCtx(ctx))
req, err := ParseRPCReq(io.LimitReader(r.Body, s.maxBodySize)) req, err := ParseRPCReq(io.LimitReader(r.Body, s.maxBodySize))
if err != nil { if err != nil {
log.Info("rejected request with bad rpc request", "source", "rpc", "err", err) log.Info("rejected request with bad rpc request", "source", "rpc", "err", err)
...@@ -92,51 +115,66 @@ func (s *Server) HandleRPC(w http.ResponseWriter, r *http.Request) { ...@@ -92,51 +115,66 @@ func (s *Server) HandleRPC(w http.ResponseWriter, r *http.Request) {
return return
} }
group := s.rpcMethodMappings[req.Method] group := s.rpcMethodMappings[req.Method]
if group == "" { if group == "" {
// use unknown below to prevent DOS vector that fills up memory // use unknown below to prevent DOS vector that fills up memory
// with arbitrary method names. // with arbitrary method names.
log.Info("blocked request for non-whitelisted method", "source", "ws", "method", req.Method) log.Info(
RecordRPCError(ctx, BackendProxyd, MethodUnknown, ErrMethodNotWhitelisted) "blocked request for non-whitelisted method",
writeRPCError(w, req.ID, ErrMethodNotWhitelisted) "source", "rpc",
return "req_id", GetReqID(ctx),
} "method", req.Method,
)
RecordRPCError(ctx, BackendProxyd, MethodUnknown, ErrMethodNotWhitelisted)
writeRPCError(w, req.ID, ErrMethodNotWhitelisted)
return
}
backendRes, err := s.backendGroups[group].Forward(ctx, req) backendRes, err := s.backendGroups[group].Forward(ctx, req)
if err != nil { if err != nil {
log.Error("error forwarding RPC request", "method", req.Method, "err", err) log.Error(
"error forwarding RPC request",
"method", req.Method,
"req_id", GetReqID(ctx),
"err", err,
)
writeRPCError(w, req.ID, err) writeRPCError(w, req.ID, err)
return return
} }
enc := json.NewEncoder(w) enc := json.NewEncoder(w)
if err := enc.Encode(backendRes); err != nil { if err := enc.Encode(backendRes); err != nil {
log.Error("error encoding response", "err", err) log.Error(
"error encoding response",
"req_id", GetReqID(ctx),
"err", err,
)
RecordRPCError(ctx, BackendProxyd, req.Method, err) RecordRPCError(ctx, BackendProxyd, req.Method, err)
writeRPCError(w, req.ID, err) writeRPCError(w, req.ID, err)
return return
} }
log.Debug("forwarded RPC method", "method", req.Method)
} }
func (s *Server) HandleWS(w http.ResponseWriter, r *http.Request) { func (s *Server) HandleWS(w http.ResponseWriter, r *http.Request) {
ctx := s.authenticate(w, r) ctx := s.populateContext(w, r)
if ctx == nil { if ctx == nil {
return return
} }
log.Info("received WS connection", "req_id", GetReqID(ctx))
clientConn, err := s.upgrader.Upgrade(w, r, nil) clientConn, err := s.upgrader.Upgrade(w, r, nil)
if err != nil { if err != nil {
log.Error("error upgrading client conn", "err", err) log.Error("error upgrading client conn", "auth", GetAuthCtx(ctx), "req_id", GetReqID(ctx), "err", err)
return return
} }
proxier, err := s.wsBackendGroup.ProxyWS(clientConn, s.wsMethodWhitelist) proxier, err := s.wsBackendGroup.ProxyWS(ctx, clientConn, s.wsMethodWhitelist)
if err != nil { if err != nil {
if errors.Is(err, ErrNoBackends) { if errors.Is(err, ErrNoBackends) {
RecordUnserviceableRequest(ctx, RPCRequestSourceWS) RecordUnserviceableRequest(ctx, RPCRequestSourceWS)
} }
log.Error("error dialing ws backend", "err", err) log.Error("error dialing ws backend", "auth", GetAuthCtx(ctx), "req_id", GetReqID(ctx), "err", err)
clientConn.Close() clientConn.Close()
return return
} }
...@@ -145,13 +183,15 @@ func (s *Server) HandleWS(w http.ResponseWriter, r *http.Request) { ...@@ -145,13 +183,15 @@ func (s *Server) HandleWS(w http.ResponseWriter, r *http.Request) {
go func() { go func() {
// Below call blocks so run it in a goroutine. // Below call blocks so run it in a goroutine.
if err := proxier.Proxy(ctx); err != nil { if err := proxier.Proxy(ctx); err != nil {
log.Error("error proxying websocket", "err", err) log.Error("error proxying websocket", "auth", GetAuthCtx(ctx), "req_id", GetReqID(ctx), "err", err)
} }
activeClientWsConnsGauge.WithLabelValues(GetAuthCtx(ctx)).Dec() activeClientWsConnsGauge.WithLabelValues(GetAuthCtx(ctx)).Dec()
}() }()
log.Info("accepted WS connection", "auth", GetAuthCtx(ctx), "req_id", GetReqID(ctx))
} }
func (s *Server) authenticate(w http.ResponseWriter, r *http.Request) context.Context { func (s *Server) populateContext(w http.ResponseWriter, r *http.Request) context.Context {
vars := mux.Vars(r) vars := mux.Vars(r)
authorization := vars["authorization"] authorization := vars["authorization"]
...@@ -159,18 +199,29 @@ func (s *Server) authenticate(w http.ResponseWriter, r *http.Request) context.Co ...@@ -159,18 +199,29 @@ func (s *Server) authenticate(w http.ResponseWriter, r *http.Request) context.Co
// handle the edge case where auth is disabled // handle the edge case where auth is disabled
// but someone sends in an auth key anyway // but someone sends in an auth key anyway
if authorization != "" { if authorization != "" {
log.Info("blocked authenticated request against unauthenticated proxy")
w.WriteHeader(404) w.WriteHeader(404)
return nil return nil
} }
return r.Context() return context.WithValue(
r.Context(),
ContextKeyReqID,
randStr(10),
)
} }
if authorization == "" || s.authenticatedPaths[authorization] == "" { if authorization == "" || s.authenticatedPaths[authorization] == "" {
log.Info("blocked unauthorized request", "authorization", authorization)
w.WriteHeader(401) w.WriteHeader(401)
return nil return nil
} }
return context.WithValue(r.Context(), ContextKeyAuth, s.authenticatedPaths[authorization]) ctx := context.WithValue(r.Context(), ContextKeyAuth, s.authenticatedPaths[authorization])
return context.WithValue(
ctx,
ContextKeyReqID,
randStr(10),
)
} }
func writeRPCError(w http.ResponseWriter, id *int, err error) { func writeRPCError(w http.ResponseWriter, id *int, err error) {
...@@ -208,3 +259,11 @@ func GetAuthCtx(ctx context.Context) string { ...@@ -208,3 +259,11 @@ func GetAuthCtx(ctx context.Context) string {
return authUser return authUser
} }
func GetReqID(ctx context.Context) string {
reqId, ok := ctx.Value(ContextKeyReqID).(string)
if !ok {
return ""
}
return reqId
}
...@@ -8,7 +8,7 @@ import { serialize } from '@ethersproject/transactions' ...@@ -8,7 +8,7 @@ import { serialize } from '@ethersproject/transactions'
import { predeploys, getContractFactory } from '@eth-optimism/contracts' import { predeploys, getContractFactory } from '@eth-optimism/contracts'
/* Imports: Internal */ /* Imports: Internal */
import {gasPriceForL2, isLiveNetwork} from './shared/utils' import { gasPriceForL2, isLiveNetwork } from './shared/utils'
import { OptimismEnv } from './shared/env' import { OptimismEnv } from './shared/env'
import { Direction } from './shared/watcher-utils' import { Direction } from './shared/watcher-utils'
......
...@@ -57,28 +57,26 @@ Network : __mainnet (chain id: 1)__ ...@@ -57,28 +57,26 @@ Network : __mainnet (chain id: 1)__
| Contract | Address | | Contract | Address |
| -------- | ------- | | -------- | ------- |
|AddressDictator|[0x7a74f7934a233e10E8757264132B2E4EbccF5098](https://etherscan.io/address/0x7a74f7934a233e10E8757264132B2E4EbccF5098)|
|BondManager|[0xcd626E1328b41fCF24737F137BcD4CE0c32bc8d1](https://etherscan.io/address/0xcd626E1328b41fCF24737F137BcD4CE0c32bc8d1)|
|CanonicalTransactionChain|[0x5E4e65926BA27467555EB562121fac00D24E9dD2](https://etherscan.io/address/0x5E4e65926BA27467555EB562121fac00D24E9dD2)|
|ChainStorageContainer-CTC-batches|[0xD16463EF9b0338CE3D73309028ef1714D220c024](https://etherscan.io/address/0xD16463EF9b0338CE3D73309028ef1714D220c024)|
|ChainStorageContainer-SCC-batches|[0xb0ddFf09c4019e31960de11bD845E836078E8EbE](https://etherscan.io/address/0xb0ddFf09c4019e31960de11bD845E836078E8EbE)|
|ChugSplashDictator|[0xD86065136E3ab1e3FCBbf47B59404c08A431051A](https://etherscan.io/address/0xD86065136E3ab1e3FCBbf47B59404c08A431051A)|
|Lib_AddressManager|[0xdE1FCfB0851916CA5101820A69b13a4E276bd81F](https://etherscan.io/address/0xdE1FCfB0851916CA5101820A69b13a4E276bd81F)| |Lib_AddressManager|[0xdE1FCfB0851916CA5101820A69b13a4E276bd81F](https://etherscan.io/address/0xdE1FCfB0851916CA5101820A69b13a4E276bd81F)|
|OVM_CanonicalTransactionChain|[0x4BF681894abEc828B212C906082B444Ceb2f6cf6](https://etherscan.io/address/0x4BF681894abEc828B212C906082B444Ceb2f6cf6)|
|OVM_ChainStorageContainer-CTC-batches|[0x3EA1a3839D8ca9a7ff3c567a9F36f4C4DbECc3eE](https://etherscan.io/address/0x3EA1a3839D8ca9a7ff3c567a9F36f4C4DbECc3eE)|
|OVM_ChainStorageContainer-CTC-queue|[0xA0b912b3Ea71A04065Ff82d3936D518ED6E38039](https://etherscan.io/address/0xA0b912b3Ea71A04065Ff82d3936D518ED6E38039)|
|OVM_ChainStorageContainer-SCC-batches|[0x77eBfdFcC906DDcDa0C42B866f26A8D5A2bb0572](https://etherscan.io/address/0x77eBfdFcC906DDcDa0C42B866f26A8D5A2bb0572)|
|OVM_ExecutionManager|[0x2745C24822f542BbfFB41c6cB20EdF766b5619f5](https://etherscan.io/address/0x2745C24822f542BbfFB41c6cB20EdF766b5619f5)|
|OVM_FraudVerifier|[0x042065416C5c665dc196076745326Af3Cd840D15](https://etherscan.io/address/0x042065416C5c665dc196076745326Af3Cd840D15)|
|OVM_L1MultiMessageRelayer|[0xF26391FBB1f77481f80a7d646AC08ba3817eA891](https://etherscan.io/address/0xF26391FBB1f77481f80a7d646AC08ba3817eA891)|
|OVM_SafetyChecker|[0xfe1F9Cf28ecDb12110aa8086e6FD343EA06035cC](https://etherscan.io/address/0xfe1F9Cf28ecDb12110aa8086e6FD343EA06035cC)|
|OVM_StateCommitmentChain|[0xE969C2724d2448F1d1A6189d3e2aA1F37d5998c1](https://etherscan.io/address/0xE969C2724d2448F1d1A6189d3e2aA1F37d5998c1)|
|OVM_StateManagerFactory|[0xd0e3e318154716BD9d007E1E6B021Eab246ff98d](https://etherscan.io/address/0xd0e3e318154716BD9d007E1E6B021Eab246ff98d)|
|OVM_StateTransitionerFactory|[0x38A6ed6fd76035684caDef38cF49a2FffA782B67](https://etherscan.io/address/0x38A6ed6fd76035684caDef38cF49a2FffA782B67)|
|Proxy__OVM_L1CrossDomainMessenger|[0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1](https://etherscan.io/address/0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1)| |Proxy__OVM_L1CrossDomainMessenger|[0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1](https://etherscan.io/address/0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1)|
|Proxy__OVM_L1StandardBridge|[0x99C9fc46f92E8a1c0deC1b1747d010903E884bE1](https://etherscan.io/address/0x99C9fc46f92E8a1c0deC1b1747d010903E884bE1)| |Proxy__OVM_L1StandardBridge|[0x99C9fc46f92E8a1c0deC1b1747d010903E884bE1](https://etherscan.io/address/0x99C9fc46f92E8a1c0deC1b1747d010903E884bE1)|
|mockOVM_BondManager|[0xCd76de5C57004d47d0216ec7dAbd3c72D8c49057](https://etherscan.io/address/0xCd76de5C57004d47d0216ec7dAbd3c72D8c49057)| |StateCommitmentChain|[0xBe5dAb4A2e9cd0F27300dB4aB94BeE3A233AEB19](https://etherscan.io/address/0xBe5dAb4A2e9cd0F27300dB4aB94BeE3A233AEB19)|
<!-- <!--
Implementation addresses. DO NOT use these addresses directly. Implementation addresses. DO NOT use these addresses directly.
Use their proxied counterparts seen above. Use their proxied counterparts seen above.
L1StandardBridge_for_verification_only:
- 0x29Ea454F8f2750e345E52e302A0c09f1A5215AC7
- https://etherscan.io/address/0x29Ea454F8f2750e345E52e302A0c09f1A5215AC7)
OVM_L1CrossDomainMessenger: OVM_L1CrossDomainMessenger:
- 0xbfba066b5cA610Fe70AdCE45FcB622F945891bb0 - 0xd9166833FF12A5F900ccfBf2c8B62a90F1Ca1FD5
- https://etherscan.io/address/0xbfba066b5cA610Fe70AdCE45FcB622F945891bb0) - https://etherscan.io/address/0xd9166833FF12A5F900ccfBf2c8B62a90F1Ca1FD5)
--> -->
## KOVAN ## KOVAN
......
This diff is collapsed.
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.
...@@ -16,6 +16,8 @@ import '@typechain/hardhat' ...@@ -16,6 +16,8 @@ import '@typechain/hardhat'
import './tasks/deploy' import './tasks/deploy'
import './tasks/l2-gasprice' import './tasks/l2-gasprice'
import './tasks/set-owner' import './tasks/set-owner'
import './tasks/validate-address-dictator'
import './tasks/validate-chugsplash-dictator'
import './tasks/whitelist' import './tasks/whitelist'
import './tasks/withdraw-fees' import './tasks/withdraw-fees'
import 'hardhat-gas-reporter' import 'hardhat-gas-reporter'
......
import { createInterface } from 'readline'
import { hexStringEquals } from '@eth-optimism/core-utils'
export const getInput = (query) => {
const rl = createInterface({
input: process.stdin,
output: process.stdout,
})
return new Promise((resolve) =>
rl.question(query, (ans) => {
rl.close()
resolve(ans)
})
)
}
const codes = {
reset: '\x1b[0m',
red: '\x1b[0;31m',
green: '\x1b[0;32m',
cyan: '\x1b[0;36m',
yellow: '\x1b[1;33m',
}
export const color = Object.fromEntries(
Object.entries(codes).map(([k]) => [
k,
(msg: string) => `${codes[k]}${msg}${codes.reset}`,
])
)
export const getArtifact = (name: string) => {
// Paths to artifacts relative to artifacts/contracts
const locations = {
'ChainStorageContainer-CTC-batches':
'L1/rollup/ChainStorageContainer.sol/ChainStorageContainer.json',
'ChainStorageContainer-SCC-batches':
'L1/rollup/ChainStorageContainer.sol/ChainStorageContainer.json',
CanonicalTransactionChain:
'L1/rollup/CanonicalTransactionChain.sol/CanonicalTransactionChain.json',
StateCommitmentChain:
'L1/rollup/StateCommitmentChain.sol/StateCommitmentChain.json',
BondManager: 'L1/verification/BondManager.sol/BondManager.json',
OVM_L1CrossDomainMessenger:
'L1/messaging/L1CrossDomainMessenger.sol/L1CrossDomainMessenger.json',
Proxy__OVM_L1CrossDomainMessenger:
'libraries/resolver/Lib_ResolvedDelegateProxy.sol/Lib_ResolvedDelegateProxy.json',
Proxy__OVM_L1StandardBridge:
'chugsplash/L1ChugSplashProxy.sol/L1ChugSplashProxy.json',
}
// eslint-disable-next-line @typescript-eslint/no-var-requires
return require(`../artifacts/contracts/${locations[name]}`)
}
export const getEtherscanUrl = (network, address: string) => {
const escPrefix = network.chainId !== 1 ? `${network.name}.` : ''
return `https://${escPrefix}etherscan.io/address/${address}`
}
// Reduces a byte string to first 32 bytes, with a '...' to indicate when it was shortened
const truncateLongString = (value: string): string => {
return value.length > 66 ? `${value.slice(0, 66)}...` : value
}
export const printComparison = (
action: string,
description: string,
expected: { name: string; value: string },
deployed: { name: string; value: string }
) => {
console.log(action + ':')
if (hexStringEquals(expected.value, deployed.value)) {
console.log(
color.green(
`${expected.name}: ${truncateLongString(expected.value)}
matches
${deployed.name}: ${truncateLongString(deployed.value)}`
)
)
console.log(color.green(`${description} looks good! 😎`))
} else {
throw new Error(`${description} looks wrong.
${expected.value}\ndoes not match\n${deployed.value}.
`)
}
console.log() // Add some whitespace
}
This diff is collapsed.
This diff is collapsed.
...@@ -502,16 +502,6 @@ ...@@ -502,16 +502,6 @@
minimatch "^3.0.4" minimatch "^3.0.4"
strip-json-comments "^3.1.1" strip-json-comments "^3.1.1"
"@eth-optimism/core-utils@0.6.0":
version "0.6.0"
resolved "https://registry.yarnpkg.com/@eth-optimism/core-utils/-/core-utils-0.6.0.tgz#e20dbdce1b3122bd6db0bc3286a4942336ff173b"
integrity sha512-WOia8t324ZbG6WUOFszwUGJSCCMh6dWen4fElW/OjtpmSHGTmOG9PCMxtw0wLTSR9fr/g9S7DhDpwhrN6q1QDQ==
dependencies:
"@ethersproject/abstract-provider" "^5.4.1"
"@ethersproject/providers" "^5.4.5"
ethers "^5.4.5"
lodash "^4.17.21"
"@eth-optimism/core-utils@^0.5.1": "@eth-optimism/core-utils@^0.5.1":
version "0.5.5" version "0.5.5"
resolved "https://registry.yarnpkg.com/@eth-optimism/core-utils/-/core-utils-0.5.5.tgz#0e2bb95b23965fb51adfb8ba6841c3afd26a6411" resolved "https://registry.yarnpkg.com/@eth-optimism/core-utils/-/core-utils-0.5.5.tgz#0e2bb95b23965fb51adfb8ba6841c3afd26a6411"
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment