-
Notifications
You must be signed in to change notification settings - Fork 3
Add support for multiple peer addresses in remote bootnode argument #98
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
Merged
michaelgpt
merged 4 commits into
main
from
AN-268-alys-support-multiple-peer-addresses-in-remote-bootnode-configuration
Aug 1, 2025
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
020e822
feat(network): support multiple peer addresses in remote bootnode con…
michaelgpt 343a65a
chore: fix cargo fmt errors
michaelgpt db0a274
chore: define supported multiaddress protocols const to improve the c…
michaelgpt af33d7d
chore: fix cargo fmt
michaelgpt File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -38,6 +38,19 @@ pub type EnrSyncCommitteeBitfield<T> = BitVector<<T as EthSpec>::SyncCommitteeSu | |
| const RECONNECT_INTERVAL_SECS: u64 = 5; | ||
| const RECONNECT_MAX_ATTEMPTS: u32 = 12; | ||
|
|
||
| /// Supported multiaddress protocols that can start a new multiaddress | ||
| const SUPPORTED_MULTIADDR_PROTOCOLS: &[&str] = &[ | ||
| "ip4", | ||
| "ip6", | ||
| "dns", | ||
| "dns4", | ||
| "dns6", | ||
| "unix", | ||
| "p2p", | ||
| "p2p-webrtc-star", | ||
| "p2p-websocket-star", | ||
| ]; | ||
|
|
||
| #[derive(NetworkBehaviour)] | ||
| struct MyBehaviour { | ||
| gossipsub: gossipsub::Behaviour, | ||
|
|
@@ -479,6 +492,177 @@ impl NetworkBackend { | |
| } | ||
| } | ||
|
|
||
| /// Parse multiple multiaddresses from a string. | ||
| /// Supports the following formats: | ||
| /// - Comma-separated: "/ip4/1.2.3.4/tcp/1234,/ip4/5.6.7.8/tcp/5678" | ||
| /// - Space-separated: "/ip4/1.2.3.4/tcp/1234 /ip4/5.6.7.8/tcp/5678" | ||
| /// - Concatenated: "/ip4/1.2.3.4/tcp/1234/ip4/5.6.7.8/tcp/5678" | ||
| fn parse_multiple_multiaddrs(input: &str) -> Result<Vec<Multiaddr>, Error> { | ||
| let mut addresses = Vec::new(); | ||
|
|
||
| // First, try to parse as comma-separated | ||
| if input.contains(',') { | ||
| for addr_str in input.split(',') { | ||
| let addr_str = addr_str.trim(); | ||
| if !addr_str.is_empty() { | ||
| let addr = Multiaddr::from_str(addr_str)?; | ||
| addresses.push(addr); | ||
| } | ||
| } | ||
| return Ok(addresses); | ||
| } | ||
|
|
||
| // Then, try to parse as space-separated | ||
| if input.contains(' ') { | ||
| for addr_str in input.split_whitespace() { | ||
| let addr_str = addr_str.trim(); | ||
| if !addr_str.is_empty() { | ||
| let addr = Multiaddr::from_str(addr_str)?; | ||
| addresses.push(addr); | ||
| } | ||
| } | ||
| return Ok(addresses); | ||
| } | ||
|
|
||
| // Finally, try to parse the concatenated format | ||
| // This is more complex as we need to split at protocol boundaries | ||
| let mut current_pos = 0; | ||
|
|
||
| while current_pos < input.len() { | ||
| // Find the next complete multiaddress starting with '/' | ||
| if let Some(slash_pos) = input[current_pos..].find('/') { | ||
| let start_pos = current_pos + slash_pos; | ||
|
|
||
| // Try to find the end of this multiaddress | ||
| let mut end_pos = input.len(); | ||
|
|
||
| // Look for the next occurrence of a protocol that could start a new multiaddress | ||
| let mut search_pos = start_pos + 1; | ||
| while search_pos < input.len() { | ||
| if let Some(next_slash) = input[search_pos..].find('/') { | ||
| let protocol_start = search_pos + next_slash + 1; | ||
| if protocol_start < input.len() { | ||
| // Find the end of the protocol name | ||
| if let Some(protocol_end) = input[protocol_start..].find('/') { | ||
| let protocol = &input[protocol_start..protocol_start + protocol_end]; | ||
| // Check if this looks like a new multiaddress protocol | ||
| if SUPPORTED_MULTIADDR_PROTOCOLS.contains(&protocol) { | ||
| // Verify this is actually a new multiaddress by trying to parse it | ||
| let potential_addr = &input[protocol_start - 1..]; | ||
| if Multiaddr::from_str(potential_addr).is_ok() { | ||
| end_pos = protocol_start - 1; | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| search_pos = protocol_start; | ||
| } else { | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| let multiaddr_str = &input[start_pos..end_pos]; | ||
| match Multiaddr::from_str(multiaddr_str) { | ||
| Ok(addr) => { | ||
| addresses.push(addr); | ||
| current_pos = end_pos; | ||
| } | ||
| Err(e) => { | ||
| return Err(Error::MultiaddrError(e)); | ||
| } | ||
| } | ||
| } else { | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| if addresses.is_empty() { | ||
| return Err(Error::MultiaddrError( | ||
| libp2p::multiaddr::Error::InvalidMultiaddr, | ||
| )); | ||
| } | ||
|
|
||
| Ok(addresses) | ||
| } | ||
|
|
||
| #[cfg(test)] | ||
| mod tests { | ||
| use super::*; | ||
|
|
||
| #[test] | ||
| fn test_parse_multiple_multiaddrs() { | ||
| // Test the example from the user query (concatenated format) | ||
| let input = "/ip4/10.38.1.103/tcp/55444/ip4/10.38.1.105/tcp/55444"; | ||
| let result = parse_multiple_multiaddrs(input).unwrap(); | ||
|
|
||
| assert_eq!(result.len(), 2); | ||
| assert_eq!(result[0].to_string(), "/ip4/10.38.1.103/tcp/55444"); | ||
| assert_eq!(result[1].to_string(), "/ip4/10.38.1.105/tcp/55444"); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_parse_comma_separated_multiaddrs() { | ||
| // Test comma-separated format | ||
| let input = "/ip4/10.38.1.103/tcp/55444,/ip4/10.38.1.105/tcp/55444"; | ||
| let result = parse_multiple_multiaddrs(input).unwrap(); | ||
|
|
||
| assert_eq!(result.len(), 2); | ||
| assert_eq!(result[0].to_string(), "/ip4/10.38.1.103/tcp/55444"); | ||
| assert_eq!(result[1].to_string(), "/ip4/10.38.1.105/tcp/55444"); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_parse_space_separated_multiaddrs() { | ||
| // Test space-separated format | ||
| let input = "/ip4/10.38.1.103/tcp/55444 /ip4/10.38.1.105/tcp/55444"; | ||
| let result = parse_multiple_multiaddrs(input).unwrap(); | ||
|
|
||
| assert_eq!(result.len(), 2); | ||
| assert_eq!(result[0].to_string(), "/ip4/10.38.1.103/tcp/55444"); | ||
| assert_eq!(result[1].to_string(), "/ip4/10.38.1.105/tcp/55444"); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_parse_single_multiaddr() { | ||
| // Test with a single multiaddress | ||
| let input = "/ip4/10.38.1.103/tcp/55444"; | ||
| let result = parse_multiple_multiaddrs(input).unwrap(); | ||
|
|
||
| assert_eq!(result.len(), 1); | ||
| assert_eq!(result[0].to_string(), "/ip4/10.38.1.103/tcp/55444"); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_parse_three_multiaddrs() { | ||
| // Test with three multiaddresses | ||
| let input = | ||
| "/ip4/10.38.1.103/tcp/55444/ip4/10.38.1.105/tcp/55444/ip4/10.38.1.106/tcp/55444"; | ||
| let result = parse_multiple_multiaddrs(input).unwrap(); | ||
|
|
||
| assert_eq!(result.len(), 3); | ||
| assert_eq!(result[0].to_string(), "/ip4/10.38.1.103/tcp/55444"); | ||
| assert_eq!(result[1].to_string(), "/ip4/10.38.1.105/tcp/55444"); | ||
| assert_eq!(result[2].to_string(), "/ip4/10.38.1.106/tcp/55444"); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_parse_empty_input() { | ||
| // Test with empty input | ||
| let input = ""; | ||
| let result = parse_multiple_multiaddrs(input); | ||
| assert!(result.is_err()); | ||
| } | ||
|
|
||
| #[test] | ||
| fn test_parse_invalid_multiaddr() { | ||
| // Test with invalid multiaddress | ||
| let input = "/invalid/protocol"; | ||
| let result = parse_multiple_multiaddrs(input); | ||
| assert!(result.is_err()); | ||
| } | ||
| } | ||
|
|
||
| pub async fn spawn_network_handler( | ||
| addr: String, | ||
| port: u16, | ||
|
|
@@ -524,8 +708,14 @@ pub async fn spawn_network_handler( | |
|
|
||
| if let Some(bootnode) = remote_bootnode { | ||
| trace!("Dialing bootnode: {}", bootnode); | ||
| let address = Multiaddr::from_str(&bootnode)?; | ||
| client.dial(address).await?; | ||
| let addresses = parse_multiple_multiaddrs(&bootnode)?; | ||
|
|
||
| for (i, address) in addresses.iter().enumerate() { | ||
| trace!("Dialing bootnode {}: {}", i + 1, address); | ||
| if let Err(e) = client.dial(address.clone()).await { | ||
| warn!("Failed to dial bootnode {} ({}): {}", i + 1, address, e); | ||
|
||
| } | ||
| } | ||
| } | ||
|
|
||
| Ok(client) | ||
|
|
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function lacks proper documentation. Consider adding a docstring that explains the parsing logic, error conditions, and provides examples of the supported formats.