diff --git a/op-monitorism/faultproof_withdrawals/runbooks/automated/.env.example b/op-monitorism/faultproof_withdrawals/runbooks/automated/.env.example index 2492132..f187ab1 100644 --- a/op-monitorism/faultproof_withdrawals/runbooks/automated/.env.example +++ b/op-monitorism/faultproof_withdrawals/runbooks/automated/.env.example @@ -1,4 +1,4 @@ # .env -L1_GETH_URL="https://l1-geth.rpc" -L2_OP_NODE_URL="https://op-node.rpc" -L2_OP_GETH_URL="https://op-geth.rpc" +mainnet_geth_url="https://l1-geth.rpc" +mainnet_op_geth_url="https://op-geth.rpc" +mainnet_op_node_url="https://op-node.rpc" \ No newline at end of file diff --git a/op-monitorism/faultproof_withdrawals/runbooks/automated/.gitignore b/op-monitorism/faultproof_withdrawals/runbooks/automated/.gitignore index 87802c1..d097ae8 100644 --- a/op-monitorism/faultproof_withdrawals/runbooks/automated/.gitignore +++ b/op-monitorism/faultproof_withdrawals/runbooks/automated/.gitignore @@ -137,4 +137,5 @@ ui-tests/.pnp.* local_data local_data/* -.env \ No newline at end of file +.env +nodes.yaml \ No newline at end of file diff --git a/op-monitorism/faultproof_withdrawals/runbooks/automated/lib/web3.py b/op-monitorism/faultproof_withdrawals/runbooks/automated/lib/web3.py index 1ff1457..d343feb 100644 --- a/op-monitorism/faultproof_withdrawals/runbooks/automated/lib/web3.py +++ b/op-monitorism/faultproof_withdrawals/runbooks/automated/lib/web3.py @@ -5,6 +5,7 @@ import urllib3 import os import requests +from pprint import pprint # Disable warnings for insecure HTTPS requests urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) @@ -130,7 +131,8 @@ def get_block_timestamp(self, blockNumber: int): "formatted_timestamp": f"{datetime.fromtimestamp(timestamp, tz=timezone.utc).strftime('%Y-%m-%d %H:%M:%S')}", } return ret - + + def get_game_data(self,withDrawalHash:str ,proofSubmitter:str): if type(withDrawalHash) is str: withDrawalHash = bytes.fromhex(withDrawalHash) @@ -140,9 +142,13 @@ def get_game_data(self,withDrawalHash:str ,proofSubmitter:str): rootClaim=game.functions.rootClaim().call() sentMessages=self.L2ToL1MessagePasser.functions.sentMessages(withDrawalHash).call() - - optimism_outputAtBlock=self.optimism_output_at_block(l2BlockNumber) - + + try: + optimism_outputAtBlock=self.optimism_output_at_block(l2BlockNumber) + except Exception as e: + print(f"Error: {str(e)}") + optimism_outputAtBlock=None + return {"gameProxyAddress":gameProxyAddress,"timestamp":timestamp,"l2BlockNumber":l2BlockNumber,"rootClaim":f"0x{rootClaim.hex()}","sentMessages":sentMessages,"optimism_outputAtBlock":optimism_outputAtBlock} @@ -163,9 +169,16 @@ def optimism_output_at_block(self,blockNumber:int): # Send the POST request response = requests.post(url, headers=headers, data=json.dumps(data),verify=not self.ignore_certificate) - # Check if the request was successful if response.status_code == 200: return response.json()["result"]["outputRoot"] else: - raise Exception(f"Error: {response.status_code} - {response.text}") \ No newline at end of file + raise Exception(f"Error: {response.status_code} - {response.text}") + + def getL2Block(self,blockNumber:int): + try: + block=self.l2_op_geth.eth.get_block(blockNumber) + return block + except Exception as e: + print(f"Error: {str(e)}") + return None \ No newline at end of file diff --git a/op-monitorism/faultproof_withdrawals/runbooks/automated/requirements.txt b/op-monitorism/faultproof_withdrawals/runbooks/automated/requirements.txt index ef8b760..e97c53b 100644 --- a/op-monitorism/faultproof_withdrawals/runbooks/automated/requirements.txt +++ b/op-monitorism/faultproof_withdrawals/runbooks/automated/requirements.txt @@ -3,4 +3,5 @@ python-dotenv requests toml web3 -urllib3 \ No newline at end of file +urllib3 +ipywidgets \ No newline at end of file diff --git a/op-monitorism/faultproof_withdrawals/runbooks/automated/triage_detection_stalled.ipynb b/op-monitorism/faultproof_withdrawals/runbooks/automated/triage_detection_stalled.ipynb index dd58284..06014a7 100644 --- a/op-monitorism/faultproof_withdrawals/runbooks/automated/triage_detection_stalled.ipynb +++ b/op-monitorism/faultproof_withdrawals/runbooks/automated/triage_detection_stalled.ipynb @@ -17,7 +17,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "id": "46adf20d-5de6-4f88-8c65-24e2c11f4ea6", "metadata": {}, "outputs": [], @@ -29,21 +29,14 @@ "from lib.web3 import *\n", "from pprint import pprint\n", "from datetime import datetime, timedelta\n", + "from IPython.display import Javascript, display\n", + "import yaml\n", "\n", "\n", "#parameters setup (there should be no need to change the one below)\n", "abi_folder_path=\"abi\"\n", "\n", - "env_file = \".env\"\n", - "if os.path.exists(env_file):\n", - " load_dotenv(env_file)\n", - " print(\"Environment variables loaded from .env file.\")\n", - "else:\n", - " print(\"No .env file found. Using system environment variables. Make sure to set them up. An example .env file is provided insite the automated folder .env.example\")\n", - "\n", - "L1_GETH_URL = os.getenv('L1_GETH_URL')\n", - "L2_OP_NODE_URL = os.getenv('L2_OP_NODE_URL')\n", - "L2_OP_GETH_URL = os.getenv('L2_OP_GETH_URL')" + "env_file = \".env\"\n" ] }, { @@ -58,7 +51,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 2, "id": "1f8a7d7e", "metadata": {}, "outputs": [], @@ -68,7 +61,12 @@ "\n", "#set cahin you need to monitor\n", "l1_chain_name=\"mainnet\"\n", - "l2_chain_name=\"op\"\n" + "l2_chain_name=\"base\"\n", + "\n", + "#set the chain URL to use as trusted node explicitly or set in .env file\n", + "L1_GETH_URL = \"\"\n", + "L2_NODE_URL = \"\"\n", + "L2_GETH_URL = \"\" " ] }, { @@ -76,25 +74,72 @@ "id": "c103440d", "metadata": {}, "source": [ - "## Loading local values and superchain regsitry values" + "## Loading local values and superchain regsitry values\n", + "You can either set the values manually temporarly\n", + "```\n", + "L1_GETH_URL = \"\"\n", + "L2_NODE_URL = \"\"\n", + "L2_GETH_URL = \"\" \n", + "```\n", + "Or you can set into .env for a more permanent use of this runbooks.\n", + "Make sure to use the correct name convention as shown below.\n", + "```\n", + "mainnet_geth_url=\"https://l1-geth.rpc\"\n", + "_geth_url=\"https://l1-geth.rpc\"\n", + "\n", + "mainnet_op_geth_url=\n", + "__get_url=\"https://op-geth.rpc\"\n", + "\n", + "mainnet_op_node_url=\"https://op-node.rpc\"\n", + "__node_url=\"https://op-geth.rpc\"\n", + "```\n", + "\n", + "\n", + "If you are part of OP you have access to the internal repository with internal nodes to call. You can save [https://github.com/ethereum-optimism/internal-data/blob/main/automated-runbooks/.env](https://github.com/ethereum-optimism/internal-data/blob/main/automated-runbooks/.env) whitin the same folder." ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "id": "d8621fe4", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "OptimismPortal2 address: https://etherscan.io/address/0x49048044D57e1C92A77f79988d21Fa8fAF74E97e#readProxyContract\n" + ] + } + ], "source": [ + "if L1_GETH_URL == \"\" or L2_NODE_URL == \"\" or L2_GETH_URL == \"\":\n", + " env_file = \".env\"\n", + " if os.path.exists(env_file):\n", + " load_dotenv(env_file)\n", + " L1_GETH_URL = os.getenv((f\"{l1_chain_name}_geth_url\"))\n", + " L2_OP_NODE_URL = os.getenv((f\"{l1_chain_name}_{l2_chain_name}_node_url\"))\n", + " L2_OP_GETH_URL = os.getenv((f\"{l1_chain_name}_{l2_chain_name}_geth_url\"))\n", + " else:\n", + " print(\"No .env file found. Using system environment variables. Make sure to set them up. An example .env file is provided insite the automated folder .env.example\")\n", + " print(\"If you have access to private nodes in OP Labs you can download the .env file from https://github.com/ethereum-optimism/internal-data/blob/main/automated-runbooks/.env\")\n", + " \n", + "if L1_GETH_URL is None or L2_NODE_URL is None or L2_GETH_URL is None:\n", + " print(\"Please set the environment variables for the chains you want to monitor\")\n", + " exit()\n", + " \n", + "superchain=get_superchain_file(l1_chain_name, l2_chain_name)\n", + "\n", "eth_scan_url=\"https://etherscan.io\"\n", "\n", "if l1_chain_name==\"sepolia\":\n", " eth_scan_url=\"https://sepolia.etherscan.io\"\n", - " \n", - "superchain=get_superchain_file(l1_chain_name, l2_chain_name)\n", + "\n", + "l2_eth_scan_url=superchain[\"explorer\"]\n", + "\n", "OptimismPortalProxy=superchain[\"addresses\"][\"OptimismPortalProxy\"]\n", "\n", - "print(f\"OptimismPortal2 address:{eth_scan_url}/address/{OptimismPortalProxy}#readProxyContract\")\n", + "print(f\"OptimismPortal2 address: {eth_scan_url}/address/{OptimismPortalProxy}#readProxyContract\")\n", "\n", "web3_utility=Web3Utility(L1_GETH_URL, L2_OP_GETH_URL,L2_OP_NODE_URL,abi_folder_path, OptimismPortalProxy, ignore_certificate=ignore_url_certificate)" ] @@ -112,10 +157,27 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "id": "e32fd923-03ef-41d5-94af-7212ca36bbb9", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Last withdrawal event:\n", + "timestamp: 1730746475 \n", + "timestamp: 2024-11-04 18:54:35\n", + "\n", + "Last withdrawal happened: \n", + "at block: https://etherscan.io/block/21116207 \n", + "with transaction hash: https://etherscan.io/tx/0xfeeff7830dd809724133295504cf130f4c7011c2795269d4855ef97937e5d4d9\n", + "Logs can be found at: https://etherscan.io/address/0x49048044D57e1C92A77f79988d21Fa8fAF74E97e#events using filter:0x798f9f13695f8f045aa5f80ed8efebb695f3c7fe65da381969f2f28bf3c60b97\n", + "\n", + "WARNING: Last withdrawal event happened less than 24 hours ago. The monitoring may be stalled\n" + ] + } + ], "source": [ "result=web3_utility.find_latest_withdrawal_event()\n", "\n", diff --git a/op-monitorism/faultproof_withdrawals/runbooks/automated/triage_potential_attack_event.ipynb b/op-monitorism/faultproof_withdrawals/runbooks/automated/triage_potential_attack_event.ipynb index 7ba931c..1ef8a40 100644 --- a/op-monitorism/faultproof_withdrawals/runbooks/automated/triage_potential_attack_event.ipynb +++ b/op-monitorism/faultproof_withdrawals/runbooks/automated/triage_potential_attack_event.ipynb @@ -17,7 +17,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "id": "46adf20d-5de6-4f88-8c65-24e2c11f4ea6", "metadata": {}, "outputs": [], @@ -32,16 +32,7 @@ "#parameters setup (there should be no need to change the one below)\n", "abi_folder_path=\"abi\"\n", "\n", - "env_file = \".env\"\n", - "if os.path.exists(env_file):\n", - " load_dotenv(env_file)\n", - " print(\"Environment variables loaded from .env file.\")\n", - "else:\n", - " print(\"No .env file found. Using system environment variables. Make sure to set them up. An example .env file is provided insite the automated folder .env.example\")\n", "\n", - "L1_GETH_URL = os.getenv('L1_GETH_URL')\n", - "L2_OP_NODE_URL = os.getenv('L2_OP_NODE_URL')\n", - "L2_OP_GETH_URL = os.getenv('L2_OP_GETH_URL')\n", "L2ToL1MessagePasserAddress=\"0x4200000000000000000000000000000000000016\"" ] }, @@ -57,20 +48,25 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 2, "id": "1f8a7d7e", "metadata": {}, "outputs": [], "source": [ + "#set ignore_certificate if you are using a local https\n", "#set ignore_certificate if you are using a local https\n", "ignore_url_certificate=True\n", "\n", "#set cahin you need to monitor\n", "l1_chain_name=\"mainnet\"\n", - "l2_chain_name=\"op\"\n", + "l2_chain_name=\"base\"\n", + "\n", + "#set the chain URL to use as trusted node explicitly or set in .env file\n", + "L1_GETH_URL = \"\"\n", + "L2_NODE_URL = \"\"\n", + "L2_GETH_URL = \"\" \n", "\n", - "Txhash=\"\"\n", - "#Txhash=\"0xf290e62898913197bc96033a29c098aeb26613d504cfa51bf5ca5ac90cca58da\"# Example of a Txhash\n" + "Txhash=\"\"\n" ] }, { @@ -78,16 +74,60 @@ "id": "c103440d", "metadata": {}, "source": [ - "## Loading local values and superchain regsitry values" + "## Loading local values and superchain regsitry values\n", + "You can either set the values manually temporarly\n", + "```\n", + "L1_GETH_URL = \"\"\n", + "L2_NODE_URL = \"\"\n", + "L2_GETH_URL = \"\" \n", + "```\n", + "Or you can set into .env for a more permanent use of this runbooks.\n", + "Make sure to use the correct name convention as shown below.\n", + "```\n", + "mainnet_geth_url=\"https://l1-geth.rpc\"\n", + "_geth_url=\"https://l1-geth.rpc\"\n", + "\n", + "mainnet_op_geth_url=\n", + "__get_url=\"https://op-geth.rpc\"\n", + "\n", + "mainnet_op_node_url=\"https://op-node.rpc\"\n", + "__node_url=\"https://op-geth.rpc\"\n", + "```\n", + "\n", + "\n", + "If you are part of OP you have access to the internal repository with internal nodes to call. You can save [https://github.com/ethereum-optimism/internal-data/blob/main/automated-runbooks/.env](https://github.com/ethereum-optimism/internal-data/blob/main/automated-runbooks/.env) whitin the same folder." ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "id": "d8621fe4", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "OptimismPortal2 address: https://etherscan.io/address/0x49048044D57e1C92A77f79988d21Fa8fAF74E97e#readProxyContract\n" + ] + } + ], "source": [ + "if L1_GETH_URL == \"\" or L2_NODE_URL == \"\" or L2_GETH_URL == \"\":\n", + " env_file = \".env\"\n", + " if os.path.exists(env_file):\n", + " load_dotenv(env_file)\n", + " L1_GETH_URL = os.getenv((f\"{l1_chain_name}_geth_url\"))\n", + " L2_OP_NODE_URL = os.getenv((f\"{l1_chain_name}_{l2_chain_name}_node_url\"))\n", + " L2_OP_GETH_URL = os.getenv((f\"{l1_chain_name}_{l2_chain_name}_geth_url\"))\n", + " else:\n", + " print(\"No .env file found. Using system environment variables. Make sure to set them up. An example .env file is provided insite the automated folder .env.example\")\n", + " print(\"If you have access to private nodes in OP Labs you can download the .env file from https://github.com/ethereum-optimism/internal-data/blob/main/automated-runbooks/.env\")\n", + " \n", + "if L1_GETH_URL is None or L2_NODE_URL is None or L2_GETH_URL is None:\n", + " print(\"Please set the environment variables for the chains you want to monitor\")\n", + " exit()\n", + " \n", "superchain=get_superchain_file(l1_chain_name, l2_chain_name)\n", "\n", "eth_scan_url=\"https://etherscan.io\"\n", @@ -126,10 +166,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "id": "7edae725-621a-4e1c-ba1d-e10f85ba7e02", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Please set up a Txhash\n" + ] + } + ], "source": [ "\n", "if Txhash==\"\":\n", @@ -148,6 +196,16 @@ " gameProxyAddress=gameData[\"gameProxyAddress\"]\n", " print(f\"gameProxyAddress: {eth_scan_url}/address/{gameProxyAddress}\")\n", "\n", + " pprint(gameData)\n", + "\n", + " l2BlockNumber=gameData[\"l2BlockNumber\"]\n", + " getL2Block=web3_utility.getL2Block(l2BlockNumber)\n", + "\n", + " if getL2Block:\n", + " print(f\"l2BlockNumber present: {l2_eth_scan_url}/block/{l2BlockNumber}\")\n", + " else:\n", + " print(f\"WARNING: l2BlockNumber not present: {l2BlockNumber}\\nTrusted node is not in sync with the chain or the game refer to the block number is not valid.\\n You can verify here: {l2_eth_scan_url}/block/{l2BlockNumber}\")\n", + "\n", " if gameData[\"rootClaim\"] == gameData[\"optimism_outputAtBlock\"]:\n", " print(f\"rootClaim: {gameData['rootClaim']} == optimism_outputAtBlock: {gameData['optimism_outputAtBlock']}\")\n", " print(\"\\nALL GOOD: Game claim is valid\")\n", @@ -161,8 +219,7 @@ " else:\n", " print(\"sentMessages: False\")\n", " print(\"\\nWARNING: Withdrawal not present on L2.\")\n", - " print(f\"Can be verified by checking {l2_eth_scan_url}/address/{L2ToL1MessagePasserAddress}#readProxyContract and calling sentMessages function with the withdrawalHash 0x{withDrawalHash}\")\n", - "\n" + " print(f\"Can be verified by checking {l2_eth_scan_url}/address/{L2ToL1MessagePasserAddress}#readProxyContract and calling sentMessages function with the withdrawalHash 0x{withDrawalHash}\")\n" ] } ],