Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
Original file line number Diff line number Diff line change
Expand Up @@ -547,6 +547,13 @@ public TekuNodeConfigBuilder withPeers(final TekuBeaconNode... nodes) {
return this;
}

public TekuNodeConfigBuilder withPeersFile(final String peersFilePath) {
mustBe(NodeType.BEACON_NODE);
LOG.debug("p2p-static-peers-file={}", peersFilePath);
configMap.put("p2p-static-peers-file", peersFilePath);
return this;
}
Comment thread
rolfyone marked this conversation as resolved.
Outdated

public TekuNodeConfigBuilder withExternalSignerUrl(final String externalSignerUrl) {
LOG.debug("validators-external-signer-url={}", externalSignerUrl);
configMap.put("validators-external-signer-url", externalSignerUrl);
Expand Down
44 changes: 40 additions & 4 deletions teku/src/main/java/tech/pegasys/teku/cli/options/P2POptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,21 @@
import static tech.pegasys.teku.networking.p2p.discovery.DiscoveryConfig.DEFAULT_P2P_PEERS_UPPER_BOUND_ALL_SUBNETS;
import static tech.pegasys.teku.validator.api.ValidatorConfig.DEFAULT_EXECUTOR_MAX_QUEUE_SIZE_ALL_SUBNETS;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.OptionalInt;
import java.util.stream.Collectors;
import picocli.CommandLine.Help.Visibility;
import picocli.CommandLine.Mixin;
import picocli.CommandLine.Option;
import tech.pegasys.teku.beacon.sync.SyncConfig;
import tech.pegasys.teku.cli.converter.OptionalIntConverter;
import tech.pegasys.teku.config.TekuConfiguration;
import tech.pegasys.teku.infrastructure.exceptions.InvalidConfigurationException;
import tech.pegasys.teku.networking.eth2.P2PConfig;
import tech.pegasys.teku.networking.p2p.discovery.DiscoveryConfig;
import tech.pegasys.teku.networking.p2p.gossip.config.GossipConfig;
Expand Down Expand Up @@ -218,6 +223,14 @@ The network interface(s) on which the node listens for P2P communication.
arity = "0..*")
private List<String> p2pStaticPeers = new ArrayList<>();

@Option(
names = {"--p2p-static-peers-file"},
paramLabel = "<FILENAME>",
description =
"Specifies a file containing a list of 'static' peers (one per line) with which to establish and maintain connections",
arity = "1")
private String p2pStaticPeersFile;
Comment thread
rolfyone marked this conversation as resolved.
Outdated

@Option(
names = {"--p2p-direct-peers"},
paramLabel = "<PEER_ADDRESSES>",
Expand Down Expand Up @@ -447,12 +460,35 @@ private OptionalInt getP2pLowerBound() {
}

private OptionalInt getP2pUpperBound() {
if (p2pUpperBound.isPresent() && p2pLowerBound.isPresent()) {
return p2pLowerBound.getAsInt() > p2pUpperBound.getAsInt() ? p2pLowerBound : p2pUpperBound;
}
Comment thread
rolfyone marked this conversation as resolved.
return p2pUpperBound;
}

private List<String> getStaticPeersList() {
List<String> staticPeers = new ArrayList<>(p2pStaticPeers);
Comment thread
rolfyone marked this conversation as resolved.
Outdated

if (p2pStaticPeersFile != null) {
try {
Path filePath = Path.of(p2pStaticPeersFile);
if (Files.exists(filePath)) {
List<String> peersFromFile =
Files.readAllLines(filePath).stream()
.map(String::trim)
.filter(line -> !line.isEmpty() && !line.startsWith("#"))
.collect(Collectors.toList());
staticPeers.addAll(peersFromFile);
} else {
throw new InvalidConfigurationException(
String.format("Static peers file not found: %s", p2pStaticPeersFile));
}
} catch (IOException e) {
throw new InvalidConfigurationException(
String.format("Failed to read static peers from file: %s", p2pStaticPeersFile), e);
}
}

return staticPeers;
}

public void configure(final TekuConfiguration.Builder builder) {
// From a discovery configuration perspective, direct peers are static peers
p2pStaticPeers.addAll(p2pDirectPeers);
Expand Down Expand Up @@ -507,7 +543,7 @@ public void configure(final TekuConfiguration.Builder builder) {
d.advertisedUdpPortIpv6(OptionalInt.of(p2pAdvertisedPortIpv6));
}
d.isDiscoveryEnabled(p2pDiscoveryEnabled)
.staticPeers(p2pStaticPeers)
.staticPeers(getStaticPeersList())
.siteLocalAddressesEnabled(siteLocalAddressesEnabled);
})
.network(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.api.Assertions.fail;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static tech.pegasys.teku.infrastructure.async.AsyncRunnerFactory.DEFAULT_MAX_QUEUE_SIZE_ALL_SUBNETS;
import static tech.pegasys.teku.networking.eth2.P2PConfig.DEFAULT_GOSSIP_BLOBS_AFTER_BLOCK_ENABLED;
Expand All @@ -27,8 +28,13 @@
import static tech.pegasys.teku.validator.api.ValidatorConfig.DEFAULT_EXECUTOR_MAX_QUEUE_SIZE_ALL_SUBNETS;

import com.google.common.base.Supplier;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import tech.pegasys.teku.beacon.sync.SyncConfig;
import tech.pegasys.teku.cli.AbstractBeaconNodeCommandTest;
import tech.pegasys.teku.config.TekuConfiguration;
Expand Down Expand Up @@ -472,4 +478,59 @@ public void defaultPortsAreSetCorrectly() {
assertThat(networkConfig.getAdvertisedPort()).isEqualTo(DEFAULT_P2P_PORT);
assertThat(networkConfig.getAdvertisedPortIpv6()).isEqualTo(DEFAULT_P2P_PORT_IPV6);
}

@Test
public void staticPeersFile_shouldReadPeersFromFile(@TempDir final Path tempDir)
throws Exception {
// Create a test file with peers
Path peersFile = tempDir.resolve("static-peers.txt");
Comment thread
rolfyone marked this conversation as resolved.
Outdated
Files.writeString(peersFile, "peer1\npeer2\n#comment\n\npeer3");
Comment thread
rolfyone marked this conversation as resolved.

TekuConfiguration tekuConfiguration =
getTekuConfigurationFromArguments(
"--p2p-static-peers-file", peersFile.toAbsolutePath().toString());

assertThat(tekuConfiguration.discovery().getStaticPeers())
.containsExactlyInAnyOrder("peer1", "peer2", "peer3");
}

@Test
public void staticPeersFile_shouldCombineWithCommandLinePeers(@TempDir final Path tempDir)
throws Exception {
// Create a test file with peers
Path peersFile = tempDir.resolve("static-peers.txt");
Files.writeString(peersFile, "peer1\npeer2");

TekuConfiguration tekuConfiguration =
getTekuConfigurationFromArguments(
"--p2p-static-peers-file",
peersFile.toAbsolutePath().toString(),
"--p2p-static-peers",
"peer3,peer4");

assertThat(tekuConfiguration.discovery().getStaticPeers())
.containsExactlyInAnyOrder("peer1", "peer2", "peer3", "peer4");
}

@Test
public void staticPeersFile_shouldThrowIfFileDoesNotExist() {
// Create a dummy instance of P2POptions
P2POptions p2pOptions = new P2POptions();

// Use reflection to access a private field and set its value
try {
Field field = P2POptions.class.getDeclaredField("p2pStaticPeersFile");
field.setAccessible(true);
field.set(p2pOptions, "/non/existent/file.txt");

// Use reflection to call a private method getStaticPeersList
Method method = P2POptions.class.getDeclaredMethod("getStaticPeersList");
method.setAccessible(true);

assertThatThrownBy(() -> method.invoke(p2pOptions))
.hasCauseInstanceOf(InvalidConfigurationException.class);
} catch (Exception e) {
fail("Test setup failed: " + e.getMessage(), e);
}
}
}