Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .github/tests/ews.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
participants:
- count: 3
- el_type: dummy
cl_type: lighthouse
cl_image: ethpandaops/lighthouse:eth-act-optional-proofs
cl_extra_params:
- --activate-zkvm

additional_services:
- ews
- dora
mev_type: flashbots
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -938,6 +938,7 @@ additional_services:
- dora
- dugtrio
- erpc
- ews
- forkmon
- forky
- full_beaconchain_explorer
Expand Down Expand Up @@ -1038,6 +1039,20 @@ bootnodoor_params:
# A list of optional extra args the bootnodoor container should spin up with
extra_args: []

# Configuration place for execution-witness-sentry (ews) - https://github.com/eth-act/zkboost
ews_params:
# EWS docker image to use
# Defaults to the latest image
image: "ghcr.io/eth-act/zkboost/execution-witness-sentry:latest"
# Number of execution witnesses to retain
# Defaults to 10
retain: 10
# Number of proofs to generate
# Defaults to 1
num_proofs: 1
# A list of optional extra env_vars the ews container should spin up with
env: {}

# Configuration place for tempo tracing backend
tempo_params:
# How long to retain traces
Expand Down
18 changes: 18 additions & 0 deletions main.star
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ get_prefunded_accounts = import_module(
"./src/prefunded_accounts/get_prefunded_accounts.star"
)
spamoor = import_module("./src/spamoor/spamoor.star")
ews = import_module("./src/ews/ews_launcher.star")

GRAFANA_USER = "admin"
GRAFANA_PASSWORD = "admin"
Expand Down Expand Up @@ -926,6 +927,23 @@ def run(plan, args={}):
index,
osaka_time,
)
elif additional_service == "ews":
plan.print("Launching execution-witness-sentry")
ews_config_template = read_file(static_files.EWS_CONFIG_TEMPLATE_FILEPATH)
ews.launch_ews(
plan,
ews_config_template,
all_participants,
args_with_right_defaults.participants,
network_params,
args_with_right_defaults.ews_params,
global_node_selectors,
global_tolerations,
args_with_right_defaults.port_publisher,
index,
args_with_right_defaults.docker_cache_params,
)
plan.print("Successfully launched execution-witness-sentry")
else:
fail("Invalid additional service %s" % (additional_service))
if launch_prometheus_grafana:
Expand Down
11 changes: 10 additions & 1 deletion src/dora/dora_launcher.star
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ def launch_dora(
)
)

# Skip dummy EL clients - they don't have real execution endpoints
el_type = participant_configs[index].el_type
if el_type == "dummy":
continue

snooper_el_engine_context = participant_contexts[
index
].snooper_el_engine_context
Expand Down Expand Up @@ -196,9 +201,13 @@ def get_config(
def new_config_template_data(
network, listen_port_num, cl_client_info, el_client_info, mev_endpoint_info
):
public_rpc = ""
if len(el_client_info) > 0:
public_rpc = el_client_info[0]["Execution_HTTP_URL"]

return {
"Network": network,
"PublicRPC": el_client_info[0]["Execution_HTTP_URL"],
"PublicRPC": public_rpc,
"ListenPortNum": listen_port_num,
"CLClientInfo": cl_client_info,
"ELClientInfo": el_client_info,
Expand Down
186 changes: 186 additions & 0 deletions src/ews/ews_launcher.star
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
shared_utils = import_module("../shared_utils/shared_utils.star")
constants = import_module("../package_io/constants.star")

SERVICE_NAME = "ews"

HTTP_PORT_NUMBER = 3000

EWS_CONFIG_FILENAME = "config.toml"

EWS_CONFIG_MOUNT_DIRPATH_ON_SERVICE = "/config"

MIN_CPU = 100
MAX_CPU = 1000
MIN_MEMORY = 256
MAX_MEMORY = 2048

USED_PORTS = {
constants.HTTP_PORT_ID: shared_utils.new_port_spec(
HTTP_PORT_NUMBER,
shared_utils.TCP_PROTOCOL,
shared_utils.HTTP_APPLICATION_PROTOCOL,
wait=None,
)
}


def launch_ews(
plan,
config_template,
participant_contexts,
participant_configs,
network_params,
ews_params,
global_node_selectors,
global_tolerations,
port_publisher,
additional_service_index,
docker_cache_params,
):
tolerations = shared_utils.get_tolerations(global_tolerations=global_tolerations)

first_real_el_info = None
head_subscription_cl_info = None
dummy_cl_client_info = []

for index, participant in enumerate(participant_contexts):
full_name, cl_client, el_client, _ = shared_utils.get_client_names(
participant, index, participant_contexts, participant_configs
)
el_type = participant_configs[index].el_type

if el_type == "dummy":
dummy_cl_client_info.append(
new_cl_client_info(
full_name,
cl_client.beacon_http_url,
)
)
else:
if first_real_el_info == None:
first_real_el_info = new_el_client_info(
full_name,
"http://{0}:{1}".format(el_client.dns_name, el_client.rpc_port_num),
"ws://{0}:{1}".format(el_client.dns_name, el_client.ws_port_num),
)
head_subscription_cl_info = new_cl_client_info(
full_name,
cl_client.beacon_http_url,
)

template_data = new_config_template_data(
network_params.network,
ews_params.retain,
ews_params.num_proofs,
first_real_el_info,
head_subscription_cl_info,
dummy_cl_client_info,
)

template_and_data = shared_utils.new_template_and_data(
config_template, template_data
)
template_and_data_by_rel_dest_filepath = {}
template_and_data_by_rel_dest_filepath[EWS_CONFIG_FILENAME] = template_and_data

config_files_artifact_name = plan.render_templates(
template_and_data_by_rel_dest_filepath, "ews-config"
)
config = get_config(
config_files_artifact_name,
ews_params,
global_node_selectors,
tolerations,
port_publisher,
additional_service_index,
docker_cache_params,
)

plan.add_service(SERVICE_NAME, config)


def get_config(
config_files_artifact_name,
ews_params,
node_selectors,
tolerations,
port_publisher,
additional_service_index,
docker_cache_params,
):
config_file_path = shared_utils.path_join(
EWS_CONFIG_MOUNT_DIRPATH_ON_SERVICE,
EWS_CONFIG_FILENAME,
)

public_ports = shared_utils.get_additional_service_standard_public_port(
port_publisher,
constants.HTTP_PORT_ID,
additional_service_index,
0,
)

IMAGE_NAME = shared_utils.docker_cache_image_calc(
docker_cache_params,
ews_params.image,
)

return ServiceConfig(
image=IMAGE_NAME,
ports=USED_PORTS,
public_ports=public_ports,
files={
EWS_CONFIG_MOUNT_DIRPATH_ON_SERVICE: config_files_artifact_name,
},
entrypoint=["/app/execution-witness-sentry"],
cmd=["--config", config_file_path],
env_vars=ews_params.env,
min_cpu=MIN_CPU,
max_cpu=MAX_CPU,
min_memory=MIN_MEMORY,
max_memory=MAX_MEMORY,
node_selectors=node_selectors,
tolerations=tolerations,
# ready_conditions=ReadyCondition(
# recipe=GetHttpRequestRecipe(
# port_id=constants.HTTP_PORT_ID,
# endpoint="/health",
# ),
# field="code",
# assertion="==",
# target_value=200,
# ),
)


def new_config_template_data(
network,
retain,
num_proofs,
el_client_info,
head_subscription_cl_info,
zkvm_cl_client_info,
):
return {
"Network": network,
"Retain": retain,
"NumProofs": num_proofs,
"ELClientInfo": el_client_info,
"HeadSubscriptionCLInfo": head_subscription_cl_info,
"ZkvmCLClientInfo": zkvm_cl_client_info,
}


def new_el_client_info(full_name, el_http_url, el_ws_url):
return {
"FullName": full_name,
"EL_HTTP_URL": el_http_url,
"EL_WS_URL": el_ws_url,
}


def new_cl_client_info(full_name, cl_http_url):
return {
"FullName": full_name,
"CL_HTTP_URL": cl_http_url,
}
1 change: 1 addition & 0 deletions src/package_io/constants.star
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ DEFAULT_MEV_RS_IMAGE_MINIMAL = "ethpandaops/mev-rs:main-minimal"
DEFAULT_COMMIT_BOOST_MEV_BOOST_IMAGE = "ghcr.io/commit-boost/pbs:latest"
DEFAULT_MOCK_MEV_IMAGE = "ethpandaops/rustic-builder:main"
DEFAULT_HELIX_RELAY_IMAGE = "ghcr.io/gattaca-com/helix-relay:main"
DEFAULT_EWS_IMAGE = "ghcr.io/eth-act/zkboost/execution-witness-sentry:latest"
DEFAULT_MEV_PUBKEY = "0xa55c1285d84ba83a5ad26420cd5ad3091e49c55a813eee651cd467db38a8c8e63192f47955e9376f6b42f6d190571cb5"
DEFAULT_MEV_SECRET_KEY = (
"0x607a11b45a7219cc61a3d9c5fd08c7eebd602a6a19a977f8d3771d5711a550f2"
Expand Down
38 changes: 38 additions & 0 deletions src/package_io/input_parser.star
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ ATTR_TO_BE_SKIPPED_AT_ROOT = (
"spamoor_params",
"bootnodoor_params",
"mempool_bridge_params",
"ews_params",
)


Expand Down Expand Up @@ -120,6 +121,7 @@ def input_parser(plan, input_args):
result["port_publisher"] = get_port_publisher_params("default")
result["spamoor_params"] = get_default_spamoor_params()
result["mempool_bridge_params"] = get_default_mempool_bridge_params()
result["ews_params"] = get_default_ews_params()

if constants.NETWORK_NAME.shadowfork in result["network_params"]["network"]:
shadow_base = result["network_params"]["network"].split("-shadowfork")[0]
Expand Down Expand Up @@ -205,6 +207,10 @@ def input_parser(plan, input_args):
for sub_attr in input_args["checkpointz_params"]:
sub_value = input_args["checkpointz_params"][sub_attr]
result["checkpointz_params"][sub_attr] = sub_value
elif attr == "ews_params":
for sub_attr in input_args["ews_params"]:
sub_value = input_args["ews_params"][sub_attr]
result["ews_params"][sub_attr] = sub_value

if result.get("disable_peer_scoring"):
result = enrich_disable_peer_scoring(result)
Expand Down Expand Up @@ -433,6 +439,23 @@ def input_parser(plan, input_args):
)
)

if "ews" in result["additional_services"]:
has_non_dummy_el = False
has_dummy_el = False
for participant in result["participants"]:
if participant["el_type"] != "dummy":
has_non_dummy_el = True
else:
has_dummy_el = True
if not has_non_dummy_el:
fail(
"ews (execution-witness-sentry) is enabled but all participants are using dummy EL. At least one participant must use a real EL client (geth, reth, nethermind, etc.) to produce blocks."
)
if not has_dummy_el:
fail(
"ews (execution-witness-sentry) is enabled but no participants are using dummy EL. At least one participant must use dummy EL to receive execution witnesses."
)

if (
"mempool_bridge" in result["additional_services"]
and result["network_params"]["network"] not in constants.PUBLIC_NETWORKS
Expand Down Expand Up @@ -866,6 +889,12 @@ def input_parser(plan, input_args):
max_mem=result["bootnodoor_params"]["max_mem"],
extra_args=result["bootnodoor_params"]["extra_args"],
),
ews_params=struct(
image=result["ews_params"]["image"],
retain=result["ews_params"]["retain"],
num_proofs=result["ews_params"]["num_proofs"],
env=result["ews_params"]["env"],
),
)


Expand Down Expand Up @@ -1805,6 +1834,15 @@ def get_default_bootnodoor_params():
}


def get_default_ews_params():
return {
"image": constants.DEFAULT_EWS_IMAGE,
"retain": 10,
"num_proofs": 1,
"env": {},
}


def get_port_publisher_params(parameter_type, input_args=None):
port_publisher_parameters = {
"nat_exit_ip": "KURTOSIS_IP_ADDR_PLACEHOLDER",
Expand Down
7 changes: 7 additions & 0 deletions src/package_io/sanity_check.star
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,12 @@ SUBCATEGORY_PARAMS = {
"max_mem",
"extra_args",
],
"ews_params": [
"image",
"retain",
"num_proofs",
"env",
],
}

ADDITIONAL_SERVICES_PARAMS = [
Expand Down Expand Up @@ -428,6 +434,7 @@ ADDITIONAL_SERVICES_PARAMS = [
"tracoor",
"mempool_bridge",
"spamoor",
"ews",
]

ADDITIONAL_CATEGORY_PARAMS = {
Expand Down
Loading