Skip to content

feat: allow exchanging p2p-host value via rlpx connection #8516

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

Closed
Closed
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@

### Upcoming Breaking Changes
### Additions and Improvements
- Support of the new Besu config option `p2p-host-share-via-rlpx` that enables Besu nodes to exchange their `p2p-host` value during RLPx hello message [#8400](https://github.com/hyperledger/besu/issues/8400)
#### Dependencies
### Bug fixes

15 changes: 14 additions & 1 deletion besu/src/main/java/org/hyperledger/besu/RunnerBuilder.java
Original file line number Diff line number Diff line change
@@ -165,6 +165,7 @@ public class RunnerBuilder {
private boolean p2pEnabled = true;
private boolean discoveryEnabled;
private String p2pAdvertisedHost;
private boolean p2pAdvertisedHostShareViaRlpx = false;
private String p2pListenInterface = NetworkUtility.INADDR_ANY;
private int p2pListenPort;
private NatMethod natMethod = NatMethod.AUTO;
@@ -275,6 +276,17 @@ public RunnerBuilder p2pAdvertisedHost(final String p2pAdvertisedHost) {
return this;
}

/**
* Defines whether to share its p2p-host value during Rlpx handshake
*
* @param p2pAdvertisedHostShareViaRlpx the P2P advertised host
* @return the runner builder
*/
public RunnerBuilder p2pAdvertisedHostShareViaRlpx(final boolean p2pAdvertisedHostShareViaRlpx) {
this.p2pAdvertisedHostShareViaRlpx = p2pAdvertisedHostShareViaRlpx;
return this;
}

/**
* Add P2P Listener interface ip/host name.
*
@@ -602,7 +614,8 @@ public Runner build() {
DiscoveryConfiguration.create()
.setBindHost(p2pListenInterface)
.setBindPort(p2pListenPort)
.setAdvertisedHost(p2pAdvertisedHost);
.setAdvertisedHost(p2pAdvertisedHost)
.setAdvertisedHostShareViaRlpx(p2pAdvertisedHostShareViaRlpx);
if (discoveryEnabled) {
final List<EnodeURL> bootstrap;
if (ethNetworkConfig.bootNodes() == null) {
3 changes: 3 additions & 0 deletions besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java
Original file line number Diff line number Diff line change
@@ -1268,6 +1268,7 @@ private Runner buildRunner() {
p2PDiscoveryConfig.peerDiscoveryEnabled(),
ethNetworkConfig,
p2PDiscoveryConfig.p2pHost(),
p2PDiscoveryConfig.p2pHostShareViaRlpxEnabled(),
p2PDiscoveryConfig.p2pInterface(),
p2PDiscoveryConfig.p2pPort(),
graphQLConfiguration,
@@ -2283,6 +2284,7 @@ private Runner synchronize(
final boolean peerDiscoveryEnabled,
final EthNetworkConfig ethNetworkConfig,
final String p2pAdvertisedHost,
final boolean p2pAdvertisedHostShareViaRlpx,
final String p2pListenInterface,
final int p2pListenPort,
final GraphQLConfiguration graphQLConfiguration,
@@ -2310,6 +2312,7 @@ private Runner synchronize(
.ethNetworkConfig(ethNetworkConfig)
.permissioningConfiguration(permissioningConfiguration)
.p2pAdvertisedHost(p2pAdvertisedHost)
.p2pAdvertisedHostShareViaRlpx(p2pAdvertisedHostShareViaRlpx)
.p2pListenInterface(p2pListenInterface)
.p2pListenPort(p2pListenPort)
.networkingConfiguration(unstableNetworkingOptions.toDomainObject())
Original file line number Diff line number Diff line change
@@ -101,6 +101,14 @@ public P2PDiscoveryOptions() {}
arity = "1")
public String p2pHost = autoDiscoverDefaultIP().getHostAddress();

/** Enables node to share its p2p-host during rlpx handshake. */
@CommandLine.Option(
names = {"--p2p-host-share-via-rlpx-enabled"},
description =
"Enables node to share its p2p-host during rlpx handshake (default: ${DEFAULT-VALUE})",
arity = "1")
public final Boolean p2pHostShareViaRlpxEnabled = false;

/** The network interface address on which this node listens for P2P communication. */
@SuppressWarnings({"FieldCanBeFinal", "FieldMayBeFinal"}) // PicoCLI requires non-final Strings.
@CommandLine.Option(
@@ -225,6 +233,7 @@ public P2PDiscoveryConfiguration toDomainObject() {
p2pHost,
p2pInterface,
p2pPort,
p2pHostShareViaRlpxEnabled,
maxPeers,
isLimitRemoteWireConnectionsEnabled,
maxRemoteConnectionsPercentage,
Original file line number Diff line number Diff line change
@@ -323,6 +323,8 @@ public void initMocks() throws Exception {
when(mockRunnerBuilder.ethNetworkConfig(any())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.networkingConfiguration(any())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.p2pAdvertisedHost(anyString())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.p2pAdvertisedHostShareViaRlpx(anyBoolean()))
.thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.p2pListenPort(anyInt())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.p2pListenInterface(anyString())).thenReturn(mockRunnerBuilder);
when(mockRunnerBuilder.permissioningConfiguration(any())).thenReturn(mockRunnerBuilder);
1 change: 1 addition & 0 deletions besu/src/test/resources/everything_config.toml
Original file line number Diff line number Diff line change
@@ -37,6 +37,7 @@ bootnodes=[
banned-node-ids=["0x6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0","0x6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0"]
banned-node-id=["0x6f8a80d14311c39f35f516fa664deaaaa13e85b2f7493f37f6144d86991ec012937307647bd3b9a82abe2974e1407241d54947bbb39763a4cac9f77166ad92a0"]
p2p-host="1.2.3.4"
p2p-host-share-via-rlpx-enabled=false
p2p-interface="0.0.0.0"
p2p-port=1234
max-peers=42
Original file line number Diff line number Diff line change
@@ -28,6 +28,7 @@ public class DiscoveryConfiguration {
private String bindHost = NetworkUtility.INADDR_ANY;
private int bindPort = 30303;
private String advertisedHost = "127.0.0.1";
private boolean advertisedHostShareViaRlpx = false;
private int bucketSize = 16;
private List<EnodeURL> bootnodes = new ArrayList<>();
private String dnsDiscoveryURL;
@@ -103,11 +104,20 @@ public String getAdvertisedHost() {
return advertisedHost;
}

public boolean getAdvertisedHostShareViaRlpx() {
return advertisedHostShareViaRlpx;
}

public DiscoveryConfiguration setAdvertisedHost(final String advertisedHost) {
this.advertisedHost = advertisedHost;
return this;
}

public DiscoveryConfiguration setAdvertisedHostShareViaRlpx(final boolean share) {
this.advertisedHostShareViaRlpx = share;
return this;
}

public int getBucketSize() {
return bucketSize;
}
Original file line number Diff line number Diff line change
@@ -28,6 +28,7 @@ public record P2PDiscoveryConfiguration(
String p2pHost,
String p2pInterface,
Integer p2pPort,
Boolean p2pHostShareViaRlpxEnabled,
Integer maxPeers,
Boolean isLimitRemoteWireConnectionsEnabled,
Percentage maxRemoteConnectionsPercentage,
Original file line number Diff line number Diff line change
@@ -544,9 +544,11 @@ private P2PNetwork doBuild() {
final PeerDenylistManager reputationManager =
new PeerDenylistManager(misbehavingPeers, maintainedPeers);
peerPermissions = PeerPermissions.combine(peerPermissions, misbehavingPeers);
final boolean p2pHostSharViaRlpx = config.getDiscovery().getAdvertisedHostShareViaRlpx();

final MutableLocalNode localNode =
MutableLocalNode.create(config.getRlpx().getClientId(), 5, supportedCapabilities);
MutableLocalNode.create(
config.getRlpx().getClientId(), 5, supportedCapabilities, p2pHostSharViaRlpx);
final PeerPrivileges peerPrivileges = new DefaultPeerPrivileges(maintainedPeers);
peerTable = new PeerTable(nodeKey.getPublicKey().getEncodedBytes());
rlpxAgent = rlpxAgent == null ? createRlpxAgent(localNode, peerPrivileges) : rlpxAgent;
Original file line number Diff line number Diff line change
@@ -27,35 +27,53 @@ class DefaultLocalNode implements MutableLocalNode {
private final AtomicBoolean isReady = new AtomicBoolean(false);
private final String clientId;
private final int p2pVersion;
private final boolean p2pHostShareViaRlpx;
private final List<Capability> supportedCapabilities;
private volatile Optional<PeerInfo> peerInfo = Optional.empty();
private volatile Optional<Peer> peer = Optional.empty();

private DefaultLocalNode(
final String clientId, final int p2pVersion, final List<Capability> supportedCapabilities) {
final String clientId,
final int p2pVersion,
final List<Capability> supportedCapabilities,
final boolean p2pHostShareViaRlpx) {
this.clientId = clientId;
this.p2pVersion = p2pVersion;
this.supportedCapabilities = supportedCapabilities;
this.p2pHostShareViaRlpx = p2pHostShareViaRlpx;
}

public static DefaultLocalNode create(
final String clientId, final int p2pVersion, final List<Capability> supportedCapabilities) {
return new DefaultLocalNode(clientId, p2pVersion, supportedCapabilities);
return new DefaultLocalNode(clientId, p2pVersion, supportedCapabilities, false);
}

public static DefaultLocalNode create(
final String clientId,
final int p2pVersion,
final List<Capability> supportedCapabilities,
final boolean p2pHostShareViaRlpx) {
return new DefaultLocalNode(clientId, p2pVersion, supportedCapabilities, p2pHostShareViaRlpx);
}

@Override
public void setEnode(final EnodeURL enode) throws NodeAlreadySetException {
if (peer.isPresent()) {
throw new NodeAlreadySetException("Attempt to set already initialized local node");
}
String advertisedAddress = enode.getHost();
if (!p2pHostShareViaRlpx) {
advertisedAddress = "";
}
this.peerInfo =
Optional.of(
new PeerInfo(
p2pVersion,
clientId,
supportedCapabilities,
enode.getListeningPortOrZero(),
enode.getNodeId()));
enode.getNodeId(),
advertisedAddress));
this.peer = Optional.of(DefaultPeer.fromEnodeURL(enode));
isReady.set(true);
}
Original file line number Diff line number Diff line change
@@ -21,8 +21,12 @@

public interface MutableLocalNode extends LocalNode {
static MutableLocalNode create(
final String clientId, final int p2pVersion, final List<Capability> supportedCapabilities) {
return DefaultLocalNode.create(clientId, p2pVersion, supportedCapabilities);
final String clientId,
final int p2pVersion,
final List<Capability> supportedCapabilities,
final boolean p2pHostShareViaRlpx) {
return DefaultLocalNode.create(
clientId, p2pVersion, supportedCapabilities, p2pHostShareViaRlpx);
}

void setEnode(EnodeURL enode) throws NodeAlreadySetException;
Original file line number Diff line number Diff line change
@@ -296,9 +296,9 @@ private CompletableFuture<PeerConnection> initiateOutboundConnection(final Peer
.whenComplete(
(conn, err) -> {
if (err != null) {
LOG.debug("Failed to connect to peer {}: {}", peer.getId(), err);
LOG.debug("Failed to connect to peer {}: {}", peer.getEnodeURLString(), err);
} else {
LOG.debug("Outbound connection established to peer: {}", peer.getId());
LOG.debug("Outbound connection established to peer: {}", peer.getEnodeURLString());
}
});
}
@@ -324,6 +324,8 @@ private void handleIncomingConnection(final PeerConnection peerConnection) {
return;
}

LOG.debug("Inbound connection established to peer: {}", peer.getEnodeURLString());

if (checkWhetherToConnect(peer, true)) {
dispatchConnect(peerConnection);
} else {
Original file line number Diff line number Diff line change
@@ -43,7 +43,9 @@
import org.hyperledger.besu.plugin.services.metrics.LabelledMetric;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
@@ -233,16 +235,31 @@ protected void decode(final ChannelHandlerContext ctx, final ByteBuf in, final L
}

private Optional<Peer> createPeer(final PeerInfo peerInfo, final ChannelHandlerContext ctx) {
final InetSocketAddress remoteAddress = ((InetSocketAddress) ctx.channel().remoteAddress());
if (remoteAddress == null) {
Optional<InetAddress> remoteAddress = Optional.empty();
String advertisedAddress = peerInfo.getAdvertisedAddress();
if (!advertisedAddress.isEmpty()) {
try {
remoteAddress = Optional.of(InetAddress.getByName(advertisedAddress));
} catch (UnknownHostException e) {
LOG.debug("unrecognised advertised address {}", advertisedAddress);
}
}

if (remoteAddress.isEmpty()) {
InetSocketAddress senderAddress = ((InetSocketAddress) ctx.channel().remoteAddress());
if (senderAddress != null) {
remoteAddress = Optional.of(senderAddress.getAddress());
}
}
if (remoteAddress.isEmpty()) {
return Optional.empty();
}
final int port = peerInfo.getPort();
return Optional.of(
DefaultPeer.fromEnodeURL(
EnodeURLImpl.builder()
.nodeId(peerInfo.getNodeId())
.ipAddress(remoteAddress.getAddress())
.ipAddress(remoteAddress.get())
.listeningPort(port)
// Discovery information is unknown, so disable it
.disableDiscovery()
Original file line number Diff line number Diff line change
@@ -45,6 +45,7 @@ public class PeerInfo implements Comparable<PeerInfo> {
private final int port;
private final Bytes nodeId;
private Address address = null;
private String advertisedAddress = "";

public PeerInfo(
final int version,
@@ -59,6 +60,21 @@ public PeerInfo(
this.nodeId = nodeId;
}

public PeerInfo(
final int version,
final String clientId,
final List<Capability> capabilities,
final int port,
final Bytes nodeId,
final String advertisedAddress) {
this.version = version;
this.clientId = clientId;
this.capabilities = capabilities;
this.port = port;
this.nodeId = nodeId;
this.advertisedAddress = advertisedAddress;
}

public static PeerInfo readFrom(final RLPInput in) {
in.enterList();
final int version = in.readUnsignedByte();
@@ -67,8 +83,12 @@ public static PeerInfo readFrom(final RLPInput in) {
in.nextIsNull() ? Collections.emptyList() : in.readList(Capability::readFrom);
final int port = in.readIntScalar();
final Bytes nodeId = in.readBytes();
String advertisedAddress = "";
if (!in.isEndOfCurrentList()) {
advertisedAddress = new String(in.readBytes().toArrayUnsafe(), StandardCharsets.UTF_8);
}
in.leaveListLenient();
return new PeerInfo(version, clientId, caps, port, nodeId);
return new PeerInfo(version, clientId, caps, port, nodeId, advertisedAddress);
}

public int getVersion() {
@@ -98,6 +118,10 @@ public Bytes getNodeId() {
return nodeId;
}

public String getAdvertisedAddress() {
return advertisedAddress;
}

public Address getAddress() {
if (address == null) {
final SECPPublicKey remotePublicKey =
@@ -114,6 +138,9 @@ public void writeTo(final RLPOutput out) {
out.writeList(getCapabilities(), Capability::writeTo);
out.writeIntScalar(getPort());
out.writeBytes(getNodeId());
if (!advertisedAddress.isEmpty()) {
out.writeBytes(wrap(getAdvertisedAddress().getBytes(StandardCharsets.UTF_8)));
}
out.endList();
}

@@ -126,6 +153,7 @@ public String toString() {
sb.append(", capabilities=").append(capabilities);
sb.append(", port=").append(port);
sb.append(", nodeId=").append(nodeId);
sb.append(", ip=").append(advertisedAddress);
sb.append('}');
return sb.toString();
}
Original file line number Diff line number Diff line change
@@ -56,6 +56,6 @@ public static LocalNode createLocalNode() {
* @return A MutableLocalNode that is not ready (the enode has not been set).
*/
public static MutableLocalNode createMutableLocalNode() {
return MutableLocalNode.create("clientId", 5, Arrays.asList(EthProtocolHelper.LATEST));
return MutableLocalNode.create("clientId", 5, Arrays.asList(EthProtocolHelper.LATEST), false);
}
}
Loading