package handler import ( "encoding/json" "fmt" "io" "net/http" "os" "strings" "github.com/ethereum-optimism/optimism/proxyd" "github.com/gorilla/mux" "github.com/pkg/errors" "gopkg.in/yaml.v3" ) type MethodTemplate struct { Method string `yaml:"method"` Block string `yaml:"block"` Response string `yaml:"response"` } type MockedHandler struct { Overrides []*MethodTemplate Autoload bool AutoloadFile string } func (mh *MockedHandler) Serve(port int) error { r := mux.NewRouter() r.HandleFunc("/", mh.Handler) http.Handle("/", r) fmt.Printf("starting server up on :%d serving MockedResponsesFile %s\n", port, mh.AutoloadFile) err := http.ListenAndServe(fmt.Sprintf(":%d", port), nil) if errors.Is(err, http.ErrServerClosed) { fmt.Printf("server closed\n") } else if err != nil { fmt.Printf("error starting server: %s\n", err) return err } return nil } func (mh *MockedHandler) Handler(w http.ResponseWriter, req *http.Request) { body, err := io.ReadAll(req.Body) if err != nil { fmt.Printf("error reading request: %v\n", err) } var template []*MethodTemplate if mh.Autoload { template = append(template, mh.LoadFromFile(mh.AutoloadFile)...) } if mh.Overrides != nil { template = append(template, mh.Overrides...) } batched := proxyd.IsBatch(body) var requests []map[string]interface{} if batched { err = json.Unmarshal(body, &requests) if err != nil { fmt.Printf("error reading request: %v\n", err) } } else { var j map[string]interface{} err = json.Unmarshal(body, &j) if err != nil { fmt.Printf("error reading request: %v\n", err) } requests = append(requests, j) } var responses []string for _, r := range requests { method := r["method"] block := "" if method == "eth_getBlockByNumber" || method == "debug_getRawReceipts" { block = (r["params"].([]interface{})[0]).(string) } var selectedResponse string for _, r := range template { if r.Method == method && r.Block == block { selectedResponse = r.Response } } if selectedResponse != "" { var rpcRes proxyd.RPCRes err = json.Unmarshal([]byte(selectedResponse), &rpcRes) if err != nil { panic(err) } idJson, _ := json.Marshal(r["id"]) rpcRes.ID = idJson res, _ := json.Marshal(rpcRes) responses = append(responses, string(res)) } } resBody := "" if batched { resBody = "[" + strings.Join(responses, ",") + "]" } else if len(responses) > 0 { resBody = responses[0] } _, err = fmt.Fprint(w, resBody) if err != nil { fmt.Printf("error writing response: %v\n", err) } } func (mh *MockedHandler) LoadFromFile(file string) []*MethodTemplate { contents, err := os.ReadFile(file) if err != nil { fmt.Printf("error reading MockedResponsesFile: %v\n", err) } var template []*MethodTemplate err = yaml.Unmarshal(contents, &template) if err != nil { fmt.Printf("error reading MockedResponsesFile: %v\n", err) } return template } func (mh *MockedHandler) AddOverride(template *MethodTemplate) { mh.Overrides = append(mh.Overrides, template) } func (mh *MockedHandler) ResetOverrides() { mh.Overrides = make([]*MethodTemplate, 0) }