consensus_config/
test_committee.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use std::net::{TcpListener, TcpStream};
6
7use iota_network_stack::Multiaddr;
8use rand::{SeedableRng as _, rngs::StdRng};
9
10use crate::{
11    Authority, AuthorityKeyPair, Committee, Epoch, NetworkKeyPair, ProtocolKeyPair, Stake,
12};
13
14/// Creates a committee for local testing, and the corresponding key pairs for
15/// the authorities.
16pub fn local_committee_and_keys(
17    epoch: Epoch,
18    authorities_stake: Vec<Stake>,
19) -> (Committee, Vec<(NetworkKeyPair, ProtocolKeyPair)>) {
20    let mut authorities = vec![];
21    let mut key_pairs = vec![];
22    let mut rng = StdRng::from_seed([0; 32]);
23    for (i, stake) in authorities_stake.into_iter().enumerate() {
24        let authority_keypair = AuthorityKeyPair::generate(&mut rng);
25        let protocol_keypair = ProtocolKeyPair::generate(&mut rng);
26        let network_keypair = NetworkKeyPair::generate(&mut rng);
27        authorities.push(Authority {
28            stake,
29            address: get_available_local_address(),
30            hostname: format!("test_host_{i}").to_string(),
31            authority_key: authority_keypair.public(),
32            protocol_key: protocol_keypair.public(),
33            network_key: network_keypair.public(),
34        });
35        key_pairs.push((network_keypair, protocol_keypair));
36    }
37
38    let committee = Committee::new(epoch, authorities);
39    (committee, key_pairs)
40}
41
42/// Returns a local address with an ephemeral port.
43fn get_available_local_address() -> Multiaddr {
44    let host = "127.0.0.1";
45    let port = get_available_port(host);
46    format!("/ip4/{}/udp/{}", host, port).parse().unwrap()
47}
48
49/// Returns an ephemeral, available port. On unix systems, the port returned
50/// will be in the TIME_WAIT state ensuring that the OS won't hand out this port
51/// for some grace period. Callers should be able to bind to this port given
52/// they use SO_REUSEADDR.
53fn get_available_port(host: &str) -> u16 {
54    const MAX_PORT_RETRIES: u32 = 1000;
55
56    for _ in 0..MAX_PORT_RETRIES {
57        if let Ok(port) = get_ephemeral_port(host) {
58            return port;
59        }
60    }
61
62    panic!("Error: could not find an available port");
63}
64
65fn get_ephemeral_port(host: &str) -> std::io::Result<u16> {
66    // Request a random available port from the OS
67    let listener = TcpListener::bind((host, 0))?;
68    let addr = listener.local_addr()?;
69
70    // Create and accept a connection (which we'll promptly drop) in order to force
71    // the port into the TIME_WAIT state, ensuring that the port will be
72    // reserved from some limited amount of time (roughly 60s on some Linux
73    // systems)
74    let _sender = TcpStream::connect(addr)?;
75    let _incoming = listener.accept()?;
76
77    Ok(addr.port())
78}