Commit 641fb1ba authored by Gyanendra Mishra's avatar Gyanendra Mishra Committed by GitHub

Merge pull request #11 from kurtosis-tech/gyani/without-fact

parity with eth2-merge-kurtosis-module
parents 4a561806 f226d24a
......@@ -31,7 +31,7 @@ jobs:
- run: kurtosis engine restart
- run: kurtosis startosis exec ${PWD}
- run: kurtosis exec ${PWD}
workflows:
......
......@@ -84,3 +84,6 @@ yarn-error.log
# Bazel
/bazel-*
# enclave dumps
dump/
......@@ -6,16 +6,23 @@ This is the Startosis version of the popular [eth2-merge-kurtosis-module](https:
### Parity Missing Tasks
- [x] main.star
- [x] launch forkmon, prometheus, grafana, testnet_verifier, transaction_spammer
- [x] do a wait for epoch finalization
- [ ] assert that finalization epoch > 0
- [x] wait for CL genesis to occur before adding forkmon - DESCOPED as I don't see the bug
- [x] Module IO (this is blocked on Startosis Args working)
- [x] forkmon (this is blocked on CL clients running)
- [x] prometheus (this is blocked on CL clients running)
- [x] grafana (this is blocked on prometheus running)
- [x] grafana needs an upload files endpoint in Startosis
- [x] static_files package
- [x] testnet_verifier (this is blocked on CL/EL clients running)
- [x] transaction_spammer (this is blocked on EL clients running)
- [ ] participant_network/participant_network
- [ ] has most data generation things, needs to start EL/CL clients
- [ ] needs upload files to be implemented
- [x] participant_network/participant_network DEMO
- [x] has most data generation things, needs to start EL/CL clients
- [x] needs upload files to be implemented
- [x] need to fill in the dictionary with all el / cl types
- [x] participant_network/participant
- [x] pure POJO should be quick to implement NO BLOCKERS
- [x] mev_boost participant_network/mev_boost NO BLOCKERS - removed some attributes that aren't used
......@@ -24,25 +31,50 @@ This is the Startosis version of the popular [eth2-merge-kurtosis-module](https:
- [x] participant_network/pre_launch_data_generator (the only missing piece here is remove_service)
- [x] data generation
- [x] remove services post generation
- [ ] participant_network/el (requires facts and waits)
- [ ] besu - facts and waits
- [ ] erigon - facts and waits
- [ ] geth - facts and waits
- [ ] nethermind - facts and waits
- [x] participant_network/el (requires facts and waits)
- [x] besu
- [x] facts and waits + private_ip_address_placeholder
- [x] framework
- [x] facts could use more waiting
- [x] erigon
- [x] facts and waits + private_ip_address_placeholder
- [x] framework
- [x] geth DEMO
- [x] facts and waits + private_ip_address_placeholder
- [x] framework TESTED
- [x] nethermind
- [x] facts and waits + private_ip_address_placeholder
- [x] framework
- [x] facts could use more waiting
- [x] el_client_context pure POJO NO BLOCKERS
- [x] el_client_launcher interface not necessary
- [ ] el_availability_waiter - facts and waits
- [x] el_availability_waiter - facts and waits - DESCOPED facts and waits will do this
- [x] el_rest_client/api_response_objects.go DESCOPED as facts will do this
- [x] el_rest_client/el_rest_client - facts and waits DESCOPED as facts will do this
- [ ] participant_network/cl (requires facts and waits)
- [ ] lighthouse - facts and waits
- [ ] loadstar - facts and waits
- [ ] nymbus - facts and waits
- [ ] prysm - facts and waits
- [ ] teku - facts and waits
- [x] participant_network/cl (requires facts and waits)
- [x] lighthouse DEMO
- [x] facts and waits
- [x] framework TESTED
- [x] lodestar
- [x] facts and waits
- [x] framework
- [x] needs longer fact & wait
- [x] nimbus - eth2-merge-kurtosis-module is broken and so is this
- [x] facts and waits
- [x] framework
- [x] prysm - doesn't work as genesis_fork version is different, like the old module
- [x] facts and waits
- [x] framework
- [x] teku
- [x] facts and waits
- [x] framework
- [x] needs longer facts and waits
- [x] cl_client_context pure POJO NO BLOCKERS
- [x] cl_client_launcher interface not necessary
- [ ] cl_availability_waiter - facts and waits
- [x] cl_availability_waiter - facts and waits - DESCOPED facts and waits will do this
- [x] cl_rest_client/api_response_objects.go DESCOPED as facts will do this
- [x] cl_rest_client/el_rest_client - DESCOPED as facts will do this
- [x] cl_node_metrics_info - pure POJO NO BLOCKERS
- [x] get render templates to have the magic strings subsituted with real values
- [x] confirm that the 0x30000038 value in `static_files/genesis-generation-config/cl/config.yaml.tmpl` is correct - this makes prysm work [DESCOPED]
- [x] confirm behavior of artifact_uuid post for loop fix
\ No newline at end of file
......@@ -6,6 +6,7 @@
- Added a lot of participant_network/pre_launch_data_generator
- Added a lot of simple objects that just keep data
- Added monitoring on top of the repo
- Almost perfect parity with the eth2-merge-kurtosis-module
### Fixes
- Fixes some bugs with the initial implementation of the monitors
......
load("github.com/kurtosis-tech/eth2-module/src/participant_network/participant_network.star", "launch_participant_network")
load("github.com/kurtosis-tech/eth2-module/src/module_io/parse_input.star", "parse_input")
load("github.com/kurtosis-tech/eth2-module/src/static_files/static_files.star", "GRAFANA_DASHBOARDS_CONFIG_DIRPATH", "GRAFANA_DASHBOARD_PROVIDERS_CONFIG_TEMPLATE_FILEPATH", "GRAFANA_DATASOURCE_CONFIG_TEMPLATE_FILEPATH", "PROMETHEUS_CONFIG_TEMPLATE_FILEPATH", "FORKMON_CONFIG_TEMPLATE_FILEPATH")
load("github.com/kurtosis-tech/eth2-module/src/participant_network/prelaunch_data_generator/genesis_constants/genesis_constants.star", "PRE_FUNDED_ACCOUNTS")
load("github.com/kurtosis-tech/eth2-module/src/transaction_spammer/transaction_spammer.star", "launch_transaction_spammer")
load("github.com/kurtosis-tech/eth2-module/src/forkmon/forkmon_launcher.star", "launch_forkmon")
load("github.com/kurtosis-tech/eth2-module/src/prometheus/prometheus_launcher.star", "launch_prometheus")
load("github.com/kurtosis-tech/eth2-module/src/grafana/grafana_launcher.star", "launch_grafana")
load("github.com/kurtosis-tech/eth2-module/src/testnet_verifier/testnet_verifier.star", "run_synchronous_testnet_verification", "launch_testnet_verifier")
module_io = import_types("github.com/kurtosis-tech/eth2-module/types.proto")
GRAFANA_USER = "admin"
GRAFANA_PASSWORD = "admin"
GRAFANA_DASHBOARD_PATH_URL = "/d/QdTOwy-nz/eth2-merge-kurtosis-module-dashboard?orgId=1"
FIRST_NODE_FINALIZATION_FACT = "cl-boot-finalization-fact"
HTTP_PORT_ID_FOR_FACT = "http"
def main(input_args):
input_args_with_right_defaults = module_io.ModuleInput(parse_input(input_args))
num_participants = len(input_args_with_right_defaults.participants)
print("Launching participant network with {0} participants and the following network params {1}".format(num_participants, input_args_with_right_defaults.network_params))
launch_participant_network(num_participants, input_args_with_right_defaults.network_params)
#TODO replace with actual values
network_params = input_args_with_right_defaults.network_params
grafana_datasource_config_template = read_file(GRAFANA_DATASOURCE_CONFIG_TEMPLATE_FILEPATH)
grafana_dashboards_config_template = read_file(GRAFANA_DASHBOARD_PROVIDERS_CONFIG_TEMPLATE_FILEPATH)
prometheus_config_template = read_file(PROMETHEUS_CONFIG_TEMPLATE_FILEPATH)
print("Read the prometheus, grafana templates")
print("Launching participant network with {0} participants and the following network params {1}".format(num_participants, network_params))
all_participants, cl_gensis_timestamp = launch_participant_network(input_args_with_right_defaults.participants, network_params, input_args_with_right_defaults.global_client_log_level)
all_el_client_contexts = []
all_cl_client_contexts = []
for participant in all_participants:
all_el_client_contexts.append(participant.el_client_context)
all_cl_client_contexts.append(participant.cl_client_context)
if not input_args_with_right_defaults.launch_additional_services:
return
print("Launching transaction spammer")
launch_transaction_spammer(PRE_FUNDED_ACCOUNTS, all_el_client_contexts[0])
print("Succesfully launched transaction spammer")
# We need a way to do time.sleep
# TODO add code that waits for CL genesis
print("Launching forkmon")
forkmon_config_template = read_file(FORKMON_CONFIG_TEMPLATE_FILEPATH)
launch_forkmon(forkmon_config_template, all_cl_client_contexts, cl_gensis_timestamp, network_params.seconds_per_slot, network_params.slots_per_epoch)
print("Succesfully launched forkmon")
print("Launching prometheus...")
prometheus_private_url = launch_prometheus(
prometheus_config_template,
all_cl_client_contexts,
)
print("Successfully launched Prometheus")
print("Launching grafana...")
launch_grafana(grafana_datasource_config_template, grafana_dashboards_config_template, prometheus_private_url)
print("Succesfully launched grafana")
if input_args_with_right_defaults.wait_for_verifications:
print("Running synchrnous testnet verifier")
run_synchronous_testnet_verification(input_args_with_right_defaults, all_el_client_contexts, all_cl_client_contexts)
print("Verification succeeded")
else:
print("Running asynchronous verification")
launch_testnet_verifier(input_args_with_right_defaults, all_el_client_contexts, all_cl_client_contexts)
print("Succesfully launched asynchronous verifier")
if input_args_with_right_defaults.wait_for_finalization:
print("Waiting for the first finalized epoch")
first_cl_client = all_cl_client_contexts[0]
first_cl_client_id = first_cl_client.beacon_service_id
define_fact(service_id = first_cl_client_id, fact_name = FIRST_NODE_FINALIZATION_FACT, fact_recipe = struct(method= "GET", endpoint = "/eth/v1/beacon/states/head/finality_checkpoints", content_type = "application/json", port_id = HTTP_PORT_ID_FOR_FACT, field_extractor = ".data.finalized.epoch"))
finalized_epoch = wait(service_id = first_cl_client_id, fact_name = FIRST_NODE_FINALIZATION_FACT)
# TODO make an assertion on the finalized_epoch > 0
print("First finalized epoch occurred successfully")
grafana_info = module_io.GrafanaInfo(
dashboard_path = "dummy_path",
user = "user",
password = "password"
dashboard_path = GRAFANA_DASHBOARD_PATH_URL,
user = GRAFANA_USER,
password = GRAFANA_PASSWORD
)
output = module_io.ModuleOutput({"grafana_info": grafana_info})
output = module_io.ModuleOutput(grafana_info = grafana_info)
print(output)
return output
......@@ -39,20 +39,20 @@ def launch_forkmon(
config_files_artifact_uuid = render_templates(template_and_data_by_rel_dest_filepath)
service_config = get_service_config(config_files_artifact_uuid)
config = get_config(config_files_artifact_uuid)
add_service(SERVICE_ID, service_config)
add_service(SERVICE_ID, config)
def get_service_config(config_files_artifact_uuid):
def get_config(config_files_artifact_uuid):
config_file_path = path_join(FORKMON_CONFIG_MOUNT_DIRPATH_ON_SERVICE, FORKMON_CONFIG_FILENAME)
return struct(
container_image_name = IMAGE_NAME,
used_ports = USED_PORTS,
files_artifact_mount_dirpaths = {
image = IMAGE_NAME,
ports = USED_PORTS,
files = {
config_files_artifact_uuid: FORKMON_CONFIG_MOUNT_DIRPATH_ON_SERVICE,
},
cmd_args = ["--config-path", config_file_path]
cmd = ["--config-path", config_file_path]
)
......
......@@ -30,9 +30,9 @@ USED_PORTS = {
def launch_grafana(datasource_config_template, dashboard_providers_config_template, prometheus_private_url):
grafana_config_artifacts_uuid, grafana_dashboards_artifacts_uuid = get_grafana_config_dir_artifact_uuid(datasource_config_template, dashboard_providers_config_template, prometheus_private_url)
service_config = get_service_config(grafana_config_artifacts_uuid, grafana_dashboards_artifacts_uuid)
config = get_config(grafana_config_artifacts_uuid, grafana_dashboards_artifacts_uuid)
add_service(SERVICE_ID, service_config)
add_service(SERVICE_ID, config)
def get_grafana_config_dir_artifact_uuid(datasource_config_template, dashboard_providers_config_template, prometheus_private_url):
......@@ -55,12 +55,12 @@ def get_grafana_config_dir_artifact_uuid(datasource_config_template, dashboard_p
return grafana_config_artifacts_uuid, grafana_dashboards_artifacts_uuid
def get_service_config(grafana_config_artifacts_uuid, grafana_dashboards_artifacts_uuid):
def get_config(grafana_config_artifacts_uuid, grafana_dashboards_artifacts_uuid):
return struct(
container_image_name = IMAGE_NAME,
used_ports = USED_PORTS,
image = IMAGE_NAME,
ports = USED_PORTS,
env_vars = {CONFIG_DIRPATH_ENV_VAR: GRAFANA_CONFIG_DIRPATH_ON_SERVICE},
files_artifact_mount_dirpaths = {
files = {
grafana_config_artifacts_uuid : GRAFANA_CONFIG_DIRPATH_ON_SERVICE,
grafana_dashboards_artifacts_uuid: GRAFANA_DASHBOARDS_DIRPATH_ON_SERVICE
}
......
# differs from kurtosis-tech/eth2-merge-kurtosis-module in the sense it dosen't have the rest_client
def new_cl_client_context(client_name, enr, ip_addr, http_port_num, cl_nodes_metrics_info):
def new_cl_client_context(client_name, enr, ip_addr, http_port_num, cl_nodes_metrics_info, beacon_service_id):
return struct(
client_name = client_name,
enr = enr,
ip_addr = ip_addr,
http_port_num = http_port_num,
cl_nodes_metrics_info = cl_nodes_metrics_info,
beacon_service_id = beacon_service_id
)
This diff is collapsed.
load("github.com/kurtosis-tech/eth2-module/src/shared_utils/shared_utils.star", "new_port_spec", "path_join", "path_dir", "TCP_PROTOCOL", "UDP_PROTOCOL")
load("github.com/kurtosis-tech/eth2-module/src/module_io/parse_input.star", "get_client_log_level_or_default")
load("github.com/kurtosis-tech/eth2-module/src/participant_network/cl/cl_client_context.star", "new_cl_client_context")
load("github.com/kurtosis-tech/eth2-module/src/participant_network/cl/cl_node_metrics_info.star", "new_cl_node_metrics_info")
load("github.com/kurtosis-tech/eth2-module/src/participant_network/mev_boost/mev_boost_context.star", "mev_boost_endpoint")
module_io = import_types("github.com/kurtosis-tech/eth2-module/types.proto")
CONSENSUS_DATA_DIRPATH_ON_SERVICE_CONTAINER = "/consensus-data"
GENESIS_DATA_MOUNT_DIRPATH_ON_SERVICE_CONTAINER = "/genesis"
VALIDATOR_KEYS_MOUNT_DIRPATH_ON_SERVICE_CONTAINER = "/validator-keys"
# Port IDs
TCP_DISCOVERY_PORT_ID = "tcp-discovery"
UDP_DISCOVERY_PORT_ID = "udp-discovery"
HTTP_PORT_ID = "http"
METRICS_PORT_ID = "metrics"
VALIDATOR_METRICS_PORT_ID = "validator-metrics"
# Port nums
DISCOVERY_PORT_NUM = 9000
HTTP_PORT_NUM = 4000
METRICS_PORT_NUM = 8008
VALIDATOR_METRICS_PORT_NUM = 5064
BEACON_SUFFIX_SERVICE_ID = "beacon"
VALIDATOR_SUFFIX_SERVICE_ID = "validator"
METRICS_PATH = "/metrics"
PRIVATE_IP_ADDRESS_PLACEHOLDER = "KURTOSIS_IP_ADDR_PLACEHOLDER"
BEACON_ENR_FACT_NAME = "beacon-enr-fact"
BEACON_HEALTH_FACT_NAME = "beacon-health-fact"
USED_PORTS = {
TCP_DISCOVERY_PORT_ID: new_port_spec(DISCOVERY_PORT_NUM, TCP_PROTOCOL),
UDP_DISCOVERY_PORT_ID: new_port_spec(DISCOVERY_PORT_NUM, UDP_PROTOCOL),
HTTP_PORT_ID: new_port_spec(HTTP_PORT_NUM, TCP_PROTOCOL),
METRICS_PORT_ID: new_port_spec(METRICS_PORT_NUM, TCP_PROTOCOL),
VALIDATOR_METRICS_PORT_ID: new_port_spec(VALIDATOR_METRICS_PORT_NUM, TCP_PROTOCOL)
}
LODESTAR_LOG_LEVELS = {
module_io.GlobalClientLogLevel.error: "error",
module_io.GlobalClientLogLevel.warn: "warn",
module_io.GlobalClientLogLevel.info: "info",
module_io.GlobalClientLogLevel.debug: "debug",
module_io.GlobalClientLogLevel.trace: "trace",
}
def launch(
launcher,
service_id,
image,
participant_log_level,
global_log_level,
bootnode_context,
el_client_context,
mev_boost_context,
node_keystore_files,
extra_beacon_params,
extra_validator_params):
beacon_node_service_id = "{0}-{1}".format(service_id, BEACON_SUFFIX_SERVICE_ID)
validator_node_service_id = "{0}-{1}".format(service_id, VALIDATOR_SUFFIX_SERVICE_ID)
log_level = get_client_log_level_or_default(participant_log_level, global_log_level, LODESTAR_LOG_LEVELS)
# Launch Beacon node
beacon_config = get_beacon_config(
launcher.cl_genesis_data,
image,
bootnode_context,
el_client_context,
mev_boost_context,
log_level,
extra_beacon_params,
)
beacon_service = add_service(beacon_node_service_id, beacon_config)
beacon_http_port = beacon_service.ports[HTTP_PORT_ID]
# TODO check whether its 200, 206 or 503 like golang
define_fact(service_id = beacon_node_service_id, fact_name = BEACON_HEALTH_FACT_NAME, fact_recipe = struct(method= "GET", endpoint = "/eth/v1/node/health", content_type = "application/json", port_id = HTTP_PORT_ID))
wait(service_id = beacon_node_service_id, fact_name = BEACON_HEALTH_FACT_NAME)
# Launch validator node
beacon_http_url = "http://{0}:{1}".format(beacon_service.ip_address, beacon_http_port.number)
validator_config = get_validator_config(
validator_node_service_id,
launcher.cl_genesis_data,
image,
log_level,
beacon_http_url,
node_keystore_files,
mev_boost_context,
extra_validator_params,
)
validator_service = add_service(validator_node_service_id, validator_config)
# TODO(old) add validator availability using the validator API: https://ethereum.github.io/beacon-APIs/?urls.primaryName=v1#/ValidatorRequiredApi | from eth2-merge-kurtosis-module
define_fact(service_id = beacon_node_service_id, fact_name = BEACON_ENR_FACT_NAME, fact_recipe = struct(method= "GET", endpoint = "/eth/v1/node/identity", field_extractor = ".data.enr", content_type = "application/json", port_id = HTTP_PORT_ID))
beacon_node_enr = wait(service_id = beacon_node_service_id, fact_name = BEACON_ENR_FACT_NAME)
beacon_metrics_port = beacon_service.ports[METRICS_PORT_ID]
beacon_metrics_url = "{0}:{1}".format(beacon_service.ip_address, beacon_metrics_port.number)
beacon_node_metrics_info = new_cl_node_metrics_info(service_id, METRICS_PATH, beacon_metrics_url)
nodes_metrics_info = [beacon_node_metrics_info]
result = new_cl_client_context(
"lodestar",
beacon_node_enr,
beacon_service.ip_address,
HTTP_PORT_NUM,
nodes_metrics_info,
beacon_node_service_id
)
return result
def get_beacon_config(
genesis_data,
image,
boot_cl_client_ctx,
el_client_ctx,
mev_boost_context,
log_level,
extra_params):
el_client_rpc_url_str = "http://{0}:{1}".format(
el_client_ctx.ip_addr,
el_client_ctx.rpc_port_num,
)
el_client_engine_rpc_url_str = "http://{0}:{1}".format(
el_client_ctx.ip_addr,
el_client_ctx.engine_rpc_port_num,
)
genesis_config_filepath = path_join(GENESIS_DATA_MOUNT_DIRPATH_ON_SERVICE_CONTAINER, genesis_data.config_yml_rel_filepath)
genesis_ssz_filepath = path_join(GENESIS_DATA_MOUNT_DIRPATH_ON_SERVICE_CONTAINER, genesis_data.genesis_ssz_rel_filepath)
jwt_secret_filepath = path_join(GENESIS_DATA_MOUNT_DIRPATH_ON_SERVICE_CONTAINER, genesis_data.jwt_secret_rel_filepath)
cmd = [
"beacon",
"--logLevel=" + log_level,
"--port={0}".format(DISCOVERY_PORT_NUM),
"--discoveryPort={0}".format(DISCOVERY_PORT_NUM),
"--dataDir=" + CONSENSUS_DATA_DIRPATH_ON_SERVICE_CONTAINER,
"--paramsFile=" + genesis_config_filepath,
"--genesisStateFile=" + genesis_ssz_filepath,
"--eth1.depositContractDeployBlock=0",
"--network.connectToDiscv5Bootnodes=true",
"--discv5=true",
"--eth1=true",
"--eth1.providerUrls=" + el_client_rpc_url_str,
"--execution.urls=" + el_client_engine_rpc_url_str,
"--rest=true",
"--rest.address=0.0.0.0",
"--rest.namespace=*",
"--rest.port={0}".format(HTTP_PORT_NUM),
"--enr.ip=" + PRIVATE_IP_ADDRESS_PLACEHOLDER,
"--enr.tcp={0}".format(DISCOVERY_PORT_NUM),
"--enr.udp={0}".format(DISCOVERY_PORT_NUM),
# Set per Pari's recommendation to reduce noise in the logs
"--subscribeAllSubnets=true",
"--jwt-secret={0}".format(jwt_secret_filepath),
# vvvvvvvvvvvvvvvvvvv METRICS CONFIG vvvvvvvvvvvvvvvvvvvvv
"--metrics",
"--metrics.address=0.0.0.0",
"--metrics.port={0}".format(METRICS_PORT_NUM),
# ^^^^^^^^^^^^^^^^^^^ METRICS CONFIG ^^^^^^^^^^^^^^^^^^^^^
]
if boot_cl_client_ctx != None :
cmd.append("--bootnodes="+boot_cl_client_ctx.enr)
if mev_boost_context != None:
cmd.append("--builder")
cmd.append("--builder.urls '{0}'".format(mev_boost_endpoint(mev_boost_context)))
if len(extra_params) > 0:
# this is a repeated<proto type>, we convert it into Starlark
cmd.extend([param for param in extra_params])
return struct(
image = image,
ports = USED_PORTS,
cmd = cmd,
files = {
genesis_data.files_artifact_uuid: GENESIS_DATA_MOUNT_DIRPATH_ON_SERVICE_CONTAINER
},
private_ip_address_placeholder = PRIVATE_IP_ADDRESS_PLACEHOLDER
)
def get_validator_config(
service_id,
genesis_data,
image,
log_level,
beacon_client_http_url,
node_keystore_files,
mev_boost_context,
extra_params):
root_dirpath = path_join(CONSENSUS_DATA_DIRPATH_ON_SERVICE_CONTAINER, service_id)
genesis_config_filepath = path_join(GENESIS_DATA_MOUNT_DIRPATH_ON_SERVICE_CONTAINER, genesis_data.config_yml_rel_filepath)
validator_keys_dirpath = path_join(VALIDATOR_KEYS_MOUNT_DIRPATH_ON_SERVICE_CONTAINER, node_keystore_files.raw_keys_relative_dirpath)
validator_secrets_dirpath = path_join(VALIDATOR_KEYS_MOUNT_DIRPATH_ON_SERVICE_CONTAINER, node_keystore_files.raw_secrets_relative_dirpath)
cmd = [
"validator",
"--logLevel=" + log_level,
"--dataDir=" + root_dirpath,
"--paramsFile=" + genesis_config_filepath,
"--server=" + beacon_client_http_url,
"--keystoresDir=" + validator_keys_dirpath,
"--secretsDir=" + validator_secrets_dirpath,
# vvvvvvvvvvvvvvvvvvv PROMETHEUS CONFIG vvvvvvvvvvvvvvvvvvvvv
"--metrics",
"--metrics.address=0.0.0.0",
"--metrics.port={0}".format(VALIDATOR_METRICS_PORT_NUM),
# ^^^^^^^^^^^^^^^^^^^ PROMETHEUS CONFIG ^^^^^^^^^^^^^^^^^^^^^
]
if mev_boost_context != None:
cmd.append("--builder")
# TODO(old) required to work? - from old module
# cmdArgs = append(cmdArgs, "--defaultFeeRecipient <your ethereum address>")
if len(extra_params) > 0:
# this is a repeated<proto type>, we convert it into Starlark
cmd.extend([param for param in extra_params])
return struct(
image = image,
ports = USED_PORTS,
cmd = cmd,
files = {
genesis_data.files_artifact_uuid: GENESIS_DATA_MOUNT_DIRPATH_ON_SERVICE_CONTAINER,
node_keystore_files.files_artifact_uuid: VALIDATOR_KEYS_MOUNT_DIRPATH_ON_SERVICE_CONTAINER
},
private_ip_address_placeholder = PRIVATE_IP_ADDRESS_PLACEHOLDER
)
def new_lodestar_launcher(cl_genesis_data):
return struct(
cl_genesis_data = cl_genesis_data,
)
load("github.com/kurtosis-tech/eth2-module/src/shared_utils/shared_utils.star", "new_port_spec", "path_join", "path_dir", "TCP_PROTOCOL", "UDP_PROTOCOL")
load("github.com/kurtosis-tech/eth2-module/src/module_io/parse_input.star", "get_client_log_level_or_default")
load("github.com/kurtosis-tech/eth2-module/src/participant_network/cl/cl_client_context.star", "new_cl_client_context")
load("github.com/kurtosis-tech/eth2-module/src/participant_network/cl/cl_node_metrics_info.star", "new_cl_node_metrics_info")
module_io = import_types("github.com/kurtosis-tech/eth2-module/types.proto")
GENESIS_DATA_MOUNTPOINT_ON_CLIENT = "/genesis-data"
VALIDATOR_KEYS_MOUNTPOINT_ON_CLIENT = "/validator-keys"
# Port IDs
TCP_DISCOVERY_PORT_ID = "tcp-discovery"
UDP_DISCOVERY_PORT_ID = "udp-discovery"
HTTP_PORT_ID = "http"
METRICS_PORT_ID = "metrics"
# Port nums
DISCOVERY_PORT_NUM = 9000
HTTP_PORT_NUM = 4000
METRICS_PORT_NUM = 8008
# Nimbus requires that its data directory already exists (because it expects you to bind-mount it), so we
# have to to create it
CONSENSUS_DATA_DIRPATH_IN_SERVICE_CONTAINER = "$HOME/consensus-data"
# Nimbus wants the data dir to have these perms
CONSENSUS_DATA_DIR_PERMS_STR = "0700"
# The entrypoint the image normally starts with (we need to override the entrypoint to create the
# consensus data directory on the image before it starts)
DEFAULT_IMAGE_ENTRYPOINT = "/home/user/nimbus-eth2/build/nimbus_beacon_node"
# Nimbus needs write access to the validator keys/secrets directories, and b/c the module container runs as root
# while the Nimbus container does not, we can't just point the Nimbus binary to the paths in the shared dir because
# it won't be able to open them. To get around this, we copy the validator keys/secrets to a path inside the Nimbus
# container that is owned by the container's user
VALIDATOR_KEYS_DIRPATH_ON_SERVICE_CONTAINER = "$HOME/validator-keys"
VALIDATOR_SECRETS_DIRPATH_ON_SERVICE_CONTAINER = "$HOME/validator-secrets"
METRICS_PATH = "/metrics"
PRIVATE_IP_ADDRESS_PLACEHOLDER = "KURTOSIS_IP_ADDR_PLACEHOLDER"
USED_PORTS = {
TCP_DISCOVERY_PORT_ID: new_port_spec(DISCOVERY_PORT_NUM, TCP_PROTOCOL),
UDP_DISCOVERY_PORT_ID: new_port_spec(DISCOVERY_PORT_NUM, UDP_PROTOCOL),
HTTP_PORT_ID: new_port_spec(HTTP_PORT_NUM, TCP_PROTOCOL),
METRICS_PORT_ID: new_port_spec(METRICS_PORT_NUM, TCP_PROTOCOL),
}
NIMBUS_LOG_LEVELS = {
module_io.GlobalClientLogLevel.error: "ERROR",
module_io.GlobalClientLogLevel.warn: "WARN",
module_io.GlobalClientLogLevel.info: "INFO",
module_io.GlobalClientLogLevel.debug: "DEBUG",
module_io.GlobalClientLogLevel.trace: "TRACE",
}
ENR_FACT_NAME = "enr-fact"
HEALTH_FACT_NAME = "health-fact"
ENTRYPOINT_ARGS = ["sh", "-c"]
def launch(
launcher,
service_id,
image,
participant_log_level,
global_log_level,
bootnode_context,
el_client_context,
mev_boost_context,
node_keystore_files,
extra_beacon_params,
extra_validator_params):
log_level = get_client_log_level_or_default(participant_log_level, global_log_level, NIMBUS_LOG_LEVELS)
extra_params = [param for param in extra_beacon_params] + [param for param in extra_validator_params]
config = get_config(launcher.cl_genesis_data, image, bootnode_context, el_client_context, mev_boost_context, log_level, node_keystore_files, extra_params)
nimbus_service = add_service(service_id, config)
# TODO check whether its 200, 206 or 503 like golang
define_fact(service_id = service_id, fact_name = HEALTH_FACT_NAME, fact_recipe = struct(method= "GET", endpoint = "/eth/v1/node/health", content_type = "application/json", port_id = HTTP_PORT_ID))
wait(service_id = service_id, fact_name = HEALTH_FACT_NAME)
define_fact(service_id = service_id, fact_name = ENR_FACT_NAME, fact_recipe = struct(method= "GET", endpoint = "/eth/v1/node/identity", field_extractor = ".data.enr", content_type = "application/json", port_id = HTTP_PORT_ID))
node_enr = wait(service_id = service_id, fact_name = ENR_FACT_NAME)
metrics_port = nimbus_service.ports[METRICS_PORT_ID]
metrics_url = "{0}:{1}".format(nimbus_service.ip_address, metrics_port.number)
nimbus_node_metrics_info = new_cl_node_metrics_info(service_id, METRICS_PATH, metrics_url)
nodes_metrics_info = [nimbus_node_metrics_info]
result = new_cl_client_context(
"nimbus",
node_enr,
nimbus_service.ip_address,
HTTP_PORT_NUM,
nodes_metrics_info,
service_id,
)
return result
def get_config(
genesis_data,
image,
boot_cl_client_ctx,
el_client_ctx,
mev_boost_context,
log_level,
node_keystore_files,
extra_params):
el_client_engine_rpc_url_str = "http://{0}:{1}".format(
el_client_ctx.ip_addr,
el_client_ctx.engine_rpc_port_num,
)
# For some reason, Nimbus takes in the parent directory of the config file (rather than the path to the config file itself)
genesis_config_parent_dirpath_on_client = path_join(GENESIS_DATA_MOUNTPOINT_ON_CLIENT, path_dir(genesis_data.config_yml_rel_filepath))
jwt_secret_filepath = path_join(GENESIS_DATA_MOUNTPOINT_ON_CLIENT, genesis_data.jwt_secret_rel_filepath)
validator_keys_dirpath = path_join(VALIDATOR_KEYS_MOUNTPOINT_ON_CLIENT, node_keystore_files.nimbus_keys_relative_dirpath)
validator_secrets_dirpath = path_join(VALIDATOR_KEYS_MOUNTPOINT_ON_CLIENT, node_keystore_files.raw_secrets_relative_dirpath)
# Sources for these flags:
# 1) https://github.com/status-im/nimbus-eth2/blob/stable/scripts/launch_local_testnet.sh
# 2) https://github.com/status-im/nimbus-eth2/blob/67ab477a27e358d605e99bffeb67f98d18218eca/scripts/launch_local_testnet.sh#L417
# WARNING: Do NOT set the --max-peers flag here, as doing so to the exact number of nodes seems to mess things up!
# See: https://github.com/kurtosis-tech/eth2-merge-kurtosis-module/issues/26
cmd = [
"mkdir",
CONSENSUS_DATA_DIRPATH_IN_SERVICE_CONTAINER,
"-m",
CONSENSUS_DATA_DIR_PERMS_STR,
"&&",
# TODO(old) COMMENT THIS OUT?
"cp",
"-R",
validator_keys_dirpath,
VALIDATOR_KEYS_DIRPATH_ON_SERVICE_CONTAINER,
"&&",
"cp",
"-R",
validator_secrets_dirpath,
VALIDATOR_SECRETS_DIRPATH_ON_SERVICE_CONTAINER,
"&&",
# If we don't do this chmod, Nimbus will spend a crazy amount of time manually correcting them
# before it starts
"chmod",
"600",
VALIDATOR_SECRETS_DIRPATH_ON_SERVICE_CONTAINER + "/*",
"&&",
DEFAULT_IMAGE_ENTRYPOINT,
"--non-interactive=true",
"--log-level=" + log_level,
"--network=" + genesis_config_parent_dirpath_on_client,
"--data-dir=" + CONSENSUS_DATA_DIRPATH_IN_SERVICE_CONTAINER,
"--web3-url=" + el_client_engine_rpc_url_str,
"--nat=extip:" + PRIVATE_IP_ADDRESS_PLACEHOLDER,
"--enr-auto-update=false",
"--rest",
"--rest-address=0.0.0.0",
"--rest-port={0}".format(HTTP_PORT_NUM),
"--validators-dir=" + VALIDATOR_KEYS_DIRPATH_ON_SERVICE_CONTAINER,
"--secrets-dir=" + VALIDATOR_SECRETS_DIRPATH_ON_SERVICE_CONTAINER,
# There's a bug where if we don't set this flag, the Nimbus nodes won't work:
# https://discord.com/channels/641364059387854899/674288681737256970/922890280120750170
# https://github.com/status-im/nimbus-eth2/issues/2451
"--doppelganger-detection=false",
# Set per Pari's recommendation to reduce noise in the logs
"--subscribe-all-subnets=true",
# Nimbus can handle a max of 256 threads, if the host has more then nimbus crashes. Setting it to 4 so it doesn't crash on build servers
"--num-threads=4",
"--jwt-secret={0}".format(jwt_secret_filepath),
# vvvvvvvvvvvvvvvvvvv METRICS CONFIG vvvvvvvvvvvvvvvvvvvvv
"--metrics",
"--metrics-address=0.0.0.0",
"--metrics-port={0}".format(METRICS_PORT_NUM),
# ^^^^^^^^^^^^^^^^^^^ METRICS CONFIG ^^^^^^^^^^^^^^^^^^^^^
]
if boot_cl_client_ctx == None:
# Copied from https://.com/status-im/nimbus-eth2/blob/67ab477a27e358d605e99bffeb67f98d18218eca/scripts/launch_local_testnet.sh#L417
# See explanation there
cmd.append("--subscribe-all-subnets")
else:
cmd.append("--bootstrap-node="+boot_cl_client_ctx.enr)
if mev_boost_context != None:
# TODO(old) add `mev-boost` support once the feature lands on `stable` - from eth2-merge-kurtosis-module
pass
if len(extra_params) > 0:
cmd.extend([param for param in extra_params])
cmd_str = " ".join(cmd)
return struct(
image = image,
ports = USED_PORTS,
cmd = [cmd_str],
entrypoint = ENTRYPOINT_ARGS,
files = {
genesis_data.files_artifact_uuid: GENESIS_DATA_MOUNTPOINT_ON_CLIENT,
node_keystore_files.files_artifact_uuid: VALIDATOR_KEYS_MOUNTPOINT_ON_CLIENT
},
private_ip_address_placeholder = PRIVATE_IP_ADDRESS_PLACEHOLDER
)
def new_nimbus_launcher(cl_genesis_data):
return struct(
cl_genesis_data = cl_genesis_data,
)
This diff is collapsed.
load("github.com/kurtosis-tech/eth2-module/src/shared_utils/shared_utils.star", "new_port_spec", "path_join", "path_dir", "TCP_PROTOCOL", "UDP_PROTOCOL")
load("github.com/kurtosis-tech/eth2-module/src/module_io/parse_input.star", "get_client_log_level_or_default")
load("github.com/kurtosis-tech/eth2-module/src/participant_network/cl/cl_client_context.star", "new_cl_client_context")
load("github.com/kurtosis-tech/eth2-module/src/participant_network/cl/cl_node_metrics_info.star", "new_cl_node_metrics_info")
load("github.com/kurtosis-tech/eth2-module/src/participant_network/mev_boost/mev_boost_context.star", "mev_boost_endpoint")
module_io = import_types("github.com/kurtosis-tech/eth2-module/types.proto")
TEKU_BINARY_FILEPATH_IN_IMAGE = "/opt/teku/bin/teku"
GENESIS_DATA_MOUNT_DIRPATH_ON_SERVICE_CONTAINER = "/genesis"
# The Docker container runs as the "teku" user so we can't write to root
CONSENSUS_DATA_DIRPATH_ON_SERVICE_CONTAINER = "/opt/teku/consensus-data"
# These will get mounted as root and Teku needs directory write permissions, so we'll copy this
# into the Teku user's home directory to get around it
VALIDATOR_KEYS_DIRPATH_ON_SERVICE_CONTAINER = "/validator-keys"
# TODO(old) Get rid of this being hardcoded; should be shared
VALIDATING_REWARDS_ACCOUNT = "0x0000000000000000000000000000000000000000"
# Port IDs
TCP_DISCOVERY_PORT_ID = "tcp-discovery"
UDP_DISCOVERY_PORT_ID = "udp-discovery"
HTTP_PORT_ID = "http"
METRICS_PORT_ID = "metrics"
# Port nums
DISCOVERY_PORT_NUM = 9000
HTTP_PORT_NUM = 4000
METRICS_PORT_NUM = 8008
# 1) The Teku container runs as the "teku" user
# 2) Teku requires write access to the validator secrets directory, so it can write a lockfile into it as it uses the keys
# 3) The module container runs as 'root'
# With these three things combined, it means that when the module container tries to write the validator keys/secrets into
# the shared directory, it does so as 'root'. When Teku tries to consum the same files, it will get a failure because it
# doesn't have permission to write to the 'validator-secrets' directory.
# To get around this, we copy the files AGAIN from
DEST_VALIDATOR_KEYS_DIRPATH_IN_SERVICE_CONTAINER = "$HOME/validator-keys"
DEST_VALIDATOR_SECRETS_DIRPATH_IN_SERVICE_CONTAINER = "$HOME/validator-secrets"
MIN_PEERS = 1
METRICS_PATH = "/metrics"
PRIVATE_IP_ADDRESS_PLACEHOLDER = "KURTOSIS_IP_ADDR_PLACEHOLDER"
USED_PORTS = {
TCP_DISCOVERY_PORT_ID: new_port_spec(DISCOVERY_PORT_NUM, TCP_PROTOCOL),
UDP_DISCOVERY_PORT_ID: new_port_spec(DISCOVERY_PORT_NUM, UDP_PROTOCOL),
HTTP_PORT_ID: new_port_spec(HTTP_PORT_NUM, TCP_PROTOCOL),
METRICS_PORT_ID: new_port_spec(METRICS_PORT_NUM, TCP_PROTOCOL),
}
ENTRYPOINT_ARGS = ["sh", "-c"]
TEKU_LOG_LEVELS = {
module_io.GlobalClientLogLevel.error: "ERROR",
module_io.GlobalClientLogLevel.warn: "WARN",
module_io.GlobalClientLogLevel.info: "INFO",
module_io.GlobalClientLogLevel.debug: "DEBUG",
module_io.GlobalClientLogLevel.trace: "TRACE",
}
ENR_FACT_NAME = "enr-fact"
HEALTH_FACT_NAME = "health-fact"
def launch(
launcher,
service_id,
image,
participant_log_level,
global_log_level,
bootnode_context,
el_client_context,
mev_boost_context,
node_keystore_files,
extra_beacon_params,
extra_validator_params):
log_level = get_client_log_level_or_default(participant_log_level, global_log_level, TEKU_LOG_LEVELS)
extra_params = [param for param in extra_beacon_params] + [param for param in extra_validator_params]
config = get_config(launcher.cl_genesis_data, image, bootnode_context, el_client_context, mev_boost_context, log_level, node_keystore_files, extra_params)
teku_service = add_service(service_id, config)
# TODO check whether its 200, 206 or 503 like golang
define_fact(service_id = service_id, fact_name = HEALTH_FACT_NAME, fact_recipe = struct(method= "GET", endpoint = "/eth/v1/node/health", content_type = "application/json", port_id = HTTP_PORT_ID))
wait(service_id = service_id, fact_name = HEALTH_FACT_NAME)
define_fact(service_id = service_id, fact_name = ENR_FACT_NAME, fact_recipe = struct(method= "GET", endpoint = "/eth/v1/node/identity", field_extractor = ".data.enr", content_type = "application/json", port_id = HTTP_PORT_ID))
node_enr = wait(service_id = service_id, fact_name = ENR_FACT_NAME)
teku_metrics_port = teku_service.ports[METRICS_PORT_ID]
teku_metrics_url = "{0}:{1}".format(teku_service.ip_address, teku_metrics_port.number)
teku_node_metrics_info = new_cl_node_metrics_info(service_id, METRICS_PATH, teku_metrics_url)
nodes_metrics_info = [teku_node_metrics_info]
result = new_cl_client_context(
"teku",
node_enr,
teku_service.ip_address,
HTTP_PORT_NUM,
nodes_metrics_info,
service_id
)
return result
def get_config(
genesis_data,
image,
boot_cl_client_ctx,
el_client_ctx,
mev_boost_context,
log_level,
node_keystore_files,
extra_params):
el_client_rpc_url_str = "http://{0}:{1}".format(
el_client_ctx.ip_addr,
el_client_ctx.rpc_port_num,
)
el_client_engine_rpc_url_str = "http://{0}:{1}".format(
el_client_ctx.ip_addr,
el_client_ctx.engine_rpc_port_num,
)
genesis_config_filepath = path_join(GENESIS_DATA_MOUNT_DIRPATH_ON_SERVICE_CONTAINER, genesis_data.config_yml_rel_filepath)
genesis_ssz_filepath = path_join(GENESIS_DATA_MOUNT_DIRPATH_ON_SERVICE_CONTAINER, genesis_data.genesis_ssz_rel_filepath)
jwt_secret_filepath = path_join(GENESIS_DATA_MOUNT_DIRPATH_ON_SERVICE_CONTAINER, genesis_data.jwt_secret_rel_filepath)
validator_keys_dirpath = path_join(VALIDATOR_KEYS_DIRPATH_ON_SERVICE_CONTAINER, node_keystore_files.teku_keys_relative_dirpath)
validator_secrets_dirpath = path_join(VALIDATOR_KEYS_DIRPATH_ON_SERVICE_CONTAINER, node_keystore_files.teku_secrets_relative_dirpath)
cmd = [
# Needed because the generated keys are owned by root and the Teku image runs as the 'teku' user
"cp",
"-R",
validator_keys_dirpath,
DEST_VALIDATOR_KEYS_DIRPATH_IN_SERVICE_CONTAINER,
"&&",
# Needed because the generated keys are owned by root and the Teku image runs as the 'teku' user
"cp",
"-R",
validator_secrets_dirpath,
DEST_VALIDATOR_SECRETS_DIRPATH_IN_SERVICE_CONTAINER,
"&&",
TEKU_BINARY_FILEPATH_IN_IMAGE,
"--Xee-version kilnv2",
"--logging=" + log_level,
"--log-destination=CONSOLE",
"--network=" + genesis_config_filepath,
"--initial-state=" + genesis_ssz_filepath,
"--data-path=" + CONSENSUS_DATA_DIRPATH_ON_SERVICE_CONTAINER,
"--data-storage-mode=PRUNE",
"--p2p-enabled=true",
# Set per Pari's recommendation, to reduce noise in the logs
"--p2p-subscribe-all-subnets-enabled=true",
"--p2p-peer-lower-bound={0}".format(MIN_PEERS),
"--eth1-endpoints=" + el_client_rpc_url_str,
"--p2p-advertised-ip=" + PRIVATE_IP_ADDRESS_PLACEHOLDER,
"--rest-api-enabled=true",
"--rest-api-docs-enabled=true",
"--rest-api-interface=0.0.0.0",
"--rest-api-port={0}".format(HTTP_PORT_NUM),
"--rest-api-host-allowlist=*",
"--data-storage-non-canonical-blocks-enabled=true",
"--validator-keys={0}:{1}".format(
DEST_VALIDATOR_KEYS_DIRPATH_IN_SERVICE_CONTAINER,
DEST_VALIDATOR_SECRETS_DIRPATH_IN_SERVICE_CONTAINER,
),
"--ee-jwt-secret-file={0}".format(jwt_secret_filepath),
"--ee-endpoint=" + el_client_engine_rpc_url_str,
"--validators-proposer-default-fee-recipient=" + VALIDATING_REWARDS_ACCOUNT,
# vvvvvvvvvvvvvvvvvvv METRICS CONFIG vvvvvvvvvvvvvvvvvvvvv
"--metrics-enabled",
"--metrics-interface=0.0.0.0",
"--metrics-host-allowlist='*'",
"--metrics-categories=BEACON,PROCESS,LIBP2P,JVM,NETWORK,PROCESS",
"--metrics-port={0}".format(METRICS_PORT_NUM),
# ^^^^^^^^^^^^^^^^^^^ METRICS CONFIG ^^^^^^^^^^^^^^^^^^^^^
]
if boot_cl_client_ctx != None:
cmd.append("--p2p-discovery-bootnodes="+boot_cl_client_ctx.enr)
if mev_boost_context != None:
cmd.append("--validators-builder-registration-default-enabled=true")
cmd.append("--builder-endpoint='{0}'".format(mev_boost_endpoint(mev_boost_context)))
if len(extra_params) > 0:
# we do the list comprehension as the default extra_params is a proto repeated string
cmd.extend([param for param in extra_params])
cmd_str = " ".join(cmd)
return struct(
image = image,
ports = USED_PORTS,
cmd = [cmd_str],
entrypoint = ENTRYPOINT_ARGS,
files = {
genesis_data.files_artifact_uuid: GENESIS_DATA_MOUNT_DIRPATH_ON_SERVICE_CONTAINER,
node_keystore_files.files_artifact_uuid: VALIDATOR_KEYS_DIRPATH_ON_SERVICE_CONTAINER
},
private_ip_address_placeholder = PRIVATE_IP_ADDRESS_PLACEHOLDER
)
def new_teku_launcher(cl_genesis_data):
return struct(
cl_genesis_data = cl_genesis_data
)
load("github.com/kurtosis-tech/eth2-module/src/shared_utils/shared_utils.star", "new_port_spec", "path_join", "TCP_PROTOCOL", "UDP_PROTOCOL")
load("github.com/kurtosis-tech/eth2-module/src/module_io/parse_input.star", "get_client_log_level_or_default")
load("github.com/kurtosis-tech/eth2-module/src/participant_network/el/el_client_context.star", "new_el_client_context")
module_io = import_types("github.com/kurtosis-tech/eth2-module/types.proto")
# The dirpath of the execution data directory on the client container
EXECUTION_DATA_DIRPATH_ON_CLIENT_CONTAINER = "/opt/besu/execution-data"
GENESIS_DATA_DIRPATH_ON_CLIENT_CONTAINER = "/opt/besu/genesis"
RPC_PORT_NUM = 8545
WS_PORT_NUM = 8546
DISCOVERY_PORT_NUM = 30303
ENGINE_HTTP_RPC_PORT_NUM = 8550
ENGINE_WS_RPC_PORT_NUM = 8551
# Port IDs
RPC_PORT_ID = "rpc"
WS_PORT_ID = "ws"
TCP_DISCOVERY_PORT_ID = "tcp-discovery"
UDP_DISCOVERY_PORT_ID = "udp-discovery"
ENGINE_HTTP_RPC_PORT_ID = "engineHttpRpc"
ENGINE_WS_RPC_PORT_ID = "engineWsRpc"
PRIVATE_IP_ADDRESS_PLACEHOLDER = "KURTOSIS_IP_ADDR_PLACEHOLDER"
USED_PORTS = {
RPC_PORT_ID: new_port_spec(RPC_PORT_NUM, TCP_PROTOCOL),
WS_PORT_ID: new_port_spec(WS_PORT_NUM, TCP_PROTOCOL),
TCP_DISCOVERY_PORT_ID: new_port_spec(DISCOVERY_PORT_NUM, TCP_PROTOCOL),
UDP_DISCOVERY_PORT_ID: new_port_spec(DISCOVERY_PORT_NUM, UDP_PROTOCOL),
ENGINE_HTTP_RPC_PORT_ID: new_port_spec(ENGINE_HTTP_RPC_PORT_NUM, TCP_PROTOCOL),
ENGINE_WS_RPC_PORT_ID: new_port_spec(ENGINE_WS_RPC_PORT_NUM, TCP_PROTOCOL)
}
ENTRYPOINT_ARGS = ["sh", "-c"]
BESU_LOG_LEVELS = {
module_io.GlobalClientLogLevel.error: "ERROR",
module_io.GlobalClientLogLevel.warn: "WARN",
module_io.GlobalClientLogLevel.info: "INFO",
module_io.GlobalClientLogLevel.debug: "DEBUG",
module_io.GlobalClientLogLevel.trace: "TRACE",
}
ENODE_FACT_NAME = "enode-fact"
def launch(
launcher,
service_id,
image,
participant_log_level,
global_log_level,
existing_el_clients,
extra_params):
log_level = get_client_log_level_or_default(participant_log_level, global_log_level, BESU_LOG_LEVELS)
config = get_config(launcher.network_id, launcher.el_genesis_data,
image, existing_el_clients, log_level, extra_params)
service = add_service(service_id, config)
define_fact(service_id = service_id, fact_name = ENODE_FACT_NAME, fact_recipe = struct(method= "POST", endpoint = "", field_extractor = ".result.enode", body = '{"method":"admin_nodeInfo","params":[],"id":1,"jsonrpc":"2.0"}', content_type = "application/json", port_id = RPC_PORT_ID))
enode = wait(service_id = service_id, fact_name = ENODE_FACT_NAME)
return new_el_client_context(
"besu",
"", # besu has no ENR
enode,
service.ip_address,
RPC_PORT_NUM,
WS_PORT_NUM,
ENGINE_HTTP_RPC_PORT_NUM
)
def get_config(network_id, genesis_data, image, existing_el_clients, log_level, extra_params):
if len(existing_el_clients) < 2:
fail("Besu node cannot be boot nodes, and due to a bug it requires two nodes to exist beforehand")
boot_node_1 = existing_el_clients[0]
boot_node_2 = existing_el_clients[1]
genesis_json_filepath_on_client = path_join(GENESIS_DATA_DIRPATH_ON_CLIENT_CONTAINER, genesis_data.besu_genesis_json_relative_filepath)
jwt_secret_json_filepath_on_client = path_join(GENESIS_DATA_DIRPATH_ON_CLIENT_CONTAINER, genesis_data.jwt_secret_relative_filepath)
launch_node_command = [
"besu",
"--logging=" + log_level,
"--data-path=" + EXECUTION_DATA_DIRPATH_ON_CLIENT_CONTAINER,
"--genesis-file=" + genesis_json_filepath_on_client,
"--network-id=" + network_id,
"--host-allowlist=*",
"--rpc-http-enabled=true",
"--rpc-http-host=0.0.0.0",
"--rpc-http-port={0}".format(RPC_PORT_NUM),
"--rpc-http-api=ADMIN,CLIQUE,ETH,NET,DEBUG,TXPOOL,ENGINE",
"--rpc-http-cors-origins=*",
"--rpc-ws-enabled=true",
"--rpc-ws-host=0.0.0.0",
"--rpc-ws-port={0}".format(WS_PORT_NUM),
"--rpc-ws-api=ADMIN,CLIQUE,ETH,NET,DEBUG,TXPOOL,ENGINE",
"--p2p-enabled=true",
"--p2p-host=" + PRIVATE_IP_ADDRESS_PLACEHOLDER,
"--p2p-port={0}".format(DISCOVERY_PORT_NUM),
"--engine-rpc-enabled=true",
"--engine-jwt-secret={0}".format(jwt_secret_json_filepath_on_client),
"--engine-host-allowlist=*",
"--engine-rpc-port={0}".format(ENGINE_HTTP_RPC_PORT_NUM),
]
if len(existing_el_clients) > 0:
launch_node_command.append("--bootnodes={0},{1}".format(boot_node_1.enode, boot_node_2.enode))
if len(extra_params) > 0:
# we do this as extra_params isn't a normal [] but a proto repeated array
launch_node_command.extend([param for param in extra_params])
launch_node_command_str = " ".join(launch_node_command)
return struct(
image = image,
ports = USED_PORTS,
cmd = [launch_node_command_str],
files = {
genesis_data.files_artifact_uuid: GENESIS_DATA_DIRPATH_ON_CLIENT_CONTAINER
},
entrypoint = ENTRYPOINT_ARGS,
private_ip_address_placeholder = PRIVATE_IP_ADDRESS_PLACEHOLDER
)
def new_besu_launcher(network_id, el_genesis_data):
return struct(
network_id = network_id,
el_genesis_data = el_genesis_data
)
load("github.com/kurtosis-tech/eth2-module/src/shared_utils/shared_utils.star", "new_port_spec", "path_join", "TCP_PROTOCOL", "UDP_PROTOCOL")
load("github.com/kurtosis-tech/eth2-module/src/module_io/parse_input.star", "get_client_log_level_or_default")
load("github.com/kurtosis-tech/eth2-module/src/participant_network/el/el_client_context.star", "new_el_client_context")
module_io = import_types("github.com/kurtosis-tech/eth2-module/types.proto")
# The dirpath of the execution data directory on the client container
EXECUTION_DATA_DIRPATH_ON_CLIENT_CONTAINER = "/home/erigon/execution-data"
GENESIS_DATA_MOUNT_DIRPATH = "/genesis"
RPC_PORT_NUM = 8545
WS_PORT_NUM = 8546
DISCOVERY_PORT_NUM = 30303
ENGINE_RPC_PORT_NUM = 8550
# Port IDs
RPC_PORT_ID = "rpc"
WS_PORT_ID = "ws"
TCP_DISCOVERY_PORT_ID = "tcp-discovery"
UDP_DISCOVERY_PORT_ID = "udp-discovery"
ENGINE_RPC_PORT_ID = "engine-rpc"
PRIVATE_IP_ADDRESS_PLACEHOLDER = "KURTOSIS_IP_ADDR_PLACEHOLDER"
USED_PORTS = {
RPC_PORT_ID: new_port_spec(RPC_PORT_NUM, TCP_PROTOCOL),
WS_PORT_ID: new_port_spec(WS_PORT_NUM, TCP_PROTOCOL),
TCP_DISCOVERY_PORT_ID: new_port_spec(DISCOVERY_PORT_NUM, TCP_PROTOCOL),
UDP_DISCOVERY_PORT_ID: new_port_spec(DISCOVERY_PORT_NUM, UDP_PROTOCOL),
}
ENTRYPOINT_ARGS = ["sh", "-c"]
ERIGON_LOG_LEVELS = {
module_io.GlobalClientLogLevel.error: "1",
module_io.GlobalClientLogLevel.warn: "2",
module_io.GlobalClientLogLevel.info: "3",
module_io.GlobalClientLogLevel.debug: "4",
module_io.GlobalClientLogLevel.trace: "5",
}
ENR_FACT_NAME = "enr-fact"
ENODE_FACT_NAME = "enode-fact"
def launch(
launcher,
service_id,
image,
participant_log_level,
global_log_level,
existing_el_clients,
extra_params):
log_level = get_client_log_level_or_default(participant_log_level, global_log_level, ERIGON_LOG_LEVELS)
config = get_config(launcher.network_id, launcher.el_genesis_data,
image, existing_el_clients, log_level, extra_params)
service = add_service(service_id, config)
define_fact(service_id = service_id, fact_name = ENR_FACT_NAME, fact_recipe = struct(method= "POST", endpoint = "", field_extractor = ".result.enr", body = '{"method":"admin_nodeInfo","params":[],"id":1,"jsonrpc":"2.0"}', content_type = "application/json", port_id = RPC_PORT_ID))
enr = wait(service_id = service_id, fact_name = ENR_FACT_NAME)
define_fact(service_id = service_id, fact_name = ENODE_FACT_NAME, fact_recipe = struct(method= "POST", endpoint = "", field_extractor = ".result.enode", body = '{"method":"admin_nodeInfo","params":[],"id":1,"jsonrpc":"2.0"}', content_type = "application/json", port_id = RPC_PORT_ID))
enode = wait(service_id = service_id, fact_name = ENODE_FACT_NAME)
return new_el_client_context(
"erigon",
enr,
enode,
service.ip_address,
RPC_PORT_NUM,
WS_PORT_NUM,
ENGINE_RPC_PORT_NUM
)
def get_config(network_id, genesis_data, image, existing_el_clients, verbosity_level, extra_params):
network_id = network_id
genesis_json_filepath_on_client = path_join(GENESIS_DATA_MOUNT_DIRPATH, genesis_data.erigon_genesis_json_relative_filepath)
jwt_secret_json_filepath_on_client = path_join(GENESIS_DATA_MOUNT_DIRPATH, genesis_data.jwt_secret_relative_filepath)
init_datadir_cmd_str = "erigon init --datadir={0} {1}".format(
EXECUTION_DATA_DIRPATH_ON_CLIENT_CONTAINER,
genesis_json_filepath_on_client,
)
# TODO remove this based on https://github.com/kurtosis-tech/eth2-merge-kurtosis-module/issues/152
if len(existing_el_clients) == 0:
fail("Erigon needs at least one node to exist, which it treats as the bootnode")
boot_node = existing_el_clients[0]
launch_node_cmd = [
"erigon",
"--log.console.verbosity=" + verbosity_level,
"--datadir=" + EXECUTION_DATA_DIRPATH_ON_CLIENT_CONTAINER,
"--networkid=" + network_id,
"--http",
"--http.addr=0.0.0.0",
"--http.corsdomain=*",
# WARNING: The admin info endpoint is enabled so that we can easily get ENR/enode, which means
# that users should NOT store private information in these Kurtosis nodes!
"--http.api=admin,engine,net,eth",
"--ws",
"--allow-insecure-unlock",
"--nat=extip:" + PRIVATE_IP_ADDRESS_PLACEHOLDER,
"--authrpc.jwtsecret={0}".format(jwt_secret_json_filepath_on_client),
"--nodiscover",
"--staticpeers={0}".format(boot_node.enode),
]
if len(extra_params) > 0:
# this is a repeated<proto type>, we convert it into Starlark
launch_node_cmd.extend([param for param in extra_params])
command_arg = [
init_datadir_cmd_str,
" ".join(launch_node_cmd)
]
command_arg_str = " && ".join(command_arg)
return struct(
image = image,
ports = USED_PORTS,
cmd = [command_arg_str],
files = {
genesis_data.files_artifact_uuid: GENESIS_DATA_MOUNT_DIRPATH
},
entrypoint = ENTRYPOINT_ARGS,
private_ip_address_placeholder = PRIVATE_IP_ADDRESS_PLACEHOLDER
)
def new_erigon_launcher(network_id, el_genesis_data):
return struct(
network_id = network_id,
el_genesis_data = el_genesis_data,
)
load("github.com/kurtosis-tech/eth2-module/src/shared_utils/shared_utils.star", "new_port_spec", "path_join", "TCP_PROTOCOL", "UDP_PROTOCOL")
load("github.com/kurtosis-tech/eth2-module/src/module_io/parse_input.star", "get_client_log_level_or_default")
load("github.com/kurtosis-tech/eth2-module/src/participant_network/el/el_client_context.star", "new_el_client_context")
module_io = import_types("github.com/kurtosis-tech/eth2-module/types.proto")
RPC_PORT_NUM = 8545
WS_PORT_NUM = 8546
DISCOVERY_PORT_NUM = 30303
ENGINE_RPC_PORT_NUM = 8551
# Port IDs
RPC_PORT_ID = "rpc"
WS_PORT_ID = "ws"
TCP_DISCOVERY_PORT_ID = "tcp-discovery"
UDP_DISCOVERY_PORT_ID = "udp-discovery"
ENGINE_RPC_PORT_ID = "engine-rpc"
ENGINE_WS_PORT_ID = "engineWs"
# TODO(old) Scale this dynamically based on CPUs available and Geth nodes mining
NUM_MINING_THREADS = 1
GENESIS_DATA_MOUNT_DIRPATH = "/genesis"
PREFUNDED_KEYS_MOUNT_DIRPATH = "/prefunded-keys"
# The dirpath of the execution data directory on the client container
EXECUTION_DATA_DIRPATH_ON_CLIENT_CONTAINER = "/execution-data"
KEYSTORE_DIRPATH_ON_CLIENT_CONTAINER = EXECUTION_DATA_DIRPATH_ON_CLIENT_CONTAINER + "/keystore"
GETH_ACCOUNT_PASSWORD = "password" # Password that the Geth accounts will be locked with
GETH_ACCOUNT_PASSWORDS_FILE = "/tmp/password.txt" # Importing an account to
PRIVATE_IP_ADDRESS_PLACEHOLDER = "KURTOSIS_IP_ADDR_PLACEHOLDER"
USED_PORTS = {
RPC_PORT_ID: new_port_spec(RPC_PORT_NUM, TCP_PROTOCOL),
WS_PORT_ID: new_port_spec(WS_PORT_NUM, TCP_PROTOCOL),
TCP_DISCOVERY_PORT_ID: new_port_spec(DISCOVERY_PORT_NUM, TCP_PROTOCOL),
UDP_DISCOVERY_PORT_ID: new_port_spec(DISCOVERY_PORT_NUM, UDP_PROTOCOL),
ENGINE_RPC_PORT_ID: new_port_spec(ENGINE_RPC_PORT_NUM, TCP_PROTOCOL)
}
ENTRYPOINT_ARGS = ["sh", "-c"]
ENR_FACT_NAME = "enr-fact"
ENODE_FACT_NAME = "enode-fact"
VERBOSITY_LEVELS = {
module_io.GlobalClientLogLevel.error: "1",
module_io.GlobalClientLogLevel.warn: "2",
module_io.GlobalClientLogLevel.info: "3",
module_io.GlobalClientLogLevel.debug: "4",
module_io.GlobalClientLogLevel.trace: "5",
}
def launch(
launcher,
service_id,
image,
participant_log_level,
global_log_level,
# If empty then the node will be launched as a bootnode
existing_el_clients,
extra_params):
log_level = get_client_log_level_or_default(participant_log_level, global_log_level, VERBOSITY_LEVELS)
config = get_config(launcher.network_id, launcher.el_genesis_data, launcher.prefunded_geth_keys_artifact_uuid,
launcher.prefunded_account_info, image, existing_el_clients, log_level, extra_params)
service = add_service(service_id, config)
define_fact(service_id = service_id, fact_name = ENR_FACT_NAME, fact_recipe = struct(method= "POST", endpoint = "", field_extractor = ".result.enr", body = '{"method":"admin_nodeInfo","params":[],"id":1,"jsonrpc":"2.0"}', content_type = "application/json", port_id = RPC_PORT_ID))
enr = wait(service_id = service_id, fact_name = ENR_FACT_NAME)
define_fact(service_id = service_id, fact_name = ENODE_FACT_NAME, fact_recipe = struct(method= "POST", endpoint = "", field_extractor = ".result.enode", body = '{"method":"admin_nodeInfo","params":[],"id":1,"jsonrpc":"2.0"}', content_type = "application/json", port_id = RPC_PORT_ID))
enode = wait(service_id = service_id, fact_name = ENODE_FACT_NAME)
return new_el_client_context(
"geth",
enr,
enode,
service.ip_address,
RPC_PORT_NUM,
WS_PORT_NUM,
ENGINE_RPC_PORT_NUM
)
def get_config(network_id, genesis_data, prefunded_geth_keys_artifact_uuid, prefunded_account_info, image, existing_el_clients, verbosity_level, extra_params):
genesis_json_filepath_on_client = path_join(GENESIS_DATA_MOUNT_DIRPATH, genesis_data.geth_genesis_json_relative_filepath)
jwt_secret_json_filepath_on_client = path_join(GENESIS_DATA_MOUNT_DIRPATH, genesis_data.jwt_secret_relative_filepath)
account_addresses_to_unlock = []
for prefunded_account in prefunded_account_info:
account_addresses_to_unlock.append(prefunded_account.address)
accounts_to_unlock_str = ",".join(account_addresses_to_unlock)
init_datadir_cmd_str = "geth init --datadir={0} {1}".format(
EXECUTION_DATA_DIRPATH_ON_CLIENT_CONTAINER,
genesis_json_filepath_on_client,
)
# We need to put the keys into the right spot
copy_keys_into_keystore_cmd_str = "cp -r {0}/* {1}/".format(
PREFUNDED_KEYS_MOUNT_DIRPATH,
KEYSTORE_DIRPATH_ON_CLIENT_CONTAINER,
)
create_passwords_file_cmd_str = '{' + ' for i in $(seq 1 {0}); do echo "{1}" >> {2}; done; '.format(
len(prefunded_account_info),
GETH_ACCOUNT_PASSWORD,
GETH_ACCOUNT_PASSWORDS_FILE,
) + '}'
launch_node_cmd = [
"geth",
"--verbosity=" + verbosity_level,
"--unlock=" + accounts_to_unlock_str,
"--password=" + GETH_ACCOUNT_PASSWORDS_FILE,
"--datadir=" + EXECUTION_DATA_DIRPATH_ON_CLIENT_CONTAINER,
"--networkid=" + network_id,
"--http",
"--http.addr=0.0.0.0",
"--http.vhosts=*",
"--http.corsdomain=*",
# WARNING: The admin info endpoint is enabled so that we can easily get ENR/enode, which means
# that users should NOT store private information in these Kurtosis nodes!
"--http.api=admin,engine,net,eth",
"--ws",
"--ws.addr=0.0.0.0",
"--ws.port={0}".format(WS_PORT_NUM),
"--ws.api=engine,net,eth",
"--ws.origins=*",
"--allow-insecure-unlock",
"--nat=extip:" + PRIVATE_IP_ADDRESS_PLACEHOLDER,
"--verbosity=" + verbosity_level,
"--authrpc.port={0}".format(ENGINE_RPC_PORT_NUM),
"--authrpc.addr=0.0.0.0",
"--authrpc.vhosts=*",
"--authrpc.jwtsecret={0}".format(jwt_secret_json_filepath_on_client),
"--syncmode=full",
]
bootnode_enode = ""
if len(existing_el_clients) > 0:
bootnode_context = existing_el_clients[0]
bootnode_enode = bootnode_context.enode
launch_node_cmd.append(
'--bootnodes="{0}"'.format(bootnode_enode),
)
if len(extra_params) > 0:
# this is a repeated<proto type>, we convert it into Starlark
launch_node_cmd.extend([param for param in extra_params])
launch_node_cmd_str = " ".join(launch_node_cmd)
subcommand_strs = [
init_datadir_cmd_str,
copy_keys_into_keystore_cmd_str,
create_passwords_file_cmd_str,
launch_node_cmd_str,
]
command_str = " && ".join(subcommand_strs)
return struct(
image = image,
ports = USED_PORTS,
cmd = [command_str],
files = {
genesis_data.files_artifact_uuid: GENESIS_DATA_MOUNT_DIRPATH,
prefunded_geth_keys_artifact_uuid: PREFUNDED_KEYS_MOUNT_DIRPATH
},
entrypoint = ENTRYPOINT_ARGS,
private_ip_address_placeholder = PRIVATE_IP_ADDRESS_PLACEHOLDER
)
def new_geth_launcher(network_id, el_genesis_data, prefunded_geth_keys_artifact_uuid, prefunded_account_info):
return struct(
network_id = network_id,
el_genesis_data = el_genesis_data,
prefunded_account_info = prefunded_account_info,
prefunded_geth_keys_artifact_uuid = prefunded_geth_keys_artifact_uuid,
)
load("github.com/kurtosis-tech/eth2-module/src/shared_utils/shared_utils.star", "new_port_spec", "path_join", "TCP_PROTOCOL", "UDP_PROTOCOL")
load("github.com/kurtosis-tech/eth2-module/src/module_io/parse_input.star", "get_client_log_level_or_default")
load("github.com/kurtosis-tech/eth2-module/src/participant_network/el/el_client_context.star", "new_el_client_context")
module_io = import_types("github.com/kurtosis-tech/eth2-module/types.proto")
# The dirpath of the execution data directory on the client container
EXECUTION_DATA_DIRPATH_ON_CLIENT_CONTAINER = "/execution-data"
GENESIS_DATA_MOUNT_DIRPATH = "/genesis"
RPC_PORT_NUM = 8545
WS_PORT_NUM = 8546
DISCOVERY_PORT_NUM = 30303
ENGINE_RPC_PORT_NUM = 8551
# Port IDs
RPC_PORT_ID = "rpc"
WS_PORT_ID = "ws"
TCP_DISCOVERY_PORT_ID = "tcp-discovery"
UDP_DISCOVERY_PORT_ID = "udp-discovery"
ENGINE_RPC_PORT_ID = "engine-rpc"
PRIVATE_IP_ADDRESS_PLACEHOLDER = "KURTOSIS_IP_ADDR_PLACEHOLDER"
USED_PORTS = {
RPC_PORT_ID: new_port_spec(RPC_PORT_NUM, TCP_PROTOCOL),
WS_PORT_ID: new_port_spec(WS_PORT_NUM, TCP_PROTOCOL),
TCP_DISCOVERY_PORT_ID: new_port_spec(DISCOVERY_PORT_NUM, TCP_PROTOCOL),
UDP_DISCOVERY_PORT_ID: new_port_spec(DISCOVERY_PORT_NUM, UDP_PROTOCOL),
ENGINE_RPC_PORT_ID: new_port_spec(ENGINE_RPC_PORT_NUM, TCP_PROTOCOL)
}
NETHERMIND_LOG_LEVELS = {
module_io.GlobalClientLogLevel.error: "ERROR",
module_io.GlobalClientLogLevel.warn: "WARN",
module_io.GlobalClientLogLevel.info: "INFO",
module_io.GlobalClientLogLevel.debug: "DEBUG",
module_io.GlobalClientLogLevel.trace: "TRACE",
}
ENODE_FACT_NAME = "enode-fact"
def launch(
launcher,
service_id,
image,
participant_log_level,
global_log_level,
existing_el_clients,
extra_params):
log_level = get_client_log_level_or_default(participant_log_level, global_log_level, NETHERMIND_LOG_LEVELS)
config = get_config(launcher.el_genesis_data, image, existing_el_clients, log_level, extra_params)
service = add_service(service_id, config)
define_fact(service_id = service_id, fact_name = ENODE_FACT_NAME, fact_recipe = struct(method= "POST", endpoint = "", field_extractor = ".result.enode", body = '{"method":"admin_nodeInfo","params":[],"id":1,"jsonrpc":"2.0"}', content_type = "application/json", port_id = RPC_PORT_ID))
enode = wait(service_id = service_id, fact_name = ENODE_FACT_NAME)
return new_el_client_context(
"nethermind",
"", # nethermind has no ENR in the eth2-merge-kurtosis-module either
# Nethermind node info endpoint doesn't return ENR field https://docs.nethermind.io/nethermind/ethereum-client/json-rpc/admin
enode,
service.ip_address,
RPC_PORT_NUM,
WS_PORT_NUM,
ENGINE_RPC_PORT_NUM,
)
def get_config(genesis_data, image, existing_el_clients, log_level, extra_params):
if len(existing_el_clients) < 2:
fail("Nethermind node cannot be boot nodes, and due to a bug it requires two nodes to exist beforehand")
bootnode_1 = existing_el_clients[0]
bootnode_2 = existing_el_clients[1]
genesis_json_filepath_on_client = path_join(GENESIS_DATA_MOUNT_DIRPATH, genesis_data.nethermind_genesis_json_relative_filepath)
jwt_secret_json_filepath_on_client = path_join(GENESIS_DATA_MOUNT_DIRPATH, genesis_data.jwt_secret_relative_filepath)
command_args = [
"--config=kiln",
"--log=" + log_level,
"--datadir=" + EXECUTION_DATA_DIRPATH_ON_CLIENT_CONTAINER,
"--Init.ChainSpecPath=" + genesis_json_filepath_on_client,
"--Init.WebSocketsEnabled=true",
"--Init.DiagnosticMode=None",
"--JsonRpc.Enabled=true",
"--JsonRpc.EnabledModules=net,eth,consensus,subscribe,web3,admin",
"--JsonRpc.Host=0.0.0.0",
# TODO(old) Set Eth isMining?
"--JsonRpc.Port={0}".format(RPC_PORT_NUM),
"--JsonRpc.WebSocketsPort={0}".format(WS_PORT_NUM),
"--Network.ExternalIp={0}".format(PRIVATE_IP_ADDRESS_PLACEHOLDER),
"--Network.LocalIp={0}".format(PRIVATE_IP_ADDRESS_PLACEHOLDER),
"--Network.DiscoveryPort={0}".format(DISCOVERY_PORT_NUM),
"--Network.P2PPort={0}".format(DISCOVERY_PORT_NUM),
"--Merge.Enabled=true",
"--Merge.TerminalTotalDifficulty=0", # merge has happened already
"--Merge.TerminalBlockNumber=null",
"--JsonRpc.JwtSecretFile={0}".format(jwt_secret_json_filepath_on_client),
"--JsonRpc.AdditionalRpcUrls=[\"http://0.0.0.0:{0}|http;ws|net;eth;subscribe;engine;web3;client\"]".format(ENGINE_RPC_PORT_NUM),
"--Network.OnlyStaticPeers=true",
"--Network.StaticPeers={0},{1}".format(
bootnode_1.enode,
bootnode_2.enode,
),
]
if len(extra_params) > 0:
# we do this as extra_params is a repeated proto aray
command_args.extend([param for param in extra_params])
return struct(
image = image,
ports = USED_PORTS,
cmd = command_args,
files = {
genesis_data.files_artifact_uuid: GENESIS_DATA_MOUNT_DIRPATH
},
private_ip_address_placeholder = PRIVATE_IP_ADDRESS_PLACEHOLDER,
)
def new_nethermind_launcher(el_genesis_data):
return struct(
el_genesis_data = el_genesis_data
)
......@@ -16,14 +16,14 @@ NETWORK_ID_TO_NAME = {
}
def launch(mev_boost_launcher, service_id, network_id):
service_config = get_service_config(mev_boost_launcher, network_id)
config = get_config(mev_boost_launcher, network_id)
mev_boost_service = add_service(service_id, service_config)
mev_boost_service = add_service(service_id, config)
return new_mev_boost_context(mev_boost_service.ip_address, FLASHBOTS_MEV_BOOST_PORT)
def get_service_config(mev_boost_launcher, network_id):
def get_config(mev_boost_launcher, network_id):
network_name = NETWORK_ID_TO_NAME.get(network_id, "network-{0}".format(network_id))
command = ["mev-boost"]
......@@ -37,9 +37,9 @@ def get_service_config(mev_boost_launcher, network_id):
command.append(",".join(mev_boost_launcher.relay_end_points))
return struct(
container_image_name = FLASHBOTS_MEV_BOOST_IMAGE,
used_ports = USED_PORTS,
cmd_args = command
image = FLASHBOTS_MEV_BOOST_IMAGE,
ports = USED_PORTS,
cmd = command
)
......
......@@ -112,7 +112,7 @@ def generate_cl_genesis_data(
exec(launcher_service_id, cmd, SUCCESSFUL_EXEC_CMD_EXIT_CODE)
cl_genesis_generation_cmd_args = [
cl_genesis_generation_cmd = [
CL_GENESIS_GENERATION_BINARY_FILEPATH_ON_CONTAINER,
"merge",
"--config", path_join(OUTPUT_DIRPATH_ON_GENERATOR, GENESIS_CONFIG_YML_FILENAME),
......@@ -122,7 +122,7 @@ def generate_cl_genesis_data(
"--state-output", path_join(OUTPUT_DIRPATH_ON_GENERATOR, GENESIS_STATE_FILENAME)
]
exec(launcher_service_id, cl_genesis_generation_cmd_args, SUCCESSFUL_EXEC_CMD_EXIT_CODE)
exec(launcher_service_id, cl_genesis_generation_cmd, SUCCESSFUL_EXEC_CMD_EXIT_CODE)
cl_genesis_data_artifact_uuid = store_file_from_service(launcher_service_id, OUTPUT_DIRPATH_ON_GENERATOR)
......
......@@ -97,7 +97,7 @@ def generate_el_genesis_data(
jwt_secret_filepath_on_generator = path_join(OUTPUT_DIRPATH_ON_GENERATOR, JWT_SECRET_FILENAME)
jwt_secret_generation_cmd_args = [
jwt_secret_generation_cmd = [
"bash",
"-c",
"openssl rand -hex 32 | tr -d \"\\n\" | sed 's/^/0x/' > {0}".format(
......@@ -105,7 +105,7 @@ def generate_el_genesis_data(
)
]
exec(launcher_service_id, jwt_secret_generation_cmd_args, SUCCESSFUL_EXEC_CMD_EXIT_CODE)
exec(launcher_service_id, jwt_secret_generation_cmd, SUCCESSFUL_EXEC_CMD_EXIT_CODE)
elGenesisDataArtifactUuid = store_file_from_service(launcher_service_id, OUTPUT_DIRPATH_ON_GENERATOR)
......
......@@ -11,24 +11,24 @@ ENTRYPOINT_ARGS = [
# Launches a prelaunch data generator IMAGE, for use in various of the genesis generation
def launch_prelaunch_data_generator(files_artifact_mountpoints):
service_config = get_service_config(files_artifact_mountpoints)
config = get_config(files_artifact_mountpoints)
service_id = "{0}{1}".format(
SERVICE_ID_PREFIX,
time.now().unix_nano,
)
add_service(service_id, service_config)
add_service(service_id, config)
return service_id
def get_service_config(
def get_config(
files_artifact_mountpoints,
):
return struct(
# TODO remove this when used_ports is optional to pass
used_ports = {},
container_image_name = IMAGE,
entry_point_args = ENTRYPOINT_ARGS,
files_artifact_mount_dirpaths = files_artifact_mountpoints,
# TODO remove this when ports is optional to pass
ports = {},
image = IMAGE,
entrypoint = ENTRYPOINT_ARGS,
files = files_artifact_mountpoints,
)
......@@ -32,8 +32,8 @@ def launch_prometheus(config_template, cl_client_contexts):
config_files_artifact_uuid = render_templates(template_and_data_by_rel_dest_filepath)
service_config = get_service_config(config_files_artifact_uuid)
prometheus_service = add_service(SERVICE_ID, service_config)
config = get_config(config_files_artifact_uuid)
prometheus_service = add_service(SERVICE_ID, config)
private_ip_address = prometheus_service.ip_address
prometheus_service_http_port = prometheus_service.ports[HTTP_PORT_ID].number
......@@ -41,15 +41,15 @@ def launch_prometheus(config_template, cl_client_contexts):
return "http://{0}:{1}".format(private_ip_address, prometheus_service_http_port)
def get_service_config(config_files_artifact_uuid):
def get_config(config_files_artifact_uuid):
config_file_path = path_join(CONFIG_DIR_MOUNTPOINT_ON_PROMETHEUS, path_base(CONFIG_FILENAME))
return struct(
container_image_name = IMAGE_NAME,
used_ports = USED_PORTS,
files_artifact_mount_dirpaths = {
image = IMAGE_NAME,
ports = USED_PORTS,
files = {
config_files_artifact_uuid : CONFIG_DIR_MOUNTPOINT_ON_PROMETHEUS
},
cmd_args = [
cmd = [
# You can check all the cli flags starting the container and going to the flags section
# in Prometheus admin page "{{prometheusPublicURL}}/flags" section
"--config.file=" + config_file_path,
......
......@@ -10,13 +10,13 @@ SYNCHRONOUS_ENTRYPOINT_ARGS = [
# this is broken check - https://github.com/ethereum/merge-testnet-verifier/issues/4
def launch_testnet_verifier(params, el_client_contexts, cl_client_contexts):
service_config = get_asynchronous_verification_service_config(params, el_client_contexts, cl_client_contexts)
add_service(SERVICE_ID, service_config)
config = get_asynchronous_verification_config(params, el_client_contexts, cl_client_contexts)
add_service(SERVICE_ID, config)
def run_synchronous_testnet_verification(params, el_client_contexts, cl_client_contexts):
service_config = get_synchronous_verification_service_config()
add_service(SERVICE_ID, service_config)
config = get_synchronous_verification_config()
add_service(SERVICE_ID, config)
command = get_cmd(params, el_client_contexts, cl_client_contexts, True)
exec(SERVICE_ID, command)
......@@ -49,20 +49,20 @@ def get_cmd(params, el_client_contexts, cl_client_contexts, add_binary_name):
def get_asynchronous_verification_service_config(params, el_client_contexts, cl_client_contexts):
def get_asynchronous_verification_config(params, el_client_contexts, cl_client_contexts):
commands = get_cmd(params, el_client_contexts, cl_client_contexts, False)
return struct(
container_image_name = IMAGE_NAME,
cmd_args = commands,
# TODO remove this when used_ports is optional in add_service
used_ports = {},
image = IMAGE_NAME,
cmd = commands,
# TODO remove this when ports is optional in add_service
ports = {},
)
def get_synchronous_verification_service_config():
def get_synchronous_verification_config():
return struct(
container_image_name = IMAGE_NAME,
entry_point_args = SYNCHRONOUS_ENTRYPOINT_ARGS,
# TODO remove this when used_ports is optional in add_service
used_ports = {},
image = IMAGE_NAME,
entrypoint = SYNCHRONOUS_ENTRYPOINT_ARGS,
# TODO remove this when ports is optional in add_service
ports = {},
)
......@@ -2,11 +2,11 @@ IMAGE_NAME = "kurtosistech/tx-fuzz:0.2.0"
SERVICE_ID = "transaction-spammer"
def launch_transaction_spammer(prefunded_addresses, el_client_context):
service_config = get_service_config(prefunded_addresses, el_client_context)
add_service(SERVICE_ID, service_config)
config = get_config(prefunded_addresses, el_client_context)
add_service(SERVICE_ID, config)
def get_service_config(prefunded_addresses, el_client_context):
def get_config(prefunded_addresses, el_client_context):
private_keys_strs = []
address_strs = []
......@@ -17,13 +17,13 @@ def get_service_config(prefunded_addresses, el_client_context):
comma_separated_private_keys = ",".join(private_keys_strs)
comma_separated_addresses = ",".join(address_strs)
return struct(
container_image_name = IMAGE_NAME,
cmd_args = [
image = IMAGE_NAME,
cmd = [
"http://{0}:{1}".format(el_client_context.ip_addr, el_client_context.rpc_port_num),
"spam",
comma_separated_private_keys,
comma_separated_addresses
],
used_ports = {}
ports = {}
)
......@@ -8,7 +8,7 @@ CONFIG_NAME: testnet # needs to exist because of Prysm. Otherwise it conflicts w
# `2**14` (= 16,384)
MIN_GENESIS_ACTIVE_VALIDATOR_COUNT: {{ .NumValidatorKeysToPreregister }}
MIN_GENESIS_TIME: {{ .UnixTimestamp }}
GENESIS_FORK_VERSION: 0x30000038
GENESIS_FORK_VERSION: 0x10000038
GENESIS_DELAY: 300
# Forking
......
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