From 6d996050df2c099ee9ba4e971f520c61cfb7696e Mon Sep 17 00:00:00 2001 From: Ethnical Date: Fri, 30 Aug 2024 16:37:45 +0200 Subject: [PATCH] Add a onchain execution on forked network with Hardcoded values. (#54) * remove the authentication and execute a hardcoded transaction when cURL is received. * refactor: functions to add them as module of the `defender` struct. * refactor: clean code. * feat: executor to mock object during testing * refactor: Previous test are now working with the new executor mocking logic. * gitignore: ignore the `run.sh` that is used with `.air.toml`. * refactor: and the clean the code * feat: add the unit tests `TestHTTPServerHasOnlyPSPExecutionRoute` and `TestDefenderInitialization` and `TestHandlePostMockFetch` * docs: update the docs of the current functions. * docs: update the docs with necessaries fields * refactor: clean up the codes + remove the `calldata` fields that is now useless from the API * docs: fix docs with typos * remove the calldata from the tests * docs: improve the readme with the tab and the field used for the each fields. * docs: improve the docs * chore: fix alignement * Update op-defender/psp_executor/defender.go Co-authored-by: Kevin Z Chen * Update op-defender/psp_executor/api_test.go Co-authored-by: Kevin Z Chen * refactor: replace `expectedErr` to `expectErr`. --------- Co-authored-by: Kevin Z Chen --- .gitignore | 1 + op-defender/cmd/defender/cli.go | 4 +- op-defender/psp_executor/README.md | 71 ++++++---- op-defender/psp_executor/api_test.go | 191 ++++++++++++++++++++++----- op-defender/psp_executor/cli.go | 17 ++- op-defender/psp_executor/defender.go | 114 ++++++++-------- 6 files changed, 279 insertions(+), 119 deletions(-) diff --git a/.gitignore b/.gitignore index 13f3d079..8ee8963a 100644 --- a/.gitignore +++ b/.gitignore @@ -64,3 +64,4 @@ crytic-export bin op-defender/.air.toml +op-defender/run.sh diff --git a/op-defender/cmd/defender/cli.go b/op-defender/cmd/defender/cli.go index 3ecc40c1..04f9754a 100644 --- a/op-defender/cmd/defender/cli.go +++ b/op-defender/cmd/defender/cli.go @@ -10,6 +10,7 @@ import ( oplog "github.com/ethereum-optimism/optimism/op-service/log" opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics" + executor "github.com/ethereum-optimism/monitorism/op-defender/psp_executor" "github.com/ethereum/go-ethereum/params" "github.com/urfave/cli/v2" @@ -57,7 +58,8 @@ func PSPExecutorMain(ctx *cli.Context, closeApp context.CancelCauseFunc) (cliapp } metricsRegistry := opmetrics.NewRegistry() - defender_thread, err := psp_executor.NewDefender(ctx.Context, log, opmetrics.With(metricsRegistry), cfg) + executor := &executor.DefenderExecutor{} + defender_thread, err := psp_executor.NewDefender(ctx.Context, log, opmetrics.With(metricsRegistry), cfg, executor) if err != nil { return nil, fmt.Errorf("Failed to create psp_executor HTTP API service: %w", err) } diff --git a/op-defender/psp_executor/README.md b/op-defender/psp_executor/README.md index 79c00429..a65f87cd 100644 --- a/op-defender/psp_executor/README.md +++ b/op-defender/psp_executor/README.md @@ -2,17 +2,60 @@ The PSP Executor service is a service designed to execute PSP onchain faster to increase our readiness and speed in case of incident response. -The service is design to listen on a port and execute a PSP onchain when a request is received. +The service is designed to listen on a port and execute a PSP onchain when a request is received. -⚠️ The service as to use a authentification method before calling this API ⚠️ +⚠️ The service has to use an authentication method before calling this API ⚠️ -### Options and Configuration +## 1. Usage + +### 1 .Run HTTP API service + +To start the HTTP API service we can use the following oneliner command: +![f112841bad84c59ea3ed1ca380740f5694f553de8755b96b1a40ece4d1c26f81](https://github.com/user-attachments/assets/17235e99-bf25-40a5-af2c-a0d9990c6276) +Settings of the HTTP API service: + +| Port | API Path | HTTP Method | +| ----------------------------- | -------------------- | ----------- | +| 8080 (Default can be changed) | `/api/psp_execution` | POST | + +```shell +go run ../cmd/defender psp_executor --privatekey XXXXXX --receiver.address 0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF --rpc.url http://localhost:8545 --port.api 8080 +``` + +### 2. Request the HTTP API + +To use the HTTP API you can use the following `curl` command with the following fields: + +![image](https://github.com/user-attachments/assets/3edc2ee5-6dfd-4872-9bc6-e3ead7444a96) + +```bash +curl -X POST http://${HTTP_API_PSP}:${PORT}/api/psp_execution \-H "Content-Type: application/json" \-d '{"pause": true, "timestamp": 1719432011, "operator": "Tom"}' +``` + +Explanation of the _mandatory_ fields: +| Field | Description | +| --------- | -------------------------------------------------------------------------------- | +| pause | A boolean value indicating whether to pause (true) or unpause (false) the SuperchainConfig.| +| timestamp | The Unix timestamp when the request is made. | +| operator | The name or identifier of the person initiating the PSP execution. | + +### 3. Metrics Server + +To monitor the _PSPexecutor service_ the metrics server can be enabled. The metrics server will expose the metrics on the specified address and port. +The metrics are using **Prometheus** and can be set with the following options: +| Option | Description | Default Value | Environment Variable | +| ------------------- | ------------------------- | ------------- | ----------------------------- | +| `--metrics.enabled` | Enable the metrics server | `false` | `$MONITORISM_METRICS_ENABLED` | +| `--metrics.addr` | Metrics listening address | `"0.0.0.0"` | `$MONITORISM_METRICS_ADDR` | +| `--metrics.port` | Metrics listening port | `7300` | `$MONITORISM_METRICS_PORT` | + +### 4. Options and Configuration The current options are the following: ``` OPTIONS: - --rpc-url value Node URL of a peer (default: "http://127.0.0.1:8545") [$PSPEXECUTOR_MON_NODE_URL] + --rpc.url value Node URL of a peer (default: "http://127.0.0.1:8545") [$PSPEXECUTOR_MON_NODE_URL] --privatekey value Private key of the account that will issue the pause () [$PSPEXECUTOR_MON_PRIVATE_KEY] --receiver.address value The receiver address of the pause request. [$PSPEXECUTOR_MON_RECEIVER_ADDRESS] --port.api value Port of the API server you want to listen on (e.g. 8080). (default: "8080") [$PSPEXECUTOR_MON_PORT_API] @@ -26,23 +69,3 @@ OPTIONS: --loop.interval.msec value Loop interval of the monitor in milliseconds (default: 60000) [$MONITORISM_LOOP_INTERVAL_MSEC] --help, -h show help ``` - -## Usage -### HTTP API service -To start the HTTP API service we can use the following oneliner command: -![f112841bad84c59ea3ed1ca380740f5694f553de8755b96b1a40ece4d1c26f81](https://github.com/user-attachments/assets/17235e99-bf25-40a5-af2c-a0d9990c6276) - -```shell -go run ../cmd/defender psp_executor --privatekey XXXXXX --receiver.address 0xDEADBEEFDEADBEEFDEADBEEFDEADBEEFDEADBEEF --rpc.url http://localhost:8545 --port.api 8080 -``` - -### cURL HTTP API - -To use the HTTP API you can use the following `curl` command: - -![image](https://github.com/user-attachments/assets/3edc2ee5-6dfd-4872-9bc6-e3ead7444a96) - -```bash -curl -X POST http://${HTTP_API_PSP}:${PORT}/api/psp_execution \-H "Content-Type: application/json" \-d '{"pause": true, "timestamp": 1719432011, "operator": "Tom"}' -``` - diff --git a/op-defender/psp_executor/api_test.go b/op-defender/psp_executor/api_test.go index 8c6adb63..afc56e2c 100644 --- a/op-defender/psp_executor/api_test.go +++ b/op-defender/psp_executor/api_test.go @@ -2,57 +2,184 @@ package psp_executor import ( "bytes" - "encoding/json" + "context" + opmetrics "github.com/ethereum-optimism/optimism/op-service/metrics" + "github.com/ethereum/go-ethereum/log" + "github.com/gorilla/mux" "net/http" "net/http/httptest" "testing" ) -// TestHandlePost tests the handlePost function for various scenarios. -func TestHandlePost(t *testing.T) { +type SimpleExecutor struct{} + +func (e *SimpleExecutor) FetchAndExecute(d *Defender) { + // Do nothing for now, for mocking purposes +} + +// TestHTTPServerHasOnlyPSPExecutionRoute tests if the HTTP server has only one route with "/api/psp_execution" path and "POST" method. +func TestHTTPServerHasOnlyPSPExecutionRoute(t *testing.T) { + // Mock dependencies or create real ones depending on your test needs + logger := log.New() //@TODO: replace with testlog https://github.com/ethereum-optimism/optimism/blob/develop/op-service/testlog/testlog.go#L61 + executor := &SimpleExecutor{} + metricsfactory := opmetrics.With(opmetrics.NewRegistry()) + mockNodeUrl := "http://rpc.tenderly.co/fork/" // Need to have the "fork" in the URL to avoid mistake for now. + cfg := CLIConfig{ + NodeURL: mockNodeUrl, + PortAPI: "8080", + } + // Initialize the Defender with necessary mock or real components + defender, err := NewDefender(context.Background(), logger, metricsfactory, cfg, executor) + + if err != nil { + t.Fatalf("Failed to create Defender: %v", err) + } + + // We Check if the router has only one route + routeCount := 0 + expectedPath := "/api/psp_execution" + expectedMethod := "POST" + var foundRoute *mux.Route + + defender.router.Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error { + routeCount++ + foundRoute = route + return nil + }) + + if routeCount != 1 { + t.Errorf("Expected 1 route, but found %d", routeCount) + } + + if foundRoute != nil { + path, _ := foundRoute.GetPathTemplate() + methods, _ := foundRoute.GetMethods() + + if path != expectedPath { + t.Errorf("Expected path %s, but found %s", expectedPath, path) + } + + if len(methods) != 1 || methods[0] != expectedMethod { + t.Errorf("Expected method %s, but found %v", expectedMethod, methods) + } + } else { + t.Error("No route found") + } +} + +// TestDefenderInitialization tests the initialization of the Defender struct with mock dependencies. +func TestDefenderInitialization(t *testing.T) { + // Mock dependencies or create real ones depending on your test needs + logger := log.New() //@TODO: replace with testlog https://github.com/ethereum-optimism/optimism/blob/develop/op-service/testlog/testlog.go#L61 + executor := &SimpleExecutor{} + metricsfactory := opmetrics.With(opmetrics.NewRegistry()) + mockNodeUrl := "http://rpc.tenderly.co/fork/" // Need to have the "fork" in the URL to avoid mistake for now. + cfg := CLIConfig{ + NodeURL: mockNodeUrl, + PortAPI: "8080", + } + // Initialize the Defender with necessary mock or real components + _, err := NewDefender(context.Background(), logger, metricsfactory, cfg, executor) + + if err != nil { + t.Fatalf("Failed to create Defender: %v", err) + } + +} + +// TestHandlePostMockFetch tests the handlePost function with HTTP status code to make sure HTTP code returned are expected in every possible cases. +func TestHandlePostMockFetch(t *testing.T) { + // Initialize the Defender with necessary mock or real components + logger := log.New() //@TODO: replace with testlog https://github.com/ethereum-optimism/optimism/blob/develop/op-service/testlog/testlog.go#L61 + metricsRegistry := opmetrics.NewRegistry() + metricsfactory := opmetrics.With(metricsRegistry) + mockNodeUrl := "http://rpc.tenderly.co/fork/" // Need to have the "fork" in the URL to avoid mistake for now. + executor := &SimpleExecutor{} + cfg := CLIConfig{ + NodeURL: mockNodeUrl, + PortAPI: "8080", + } + + defender, err := NewDefender(context.Background(), logger, metricsfactory, cfg, executor) + if err != nil { + t.Fatalf("Failed to create Defender: %v", err) + } + + // Define test cases tests := []struct { name string - body RequestData + body string expectedStatus int - expectedBody string + path string }{ { - name: "Network Authentication Required", - body: RequestData{ - Pause: false, - Timestamp: 0, - Operator: "", - Calldata: "", - }, - expectedStatus: http.StatusNetworkAuthenticationRequired, - expectedBody: "Network Authentication Required\n", //do not forget the newline character. + path: "/api/psp_execution", + name: "Valid Request", // Check if the request is valid as expected return the 200 status code. + body: `{"pause":true,"timestamp":1596240000,"operator":"0x123"}`, + expectedStatus: http.StatusOK, + }, + { + path: "/api/psp_execution", + name: "Invalid JSON", // Check if the JSON is invalid return the 400 status code. + body: `{"pause":true, "timestamp":"invalid","operator":}`, + expectedStatus: http.StatusBadRequest, + }, + { + path: "/api/psp_execution", + name: "Missing Fields", // Check if the required fields are missing return the 400 status code. + body: `{"timestamp":1596240000}`, + expectedStatus: http.StatusBadRequest, + }, + { + path: "/api/", + name: "Incorrect Path Fields", // Check if the path is incorrect return the 404 status code. + body: `{"pause":true,"timestamp":1596240000,"operator":"0x123"}`, + expectedStatus: http.StatusNotFound, }, } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - // Convert the body to JSON - jsonBody, _ := json.Marshal(tt.body) - req, err := http.NewRequest("POST", "/api/psp_execution", bytes.NewBuffer(jsonBody)) + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + req, err := http.NewRequest("POST", tc.path, bytes.NewBufferString(tc.body)) if err != nil { - t.Fatal(err) + t.Fatalf("Could not create request: %v", err) } + recorder := httptest.NewRecorder() - // Create a ResponseRecorder to record the response. - rr := httptest.NewRecorder() - handler := http.HandlerFunc(handlePost) // Call the `handlePost` that is the entrypoint of the API. + // Get the servermux of the defender.router to check the routes + muxrouter := defender.router + // Use the mux to serve the request + muxrouter.ServeHTTP(recorder, req) - // Call the handler function - handler.ServeHTTP(rr, req) - - // Check the status code - if status := rr.Code; status != tt.expectedStatus { - t.Errorf("handler returned wrong status code: got `%v` want `%v`", status, tt.expectedStatus) + if status := recorder.Code; status != tc.expectedStatus { + t.Errorf("handler \"%s\" returned wrong status code: got %v want %v", + tc.name, status, tc.expectedStatus) } + }) + } +} - // Check the response body - if rr.Body.String() != tt.expectedBody { - t.Errorf("handler returned unexpected body: got `%v` want `%v`", rr.Body.String(), tt.expectedBody) +// TestCheckAndReturnRPC tests that the CheckAndReturnRPC function returns the correct client or error for an incorrect URL provided. +func TestCheckAndReturnRPC(t *testing.T) { + tests := []struct { + name string + rpcURL string + expectErr bool + }{ + {"Empty URL", "", true}, + {"Production URL", "https://mainnet.infura.io", true}, + {"Valid Tenderly Fork URL", "https://rpc.tenderly.co/fork/some-id", false}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + client, err := CheckAndReturnRPC(tt.rpcURL) + if (err != nil) != tt.expectErr { + t.Errorf("CheckAndReturnRPC() error = %v, expectedErr %v", err, tt.expectErr) + return + } + if !tt.expectErr && client == nil { + t.Errorf("CheckAndReturnRPC() returned nil client for valid URL") } }) } diff --git a/op-defender/psp_executor/cli.go b/op-defender/psp_executor/cli.go index 88cef592..b9708222 100644 --- a/op-defender/psp_executor/cli.go +++ b/op-defender/psp_executor/cli.go @@ -17,15 +17,15 @@ const ( ) type CLIConfig struct { - NodeUrl string + NodeURL string privatekeyflag string - portapi string - receiverAddress string - hexString string + PortAPI string + ReceiverAddress string + HexString string } func ReadCLIFlags(ctx *cli.Context) (CLIConfig, error) { - cfg := CLIConfig{NodeUrl: ctx.String(NodeURLFlagName)} + cfg := CLIConfig{NodeURL: ctx.String(NodeURLFlagName)} if len(PrivateKeyFlagName) == 0 { return cfg, fmt.Errorf("must have a PrivateKeyFlagName set to execute the pause on mainnet") } @@ -33,16 +33,15 @@ func ReadCLIFlags(ctx *cli.Context) (CLIConfig, error) { if len(PortAPIFlagName) == 0 { return cfg, fmt.Errorf("must have a PortAPIFlagName set to execute the pause on mainnet") } - cfg.receiverAddress = ctx.String(ReceiverAddressFlagName) + cfg.PortAPI = ctx.String(PortAPIFlagName) if len(ReceiverAddressFlagName) == 0 { return cfg, fmt.Errorf("must have a ReceiverAddressFlagName set to receive the pause on mainnet.") } - - cfg.hexString = ctx.String(DataFlagName) + cfg.ReceiverAddress = ctx.String(ReceiverAddressFlagName) if len(DataFlagName) == 0 { return cfg, fmt.Errorf("must have a `data` set to execute the calldata on mainnet.") } - cfg.portapi = ctx.String(PortAPIFlagName) + cfg.HexString = ctx.String(DataFlagName) return cfg, nil } diff --git a/op-defender/psp_executor/defender.go b/op-defender/psp_executor/defender.go index 2f946b4c..824578f6 100644 --- a/op-defender/psp_executor/defender.go +++ b/op-defender/psp_executor/defender.go @@ -17,6 +17,7 @@ import ( "github.com/prometheus/client_golang/prometheus" "math/big" "net/http" + "strings" ) // ********************************************************************** @@ -38,146 +39,152 @@ const ( LocalhostRPC = "http://localhost:8545" ) -type Account struct { - Address common.Address - Nickname string +// DefenderExecutor is a struct that implements the Executor interface. +type DefenderExecutor struct{} + +// Executor is an interface that defines the FetchAndExecute method. +type Executor interface { + FetchAndExecute(d *Defender) // For doc see the `FetchAndExecute()` function. } +// Defender is a struct that represents the Defender API server. type Defender struct { log log.Logger port string SuperChainConfigAddress string l1Client *ethclient.Client router *mux.Router + executor Executor // metrics latestPspNonce *prometheus.GaugeVec unexpectedRpcErrors *prometheus.CounterVec } -// Define a struct that represents the data structure of your response +// Define a struct that represents the data structure of your response through the HTTP API. type Response struct { Message string `json:"message"` Status int `json:"status"` } +// Define a struct that represents the data structure of your request through the HTTP API. type RequestData struct { Pause bool `json:"pause"` Timestamp int64 `json:"timestamp"` Operator string `json:"operator"` - Calldata string `json:"calldata"` //temporary field as the calldata will be fetched from the GCP in the future (so will be removed in the future PR). } // handlePost handles POST requests and processes the JSON body -func handlePost(w http.ResponseWriter, r *http.Request) { +func (d *Defender) handlePost(w http.ResponseWriter, r *http.Request) { var data RequestData + + d.log.Info("Received HTTP request", "method", r.Method, "url", r.URL) // Log the requests for traceability. // Decode the JSON body into the struct if err := json.NewDecoder(r.Body).Decode(&data); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } - // return HTTP code 511 for the first PR. - http.Error(w, "Network Authentication Required", 511) + //check that all the fields are set + if data.Pause == false || data.Timestamp == 0 || data.Operator == "" { + http.Error(w, "Missing fields in the request", http.StatusBadRequest) + return + } + + // Execute the PSP on the chain. + d.executor.FetchAndExecute(d) //TODO: Make sure, a malformed HTTP request can't arrived here. return } // NewAPI creates a new HTTP API Server for the PSP Executor and starts listening on the specified port from the args passed. -func NewDefender(ctx context.Context, log log.Logger, m metrics.Factory, cfg CLIConfig) (*Defender, error) { +func NewDefender(ctx context.Context, log log.Logger, m metrics.Factory, cfg CLIConfig, executor Executor) (*Defender, error) { // Set the route and handler function for the `/api/psp_execution` endpoint. - router := mux.NewRouter() - router.HandleFunc("/api/psp_execution", handlePost).Methods("POST") - l1client, err := GetTheL1Client() + l1client, err := CheckAndReturnRPC(cfg.NodeURL) if err != nil { return nil, fmt.Errorf("failed to fetch l1 RPC: %w", err) } - - if cfg.portapi == "" { + if cfg.PortAPI == "" { return nil, fmt.Errorf("port.api is not set.") } defender := &Defender{ log: log, l1Client: l1client, - port: cfg.portapi, - router: router, + port: cfg.PortAPI, + executor: executor, } + defender.router = mux.NewRouter() + defender.router.HandleFunc("/api/psp_execution", defender.handlePost).Methods("POST") defender.log.Info("Starting HTTP JSON API PSP Execution server...", "port", defender.port) return defender, nil } -// FetchAndExecute() will fetch the privatekey, and correct PSP from GCP and execute it on the correct chain. -func FetchAndExecute() { - //1. Fetch the privatekey of the account in GCP secret Manager. - privatekey, err := FetchPrivateKeyInGcp() - if err != nil { - log.Crit("Failed to fetch the privatekey from GCP secret manager: %v", err.Error()) - } - // 2. Fetch the CORRECT Nounce PSP in the GCP secret Manager and return the data to execute. - superchainconfig_address, safe_address, data, err := FetchPSPInGCP() +// FetchAndExecute() will fetch the PSP and execute it this onchain. +// For now, the function is not fully implemented and will make a dummy transaction on chain (see `pspExecutionOnChain()`). +// In the future, the function will fetch the PSPs from a secret file and execute it onchain through a EVM transaction. +func (e *DefenderExecutor) FetchAndExecute(d *Defender) { + ctx := context.Background() + privateKey, err := FetchPrivateKeyInGcp() if err != nil { - log.Crit("Failed to fetch the PSP from GCP secret manager: %v", err.Error()) + d.log.Crit("Failed to fetch the private key from GCP", "error", err) + return } - // 3. Get the L1 client and ensure RPC is correct. - l1client, err := GetTheL1Client() + configAddress, safeAddress, data, err := FetchPSPInGCP() if err != nil { - log.Crit("Failed to get the L1client: %v", err.Error()) + d.log.Crit("Failed to fetch PSP data from GCP", "error", err) + return } - - // 4. Execute the PSP on the chain. - ctx := context.Background() //TODO: Check if we really do need to context if yes we will keep it otherwise we will remove this. - PspExecutionOnChain(ctx, l1client, superchainconfig_address, privatekey, safe_address, data) + PspExecutionOnChain(ctx, d.l1Client, configAddress, privateKey, safeAddress, data) } -// GetTheL1Client() will return the L1 client based on the RPC provided in the config and ensure that the RPC is not production one. -func GetTheL1Client() (*ethclient.Client, error) { - client, err := ethclient.Dial(LocalhostRPC) //Need to change this to the correct RPC (mainnet or sepolia) but for now hardcoded to localhost. - if LocalhostRPC != "http://localhost:8545" { - log.Warn("This is not the RPC localhost are you sure you want to continue (yes/no)") - var response string - fmt.Scanln(&response) - if response != "yes" { - log.Crit("Not yes, We Exiting the program.") - } +// CheckAndReturnRPC() will return the L1 client based on the RPC provided in the config and ensure that the RPC is not production one. +func CheckAndReturnRPC(rpc_url string) (*ethclient.Client, error) { + if rpc_url == "" { + return nil, fmt.Errorf("rpc.url is not set.") + } + if !strings.Contains(rpc_url, "rpc.tenderly.co/fork") { // Check if the RPC is a production one. if yes return an error, as we should not execute the pause on the production chain in the first version. + return nil, fmt.Errorf("rpc.url doesn't contains \"fork\" is a production RPC.") } + + client, err := ethclient.Dial(rpc_url) if err != nil { - log.Crit("Failed to connect to the Ethereum client: %v", err.Error()) + + log.Crit("Failed to connect to the Ethereum client", "error", err) } return client, nil } // FetchPrivateKey() will fetch the privatekey of the account that will execute the pause (from the GCP secret manager). func FetchPrivateKeyInGcp() (string, error) { - return "2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6", nil // Mock with a well-known private key from test test ... test junk derivation (9). + return "2a871d0798f97d79848a013d4936a73bf4cc922c825d33c1cf7073dff6d409c6", nil // Mock with a well-known private key from "test test ... test junk" derivation (9). } // FetchPSPInGCP() will fetch the correct PSPs into GCP and return the Data. -func FetchPSPInGCP() (string, string, []byte, error) { //superchainconfig_address, safe_address, data, error - // need to fetch check first the nonce with the same method with `checkPauseStatus` and then return the data for this PSP. - - return "0xC2Be75506d5724086DEB7245bd260Cc9753911Be", "0x4141414142424242414141414242424241414141", []byte{0x41, 0x42, 0x43}, nil //errors.New("Not implemented") mock with simple value to make a call on L1. +func FetchPSPInGCP() (string, string, []byte, error) { + //In the future, we need to check first the nonce and then `checkPauseStatus` and then return the data for the latest PSPs. + return "0xC2Be75506d5724086DEB7245bd260Cc9753911Be", "0x4141414142424242414141414242424241414141", []byte{0x41, 0x42, 0x43}, nil } // PSPexecution(): PSPExecutionOnChain is a core function that will check that status of the superchain is not paused and then send onchain transaction to pause the superchain. func PspExecutionOnChain(ctx context.Context, l1client *ethclient.Client, superchainconfig_address string, privatekey string, safe_address string, data []byte) { - log.Info("PSP Execution Pause") pause_before_transaction := checkPauseStatus(ctx, l1client, superchainconfig_address) if pause_before_transaction { log.Crit("The SuperChainConfig is already paused! Exiting the program.") } - log.Info("Before the transaction the status of the `pause` is set to: ", pause_before_transaction) + log.Info("[Before Transaction] status of the pause()", "pause", pause_before_transaction) txHash, err := sendTransaction(l1client, privatekey, safe_address, big.NewInt(1), data) // 1 wei if err != nil { log.Crit("Failed to send transaction:", "error", err) } - log.Info("Transaction sent! Tx Hash: %s\n", txHash) + log.Info("Transaction sent!", "TxHash", txHash) pause_after_transaction := checkPauseStatus(ctx, l1client, superchainconfig_address) - log.Info("After the transaction the status of the `pause` is set to: ", pause_after_transaction) + log.Info("[After Transaction] status of the pause()", "pause", pause_after_transaction) } +// Run() will start the Defender API server and block the thread. func (d *Defender) Run(ctx context.Context) { err := http.ListenAndServe(":"+d.port, d.router) // Start the HTTP server blocking thread for now. if err != nil { @@ -185,6 +192,7 @@ func (d *Defender) Run(ctx context.Context) { } } +// Close() will close the Defender API server and the L1 client. func (d *Defender) Close(_ context.Context) error { d.l1Client.Close() //close the L1 client. return nil