diff --git a/src/env_vars.py b/src/env_vars.py index 8a8ad4726..32e96a7e1 100644 --- a/src/env_vars.py +++ b/src/env_vars.py @@ -33,4 +33,4 @@ PG_USER = get_env_var("POSTGRES_USER", "postgres") PG_PASS = get_env_var("POSTGRES_PASSWORD", "test123") # example for .env file -# RLN_CREDENTIALS = {"rln-relay-cred-password": "password", "rln-relay-eth-client-address": "wss://sepolia.infura.io/ws/v3/api_key", "rln-relay-eth-contract-address": "0xF471d71E9b1455bBF4b85d475afb9BB0954A29c4", "rln-relay-eth-private-key-1": "1111111111111111111111111111111111111111111111111111111111111111", "rln-relay-eth-private-key-2": "1111111111111111111111111111111111111111111111111111111111111111"} +# RLN_CREDENTIALS = {"rln-relay-cred-password": "password", "rln-relay-eth-client-address": "https://rpc.sepolia.linea.build", "rln-relay-eth-contract-address": "0xB9cd878C90E49F797B4431fBF4fb333108CB90e6", "rln-relay-eth-private-key-1": "", "rln-relay-eth-private-key-2": "", "rln-relay-eth-private-key-3": "", "rln-relay-eth-private-key-4": "", "rln-relay-eth-private-key-5": ""} diff --git a/src/node/waku_node.py b/src/node/waku_node.py index c6bfe7f41..f2a5356fc 100644 --- a/src/node/waku_node.py +++ b/src/node/waku_node.py @@ -5,11 +5,13 @@ import random import re import shutil import string +import subprocess import pytest import requests from src.libs.common import delay from src.libs.custom_logger import get_custom_logger -from tenacity import retry, stop_after_delay, wait_fixed +from tenacity import retry, stop_after_delay, wait_fixed, sleep +from docker.errors import NotFound as DockerNotFound from src.node.api_clients.rest import REST from src.node.docker_mananger import DockerManager from src.env_vars import DOCKER_LOG_DIR @@ -38,8 +40,24 @@ def sanitize_docker_flags(input_flags): @retry(stop=stop_after_delay(180), wait=wait_fixed(0.5), reraise=True) -def rln_credential_store_ready(creds_file_path, single_check=False): +def rln_credential_store_ready(creds_file_path, single_check=False, require_credentials=False): if os.path.exists(creds_file_path): + subprocess.run(["sudo", "-n", "chmod", "a+r", creds_file_path], check=False) + if require_credentials: + try: + with open(creds_file_path, "r", encoding="utf-8") as creds_file: + keystore_data = json.load(creds_file) + except (OSError, json.JSONDecodeError) as ex: + if single_check: + return False + raise ValueError(f"Failed to parse RLN keystore at {creds_file_path}: {ex}") + + credentials = keystore_data.get("credentials", {}) if isinstance(keystore_data, dict) else {} + if not credentials: + if single_check: + return False + raise ValueError(f"RLN keystore exists but has no credentials yet: {creds_file_path}") + return True elif not single_check: raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), creds_file_path) @@ -83,8 +101,10 @@ class WakuNode: self._log_path = os.path.join(DOCKER_LOG_DIR, f"{docker_log_prefix}__{self._image_name.replace('/', '_')}.log") self._docker_manager = DockerManager(self._image_name) self._container = None + self.rln_membership_index = None self.start_args = {} self._wrapper_node = None + self._rln_creds_set = False logger.debug(f"WakuNode instance initialized with log path {self._log_path}") @property @@ -178,13 +198,14 @@ class WakuNode: del default_args["pubsub-topic"] rln_args, rln_creds_set, keystore_path = self.parse_rln_credentials(default_args, False) + self._rln_creds_set = rln_creds_set default_args.pop("rln-creds-id", None) default_args.pop("rln-creds-source", None) default_args.pop("rln-keystore-prefix", None) if rln_creds_set: - rln_credential_store_ready(keystore_path) + rln_credential_store_ready(keystore_path, require_credentials=True) default_args.update(rln_args) else: logger.info(f"RLN credentials not set or credential store not available, starting without RLN") @@ -208,7 +229,7 @@ class WakuNode: DS.waku_nodes.append(self) delay(1) try: - self.ensure_ready(timeout_duration=wait_for_node_sec) + self.ensure_ready(timeout_duration=wait_for_node_sec, rln_required=self._rln_creds_set) except Exception as ex: logger.error(f"REST service did not become ready in time: {ex}") raise @@ -284,13 +305,32 @@ class WakuNode: logger.debug(f"Waiting for keystore {keystore_path}") try: - rln_credential_store_ready(keystore_path) + rln_credential_store_ready(keystore_path, require_credentials=True) + self.rln_membership_index = str(self.get_rln_membership_index_from_log()) + logger.debug(f"Detected RLN membership index from registration logs: {self.rln_membership_index}") + self.stop() except Exception as ex: logger.error(f"File {keystore_path} with RLN credentials did not become available in time {ex}") raise else: logger.warn("RLN credentials not set, no action performed") + return self.rln_membership_index + + @retry(stop=stop_after_delay(10), wait=wait_fixed(0.2), reraise=True) + def get_rln_membership_index_from_log(self): + if not os.path.exists(self._log_path): + raise FileNotFoundError(errno.ENOENT, os.strerror(errno.ENOENT), self._log_path) + + with open(self._log_path, "r", encoding="utf-8", errors="ignore") as log_file: + log_data = log_file.read() + + matches = re.findall(r"membershipIndex=(\d+)", log_data) + if not matches: + raise ValueError("Could not infer RLN membership index from registration logs") + + return int(matches[-1]) + @retry(stop=stop_after_delay(5), wait=wait_fixed(0.1), reraise=True) def stop(self): if self._is_wrapper: @@ -301,7 +341,12 @@ class WakuNode: def _stop_docker(self): if self._container: logger.debug(f"Stopping container with id {self._container.short_id}") - self._container.stop() + try: + self._container.stop() + except DockerNotFound: + logger.debug(f"Container {self._container.short_id} already exited and removed, treating as stopped.") + self._container = None + return try: self._container.remove() except: @@ -344,7 +389,7 @@ class WakuNode: logger.debug(f"Unpause container with id {self._container.short_id}") self._container.unpause() - def ensure_ready(self, timeout_duration=10): + def ensure_ready(self, timeout_duration=10, rln_required=False): @retry(stop=stop_after_delay(timeout_duration), wait=wait_fixed(0.1), reraise=True) def check_healthy(node=self): self.health_response = node.health() @@ -357,9 +402,12 @@ class WakuNode: if self.health_response.get("nodeHealth") != "READY": raise AssertionError("Waiting for the node health status: READY") - # for p in self.health_response.get("protocolsHealth"): - # if p.get("Rln Relay") != "READY": - # raise AssertionError("Waiting for the Rln relay status: READY") + for p in self.health_response.get("protocolsHealth"): + if rln_required and "Rln Relay" in p: + if p["Rln Relay"] != "READY": + raise AssertionError("Waiting for the Rln relay status: READY") + # TODO: Remove once Rln Relay reflects true RLN status + sleep(20) logger.info("Node protocols are initialized !!") @@ -532,6 +580,21 @@ class WakuNode: def is_nwaku(self): return "nwaku" in self.image + def prepare_rln_storage_paths(self, cwd, keystore_prefix, selected_id, reset_existing=False): + keystore_dir = os.path.join(cwd, f"keystore_{keystore_prefix}_{selected_id}") + rln_tree_dir = os.path.join(cwd, f"rln_tree_{keystore_prefix}_{selected_id}") + + if reset_existing: + for path, path_name in [(keystore_dir, "keystore"), (rln_tree_dir, "rln tree")]: + if os.path.exists(path): + logger.warning(f"Resetting existing RLN {path_name} directory before registration: {path}") + shutil.rmtree(path, ignore_errors=True) + + os.makedirs(keystore_dir, exist_ok=True) + os.makedirs(rln_tree_dir, exist_ok=True) + + return keystore_dir, rln_tree_dir + def parse_rln_credentials(self, default_args, is_registration): rln_args = {} keystore_path = None @@ -544,6 +607,13 @@ class WakuNode: return rln_args, False, keystore_path imported_creds = json.loads(rln_creds_source) + rln_chain_id = imported_creds.get("rln-relay-chain-id") + if rln_chain_id is None: + eth_client_address = imported_creds.get("rln-relay-eth-client-address", "") + if "linea" in eth_client_address: + rln_chain_id = "59141" + elif "sepolia" in eth_client_address: + rln_chain_id = "11155111" if len(imported_creds) < 4 or any(value is None for value in imported_creds.values()): logger.warn(f"One or more of required RLN credentials were not set properly") @@ -552,6 +622,13 @@ class WakuNode: eth_private_key = select_private_key(imported_creds, selected_id) cwd = os.getcwd() + keystore_prefix = default_args.get("rln-keystore-prefix") + + if not keystore_prefix: + logger.warn("rln-keystore-prefix is missing, cannot mount RLN state and keystore") + return rln_args, False, keystore_path + + keystore_dir, rln_tree_dir = self.prepare_rln_storage_paths(cwd, keystore_prefix, selected_id, reset_existing=is_registration) if self.is_nwaku(): if is_registration: @@ -574,6 +651,8 @@ class WakuNode: { "rln-relay-cred-path": "/keystore/keystore.json", "rln-relay-cred-password": imported_creds["rln-relay-cred-password"], + "rln-relay-eth-client-address": imported_creds["rln-relay-eth-client-address"], + "rln-relay-eth-contract-address": imported_creds["rln-relay-eth-contract-address"], } ) else: @@ -587,12 +666,15 @@ class WakuNode: } ) - keystore_path = cwd + "/keystore_" + default_args["rln-keystore-prefix"] + "_" + selected_id + "/keystore.json" + if rln_chain_id is not None: + rln_args["rln-relay-chain-id"] = str(rln_chain_id) + + keystore_path = os.path.join(keystore_dir, "keystore.json") self._volumes.extend( [ - cwd + "/rln_tree_" + default_args["rln-keystore-prefix"] + "_" + selected_id + ":/etc/rln_tree", - cwd + "/keystore_" + default_args["rln-keystore-prefix"] + "_" + selected_id + ":/keystore", + f"{rln_tree_dir}:/etc/rln_tree", + f"{keystore_dir}:/keystore", ] ) diff --git a/src/steps/rln.py b/src/steps/rln.py index cd2ca138e..1bf051df2 100644 --- a/src/steps/rln.py +++ b/src/steps/rln.py @@ -26,6 +26,7 @@ class StepsRLN(StepsCommon): multiaddr_list = [] lightpush_nodes = [] keystore_prefixes = [] + rln_membership_indexes = [] @allure.step def generate_keystore_prefixes(self, count=2): @@ -38,15 +39,19 @@ class StepsRLN(StepsCommon): @allure.step def register_rln_relay_nodes(self, count, orig_prefixes): if count > 0: - logger.debug(111111111111111) self.keystore_prefixes = self.generate_keystore_prefixes(count) + self.rln_membership_indexes = [] for i, prefix in enumerate(self.keystore_prefixes): - logger.debug(000000000000000000000) - self.register_rln_single_node(prefix=prefix, rln_creds_source=RLN_CREDENTIALS, rln_creds_id=f"{i+1}") + membership_index = self.register_rln_single_node(prefix=prefix, rln_creds_source=RLN_CREDENTIALS, rln_creds_id=f"{i+1}") + self.rln_membership_indexes.append(membership_index) else: - self.keystore_prefixes = orig_prefixes + self.keystore_prefixes = orig_prefixes.get("keystore_prefixes", []) + self.rln_membership_indexes = orig_prefixes.get("rln_membership_indexes", []) - return self.keystore_prefixes + return { + "keystore_prefixes": self.keystore_prefixes, + "rln_membership_indexes": self.rln_membership_indexes, + } @allure.step def setup_main_rln_relay_nodes(self, **kwargs): @@ -60,7 +65,7 @@ class StepsRLN(StepsCommon): relay="true", rln_creds_source=RLN_CREDENTIALS, rln_creds_id="1", - rln_relay_membership_index="1", + rln_relay_membership_index=self.resolve_rln_membership_index(0, **kwargs), rln_keystore_prefix=self.keystore_prefixes[0], **kwargs, ) @@ -78,7 +83,7 @@ class StepsRLN(StepsCommon): discv5_bootstrap_node=self.enr_uri, rln_creds_source=RLN_CREDENTIALS, rln_creds_id="2", - rln_relay_membership_index="1", + rln_relay_membership_index=self.resolve_rln_membership_index(1, **kwargs), rln_keystore_prefix=self.keystore_prefixes[1], **kwargs, ) @@ -101,7 +106,7 @@ class StepsRLN(StepsCommon): discv5_bootstrap_node=self.enr_uri, rln_creds_source=RLN_CREDENTIALS, rln_creds_id=f"{index + 3}", - rln_relay_membership_index="1", + rln_relay_membership_index=self.resolve_rln_membership_index(index + 2, **kwargs), rln_keystore_prefix=self.keystore_prefixes[index + 2], **kwargs, ) @@ -118,7 +123,7 @@ class StepsRLN(StepsCommon): lightpushnode=self.multiaddr_list[0], rln_creds_source=RLN_CREDENTIALS, rln_creds_id="2", - rln_relay_membership_index="1", + rln_relay_membership_index=self.resolve_rln_membership_index(1, **kwargs), rln_keystore_prefix=self.keystore_prefixes[1], **kwargs, ) @@ -131,7 +136,23 @@ class StepsRLN(StepsCommon): def register_rln_single_node(self, prefix="", **kwargs): logger.debug("Registering RLN credentials for single node") self.node = WakuNode(DEFAULT_NWAKU, f"node_{gen_step_id()}") - self.node.register_rln(rln_keystore_prefix=prefix, rln_creds_source=kwargs["rln_creds_source"], rln_creds_id=kwargs["rln_creds_id"]) + return self.node.register_rln(rln_keystore_prefix=prefix, rln_creds_source=kwargs["rln_creds_source"], rln_creds_id=kwargs["rln_creds_id"]) + + @allure.step + def resolve_rln_membership_index(self, index, **kwargs): + explicit_index = kwargs.get("rln_relay_membership_index") + if explicit_index is not None: + return str(explicit_index) + + if len(self.rln_membership_indexes) > index and self.rln_membership_indexes[index] is not None: + inferred_index = str(self.rln_membership_indexes[index]) + logger.debug(f"Using inferred RLN membership index for position {index}: {inferred_index}") + return inferred_index + + raise ValueError( + f"RLN membership index for position {index} is not available. " + "Register credentials and persist rln_membership_indexes together with keystore_prefixes before node startup." + ) @allure.step def check_rln_registration(self, prefix, key_id): diff --git a/src/test_data.py b/src/test_data.py index 4166c30ae..659dd39ae 100644 --- a/src/test_data.py +++ b/src/test_data.py @@ -168,7 +168,7 @@ SAMPLE_TIMESTAMPS = [ {"description": "Missing", "value": None, "valid_for": []}, ] -PUBSUB_TOPICS_RLN = ["/waku/2/rs/1/0"] +PUBSUB_TOPICS_RLN = [f"/waku/2/rs/{DEFAULT_CLUSTER_ID}/0"] LOG_ERROR_KEYWORDS = [ "crash", diff --git a/tests/relay/test_rln.py b/tests/relay/test_rln.py index 6f48ea8d2..311fcf77d 100644 --- a/tests/relay/test_rln.py +++ b/tests/relay/test_rln.py @@ -13,7 +13,6 @@ from src.test_data import SAMPLE_INPUTS logger = get_custom_logger(__name__) -@pytest.mark.skip(reason="RLN functional changes. To be updated by Roman Zajic") @pytest.mark.xdist_group(name="RLN serial tests") class TestRelayRLN(StepsRLN, StepsRelay): SAMPLE_INPUTS_RLN = SAMPLE_INPUTS + SAMPLE_INPUTS + SAMPLE_INPUTS @@ -22,7 +21,14 @@ class TestRelayRLN(StepsRLN, StepsRelay): def test_valid_payloads_lightpush_at_spam_rate(self, pytestconfig): message_limit = 1 epoch_sec = 1 - pytestconfig.cache.set("keystore-prefixes", self.register_rln_relay_nodes(2, [])) + rln_state = self.register_rln_relay_nodes(2, []) + pytestconfig.cache.set( + "keystore-prefixes", + { + "keystore_prefixes": rln_state["keystore_prefixes"], + "rln_membership_indexes": rln_state["rln_membership_indexes"], + }, + ) self.setup_first_rln_relay_node(lightpush="true", rln_relay_user_message_limit=message_limit, rln_relay_epoch_sec=epoch_sec) self.setup_second_rln_lightpush_node(rln_relay_user_message_limit=message_limit, rln_relay_epoch_sec=epoch_sec) self.subscribe_main_relay_nodes() @@ -37,11 +43,17 @@ class TestRelayRLN(StepsRLN, StepsRelay): if i > message_limit and (now - start) <= epoch_sec: raise AssertionError("Publish with RLN enabled at spam rate worked!!!") except Exception as e: - assert "RLN validation failed" or "NonceLimitReached" in str(e) + error_str = str(e) + assert "RLN validation failed" in error_str or "NonceLimitReached" in error_str + if "NonceLimitReached" in error_str: + assert "503" in error_str, f"Expected HTTP 503 for NonceLimitReached, got: {error_str}" def test_valid_payloads_at_slow_rate(self, pytestconfig): message_limit = 20 - self.register_rln_relay_nodes(0, pytestconfig.cache.get("keystore-prefixes", [])) + self.register_rln_relay_nodes( + 0, + pytestconfig.cache.get("keystore-prefixes", {"keystore_prefixes": [], "rln_membership_indexes": []}), + ) self.setup_main_rln_relay_nodes(rln_relay_user_message_limit=message_limit, rln_relay_epoch_sec=600) self.subscribe_main_relay_nodes() failed_payloads = [] @@ -63,7 +75,10 @@ class TestRelayRLN(StepsRLN, StepsRelay): def test_valid_payloads_at_spam_rate(self, pytestconfig): message_limit = 20 epoch_sec = 600 - self.register_rln_relay_nodes(0, pytestconfig.cache.get("keystore-prefixes", [])) + self.register_rln_relay_nodes( + 0, + pytestconfig.cache.get("keystore-prefixes", {"keystore_prefixes": [], "rln_membership_indexes": []}), + ) self.setup_main_rln_relay_nodes(rln_relay_user_message_limit=message_limit, rln_relay_epoch_sec=epoch_sec) self.subscribe_main_relay_nodes() start = math.trunc(time()) @@ -77,10 +92,16 @@ class TestRelayRLN(StepsRLN, StepsRelay): if i > message_limit and (now - start) <= epoch_sec: raise AssertionError("Publish with RLN enabled at spam rate worked!!!") except Exception as e: - assert "RLN validation failed" or "NonceLimitReached" in str(e) + error_str = str(e) + assert "RLN validation failed" in error_str or "NonceLimitReached" in error_str + if "NonceLimitReached" in error_str: + assert "500" in error_str, f"Expected HTTP 500 for NonceLimitReached, got: {error_str}" def test_valid_payload_at_variable_rate(self, pytestconfig): - self.register_rln_relay_nodes(0, pytestconfig.cache.get("keystore-prefixes", [])) + self.register_rln_relay_nodes( + 0, + pytestconfig.cache.get("keystore-prefixes", {"keystore_prefixes": [], "rln_membership_indexes": []}), + ) self.setup_main_rln_relay_nodes(rln_relay_user_message_limit=1, rln_relay_epoch_sec=1) self.subscribe_main_relay_nodes() payload_desc = SAMPLE_INPUTS[0]["description"] @@ -101,11 +122,17 @@ class TestRelayRLN(StepsRLN, StepsRelay): else: previous = now except Exception as e: - assert "RLN validation failed" or "NonceLimitReached" in str(e) + error_str = str(e) + assert "RLN validation failed" in error_str or "NonceLimitReached" in error_str + if "NonceLimitReached" in error_str: + assert "500" in error_str, f"Expected HTTP 500 for NonceLimitReached, got: {error_str}" def test_valid_payloads_random_epoch_at_slow_rate(self, pytestconfig): epoch_sec = random.randint(2, 5) - self.register_rln_relay_nodes(0, pytestconfig.cache.get("keystore-prefixes", [])) + self.register_rln_relay_nodes( + 0, + pytestconfig.cache.get("keystore-prefixes", {"keystore_prefixes": [], "rln_membership_indexes": []}), + ) self.setup_main_rln_relay_nodes(rln_relay_user_message_limit=1, rln_relay_epoch_sec=epoch_sec) self.subscribe_main_relay_nodes() failed_payloads = [] @@ -122,7 +149,10 @@ class TestRelayRLN(StepsRLN, StepsRelay): def test_valid_payloads_random_user_message_limit(self, pytestconfig): user_message_limit = random.randint(2, 4) - self.register_rln_relay_nodes(0, pytestconfig.cache.get("keystore-prefixes", [])) + self.register_rln_relay_nodes( + 0, + pytestconfig.cache.get("keystore-prefixes", {"keystore_prefixes": [], "rln_membership_indexes": []}), + ) self.setup_main_rln_relay_nodes(rln_relay_user_message_limit=user_message_limit, rln_relay_epoch_sec=1) self.subscribe_main_relay_nodes() failed_payloads = [] @@ -136,12 +166,18 @@ class TestRelayRLN(StepsRLN, StepsRelay): failed_payloads.append(payload["description"]) assert not failed_payloads, f"Payloads failed: {failed_payloads}" - @pytest.mark.skip(reason="Waiting for issue resolution https://github.com/waku-org/nwaku/issues/3208") @pytest.mark.timeout(600) def test_valid_payloads_dynamic_at_spam_rate(self, pytestconfig): message_limit = 100 epoch_sec = 600 - pytestconfig.cache.set("keystore-prefixes", self.register_rln_relay_nodes(2, [])) + rln_state = self.register_rln_relay_nodes(2, []) + pytestconfig.cache.set( + "keystore-prefixes", + { + "keystore_prefixes": rln_state["keystore_prefixes"], + "rln_membership_indexes": rln_state["rln_membership_indexes"], + }, + ) self.setup_main_rln_relay_nodes( rln_relay_user_message_limit=message_limit, rln_relay_epoch_sec=epoch_sec, @@ -160,13 +196,22 @@ class TestRelayRLN(StepsRLN, StepsRelay): if i > message_limit and (now - start) <= epoch_sec: raise AssertionError("Publish with RLN enabled at spam rate worked!!!") except Exception as e: - assert "RLN validation failed" or "NonceLimitReached" in str(e) + error_str = str(e) + assert "RLN validation failed" in error_str or "NonceLimitReached" in error_str + if "NonceLimitReached" in error_str: + assert "500" in error_str, f"Expected HTTP 500 for NonceLimitReached, got: {error_str}" - @pytest.mark.skip(reason="Waiting for issue resolution https://github.com/waku-org/nwaku/issues/3208") @pytest.mark.timeout(600) def test_valid_payloads_dynamic_at_slow_rate(self, pytestconfig): message_limit = 100 - pytestconfig.cache.set("keystore-prefixes", self.register_rln_relay_nodes(2, [])) + rln_state = self.register_rln_relay_nodes(2, []) + pytestconfig.cache.set( + "keystore-prefixes", + { + "keystore_prefixes": rln_state["keystore_prefixes"], + "rln_membership_indexes": rln_state["rln_membership_indexes"], + }, + ) self.setup_main_rln_relay_nodes( rln_relay_user_message_limit=message_limit, rln_relay_epoch_sec=600, @@ -192,7 +237,10 @@ class TestRelayRLN(StepsRLN, StepsRelay): def test_valid_payloads_n1_with_rln_n2_without_rln_at_spam_rate(self, pytestconfig): message_limit = 1 epoch_sec = 1 - self.register_rln_relay_nodes(0, pytestconfig.cache.get("keystore-prefixes", [])) + self.register_rln_relay_nodes( + 0, + pytestconfig.cache.get("keystore-prefixes", {"keystore_prefixes": [], "rln_membership_indexes": []}), + ) self.setup_first_rln_relay_node(rln_relay_user_message_limit=message_limit, rln_relay_epoch_sec=epoch_sec) self.setup_second_relay_node() self.subscribe_main_relay_nodes() @@ -206,10 +254,20 @@ class TestRelayRLN(StepsRLN, StepsRelay): if i > message_limit and (now - start) <= epoch_sec: raise AssertionError("Publish with RLN enabled at spam rate worked!!!") except Exception as e: - assert "RLN validation failed" or "NonceLimitReached" in str(e) + error_str = str(e) + assert "RLN validation failed" in error_str or "NonceLimitReached" in error_str + if "NonceLimitReached" in error_str: + assert "500" in error_str, f"Expected HTTP 500 for NonceLimitReached, got: {error_str}" def test_valid_payloads_with_optional_nodes_at_slow_rate(self, pytestconfig): - pytestconfig.cache.set("keystore-prefixes", self.register_rln_relay_nodes(5, [])) + rln_state = self.register_rln_relay_nodes(5, []) + pytestconfig.cache.set( + "keystore-prefixes", + { + "keystore_prefixes": rln_state["keystore_prefixes"], + "rln_membership_indexes": rln_state["rln_membership_indexes"], + }, + ) self.setup_main_rln_relay_nodes(rln_relay_user_message_limit=1, rln_relay_epoch_sec=1) self.setup_optional_rln_relay_nodes(rln_relay_user_message_limit=1, rln_relay_epoch_sec=1) self.subscribe_main_relay_nodes() @@ -228,7 +286,10 @@ class TestRelayRLN(StepsRLN, StepsRelay): assert not failed_payloads, f"Payloads failed: {failed_payloads}" def test_valid_payloads_with_optional_nodes_at_spam_rate(self, pytestconfig): - self.register_rln_relay_nodes(0, pytestconfig.cache.get("keystore-prefixes", [])) + self.register_rln_relay_nodes( + 0, + pytestconfig.cache.get("keystore-prefixes", {"keystore_prefixes": [], "rln_membership_indexes": []}), + ) self.setup_main_rln_relay_nodes(rln_relay_user_message_limit=1, rln_relay_epoch_sec=1) self.setup_optional_rln_relay_nodes(rln_relay_user_message_limit=1, rln_relay_epoch_sec=1) self.subscribe_main_relay_nodes() @@ -246,4 +307,7 @@ class TestRelayRLN(StepsRLN, StepsRelay): else: previous = now except Exception as e: - assert "RLN validation failed" or "NonceLimitReached" in str(e) + error_str = str(e) + assert "RLN validation failed" in error_str or "NonceLimitReached" in error_str + if "NonceLimitReached" in error_str: + assert "500" in error_str, f"Expected HTTP 500 for NonceLimitReached, got: {error_str}"