diff --git a/kurtosis.yml b/kurtosis.yml index c34fd2c49..9e14e9810 100644 --- a/kurtosis.yml +++ b/kurtosis.yml @@ -1 +1 @@ -name: "github.com/kurtosis-tech/ethereum-package" +name: "github.com/chainbound/ethereum-package" diff --git a/main.star b/main.star index 3855cfb82..24b874195 100644 --- a/main.star +++ b/main.star @@ -29,14 +29,17 @@ blockscout = import_module("./src/blockscout/blockscout_launcher.star") prometheus = import_module("./src/prometheus/prometheus_launcher.star") grafana = import_module("./src/grafana/grafana_launcher.star") mev_boost = import_module("./src/mev/mev_boost/mev_boost_launcher.star") +bolt_boost = import_module("./src/mev/bolt_boost/bolt_boost_launcher.star") mock_mev = import_module("./src/mev/mock_mev/mock_mev_launcher.star") mev_relay = import_module("./src/mev/mev_relay/mev_relay_launcher.star") +helix_relay = import_module("./src/mev/mev_relay/helix_launcher.star") mev_flood = import_module("./src/mev/mev_flood/mev_flood_launcher.star") mev_custom_flood = import_module( "./src/mev/mev_custom_flood/mev_custom_flood_launcher.star" ) broadcaster = import_module("./src/broadcaster/broadcaster.star") assertoor = import_module("./src/assertoor/assertoor_launcher.star") +bolt_sidecar = import_module("./src/mev/bolt_sidecar/bolt_sidecar_launcher.star") GRAFANA_USER = "admin" GRAFANA_PASSWORD = "admin" @@ -125,6 +128,11 @@ def run(plan, args={}): ) ) + #bolt_sidecar_context=struct( + # ip_addr="", + # metrics_port_num=0, + #) + bolt_sidecar_context = None all_el_contexts = [] all_cl_contexts = [] all_vc_contexts = [] @@ -207,6 +215,7 @@ def run(plan, args={}): beacon_uris = ",".join( ["{0}".format(context.beacon_http_url) for context in all_cl_contexts] ) + plan.print(beacon_uris) first_cl_client = all_cl_contexts[0] first_client_beacon_name = first_cl_client.beacon_service_name @@ -232,15 +241,16 @@ def run(plan, args={}): timeout="20m", service_name=first_client_beacon_name, ) - endpoint = mev_relay.launch_mev_relay( + helix_endpoint = helix_relay.launch_helix_relay( plan, mev_params, - network_params.network_id, + network_params, beacon_uris, genesis_validators_root, builder_uri, network_params.seconds_per_slot, persistent, + final_genesis_timestamp, global_node_selectors, ) mev_flood.spam_in_background( @@ -251,36 +261,106 @@ def run(plan, args={}): contract_owner.private_key, normal_user.private_key, ) - mev_endpoints.append(endpoint) + mev_endpoints.append(helix_endpoint) - # spin up the mev boost contexts if some endpoints for relays have been passed - all_mevboost_contexts = [] - if mev_endpoints: - for index, participant in enumerate(all_participants): - index_str = shared_utils.zfill_custom( - index + 1, len(str(len(all_participants))) - ) - if args_with_right_defaults.participants[index].validator_count != 0: - mev_boost_launcher = mev_boost.new_mev_boost_launcher( - MEV_BOOST_SHOULD_CHECK_RELAY, - mev_endpoints, - ) - mev_boost_service_name = "{0}-{1}-{2}-{3}".format( - input_parser.MEV_BOOST_SERVICE_NAME_PREFIX, - index_str, - participant.cl_type, - participant.el_type, + # spin up the mev boost contexts if some endpoints for relays have been passed + all_mevboost_contexts = [] + if mev_endpoints: + for index, participant in enumerate(all_participants): + index_str = shared_utils.zfill_custom( + index + 1, len(str(len(all_participants))) ) - mev_boost_context = mev_boost.launch( - plan, - mev_boost_launcher, - mev_boost_service_name, - network_params.network_id, - mev_params.mev_boost_image, - mev_params.mev_boost_args, - global_node_selectors, - ) - all_mevboost_contexts.append(mev_boost_context) + if args_with_right_defaults.participants[index].validator_count != 0: + # Initialize the Bolt Sidecar configure if needed + bolt_sidecar_config = None + if mev_params.bolt_sidecar_image != None: + # NOTE: this is a stub missing the `"constraints_api_url"` entry + bolt_sidecar_config = { + "beacon_api_url": participant.cl_context.beacon_http_url, + "execution_api_url": "http://{0}:{1}".format( + participant.el_context.ip_addr, + participant.el_context.rpc_port_num, + ), + "engine_api_url": "http://{0}:{1}".format( + participant.el_context.ip_addr, + participant.el_context.engine_rpc_port_num + ), + "jwt_hex": raw_jwt_secret, + "metrics_port": bolt_sidecar.BOLT_SIDECAR_METRICS_PORT, + "validator_keystore_files_artifact_uuid": participant.cl_context.validator_keystore_files_artifact_uuid, + "participant_index": index, + } + + if mev_params.bolt_boost_image == None: + mev_boost_launcher = mev_boost.new_mev_boost_launcher( + MEV_BOOST_SHOULD_CHECK_RELAY, + mev_endpoints, + ) + mev_boost_service_name = "{0}-{1}-{2}-{3}".format( + input_parser.MEV_BOOST_SERVICE_NAME_PREFIX, + index_str, + participant.cl_type, + participant.el_type, + ) + mev_boost_context = mev_boost.launch( + plan, + mev_boost_launcher, + mev_boost_service_name, + network_params.network_id, + mev_params.mev_boost_image, + mev_params.mev_boost_args, + network_params, + final_genesis_timestamp, + global_node_selectors, + ) + if bolt_sidecar_config != None: + bolt_sidecar_config["constraints_api_url"] = "http://{0}:{1}".format( + mev_boost_context.private_ip_address, mev_boost_context.port + ) + all_mevboost_contexts.append(mev_boost_context) + + else: + bolt_boost_service_name = "{0}-{1}-{2}-{3}".format( + input_parser.BOLT_BOOST_SERVICE_NAME_PREFIX, + index_str, + participant.cl_type, + participant.el_type, + ) + relays_config = [{ + "id": "helix_relay", + "url": helix_endpoint, + }] + if bolt_sidecar_config != None: + bolt_sidecar_config["constraints_api_url"] = "http://{0}:{1}".format( + bolt_boost_service_name, input_parser.FLASHBOTS_MEV_BOOST_PORT + ) + bolt_boost_context = bolt_boost.launch( + plan, + bolt_boost_service_name, + mev_params.bolt_boost_image, + relays_config, + bolt_sidecar_config, + network_params, + final_genesis_timestamp, + global_node_selectors, + ) + all_mevboost_contexts.append(bolt_boost_context) + + if bolt_sidecar_config != None: + service_name = "{0}-{1}-{2}-{3}".format( + input_parser.BOLT_SIDECAR_SERVICE_NAME_PREFIX, + index_str, + participant.cl_type, + participant.el_type, + ) + bolt_sidecar_config["service_name"] = service_name + bolt_sidecar_context = bolt_sidecar.launch_bolt_sidecar( + plan, + mev_params.bolt_sidecar_image, + bolt_sidecar_config, + network_params, + global_node_selectors, + ) if len(args_with_right_defaults.additional_services) == 0: output = struct( @@ -303,6 +383,7 @@ def run(plan, args={}): tx_spammer_params, network_params.electra_fork_epoch, global_node_selectors, + network_params ) plan.print("Successfully launched transaction spammer") elif additional_service == "blob_spammer": @@ -449,6 +530,7 @@ def run(plan, args={}): all_ethereum_metrics_exporter_contexts, all_xatu_sentry_contexts, global_node_selectors, + bolt_sidecar_context, ) plan.print("Launching grafana...") @@ -495,6 +577,7 @@ def run(plan, args={}): all_participants=all_participants, final_genesis_timestamp=final_genesis_timestamp, genesis_validators_root=genesis_validators_root, + mev_params=mev_params, ) return output diff --git a/src/grafana/grafana_launcher.star b/src/grafana/grafana_launcher.star index eec063713..ef131739f 100644 --- a/src/grafana/grafana_launcher.star +++ b/src/grafana/grafana_launcher.star @@ -3,7 +3,7 @@ static_files = import_module("../static_files/static_files.star") SERVICE_NAME = "grafana" -IMAGE_NAME = "grafana/grafana-enterprise:9.5.12" +IMAGE_NAME = "grafana/grafana-enterprise:11.1.0" HTTP_PORT_ID = "http" HTTP_PORT_NUMBER_UINT16 = 3000 diff --git a/src/mev/bolt_boost/bolt_boost_launcher.star b/src/mev/bolt_boost/bolt_boost_launcher.star new file mode 100644 index 000000000..0ca3aa881 --- /dev/null +++ b/src/mev/bolt_boost/bolt_boost_launcher.star @@ -0,0 +1,138 @@ +shared_utils = import_module("../../shared_utils/shared_utils.star") +mev_boost_context_module = import_module("../mev_boost/mev_boost_context.star") +static_files = import_module("../../static_files/static_files.star") +constants = import_module("../../package_io/constants.star") +input_parser = import_module("../../package_io/input_parser.star") + +BOLT_BOOST_CONFIG_FILENAME="cb-bolt-config.toml" +BOLT_BOOST_CONFIG_MOUNT_DIRPATH_ON_SERVICE="/config" + +USED_PORTS = { + "api": shared_utils.new_port_spec( + input_parser.FLASHBOTS_MEV_BOOST_PORT, "TCP", wait="5s" + ) +} + +# The min/max CPU/memory that mev-boost can use +MIN_CPU = 10 +MAX_CPU = 500 +MIN_MEMORY = 16 +MAX_MEMORY = 256 + + +def launch( + plan, + service_name, + bolt_boost_image, + relays_config, + bolt_sidecar_config, + network_params, + final_genesis_timestamp, + global_node_selectors, +): + plan.print(network_params) + config = get_bolt_boost_config( + plan, + bolt_boost_image, + relays_config, + bolt_sidecar_config, + network_params, + final_genesis_timestamp, + global_node_selectors, + ) + + bolt_boost_service = plan.add_service(service_name, config) + + return mev_boost_context_module.new_mev_boost_context( + bolt_boost_service.ip_address, bolt_boost_service.ports["api"].number + ) + + +def get_bolt_boost_config( + plan, + image, + relays_config, + bolt_sidecar_config, + network_params, + final_genesis_timestamp, + node_selectors, +): + # Read the template file for Bolt Boost configuration + bolt_boost_config_template = read_file( + static_files.BOLT_BOOST_CONFIG_TEMPLATE_FILEPATH + ) + + # Generate the data to be used in the Bolt Boost configuration, + # wrap them together in a struct + bolt_boost_config_template_data = new_bolt_boost_config_template_data( + image, + relays_config, + bolt_sidecar_config, + network_params, + final_genesis_timestamp, + ) + bolt_boost_config_template_and_data = shared_utils.new_template_and_data( + bolt_boost_config_template, bolt_boost_config_template_data + ) + + # Map the relative destination filepaths to the template/data pairs + template_and_data_by_rel_dest_filepath = {} + template_and_data_by_rel_dest_filepath[BOLT_BOOST_CONFIG_FILENAME] = bolt_boost_config_template_and_data + + # Render the templates to files in the artifact directory + config_files_artifact_name = plan.render_templates( + template_and_data_by_rel_dest_filepath + ) + + return ServiceConfig( + image=image, + ports=USED_PORTS, + files={ + BOLT_BOOST_CONFIG_MOUNT_DIRPATH_ON_SERVICE: config_files_artifact_name + }, + env_vars={ + "RUST_LOG": "debug", + "CB_CONFIG": shared_utils.path_join( + BOLT_BOOST_CONFIG_MOUNT_DIRPATH_ON_SERVICE, + BOLT_BOOST_CONFIG_FILENAME, + ) + }, + min_cpu=MIN_CPU, + max_cpu=MAX_CPU, + min_memory=MIN_MEMORY, + max_memory=MAX_MEMORY, + node_selectors=node_selectors, + ) + +def new_bolt_boost_config_template_data( + image, + relays_config, + bolt_sidecar_config, + network_params, + final_genesis_timestamp, +): + return { + "chain_config": { + "name": network_params.network.capitalize(), + "genesis_timestamp": final_genesis_timestamp, + "seconds_per_slot": network_params.seconds_per_slot, + "genesis_fork_version": constants.GENESIS_FORK_VERSION, + }, + "image": image, + "port": input_parser.FLASHBOTS_MEV_BOOST_PORT, + "relays_config": [ + { + "id": relay_config["id"], + "url": relay_config["url"], + } for relay_config in relays_config + ], + "bolt_sidecar_config": { + "constraints_api_url": bolt_sidecar_config["constraints_api_url"], + "beacon_api_url": bolt_sidecar_config["beacon_api_url"], + "execution_api_url": bolt_sidecar_config["execution_api_url"], + "engine_api_url": bolt_sidecar_config["engine_api_url"], + "jwt_hex": bolt_sidecar_config["jwt_hex"], + "metrics_port": bolt_sidecar_config["metrics_port"], + "builder_proxy_port": input_parser.FLASHBOTS_MEV_BOOST_PORT, + } + } diff --git a/src/mev/bolt_sidecar/bolt_sidecar_launcher.star b/src/mev/bolt_sidecar/bolt_sidecar_launcher.star new file mode 100644 index 000000000..65df81fd4 --- /dev/null +++ b/src/mev/bolt_sidecar/bolt_sidecar_launcher.star @@ -0,0 +1,99 @@ +redis_module = import_module("github.com/kurtosis-tech/redis-package/main.star") +postgres_module = import_module("github.com/kurtosis-tech/postgres-package/main.star") +constants = import_module("../../package_io/constants.star") +input_parser = import_module("../../package_io/input_parser.star") +validator_keystore_generator = import_module("../../prelaunch_data_generator/validator_keystores/validator_keystore_generator.star") + +BOLT_SIDECAR_COMMITMENTS_API_PORT = 9061 +BOLT_SIDECAR_METRICS_PORT = 9063 +BOLT_SIDECAR_KEYS_DIRMOUNT_PATH_ON_SERVICE = "/keys" + +# The min/max CPU/memory that bolt-sidecar can use +BOLT_SIDECAR_MIN_CPU = 100 +BOLT_SIDECAR_MAX_CPU = 1000 +BOLT_SIDECAR_MIN_MEMORY = 128 +BOLT_SIDECAR_MAX_MEMORY = 1024 + +def launch_bolt_sidecar( + plan, + image, + sidecar_config, + network_params, + node_selectors, +): + env_vars = { + "RUST_LOG": "bolt_sidecar=trace", + } + + node_keystore_path = validator_keystore_generator.NODE_KEYSTORES_OUTPUT_DIRPATH_FORMAT_STR.format(sidecar_config["participant_index"]) + full_keystore_path = "{0}{1}/keys".format(BOLT_SIDECAR_KEYS_DIRMOUNT_PATH_ON_SERVICE, node_keystore_path) + full_keystore_secrets_path = "{0}{1}/secrets".format(BOLT_SIDECAR_KEYS_DIRMOUNT_PATH_ON_SERVICE, node_keystore_path) + + api = plan.add_service( + name=sidecar_config["service_name"], + config=ServiceConfig( + image=image, + cmd=[ + "--port", + str(BOLT_SIDECAR_COMMITMENTS_API_PORT), + "--execution-api-url", + sidecar_config["execution_api_url"], + "--beacon-api-url", + sidecar_config["beacon_api_url"], + "--engine-api-url", + sidecar_config["engine_api_url"], + "--constraints-api-url", + sidecar_config["constraints_api_url"], + "--constraints-proxy-port", + str(input_parser.BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT), + "--engine-jwt-hex", + sidecar_config["jwt_hex"], + "--fee-recipient", + "0x0000000000000000000000000000000000000000", + "--builder-private-key", # Random private key for testing + "0x20c815cb2d37561479c7b6cae9737356b144760d00f1387bff17df4a3712c262", + "--commitment-private-key", # Random private key for testing + "0x18d1c5302e734fd6fbfaa51828d42c4c6d3cbe020c42bab7dd15a2799cf00b82", + "--commitment-deadline", + str(100), + "--chain", + network_params.network, + "--slot-time", + str(network_params.seconds_per_slot), + # "--keystore-password", + # validator_keystore_generator.PRYSM_PASSWORD, + "--keystore-secrets-path", + full_keystore_secrets_path, + "--keystore-path", + full_keystore_path, + "--metrics-port", + str(BOLT_SIDECAR_METRICS_PORT), + ], + # + mev_params.mev_relay_api_extra_args, + ports={ + "api": PortSpec( + number=BOLT_SIDECAR_COMMITMENTS_API_PORT, transport_protocol="TCP" + ), + "bolt-sidecar": PortSpec( + number=input_parser.BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT, transport_protocol="TCP" + ), + "metrics": PortSpec( + number=BOLT_SIDECAR_METRICS_PORT, transport_protocol="TCP" + ), + }, + files={ + BOLT_SIDECAR_KEYS_DIRMOUNT_PATH_ON_SERVICE: sidecar_config["validator_keystore_files_artifact_uuid"], + }, + env_vars=env_vars, + min_cpu=BOLT_SIDECAR_MIN_CPU, + max_cpu=BOLT_SIDECAR_MAX_CPU, + min_memory=BOLT_SIDECAR_MIN_MEMORY, + max_memory=BOLT_SIDECAR_MAX_MEMORY, + node_selectors=node_selectors, + ), + ) + + return struct( + ip_addr=api.ip_address, + metrics_port_num=BOLT_SIDECAR_METRICS_PORT, + ) diff --git a/src/mev/mev_boost/mev_boost_launcher.star b/src/mev/mev_boost/mev_boost_launcher.star index f92cd1525..e414f74d2 100644 --- a/src/mev/mev_boost/mev_boost_launcher.star +++ b/src/mev/mev_boost/mev_boost_launcher.star @@ -30,6 +30,8 @@ def launch( network_id, mev_boost_image, mev_boost_args, + network_params, + final_genesis_timestamp, global_node_selectors, ): config = get_config( @@ -37,6 +39,8 @@ def launch( network_id, mev_boost_image, mev_boost_args, + network_params, + final_genesis_timestamp, global_node_selectors, ) @@ -52,6 +56,8 @@ def get_config( network_id, mev_boost_image, mev_boost_args, + network_params, + final_genesis_timestamp, node_selectors, ): command = mev_boost_args @@ -61,10 +67,7 @@ def get_config( ports=USED_PORTS, cmd=command, env_vars={ - # TODO(maybe) remove the hardcoding - # This is set to match this file https://github.com/kurtosis-tech/ethereum-package/blob/main/static_files/genesis-generation-config/cl/config.yaml.tmpl#L11 - # latest-notes - # does this need genesis time to be set as well + "GENESIS_TIMESTAMP": str(final_genesis_timestamp), "GENESIS_FORK_VERSION": "0x10000038", "BOOST_LISTEN_ADDR": "0.0.0.0:{0}".format( input_parser.FLASHBOTS_MEV_BOOST_PORT @@ -72,6 +75,7 @@ def get_config( # maybe this is breaking; this isn't verifyign the bid and not sending it to the validator "SKIP_RELAY_SIGNATURE_CHECK": "1", "RELAYS": mev_boost_launcher.relay_end_points[0], + "SLOT_SEC": str(network_params.seconds_per_slot) }, min_cpu=MIN_CPU, max_cpu=MAX_CPU, diff --git a/src/mev/mev_relay/helix_launcher.star b/src/mev/mev_relay/helix_launcher.star new file mode 100644 index 000000000..38d557caa --- /dev/null +++ b/src/mev/mev_relay/helix_launcher.star @@ -0,0 +1,218 @@ +redis_module = import_module("github.com/kurtosis-tech/redis-package/main.star") +postgres_module = import_module("github.com/kurtosis-tech/postgres-package/main.star") +constants = import_module("../../package_io/constants.star") +shared_utils = import_module("../../shared_utils/shared_utils.star") +static_files = import_module("../../static_files/static_files.star") + +# Misc constants +SERVICE_NAME="helix-relay" +HELIX_CONFIG_FILENAME="helix-config.yaml" +HELIX_NETWORK_CONFIG_FILENAME = "network-config.yaml" +HELIX_CONFIG_MOUNT_DIRPATH_ON_SERVICE="/config" + +# The secret key and public key for the relay, exposed as environment variables +DUMMY_SECRET_KEY = "0x607a11b45a7219cc61a3d9c5fd08c7eebd602a6a19a977f8d3771d5711a550f2" +DUMMY_PUB_KEY = "0xa55c1285d84ba83a5ad26420cd5ad3091e49c55a813eee651cd467db38a8c8e63192f47955e9376f6b42f6d190571cb5" + +# This is currenlty hardcoded in the Helix relay +HELIX_RELAY_ENDPOINT_PORT = 4040 +HELIX_RELAY_WEBSITE_PORT = 8080 + +# The min/max CPU/memory that mev-relay can use +RELAY_MIN_CPU = 2000 # 2 cores +RELAY_MAX_CPU = 4000 # 2 cores +RELAY_MIN_MEMORY = 128 +RELAY_MAX_MEMORY = 1024 + +# The min/max CPU/memory that postgres can use +POSTGRES_MIN_CPU = 10 +POSTGRES_MAX_CPU = 1000 +POSTGRES_MIN_MEMORY = 32 +POSTGRES_MAX_MEMORY = 1024 + +# The min/max CPU/memory that redis can use +REDIS_MIN_CPU = 10 +REDIS_MAX_CPU = 1000 +REDIS_MIN_MEMORY = 16 +REDIS_MAX_MEMORY = 1024 + +def launch_helix_relay( + plan, + mev_params, + network_params, + beacon_uris, + validator_root, + builder_uri, + seconds_per_slot, + persistent, + genesis_timestamp, + global_node_selectors, +): + plan.print(network_params) + + node_selectors = global_node_selectors + + # Read the template files with Helix configuration and network configuration + helix_config_template = read_file( + static_files.HELIX_CONFIG_TEMPLATE_FILEPATH + ) + helix_network_config_template = read_file( + static_files.HELIX_NETWORK_CONFIG_TEMPLATE_FILEPATH + ) + + # Start both the Redis and Postgres services + redis = redis_module.run( + plan, + service_name="mev-relay-redis", + min_cpu=REDIS_MIN_CPU, + max_cpu=REDIS_MAX_CPU, + min_memory=REDIS_MIN_MEMORY, + max_memory=REDIS_MAX_MEMORY, + node_selectors=node_selectors, + ) + postgres = postgres_module.run( + plan, + # Postgres image with TimescaleDB extension: + # References: + # - https://docs.timescale.com/ + # - https://github.com/gattaca-com/helix/blob/9e078f1ec4710869b2e41e1ca20d31e1c7cfde52/crates/database/src/postgres/postgres_db_service_tests.rs#L41-L44 + image="timescale/timescaledb-ha:pg16", + password="postgres", + user="postgres", + database="helixdb", + service_name="helix-postgres", + persistent=persistent, + launch_adminer=True, + min_cpu=POSTGRES_MIN_CPU, + max_cpu=POSTGRES_MAX_CPU, + min_memory=POSTGRES_MIN_MEMORY, + max_memory=POSTGRES_MAX_MEMORY, + node_selectors=node_selectors, + ) + + image = mev_params.helix_relay_image + + # Convert beacon_uris from a comma-separated string to a list of URIs + beacon_uris = [uri.strip() for uri in beacon_uris.split(",")] + + network_config_dir_path_on_service = "{0}/{1}".format( + HELIX_CONFIG_MOUNT_DIRPATH_ON_SERVICE, HELIX_NETWORK_CONFIG_FILENAME + ) + + # See https://github.com/kurtosis-tech/postgres-package#use-this-package-in-your-package + # and https://docs.kurtosis.com/api-reference/starlark-reference/service/ + helix_config_template_data = new_config_template_data( + postgres.service.hostname, + postgres.port.number, + postgres.database, + postgres.user, + postgres.password, + redis.url, + builder_uri, + beacon_uris, + network_config_dir_path_on_service, + validator_root, + genesis_timestamp, + mev_params.helix_relay_config_extension, + ) + + helix_config_template_and_data = shared_utils.new_template_and_data( + helix_config_template, helix_config_template_data + ) + + helix_network_config_template_and_data = shared_utils.new_template_and_data( + helix_network_config_template, network_params + ) + template_and_data_by_rel_dest_filepath = {} + template_and_data_by_rel_dest_filepath[HELIX_CONFIG_FILENAME] = helix_config_template_and_data + template_and_data_by_rel_dest_filepath[HELIX_NETWORK_CONFIG_FILENAME] = helix_network_config_template_and_data + + config_files_artifact_name = plan.render_templates( + template_and_data_by_rel_dest_filepath + ) + + env_vars = { + "RELAY_KEY": DUMMY_SECRET_KEY, + "RUST_LOG": "helix_cmd=trace,helix_api=trace,helix_common=trace,helix_datastore=trace,helix_housekeeper=trace,helix_database=trace,helix_beacon_client=trace", + } + + helix = plan.add_service( + name=SERVICE_NAME, + config=ServiceConfig( + image=image, + files={ + HELIX_CONFIG_MOUNT_DIRPATH_ON_SERVICE: config_files_artifact_name + }, + cmd=[ + "--config", + shared_utils.path_join( + HELIX_CONFIG_MOUNT_DIRPATH_ON_SERVICE, + HELIX_CONFIG_FILENAME, + ) + ], + ports={ + "api": PortSpec( + number=HELIX_RELAY_ENDPOINT_PORT, transport_protocol="TCP" + ) + # "website": PortSpec( + # number=HELIX_RELAY_WEBSITE_PORT, transport_protocol="TCP" + # ) + }, + env_vars=env_vars, + min_cpu=RELAY_MIN_CPU, + max_cpu=RELAY_MAX_CPU, + min_memory=RELAY_MIN_MEMORY, + max_memory=RELAY_MAX_MEMORY, + node_selectors=node_selectors, + ), + ) + + plan.print(json.indent(json.encode(helix_config_template_data))) + + return "http://{0}@{1}:{2}".format( + DUMMY_PUB_KEY, helix.ip_address, HELIX_RELAY_ENDPOINT_PORT + ) + +def new_config_template_data( + postgres_hostname, + postgres_port, + postgres_db_name, + postgres_user, + postgres_password, + redis_url, + blocksim_url, + beacon_uris, + network_config_dir_path, + genesis_validator_root, + genesis_time, + config_extension, +): + config_hashmap = { + "postgres": { + "hostname": postgres_hostname, + "port": postgres_port, + "db_name": postgres_db_name, + "user": postgres_user, + "password": postgres_password, + }, + "redis": { + "url": redis_url, + }, + "simulator": { + "url": blocksim_url, + }, + "beacon_clients": [ + {"url": uri} for uri in beacon_uris + ], + "network_config": { + "dir_path": network_config_dir_path, + "genesis_validator_root": genesis_validator_root, + "genesis_time": genesis_time, + }, + } + + if config_extension != None: + for key, value in config_extension.items(): + config_hashmap[key] = value + + return config_hashmap diff --git a/src/package_io/input_parser.star b/src/package_io/input_parser.star index 703e6a2fd..26bb52c0c 100644 --- a/src/package_io/input_parser.star +++ b/src/package_io/input_parser.star @@ -42,7 +42,10 @@ HIGH_DENEB_VALUE_FORK_VERKLE = 2000000000 # MEV Params FLASHBOTS_MEV_BOOST_PORT = 18550 +BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT = 18551 MEV_BOOST_SERVICE_NAME_PREFIX = "mev-boost" +BOLT_BOOST_SERVICE_NAME_PREFIX = "bolt-boost" +BOLT_SIDECAR_SERVICE_NAME_PREFIX = "bolt-sidecar" # Minimum number of validators required for a network to be valid is 64 MIN_VALIDATORS = 64 @@ -140,12 +143,20 @@ def input_parser(plan, input_args): result = enrich_disable_peer_scoring(result) if result.get("mev_type") in ("mock", "full"): - result = enrich_mev_extra_params( - result, - MEV_BOOST_SERVICE_NAME_PREFIX, - FLASHBOTS_MEV_BOOST_PORT, - result.get("mev_type"), - ) + if result.get("mev_params")["bolt_sidecar_image"] == None: + result = enrich_mev_extra_params( + result, + MEV_BOOST_SERVICE_NAME_PREFIX, + FLASHBOTS_MEV_BOOST_PORT, + result.get("mev_type"), + ) + else: + result = enrich_mev_extra_params( + result, + BOLT_SIDECAR_SERVICE_NAME_PREFIX, + BOLT_SIDECAR_CONSTRAINTS_PROXY_PORT, + result.get("mev_type"), + ) return struct( participants=[ @@ -236,10 +247,13 @@ def input_parser(plan, input_args): preset=result["network_params"]["preset"], ), mev_params=struct( + bolt_boost_image=result["mev_params"]["bolt_boost_image"], + helix_relay_image=result["mev_params"]["helix_relay_image"], mev_relay_image=result["mev_params"]["mev_relay_image"], mev_builder_image=result["mev_params"]["mev_builder_image"], mev_builder_cl_image=result["mev_params"]["mev_builder_cl_image"], mev_boost_image=result["mev_params"]["mev_boost_image"], + bolt_sidecar_image=result["mev_params"]["bolt_sidecar_image"], mev_boost_args=result["mev_params"]["mev_boost_args"], mev_relay_api_extra_args=result["mev_params"]["mev_relay_api_extra_args"], mev_relay_housekeeper_extra_args=result["mev_params"][ @@ -248,6 +262,7 @@ def input_parser(plan, input_args): mev_relay_website_extra_args=result["mev_params"][ "mev_relay_website_extra_args" ], + helix_relay_config_extension=result["mev_params"]["helix_relay_config_extension"], mev_builder_extra_args=result["mev_params"]["mev_builder_extra_args"], mev_flood_image=result["mev_params"]["mev_flood_image"], mev_flood_extra_args=result["mev_params"]["mev_flood_extra_args"], @@ -708,6 +723,7 @@ def default_participant(): def get_default_mev_params(): return { + "bolt_boost_image": None, "mev_relay_image": MEV_BOOST_RELAY_DEFAULT_IMAGE, "mev_builder_image": "flashbots/builder:latest", "mev_builder_cl_image": "sigp/lighthouse:latest", @@ -716,7 +732,8 @@ def get_default_mev_params(): "mev_relay_api_extra_args": [], "mev_relay_housekeeper_extra_args": [], "mev_relay_website_extra_args": [], - "mev_builder_extra_args": [], + "helix_relay_config_extension": None, + "mev_builder_extra_args": None, "mev_flood_image": "flashbots/mev-flood", "mev_flood_extra_args": [], "mev_flood_seconds_per_bundle": 15, @@ -793,8 +810,9 @@ def enrich_mev_extra_params(parsed_arguments_dict, mev_prefix, mev_port, mev_typ index_str = shared_utils.zfill_custom( index + 1, len(str(len(parsed_arguments_dict["participants"]))) ) + mev_url = "http://{0}-{1}-{2}-{3}:{4}".format( - MEV_BOOST_SERVICE_NAME_PREFIX, + mev_prefix, index_str, participant["cl_type"], participant["el_type"], @@ -849,7 +867,8 @@ def enrich_mev_extra_params(parsed_arguments_dict, mev_prefix, mev_port, mev_typ # TODO(maybe) make parts of this more passable like the mev-relay-endpoint & forks "el_extra_params": [ "--builder", - "--builder.remote_relay_endpoint=http://mev-relay-api:9062", + # TODO: (thedevbirb) this should indeed more passable. Now hardcoded for Helix relay + "--builder.remote_relay_endpoint=http://helix-relay:4040", "--builder.beacon_endpoints=http://cl-{0}-lighthouse-geth-builder:4000".format( index_str ), diff --git a/src/prelaunch_data_generator/validator_keystores/validator_keystore_generator.star b/src/prelaunch_data_generator/validator_keystores/validator_keystore_generator.star index 3f2a22ec9..f8e4c7aa2 100644 --- a/src/prelaunch_data_generator/validator_keystores/validator_keystore_generator.star +++ b/src/prelaunch_data_generator/validator_keystores/validator_keystore_generator.star @@ -11,6 +11,7 @@ PRYSM_PASSWORD_FILEPATH_ON_GENERATOR = "/tmp/prysm-password.txt" KEYSTORES_GENERATION_TOOL_NAME = "/app/eth2-val-tools" ETH_VAL_TOOLS_IMAGE = "protolambda/eth2-val-tools:latest" +# ETH_VAL_TOOLS_IMAGE = "ghcr.io/chainbound/eth2-val-tools:0.1.0" SUCCESSFUL_EXEC_CMD_EXIT_CODE = 0 @@ -94,6 +95,7 @@ def generate_validator_keystores(plan, mnemonic, participants): running_total_validator_count += participant.validator_count stop_index = start_index + participant.validator_count + # NOTE: removed --insecure flag. generate_keystores_cmd = '{0} keystores --insecure --prysm-pass {1} --out-loc {2} --source-mnemonic "{3}" --source-min {4} --source-max {5}'.format( KEYSTORES_GENERATION_TOOL_NAME, PRYSM_PASSWORD, @@ -217,7 +219,8 @@ def generate_valdiator_keystores_in_parallel(plan, mnemonic, participants): ) finished_files_to_verify.append(generation_finished_filepath) - generate_keystores_cmd = 'nohup {0} keystores --insecure --prysm-pass {1} --out-loc {2} --source-mnemonic "{3}" --source-min {4} --source-max {5} && touch {6}'.format( + # NOTE: removed --insecure flag + generate_keystores_cmd = 'nohup {0} keystores --prysm-pass {1} --out-loc {2} --source-mnemonic "{3}" --source-min {4} --source-max {5} && touch {6}'.format( KEYSTORES_GENERATION_TOOL_NAME, PRYSM_PASSWORD, output_dirpath, diff --git a/src/prometheus/prometheus_launcher.star b/src/prometheus/prometheus_launcher.star index f2650bf38..06b076f09 100644 --- a/src/prometheus/prometheus_launcher.star +++ b/src/prometheus/prometheus_launcher.star @@ -28,6 +28,7 @@ def launch_prometheus( ethereum_metrics_exporter_contexts, xatu_sentry_contexts, global_node_selectors, + bolt_sidecar_context, ): metrics_jobs = get_metrics_jobs( el_contexts, @@ -36,6 +37,7 @@ def launch_prometheus( additional_metrics_jobs, ethereum_metrics_exporter_contexts, xatu_sentry_contexts, + bolt_sidecar_context, ) prometheus_url = prometheus.run( plan, @@ -58,6 +60,7 @@ def get_metrics_jobs( additional_metrics_jobs, ethereum_metrics_exporter_contexts, xatu_sentry_contexts, + bolt_sidecar_context, ): metrics_jobs = [] # Adding execution clients metrics jobs @@ -187,6 +190,20 @@ def get_metrics_jobs( continue metrics_jobs.append(job) + # Adding bolt-sidecar metrics job + if bolt_sidecar_context != None: + metrics_jobs.append( + new_metrics_job( + job_name="bolt-sidecar", + endpoint="{}:{}".format( + bolt_sidecar_context.ip_addr, + bolt_sidecar_context.metrics_port_num, + ), + metrics_path="/metrics", + labels={}, + ) + ) + return metrics_jobs diff --git a/src/static_files/static_files.star b/src/static_files/static_files.star index 98d2c838e..c5d81d4be 100644 --- a/src/static_files/static_files.star +++ b/src/static_files/static_files.star @@ -31,6 +31,25 @@ ASSERTOOR_TESTS_CONFIG_DIRPATH = ( STATIC_FILES_DIRPATH + ASSERTOOR_CONFIG_DIRPATH + "/tests" ) +# helix config +HELIX_CONFIG_DIRPATH = "/helix-relay-config" +HELIX_CONFIG_FULL_DIRPATH = ( + STATIC_FILES_DIRPATH + HELIX_CONFIG_DIRPATH +) +HELIX_CONFIG_TEMPLATE_FILEPATH = ( + STATIC_FILES_DIRPATH + HELIX_CONFIG_DIRPATH + "/config.yaml.tmpl" +) +HELIX_NETWORK_CONFIG_TEMPLATE_FILEPATH=HELIX_CONFIG_FULL_DIRPATH + "/network-config.yaml.tmpl" + +# bolt-boost config +BOLT_BOOST_CONFIG_DIRPATH = "/bolt-boost-config" +BOLT_BOOST_CONFIG_FULL_DIRPATH = ( + STATIC_FILES_DIRPATH + BOLT_BOOST_CONFIG_DIRPATH +) +BOLT_BOOST_CONFIG_TEMPLATE_FILEPATH = ( + STATIC_FILES_DIRPATH + BOLT_BOOST_CONFIG_DIRPATH + "/cb-bolt-boost-config.toml.tmpl" +) + # xatu-sentry config XATU_SENTRY_CONFIG_DIRPATH = "/xatu-sentry-config" XATU_SENTRY_CONFIG_TEMPLATE_FILEPATH = ( diff --git a/src/transaction_spammer/transaction_spammer.star b/src/transaction_spammer/transaction_spammer.star index 275492a6d..560ea5cb0 100644 --- a/src/transaction_spammer/transaction_spammer.star +++ b/src/transaction_spammer/transaction_spammer.star @@ -15,6 +15,7 @@ def launch_transaction_spammer( tx_spammer_params, electra_fork_epoch, global_node_selectors, + network_params ): config = get_config( prefunded_addresses, @@ -22,6 +23,7 @@ def launch_transaction_spammer( tx_spammer_params.tx_spammer_extra_args, electra_fork_epoch, global_node_selectors, + network_params ) plan.add_service(SERVICE_NAME, config) @@ -32,17 +34,19 @@ def get_config( tx_spammer_extra_args, electra_fork_epoch, node_selectors, + network_params, ): # Temp hack to use the old tx-fuzz image until we can get the new one working if electra_fork_epoch != None: - tx_spammer_image = "ethpandaops/tx-fuzz:kaustinen-281adbc" + tx_spammer_image = "ghcr.io/chainbound/tx-fuzz:0.1.0" else: - tx_spammer_image = "ethpandaops/tx-fuzz:master" + tx_spammer_image = "ghcr.io/chainbound/tx-fuzz:0.1.0" cmd = [ "spam", "--rpc={}".format(el_uri), "--sk={0}".format(prefunded_addresses[3].private_key), + "--slot-time={}".format(network_params.seconds_per_slot), ] if len(tx_spammer_extra_args) > 0: diff --git a/static_files/bolt-boost-config/cb-bolt-boost-config.toml.tmpl b/static_files/bolt-boost-config/cb-bolt-boost-config.toml.tmpl new file mode 100644 index 000000000..f15c028da --- /dev/null +++ b/static_files/bolt-boost-config/cb-bolt-boost-config.toml.tmpl @@ -0,0 +1,143 @@ +# The main configuration file for the Commit-Boost sidecar. +# Some fields are optional and can be omitted, in which case the default value, if present, will be used. + +# Chain spec id. Supported values: Mainnet, Holesky, Helder, Custom +{{- if eq .chain_config.name "Kurtosis" }} +[chain] +genesis_time_secs = {{ .chain_config.genesis_timestamp }} +slot_time_secs = {{ .chain_config.seconds_per_slot }} +genesis_fork_version = "{{ .chain_config.genesis_fork_version }}" +{{- else }} +chain = "{{ .chain_config.name }}" +{{- end }} + +# Configuration for the PBS module +[pbs] +# Docker image to use for the PBS module. +# BOLT: We use the bolt-boost PBS module here. +docker_image = "{{ .image }}" + +# Whether to enable the PBS module to request signatures from the Signer module (not used in the default PBS image) +# OPTIONAL, DEFAULT: false +with_signer = {{ or .with_signer false }} + +# Port to receive BuilderAPI calls from beacon node +port = {{ .port }} + +# Whether to forward `status` calls to relays or skip and return 200 +# OPTIONAL, DEFAULT: true +relay_check = {{ or .relay_check true }} + +# Timeout in milliseconds for the `get_header` call to relays. Note that the CL has also a timeout (e.g. 1 second) so +# this should be lower than that, leaving some margin for overhead +# OPTIONAL, DEFAULT: 950 +timeout_get_header_ms = {{ or .timeout_get_header_ms "950" }} + +# Timeout in milliseconds for the `submit_blinded_block` call to relays. +# OPTIONAL, DEFAULT: 4000 +timeout_get_payload_ms = {{ or .timeout_get_payload_ms "4000" }} + +# Timeout in milliseconds for the `register_validator` call to relays. +# OPTIONAL, DEFAULT: 3000 +timeout_register_validator_ms = {{ or .timeout_register_validator_ms "3000" }} + +# Whether to skip signature verification of headers against the relay pubkey +# OPTIONAL, DEFAULT: false +skip_sigverify = {{ or .skip_sigverify false }} + +# Minimum bid in ETH that will be accepted from `get_header` +# OPTIONAL, DEFAULT: 0.0 +min_bid_eth = {{ or .min_bid_eth "0.0" }} + +# List of URLs of relay monitors to send registrations to +# OPTIONAL +relay_monitors = [] + +# How late in milliseconds in the slot is "late". This impacts the `get_header` requests, by shortening timeouts for `get_header` calls to +# relays and make sure a header is returned within this deadline. If the request from the CL comes later in the slot, then fetching headers is skipped +# to force local building and minimizing the risk of missed slots. +# OPTIONAL, DEFAULT: 2000 +late_in_slot_time_ms = {{ or .late_in_slot_time_ms "2000" }} + +# The PBS module needs one or more [[relays]] as defined below. +{{ range $relay_config := .relays_config }} +[[relays]] +# Relay ID to use in telemetry +# OPTIONAL, DEFAULT: URL hostname +id = "{{ $relay_config.id }}" + +# Relay URL in the format scheme://pubkey@host +url = "{{ $relay_config.url }}" + +# Headers to send with each request for this relay +# OPTIONAL +# headers = {{ or $relay_config.headers "{}" }} + +# Whether to enable timing games, as tuned by `target_first_request_ms` and `frequency_get_header_ms`. +# OPTIONAL, DEFAULT: false +enable_timing_games = {{ or $relay_config.enable_timing_games false }} + +# Target time in slot when to send the first header request +# OPTIONAL +target_first_request_ms = {{ or $relay_config.target_first_request_ms "200" }} + +# Frequency in ms to send get_header requests +# OPTIONAL +frequency_get_header_ms = {{ or .frequency_get_header_ms "300" }} + +{{ end }} + +# Configuration for the Signer Module, only required if any `commit` module is present, or if `pbs.with_signer = true` +# OPTIONAL +[signer] +# Docker image to use for the Signer module. +# OPTIONAL, DEFAULT: ghcr.io/commit-boost/signer:latest +docker_image = "{{ or .signer_image "ghcr.io/commit-boost/signer:latest" }}" + +# Configuration for how the Signer module should load validator keys. Currently two types of loaders are supported: +# - File: load keys from a plain text file (unsafe, use only for testing purposes) +# - ValidatorsDir: load keys from a `keys` and `secrets` folder (ERC-2335 style keystores as used in Lighthouse) +[signer.loader] +# File: path to the keys file +key_path = "./keys.json" +# ValidatorsDir: path to the keys directory +# keys_path = "" +# ValidatorsDir: path to the secrets directory +# secrets_path = "" + +# Commit-Boost can optionally run "modules" which extend the capabilities of the sidecar. +# Currently, two types of modules are supported: +# - "commit": modules which request commitment signatures from the validator keys +# - "events": modules which callback to BuilderAPI events as triggered from the PBS modules, used e.g. for monitoring +# If any "commit" module is present, then the [signer] section should also be configured +# OPTIONAL +[[modules]] +# Unique ID of the module +id = "BOLT" +# Type of the module. Supported values: commit, events +type = "commit" +# Docker image of the module +docker_image = "{{ .image }}" + +[modules.env] +BOLT_SIDECAR_CHAIN = "{{ or .chain "Kurtosis" }}" + +# The address of the PBS module +BOLT_SIDECAR_CONSTRAINTS_API = "{{ .bolt_sidecar_config.constraints_api_url }}" +BOLT_SIDECAR_BEACON_API = "{{ .bolt_sidecar_config.beacon_api_url }}" +BOLT_SIDECAR_EXECUTION_API = "{{ .bolt_sidecar_config.execution_api_url }}" + +# The execution layer engine API endpoint +BOLT_SIDECAR_ENGINE_API = "{{ .bolt_sidecar_config.engine_api_url }}" + +# The engine JWT +BOLT_SIDECAR_JWT_HEX = "{{ .bolt_sidecar_config.jwt_hex }}" + +# The port on which the sidecar builder-API will listen on. This is what your beacon node should connect to. +BOLT_SIDECAR_BUILDER_PROXY_PORT = {{ .bolt_sidecar_config.builder_proxy_port }} + +# The fee recipient +BOLT_SIDECAR_FEE_RECIPIENT = "{{ or .bolt_sidecar_config.fee_recipient "0x0000000000000000000000000000000000000000" }}" + +# Metrics port +BOLT_SIDECAR_METRICS_PORT = {{ or .bolt_sidecar_config.metrics_port "10000" }} diff --git a/static_files/grafana-config/dashboards/bolt-sidecar.json b/static_files/grafana-config/dashboards/bolt-sidecar.json new file mode 100644 index 000000000..7bfc18071 --- /dev/null +++ b/static_files/grafana-config/dashboards/bolt-sidecar.json @@ -0,0 +1,1142 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": 1, + "links": [], + "liveNow": false, + "panels": [ + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 11, + "panels": [], + "title": "Sidecar Metrics", + "type": "row" + }, + { + "datasource": {}, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 5, + "x": 0, + "y": 1 + }, + "id": 3, + "options": { + "colorMode": "value", + "graphMode": "none", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "11.1.0", + "targets": [ + { + "datasource": {}, + "editorMode": "builder", + "expr": "bolt_sidecar_latest_head", + "legendFormat": "__auto", + "range": true, + "refId": "A" + } + ], + "title": "Latest slot", + "type": "stat" + }, + { + "datasource": {}, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "green", + "value": 80 + } + ] + }, + "unit": "h" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 7, + "x": 5, + "y": 1 + }, + "id": 16, + "options": { + "minVizHeight": 75, + "minVizWidth": 75, + "orientation": "auto", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": true + }, + "showThresholdLabels": false, + "showThresholdMarkers": true, + "sizing": "auto", + "text": {} + }, + "pluginVersion": "11.1.0", + "targets": [ + { + "datasource": {}, + "editorMode": "code", + "exemplar": false, + "expr": "(time() - container_start_time_seconds{name=~\"bolt-(sidecar|mev-boost|boost).*\"})/3600", + "format": "time_series", + "instant": true, + "interval": "", + "legendFormat": "{{name}}", + "range": false, + "refId": "A" + } + ], + "title": "Uptime", + "type": "gauge" + }, + { + "datasource": {}, + "description": "Tracks the reasons for rejecting an inclusion request.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + } + }, + "mappings": [] + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 1 + }, + "id": 10, + "options": { + "displayLabels": ["name"], + "legend": { + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "pieType": "donut", + "reduceOptions": { + "calcs": ["lastNotNull"], + "fields": "", + "values": false + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": {}, + "editorMode": "builder", + "exemplar": false, + "expr": "bolt_sidecar_validation_errors", + "legendFormat": "{{type}}", + "range": true, + "refId": "A" + } + ], + "title": "Invalid transaction reasons", + "type": "piechart" + }, + { + "datasource": {}, + "description": "The number of preconfirmed transactions categorized by transaction type.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic", + "seriesBy": "last" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 9 + }, + "id": 9, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": {}, + "editorMode": "builder", + "expr": "bolt_sidecar_transactions_preconfirmed", + "legendFormat": "{{type}}", + "range": true, + "refId": "A" + } + ], + "title": "Preconfirmed transactions", + "type": "timeseries" + }, + { + "datasource": {}, + "description": "The revenue from all of the preconfirmed transactions. This is \"gross\" revenue because it just adds all of the priority fees in those transactions, which may be partly split with the builder in case of a PBS block.", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "semi-dark-blue", + "mode": "fixed" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "ETH", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 9 + }, + "id": 17, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": {}, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "bolt_sidecar_gross_tip_revenue / 1000000000000000000", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "legendFormat": "{{job}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Gross tip revenue", + "type": "timeseries" + }, + { + "datasource": {}, + "description": "The number of inclusion requests that were accepted (i.e. committed to).", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "green", + "mode": "fixed" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "stepAfter", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 17 + }, + "id": 5, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": {}, + "editorMode": "builder", + "expr": "bolt_sidecar_inclusion_commitments_accepted", + "legendFormat": "requests", + "range": true, + "refId": "A" + } + ], + "title": "Inclusion requests accepted", + "type": "timeseries" + }, + { + "datasource": {}, + "description": "The number of inclusion requests received.", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "light-orange", + "mode": "fixed" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "stepAfter", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 17 + }, + "id": 4, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": {}, + "editorMode": "builder", + "expr": "bolt_sidecar_inclusion_commitments_received", + "legendFormat": "requests", + "range": true, + "refId": "A" + } + ], + "title": "Inclusion requests", + "type": "timeseries" + }, + { + "datasource": {}, + "description": "The number of PBS blocks proposed (as opposed to local fallback blocks).", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "dark-blue", + "mode": "fixed" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "stepAfter", + "lineStyle": { + "fill": "solid" + }, + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 25 + }, + "id": 2, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": {}, + "editorMode": "builder", + "expr": "bolt_sidecar_remote_blocks_proposed", + "legendFormat": "blocks", + "range": true, + "refId": "A" + } + ], + "title": "PBS blocks proposed", + "type": "timeseries" + }, + { + "datasource": {}, + "description": "The number of fallback blocks proposed. Fallback blocks are only proposed in case of a PBS failure. If this number is high, something might be wrong.", + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "red", + "mode": "shades" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 9, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "stepAfter", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 25 + }, + "id": 8, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "9.5.12", + "targets": [ + { + "datasource": {}, + "disableTextWrap": false, + "editorMode": "builder", + "exemplar": false, + "expr": "bolt_sidecar_local_blocks_proposed", + "fullMetaSearch": false, + "includeNullMetadata": true, + "instant": false, + "interval": "", + "legendFormat": "blocks", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "Fallback blocks proposed", + "type": "timeseries" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 33 + }, + "id": 12, + "panels": [], + "title": "System Metrics", + "type": "row" + }, + { + "datasource": {}, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "percent" + }, + "overrides": [] + }, + "gridPos": { + "h": 7, + "w": 24, + "x": 0, + "y": 34 + }, + "id": 13, + "options": { + "legend": { + "calcs": ["mean", "max"], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "7.4.5", + "targets": [ + { + "datasource": {}, + "editorMode": "code", + "expr": "sum(rate(container_cpu_usage_seconds_total{name=~\"bolt-sidecar.*|bolt-mev-boost.*|bolt-boost.*\"}[5m])) by (name) *100", + "hide": false, + "interval": "", + "legendFormat": "{{name}}", + "range": true, + "refId": "A" + } + ], + "title": "CPU usage", + "type": "timeseries" + }, + { + "datasource": {}, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "bytes" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 24, + "x": 0, + "y": 41 + }, + "id": 14, + "options": { + "legend": { + "calcs": ["mean", "max"], + "displayMode": "table", + "placement": "right", + "showLegend": true + }, + "tooltip": { + "mode": "multi", + "sort": "none" + } + }, + "pluginVersion": "7.4.5", + "targets": [ + { + "datasource": {}, + "editorMode": "code", + "expr": "sum(container_memory_rss{name=~\"bolt-sidecar.*|bolt-boost.*|bolt-mev-boost.*\"}) by (name)", + "hide": false, + "interval": "", + "legendFormat": "{{name}}", + "range": true, + "refId": "A" + } + ], + "title": "Memory usage", + "type": "timeseries" + }, + { + "datasource": {}, + "description": "Total HTTP requests grouped by HTTP method & response code.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisBorderShow": false, + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "never", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 49 + }, + "id": 6, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": {}, + "editorMode": "builder", + "expr": "bolt_sidecar_http_requests_total", + "legendFormat": "{{method}} / {{status}}", + "range": true, + "refId": "A" + } + ], + "title": "Total HTTP requests", + "type": "timeseries" + }, + { + "datasource": {}, + "description": "Request processing durations per HTTP method.", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "fillOpacity": 30, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineWidth": 0, + "stacking": { + "group": "A", + "mode": "none" + } + }, + "decimals": 0, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 49 + }, + "id": 7, + "options": { + "bucketOffset": 0, + "combine": false, + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + } + }, + "targets": [ + { + "datasource": {}, + "disableTextWrap": false, + "editorMode": "builder", + "expr": "sum by(method) (bolt_sidecar_http_requests_duration_seconds)", + "fullMetaSearch": false, + "includeNullMetadata": false, + "legendFormat": "{{method}}", + "range": true, + "refId": "A", + "useBackend": false + } + ], + "title": "HTTP request durations", + "type": "histogram" + } + ], + "refresh": "", + "schemaVersion": 39, + "tags": [], + "templating": { + "list": [] + }, + "time": { + "from": "now-15m", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "bolt sidecar", + "uid": "bolt-sidecar", + "version": 24, + "weekStart": "" +} diff --git a/static_files/helix-relay-config/config.yaml.tmpl b/static_files/helix-relay-config/config.yaml.tmpl new file mode 100644 index 000000000..7151af766 --- /dev/null +++ b/static_files/helix-relay-config/config.yaml.tmpl @@ -0,0 +1,51 @@ +postgres: + hostname: {{ .postgres.hostname }} + port: {{ .postgres.port }} + db_name: {{ .postgres.db_name }} + user: {{ .postgres.user }} + password: {{ .postgres.password }} + region: 0 + region_name: "bolt" + +redis: + url: {{ .redis.url }} + +simulator: + url: {{ .simulator.url }} + +beacon_clients: + {{ range $bcConfig := .beacon_clients }} + - url: "{{ $bcConfig.url }}" + {{- end }} + +builders: + # Reference: https://github.com/chainbound/bolt-builder/blob/main/cmd/utils/flags.go#L691-L691 + - pub_key: "aa1488eae4b06a1fff840a2b6db167afc520758dc2c8af0dfb57037954df3431b747e2f900fe8805f05d635e9a29717b" + builder_info: + collateral: "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" + is_optimistic: false + builder_id: "Bolt Builder" + +{{- if .constraints_api_config }} +constraints_api_config: + check_constraints_signature: {{ .constraints_api_config.check_constraints_signature }} + max_block_value_to_verify_wei: {{ .constraints_api_config.max_block_value_to_verify_wei }} +{{- end }} + +network_config: + !Custom # this is a custom enum type and requies a '!' + dir_path: {{ .network_config.dir_path }} + genesis_validator_root: {{ .network_config.genesis_validator_root }} + genesis_time: {{ .network_config.genesis_time }} + +# If empty, all routes are enabled +router_config: + enabled_routes: [] + +website: + enabled: true + port: 8080 + listen_address: "0.0.0.0" + network_name: "kurtosis" + relay_url: http://0xa55c1285d84ba83a5ad26420cd5ad3091e49c55a813eee651cd467db38a8c8e63192f47955e9376f6b42f6d190571cb5@helix-relay:4040 + relay_pubkey: 0xa55c1285d84ba83a5ad26420cd5ad3091e49c55a813eee651cd467db38a8c8e63192f47955e9376f6b42f6d190571cb5 diff --git a/static_files/helix-relay-config/network-config.yaml.tmpl b/static_files/helix-relay-config/network-config.yaml.tmpl new file mode 100644 index 000000000..2bcaee005 --- /dev/null +++ b/static_files/helix-relay-config/network-config.yaml.tmpl @@ -0,0 +1,35 @@ +# The preset for the network configuration. +# Needed for `NetworkConfig` inside Helix + +PRESET_BASE: {{ .preset }} +CONFIG_NAME: "mainnet" +TERMINAL_TOTAL_DIFFICULTY: 0 +TERMINAL_BLOCK_HASH: "0x0000000000000000000000000000000000000000000000000000000000000000" +TERMINAL_BLOCK_HASH_ACTIVATION_EPOCH: 18446744073709551615 +MIN_GENESIS_ACTIVE_VALIDATOR_COUNT: 2 +MIN_GENESIS_TIME: 1727185220 +GENESIS_FORK_VERSION: "0x10000038" +GENESIS_DELAY: 20 +ALTAIR_FORK_VERSION: "0x20000038" +ALTAIR_FORK_EPOCH: 0 +BELLATRIX_FORK_VERSION: "0x30000038" +BELLATRIX_FORK_EPOCH: 0 +CAPELLA_FORK_VERSION: "0x40000038" +CAPELLA_FORK_EPOCH: 0 +DENEB_FORK_VERSION: "0x50000038" +DENEB_FORK_EPOCH: 0 +SECONDS_PER_SLOT: {{ .seconds_per_slot }} +SECONDS_PER_ETH1_BLOCK: {{ .seconds_per_slot }} +MIN_VALIDATOR_WITHDRAWABILITY_DELAY: {{ .min_validator_withdrawability_delay }} +SHARD_COMMITTEE_PERIOD: {{ .shard_committee_period }} +ETH1_FOLLOW_DISTANCE: {{ .eth1_follow_distance }} +INACTIVITY_SCORE_BIAS: 4 +INACTIVITY_SCORE_RECOVERY_RATE: 16 +EJECTION_BALANCE: {{ .ejection_balance }} +MIN_PER_EPOCH_CHURN_LIMIT: 4 +MAX_PER_EPOCH_ACTIVATION_CHURN_LIMIT: {{ .max_churn }} +CHURN_LIMIT_QUOTIENT: 65536 +PROPOSER_SCORE_BOOST: 40 +DEPOSIT_CHAIN_ID: {{ .network_id }} +DEPOSIT_NETWORK_ID: {{ .network_id }} +DEPOSIT_CONTRACT_ADDRESS: {{ .deposit_contract_address }}