Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(consensus): allow running multiple simulations of consensus #2227

Merged
merged 1 commit into from
Jul 22, 2024
Merged
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
87 changes: 43 additions & 44 deletions crates/sequencing/papyrus_consensus/run_consensus.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def check_height(self):
height = self.get_height()
if self.height_and_timestamp[0] != height:
if self.height_and_timestamp[0] is not None and height is not None:
assert height > self.height_and_timestamp[0] , "Height should be increasing."
assert height > self.height_and_timestamp[0], "Height should be increasing."
self.height_and_timestamp = (height, time.time())

return self.height_and_timestamp
Expand All @@ -55,6 +55,9 @@ def find_free_port():
return s.getsockname()[1]


BOOTNODE_TCP_PORT = find_free_port()


# Returns if the simulation should exit.
def monitor_simulation(nodes, start_time, duration, stagnation_timeout):
curr_time = time.time()
Expand Down Expand Up @@ -91,31 +94,61 @@ def run_simulation(nodes, duration, stagnation_timeout):
node.stop()


def build_peernode(base_layer_node_url, temp_dir, num_validators, i):
def build_node(base_layer_node_url, temp_dir, num_validators, i):
is_bootstrap = i == 1
tcp_port = BOOTNODE_TCP_PORT if is_bootstrap else find_free_port()
monitoring_gateway_server_port = find_free_port()

cmd = (
f"RUST_LOG=papyrus_consensus=debug,papyrus=info "
f"target/release/papyrus_node --network.#is_none false "
f"--base_layer.node_url {base_layer_node_url} "
f"--storage.db_config.path_prefix {temp_dir}/data{i} "
f"--consensus.#is_none false --consensus.validator_id 0x{i} "
f"--consensus.num_validators {num_validators} "
f"--network.tcp_port {find_free_port()} "
f"--network.tcp_port {tcp_port} "
f"--rpc.server_address 127.0.0.1:{find_free_port()} "
f"--monitoring_gateway.server_address 127.0.0.1:{monitoring_gateway_server_port} "
f"--network.bootstrap_peer_multiaddr.#is_none false "
f"--network.bootstrap_peer_multiaddr /ip4/127.0.0.1/tcp/10000/p2p/{BOOT_NODE_PEER_ID} "
f"--collect_metrics true"
# Use sed to strip special formatting characters
f"| sed -r 's/\\x1B\\[[0-9;]*[mK]//g' > {temp_dir}/validator{i}.txt"
f"--collect_metrics true "
)

if is_bootstrap:
cmd += (
f"--network.secret_key {SECRET_KEY} "
+ f"| sed -r 's/\\x1B\\[[0-9;]*[mK]//g' > {temp_dir}/validator{i}.txt"
)

else:
cmd += (
f"--network.bootstrap_peer_multiaddr.#is_none false "
f"--network.bootstrap_peer_multiaddr /ip4/127.0.0.1/tcp/{BOOTNODE_TCP_PORT}/p2p/{BOOT_NODE_PEER_ID} "
+ f"| sed -r 's/\\x1B\\[[0-9;]*[mK]//g' > {temp_dir}/validator{i}.txt"
)

return Node(
validator_id=i,
monitoring_gateway_server_port=monitoring_gateway_server_port,
cmd=cmd,
)


def build_all_nodes(base_layer_node_url, temp_dir, num_validators):
# Validators are started in a specific order to ensure proper network formation:
# 1. The bootnode (validator 1) is started first for network peering.
# 2. Validators 2+ are started next to join the network through the bootnode.
# 3. Validator 0, which is the proposer, is started last so the validators don't miss the proposals.
nodes = []

nodes.append(build_node(base_layer_node_url, temp_dir, num_validators, 1)) # Bootstrap

for i in range(2, num_validators):
nodes.append(build_node(base_layer_node_url, temp_dir, num_validators, i))

nodes.append(build_node(base_layer_node_url, temp_dir, num_validators, 0)) # Proposer

return nodes


def main(base_layer_node_url, num_validators, stagnation_threshold, duration):
assert num_validators >= 2, "At least 2 validators are required for the simulation."
# Building the Papyrus Node package assuming its output will be located in the papyrus target directory.
Expand All @@ -130,46 +163,12 @@ def main(base_layer_node_url, num_validators, stagnation_threshold, duration):
data_dir = os.path.join(temp_dir, f"data{i}")
os.makedirs(data_dir)

# Validators are started in a specific order to ensure proper network formation:
# 1. The bootnode (validator 1) is started first for network peering.
# 2. Validators 2+ are started next to join the network through the bootnode.
# 3. Validator 0, which is the proposer, is started last so the validators don't miss the proposals.

nodes = []
# Ensure validator 1 runs first
monitoring_gateway_server_port = find_free_port()
bootnode_command = (
f"RUST_LOG=papyrus_consensus=debug,papyrus=info "
f"target/release/papyrus_node --network.#is_none false "
f"--base_layer.node_url {base_layer_node_url} "
f"--network.secret_key {SECRET_KEY} "
f"--storage.db_config.path_prefix {temp_dir}/data1 "
f"--consensus.#is_none false --consensus.validator_id 0x1 "
f"--consensus.num_validators {num_validators} "
f"--monitoring_gateway.server_address 127.0.0.1:{monitoring_gateway_server_port} "
f"--collect_metrics true"
# Use sed to strip special formatting characters
f"| sed -r 's/\\x1B\\[[0-9;]*[mK]//g' > {temp_dir}/validator1.txt"
)
nodes.append(
Node(
validator_id=1,
monitoring_gateway_server_port=monitoring_gateway_server_port,
cmd=bootnode_command,
)
)

# Add other validators
nodes.extend(
build_peernode(base_layer_node_url, temp_dir, num_validators, i)
for i in range(2, num_validators)
)
# Ensure validator 0 runs last
nodes.append(build_peernode(base_layer_node_url, temp_dir, num_validators, 0))
nodes = build_all_nodes(base_layer_node_url, temp_dir, num_validators)

# Run validator commands in parallel and manage duration time
print("Running validators...")
run_simulation(nodes, duration, stagnation_threshold)
print(f"Output files were stored in: {temp_dir}")
print("Simulation complete.")


Expand Down
Loading