diff --git a/.circleci/tests/mev.json b/.circleci/tests/mev.json index e659b3105..b7780d57a 100644 --- a/.circleci/tests/mev.json +++ b/.circleci/tests/mev.json @@ -1,7 +1,15 @@ { "mev_type": "full", + "additional_services": [ + "tx_spammer", + "blob_spammer", + "custom_flood", + "el_forkmon", + "beacon_metrics_gazer", + "dora", + "prometheus_grafana" + ], "mev_params": { - "launch_custom_flood": true, "mev_relay_image": "flashbots/mev-boost-relay:0.27" }, "network_params": { diff --git a/README.md b/README.md index 473fad3ed..d06d4e70a 100644 --- a/README.md +++ b/README.md @@ -247,7 +247,8 @@ To configure the package behaviour, you can modify your `network_params.json` fi "additional_services": [ "tx_spammer", "blob_spammer", - "goomy_blob" + "custom_flood", + "goomy_blob", "el_forkmon", "beacon_metrics_gazer", "dora", @@ -307,9 +308,10 @@ To configure the package behaviour, you can modify your `network_params.json` fi "mev_flood_extra_args": [], // Number of seconds between bundles for mev-flood "mev_flood_seconds_per_bundle": 15, - // A custom flood script that increases the balance of the coinbase addresss leading to more reliable - // payload delivery - "launch_custom_flood": false + // Optional parameters to send to the custom_flood script that sends reliable payloads + "custom_flood_params": { + "interval_between_transactions": 1 + } }, // A list of locators for grafana dashboards to be loaded be the grafana service "grafana_additional_dashboards": [] diff --git a/examples/capella-mev.json b/examples/capella-mev.json index ebbac0649..df766ce84 100644 --- a/examples/capella-mev.json +++ b/examples/capella-mev.json @@ -20,7 +20,6 @@ }, "mev_params": { "mev_flood_seconds_per_bundle": 12, - "launch_custom_flood": false, "mev_flood_extra_args": [ "--txsPerBundle=300" ], "mev_flood_image": "flashbots/mev-flood:0.0.9", "mev_relay_image": "flashbots/mev-boost-relay:0.27.0" diff --git a/main.star b/main.star index 147b5654e..2fed8a416 100644 --- a/main.star +++ b/main.star @@ -190,13 +190,6 @@ def run(plan, args={}): mev_params.mev_flood_seconds_per_bundle, genesis_constants.PRE_FUNDED_ACCOUNTS, ) - if args_with_right_defaults.mev_params.launch_custom_flood: - mev_custom_flood_module.spam_in_background( - plan, - genesis_constants.PRE_FUNDED_ACCOUNTS[-1].private_key, - genesis_constants.PRE_FUNDED_ACCOUNTS[0].address, - el_uri, - ) mev_endpoints.append(endpoint) # spin up the mev boost contexts if some endpoints for relays have been passed @@ -313,6 +306,14 @@ def run(plan, args={}): elif additional_service == "prometheus_grafana": # Allow prometheus to be launched last so is able to collect metrics from other services launch_prometheus_grafana = True + elif additional_service == "custom_flood": + mev_custom_flood_module.spam_in_background( + plan, + genesis_constants.PRE_FUNDED_ACCOUNTS[-1].private_key, + genesis_constants.PRE_FUNDED_ACCOUNTS[0].address, + el_uri, + args_with_right_defaults.custom_flood_params, + ) else: fail("Invalid additional service %s" % (additional_service)) if launch_prometheus_grafana: diff --git a/network_params.json b/network_params.json index a25241b35..f0b3a5af2 100644 --- a/network_params.json +++ b/network_params.json @@ -47,8 +47,7 @@ "mev_builder_extra_args": [], "mev_flood_image": "flashbots/mev-flood", "mev_flood_extra_args": [], - "mev_flood_seconds_per_bundle": 15, - "launch_custom_flood": false + "mev_flood_seconds_per_bundle": 15 }, "grafana_additional_dashboards": [] } diff --git a/src/mev_custom_flood/mev_custom_flood_launcher.star b/src/mev_custom_flood/mev_custom_flood_launcher.star index 2a255dca2..0b2df7843 100644 --- a/src/mev_custom_flood/mev_custom_flood_launcher.star +++ b/src/mev_custom_flood/mev_custom_flood_launcher.star @@ -2,7 +2,7 @@ PYTHON_IMAGE = "python:3.11-alpine" CUSTOM_FLOOD_SERVICE_NAME = "mev-custom-flood" -def spam_in_background(plan, sender_key, receiver_key, el_uri): +def spam_in_background(plan, sender_key, receiver_key, el_uri, params): sender_script = plan.upload_files("./sender.py") plan.add_service( @@ -21,12 +21,18 @@ def spam_in_background(plan, sender_key, receiver_key, el_uri): plan.exec( service_name=CUSTOM_FLOOD_SERVICE_NAME, - recipe=ExecRecipe(["pip", "install", "web3"]), + recipe=ExecRecipe(["pip", "install", "web3", "click"]), ) plan.exec( service_name=CUSTOM_FLOOD_SERVICE_NAME, recipe=ExecRecipe( - ["/bin/sh", "-c", "nohup python /tmp/sender.py > /dev/null 2>&1 &"] + [ + "/bin/sh", + "-c", + "nohup python /tmp/sender.py --interval_between_transactions {} > /dev/null 2>&1 &".format( + params.interval_between_transactions + ), + ] ), ) diff --git a/src/mev_custom_flood/sender.py b/src/mev_custom_flood/sender.py index 8b540f2b1..487b29fbe 100644 --- a/src/mev_custom_flood/sender.py +++ b/src/mev_custom_flood/sender.py @@ -2,12 +2,15 @@ this is s a really dumb script that sends tokens to the receiver from the sender every 3 seconds this is being used as of 2023-09-06 to guarantee that payloads are delivered """ +from functools import partial from web3 import Web3 from web3.middleware import construct_sign_and_send_raw_middleware import os import time import logging +import click + VALUE_TO_SEND = 0x9184 @@ -17,49 +20,50 @@ datefmt='%H:%M:%S', level=logging.DEBUG) +# this is the last prefunded address +SENDER = os.getenv("SENDER_PRIVATE_KEY", "17fdf89989597e8bcac6cdfcc001b6241c64cece2c358ffc818b72ca70f5e1ce") +# this is the first prefunded address +RECEIVER = os.getenv("RECEIVER_PUBLIC_KEY", "0x878705ba3f8Bc32FCf7F4CAa1A35E72AF65CF766") +EL_URI = os.getenv("EL_RPC_URI", 'http://0.0.0.0:53913') -def flood(): - # this is the last prefunded address - sender = os.getenv("SENDER_PRIVATE_KEY", "17fdf89989597e8bcac6cdfcc001b6241c64cece2c358ffc818b72ca70f5e1ce") - # this is the first prefunded address - receiver = os.getenv("RECEIVER_PUBLIC_KEY", "0x878705ba3f8Bc32FCf7F4CAa1A35E72AF65CF766") - el_uri = os.getenv("EL_RPC_URI", 'http://0.0.0.0:53913') - - logging.info(f"Using sender {sender} receiver {receiver} and el_uri {el_uri}") - - w3 = Web3(Web3.HTTPProvider(el_uri)) - - sender_account = w3.eth.account.from_key(sender) - - while True: - time.sleep(3) +def send_transaction(): + # Setting w3 as constant causes recursion exceeded error after ~500 transactions + # Thus it's created everytime a transaction is sent + w3 = Web3(Web3.HTTPProvider(EL_URI)) - w3.middleware_onion.add(construct_sign_and_send_raw_middleware(sender_account)) + sender_account = w3.eth.account.from_key(SENDER) + w3.middleware_onion.add(construct_sign_and_send_raw_middleware(sender_account)) - transaction = { - "from": sender_account.address, - "value": VALUE_TO_SEND, - "to": receiver, - "data": "0xabcd", - "gasPrice": w3.eth.gas_price, - "nonce": w3.eth.get_transaction_count(sender_account.address) - } + transaction = { + "from": sender_account.address, + "value": VALUE_TO_SEND, + "to": RECEIVER, + "data": "0xabcd", + "gasPrice": w3.eth.gas_price, + "nonce": w3.eth.get_transaction_count(sender_account.address) + } - estimated_gas = w3.eth.estimate_gas(transaction) + transaction["gas"] = estimated_gas - transaction["gas"] = estimated_gas + tx_hash = w3.eth.send_transaction(transaction) - tx_hash = w3.eth.send_transaction(transaction) + tx = w3.eth.get_transaction(tx_hash) + logging.info(tx_hash.hex()) + assert tx["from"] == sender_account.address - tx = w3.eth.get_transaction(tx_hash) - logging.info(tx_hash.hex()) - assert tx["from"] == sender_account.address +def delayed_send(interval_between_transactions): + send_transaction() + time.sleep(interval_between_transactions) -def run_infinitely(): +@click.command() +@click.option('--interval_between_transactions', default=0.5, help='Interval between successive transaction sends (in seconds). The value may be an integer or decimal') +def run_infinitely(interval_between_transactions): + logging.info(f"Using sender {SENDER} receiver {RECEIVER} and el_uri {EL_URI}") + spam = send_transaction if interval_between_transactions == 0 else partial(delayed_send, interval_between_transactions) while True: try: - flood() + spam() except Exception as e: print("e") print("restarting flood as previous one failed") diff --git a/src/package_io/parse_input.star b/src/package_io/parse_input.star index e96b69c5c..c39e9e317 100644 --- a/src/package_io/parse_input.star +++ b/src/package_io/parse_input.star @@ -46,6 +46,7 @@ ATTR_TO_BE_SKIPPED_AT_ROOT = ( "mev_params", "goomy_blob_params", "tx_spammer_params", + "custom_flood_params", ) package_io_constants = import_module("../package_io/constants.star") @@ -65,6 +66,7 @@ def parse_input(plan, input_args): result["additional_services"] = DEFAULT_ADDITIONAL_SERVICES result["grafana_additional_dashboards"] = [] result["tx_spammer_params"] = get_default_tx_spammer_params() + result["custom_flood_params"] = get_default_custom_flood_params() for attr in input_args: value = input_args[attr] @@ -80,6 +82,10 @@ def parse_input(plan, input_args): for sub_attr in input_args["tx_spammer_params"]: sub_value = input_args["tx_spammer_params"][sub_attr] result["tx_spammer_params"][sub_attr] = sub_value + elif attr == "custom_flood_params": + for sub_attr in input_args["custom_flood_params"]: + sub_value = input_args["custom_flood_params"][sub_attr] + result["custom_flood_params"][sub_attr] = sub_value if result.get("mev_type") in ("mock", "full"): result = enrich_mev_extra_params( @@ -101,7 +107,6 @@ def parse_input(plan, input_args): ) result["goomy_blob_params"] = get_default_goomy_blob_params() - return struct( participants=[ struct( @@ -168,7 +173,6 @@ def parse_input(plan, input_args): mev_flood_seconds_per_bundle=result["mev_params"][ "mev_flood_seconds_per_bundle" ], - launch_custom_flood=result["mev_params"]["launch_custom_flood"], ), tx_spammer_params=struct( tx_spammer_extra_args=result["tx_spammer_params"]["tx_spammer_extra_args"], @@ -176,6 +180,11 @@ def parse_input(plan, input_args): goomy_blob_params=struct( goomy_blob_args=result["goomy_blob_params"]["goomy_blob_args"], ), + custom_flood_params=struct( + interval_between_transactions=result["custom_flood_params"][ + "interval_between_transactions" + ], + ), launch_additional_services=result["launch_additional_services"], additional_services=result["additional_services"], wait_for_finalization=result["wait_for_finalization"], @@ -399,8 +408,6 @@ def get_default_mev_params(): "mev_flood_image": "flashbots/mev-flood", "mev_flood_extra_args": [], "mev_flood_seconds_per_bundle": 15, - # this is a simple script that increases the balance of the coinbase address at a cadence - "launch_custom_flood": False, } @@ -412,6 +419,11 @@ def get_default_goomy_blob_params(): return {"goomy_blob_args": []} +def get_default_custom_flood_params(): + # this is a simple script that increases the balance of the coinbase address at a cadence + return {"interval_between_transactions": 1} + + # TODO perhaps clean this up into a map def enrich_mev_extra_params(parsed_arguments_dict, mev_prefix, mev_port, mev_type): for index, participant in enumerate(parsed_arguments_dict["participants"]):