Commit f1e18ca7 authored by Piotr Piwoński's avatar Piotr Piwoński Committed by GitHub

feat: Add configurable spamming frequency to custom flood (#283)

The PR aims to make custom flood more usable and configuration more
consistent:
* Add `delay` parameter to custom flood to control frequency of
transactions
* Remove `launch_custom_flood` parameter
* Add custom_flood to `additional_services`
parent b0ee145e
{ {
"mev_type": "full", "mev_type": "full",
"additional_services": [
"tx_spammer",
"blob_spammer",
"custom_flood",
"el_forkmon",
"beacon_metrics_gazer",
"dora",
"prometheus_grafana"
],
"mev_params": { "mev_params": {
"launch_custom_flood": true,
"mev_relay_image": "flashbots/mev-boost-relay:0.27" "mev_relay_image": "flashbots/mev-boost-relay:0.27"
}, },
"network_params": { "network_params": {
......
...@@ -247,7 +247,8 @@ To configure the package behaviour, you can modify your `network_params.json` fi ...@@ -247,7 +247,8 @@ To configure the package behaviour, you can modify your `network_params.json` fi
"additional_services": [ "additional_services": [
"tx_spammer", "tx_spammer",
"blob_spammer", "blob_spammer",
"goomy_blob" "custom_flood",
"goomy_blob",
"el_forkmon", "el_forkmon",
"beacon_metrics_gazer", "beacon_metrics_gazer",
"dora", "dora",
...@@ -307,9 +308,10 @@ To configure the package behaviour, you can modify your `network_params.json` fi ...@@ -307,9 +308,10 @@ To configure the package behaviour, you can modify your `network_params.json` fi
"mev_flood_extra_args": [], "mev_flood_extra_args": [],
// Number of seconds between bundles for mev-flood // Number of seconds between bundles for mev-flood
"mev_flood_seconds_per_bundle": 15, "mev_flood_seconds_per_bundle": 15,
// A custom flood script that increases the balance of the coinbase addresss leading to more reliable // Optional parameters to send to the custom_flood script that sends reliable payloads
// payload delivery "custom_flood_params": {
"launch_custom_flood": false "interval_between_transactions": 1
}
}, },
// A list of locators for grafana dashboards to be loaded be the grafana service // A list of locators for grafana dashboards to be loaded be the grafana service
"grafana_additional_dashboards": [] "grafana_additional_dashboards": []
......
...@@ -20,7 +20,6 @@ ...@@ -20,7 +20,6 @@
}, },
"mev_params": { "mev_params": {
"mev_flood_seconds_per_bundle": 12, "mev_flood_seconds_per_bundle": 12,
"launch_custom_flood": false,
"mev_flood_extra_args": [ "--txsPerBundle=300" ], "mev_flood_extra_args": [ "--txsPerBundle=300" ],
"mev_flood_image": "flashbots/mev-flood:0.0.9", "mev_flood_image": "flashbots/mev-flood:0.0.9",
"mev_relay_image": "flashbots/mev-boost-relay:0.27.0" "mev_relay_image": "flashbots/mev-boost-relay:0.27.0"
......
...@@ -190,13 +190,6 @@ def run(plan, args={}): ...@@ -190,13 +190,6 @@ def run(plan, args={}):
mev_params.mev_flood_seconds_per_bundle, mev_params.mev_flood_seconds_per_bundle,
genesis_constants.PRE_FUNDED_ACCOUNTS, 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) mev_endpoints.append(endpoint)
# spin up the mev boost contexts if some endpoints for relays have been passed # spin up the mev boost contexts if some endpoints for relays have been passed
...@@ -313,6 +306,14 @@ def run(plan, args={}): ...@@ -313,6 +306,14 @@ def run(plan, args={}):
elif additional_service == "prometheus_grafana": elif additional_service == "prometheus_grafana":
# Allow prometheus to be launched last so is able to collect metrics from other services # Allow prometheus to be launched last so is able to collect metrics from other services
launch_prometheus_grafana = True 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: else:
fail("Invalid additional service %s" % (additional_service)) fail("Invalid additional service %s" % (additional_service))
if launch_prometheus_grafana: if launch_prometheus_grafana:
......
...@@ -47,8 +47,7 @@ ...@@ -47,8 +47,7 @@
"mev_builder_extra_args": [], "mev_builder_extra_args": [],
"mev_flood_image": "flashbots/mev-flood", "mev_flood_image": "flashbots/mev-flood",
"mev_flood_extra_args": [], "mev_flood_extra_args": [],
"mev_flood_seconds_per_bundle": 15, "mev_flood_seconds_per_bundle": 15
"launch_custom_flood": false
}, },
"grafana_additional_dashboards": [] "grafana_additional_dashboards": []
} }
...@@ -2,7 +2,7 @@ PYTHON_IMAGE = "python:3.11-alpine" ...@@ -2,7 +2,7 @@ PYTHON_IMAGE = "python:3.11-alpine"
CUSTOM_FLOOD_SERVICE_NAME = "mev-custom-flood" 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") sender_script = plan.upload_files("./sender.py")
plan.add_service( plan.add_service(
...@@ -21,12 +21,18 @@ def spam_in_background(plan, sender_key, receiver_key, el_uri): ...@@ -21,12 +21,18 @@ def spam_in_background(plan, sender_key, receiver_key, el_uri):
plan.exec( plan.exec(
service_name=CUSTOM_FLOOD_SERVICE_NAME, service_name=CUSTOM_FLOOD_SERVICE_NAME,
recipe=ExecRecipe(["pip", "install", "web3"]), recipe=ExecRecipe(["pip", "install", "web3", "click"]),
) )
plan.exec( plan.exec(
service_name=CUSTOM_FLOOD_SERVICE_NAME, service_name=CUSTOM_FLOOD_SERVICE_NAME,
recipe=ExecRecipe( 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
),
]
), ),
) )
...@@ -2,12 +2,15 @@ ...@@ -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 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 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 import Web3
from web3.middleware import construct_sign_and_send_raw_middleware from web3.middleware import construct_sign_and_send_raw_middleware
import os import os
import time import time
import logging import logging
import click
VALUE_TO_SEND = 0x9184 VALUE_TO_SEND = 0x9184
...@@ -17,49 +20,50 @@ logging.basicConfig(filename="/tmp/sender.log", ...@@ -17,49 +20,50 @@ logging.basicConfig(filename="/tmp/sender.log",
datefmt='%H:%M:%S', datefmt='%H:%M:%S',
level=logging.DEBUG) 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(): def send_transaction():
# this is the last prefunded address # Setting w3 as constant causes recursion exceeded error after ~500 transactions
sender = os.getenv("SENDER_PRIVATE_KEY", "17fdf89989597e8bcac6cdfcc001b6241c64cece2c358ffc818b72ca70f5e1ce") # Thus it's created everytime a transaction is sent
# this is the first prefunded address w3 = Web3(Web3.HTTPProvider(EL_URI))
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)
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 = { transaction = {
"from": sender_account.address, "from": sender_account.address,
"value": VALUE_TO_SEND, "value": VALUE_TO_SEND,
"to": receiver, "to": RECEIVER,
"data": "0xabcd", "data": "0xabcd",
"gasPrice": w3.eth.gas_price, "gasPrice": w3.eth.gas_price,
"nonce": w3.eth.get_transaction_count(sender_account.address) "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) def delayed_send(interval_between_transactions):
logging.info(tx_hash.hex()) send_transaction()
assert tx["from"] == sender_account.address 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: while True:
try: try:
flood() spam()
except Exception as e: except Exception as e:
print("e") print("e")
print("restarting flood as previous one failed") print("restarting flood as previous one failed")
......
...@@ -46,6 +46,7 @@ ATTR_TO_BE_SKIPPED_AT_ROOT = ( ...@@ -46,6 +46,7 @@ ATTR_TO_BE_SKIPPED_AT_ROOT = (
"mev_params", "mev_params",
"goomy_blob_params", "goomy_blob_params",
"tx_spammer_params", "tx_spammer_params",
"custom_flood_params",
) )
package_io_constants = import_module("../package_io/constants.star") package_io_constants = import_module("../package_io/constants.star")
...@@ -65,6 +66,7 @@ def parse_input(plan, input_args): ...@@ -65,6 +66,7 @@ def parse_input(plan, input_args):
result["additional_services"] = DEFAULT_ADDITIONAL_SERVICES result["additional_services"] = DEFAULT_ADDITIONAL_SERVICES
result["grafana_additional_dashboards"] = [] result["grafana_additional_dashboards"] = []
result["tx_spammer_params"] = get_default_tx_spammer_params() result["tx_spammer_params"] = get_default_tx_spammer_params()
result["custom_flood_params"] = get_default_custom_flood_params()
for attr in input_args: for attr in input_args:
value = input_args[attr] value = input_args[attr]
...@@ -80,6 +82,10 @@ def parse_input(plan, input_args): ...@@ -80,6 +82,10 @@ def parse_input(plan, input_args):
for sub_attr in input_args["tx_spammer_params"]: for sub_attr in input_args["tx_spammer_params"]:
sub_value = input_args["tx_spammer_params"][sub_attr] sub_value = input_args["tx_spammer_params"][sub_attr]
result["tx_spammer_params"][sub_attr] = sub_value 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"): if result.get("mev_type") in ("mock", "full"):
result = enrich_mev_extra_params( result = enrich_mev_extra_params(
...@@ -101,7 +107,6 @@ def parse_input(plan, input_args): ...@@ -101,7 +107,6 @@ def parse_input(plan, input_args):
) )
result["goomy_blob_params"] = get_default_goomy_blob_params() result["goomy_blob_params"] = get_default_goomy_blob_params()
return struct( return struct(
participants=[ participants=[
struct( struct(
...@@ -168,7 +173,6 @@ def parse_input(plan, input_args): ...@@ -168,7 +173,6 @@ def parse_input(plan, input_args):
mev_flood_seconds_per_bundle=result["mev_params"][ mev_flood_seconds_per_bundle=result["mev_params"][
"mev_flood_seconds_per_bundle" "mev_flood_seconds_per_bundle"
], ],
launch_custom_flood=result["mev_params"]["launch_custom_flood"],
), ),
tx_spammer_params=struct( tx_spammer_params=struct(
tx_spammer_extra_args=result["tx_spammer_params"]["tx_spammer_extra_args"], tx_spammer_extra_args=result["tx_spammer_params"]["tx_spammer_extra_args"],
...@@ -176,6 +180,11 @@ def parse_input(plan, input_args): ...@@ -176,6 +180,11 @@ def parse_input(plan, input_args):
goomy_blob_params=struct( goomy_blob_params=struct(
goomy_blob_args=result["goomy_blob_params"]["goomy_blob_args"], 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"], launch_additional_services=result["launch_additional_services"],
additional_services=result["additional_services"], additional_services=result["additional_services"],
wait_for_finalization=result["wait_for_finalization"], wait_for_finalization=result["wait_for_finalization"],
...@@ -399,8 +408,6 @@ def get_default_mev_params(): ...@@ -399,8 +408,6 @@ def get_default_mev_params():
"mev_flood_image": "flashbots/mev-flood", "mev_flood_image": "flashbots/mev-flood",
"mev_flood_extra_args": [], "mev_flood_extra_args": [],
"mev_flood_seconds_per_bundle": 15, "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(): ...@@ -412,6 +419,11 @@ def get_default_goomy_blob_params():
return {"goomy_blob_args": []} 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 # TODO perhaps clean this up into a map
def enrich_mev_extra_params(parsed_arguments_dict, mev_prefix, mev_port, mev_type): def enrich_mev_extra_params(parsed_arguments_dict, mev_prefix, mev_port, mev_type):
for index, participant in enumerate(parsed_arguments_dict["participants"]): for index, participant in enumerate(parsed_arguments_dict["participants"]):
......
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