Skip to content

Commit ac15202

Browse files
zilm13Nashatyrev
andauthored
Enable enr field in GetPeers and GetPeerById (Consensys#8641) (#155)
* Expose discovery NodeId to DiscoveryPeer and LibP2PPeer (#38) * Add DiscoveryPeer.getNodeId() * Add Eth2Peer.getDiscoveryNodeId() method. * Refactoring, discovery updated to 24.9.1 --------- Co-authored-by: Anton Nashatyrev <[email protected]>
1 parent abca9a3 commit ac15202

File tree

24 files changed

+240
-81
lines changed

24 files changed

+240
-81
lines changed

data/beaconrestapi/src/integration-test/resources/tech/pegasys/teku/beaconrestapi/beacon/schema/Peer.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
},
1111
"enr" : {
1212
"type" : "string",
13-
"description" : "Ethereum node record. Not currently populated. [Read more](https://eips.ethereum.org/EIPS/eip-778)",
13+
"description" : "Ethereum node record. [Read more](https://eips.ethereum.org/EIPS/eip-778)",
1414
"example" : "enr:-IS4QHCYrYZbAKWCBRlAy5zzaDZXJBGkcnh4MHcBFZntXNFrdvJjX04jRzjzCBOonrkTfj499SZuOh8R33Ls8RRcy5wBgmlkgnY0gmlwhH8AAAGJc2VjcDI1NmsxoQPKY0yuDUmstAHYpMa2_oxVtw0RW_QAdpzBQA8yWM0xOIN1ZHCCdl8"
1515
},
1616
"last_seen_p2p_address" : {

data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/node/GetPeerById.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,17 @@
2626
import java.util.function.Function;
2727
import tech.pegasys.teku.api.DataProvider;
2828
import tech.pegasys.teku.api.NetworkDataProvider;
29+
import tech.pegasys.teku.api.peer.Eth2PeerWithEnr;
2930
import tech.pegasys.teku.infrastructure.json.types.SerializableTypeDefinition;
3031
import tech.pegasys.teku.infrastructure.restapi.endpoints.EndpointMetadata;
3132
import tech.pegasys.teku.infrastructure.restapi.endpoints.RestApiEndpoint;
3233
import tech.pegasys.teku.infrastructure.restapi.endpoints.RestApiRequest;
33-
import tech.pegasys.teku.networking.eth2.peers.Eth2Peer;
3434

3535
public class GetPeerById extends RestApiEndpoint {
3636
public static final String ROUTE = "/eth/v1/node/peers/{peer_id}";
3737

38-
private static final SerializableTypeDefinition<Eth2Peer> PEERS_BY_ID_RESPONSE_TYPE =
39-
SerializableTypeDefinition.object(Eth2Peer.class)
38+
private static final SerializableTypeDefinition<Eth2PeerWithEnr> PEERS_BY_ID_RESPONSE_TYPE =
39+
SerializableTypeDefinition.object(Eth2PeerWithEnr.class)
4040
.name("GetPeerResponse")
4141
.withField("data", PEER_DATA_TYPE, Function.identity())
4242
.build();
@@ -64,7 +64,8 @@ public GetPeerById(final DataProvider provider) {
6464
@Override
6565
public void handleRequest(RestApiRequest request) throws JsonProcessingException {
6666
request.header(Header.CACHE_CONTROL, CACHE_NONE);
67-
Optional<Eth2Peer> peer = network.getEth2PeerById(request.getPathParameter(PEER_ID_PARAMETER));
67+
final Optional<Eth2PeerWithEnr> peer =
68+
network.getEth2PeerById(request.getPathParameter(PEER_ID_PARAMETER));
6869
if (peer.isEmpty()) {
6970
request.respondError(SC_NOT_FOUND, "Peer not found");
7071
} else {

data/beaconrestapi/src/main/java/tech/pegasys/teku/beaconrestapi/handlers/v1/node/GetPeers.java

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,18 +23,17 @@
2323
import com.fasterxml.jackson.core.JsonProcessingException;
2424
import java.util.List;
2525
import java.util.Objects;
26-
import java.util.Optional;
2726
import java.util.function.Function;
2827
import tech.pegasys.teku.api.DataProvider;
2928
import tech.pegasys.teku.api.NetworkDataProvider;
29+
import tech.pegasys.teku.api.peer.Eth2PeerWithEnr;
3030
import tech.pegasys.teku.api.response.v1.node.Direction;
3131
import tech.pegasys.teku.api.response.v1.node.State;
3232
import tech.pegasys.teku.infrastructure.json.types.DeserializableTypeDefinition;
3333
import tech.pegasys.teku.infrastructure.json.types.SerializableTypeDefinition;
3434
import tech.pegasys.teku.infrastructure.restapi.endpoints.EndpointMetadata;
3535
import tech.pegasys.teku.infrastructure.restapi.endpoints.RestApiEndpoint;
3636
import tech.pegasys.teku.infrastructure.restapi.endpoints.RestApiRequest;
37-
import tech.pegasys.teku.networking.eth2.peers.Eth2Peer;
3837

3938
public class GetPeers extends RestApiEndpoint {
4039
public static final String ROUTE = "/eth/v1/node/peers";
@@ -45,41 +44,42 @@ public class GetPeers extends RestApiEndpoint {
4544
private static final DeserializableTypeDefinition<Direction> DIRECTION_TYPE =
4645
DeserializableTypeDefinition.enumOf(Direction.class);
4746

48-
static final SerializableTypeDefinition<Eth2Peer> PEER_DATA_TYPE =
49-
SerializableTypeDefinition.object(Eth2Peer.class)
47+
static final SerializableTypeDefinition<Eth2PeerWithEnr> PEER_DATA_TYPE =
48+
SerializableTypeDefinition.object(Eth2PeerWithEnr.class)
5049
.name("Peer")
5150
.withField(
5251
"peer_id",
5352
string(
5453
"Cryptographic hash of a peer’s public key. "
5554
+ "'[Read more](https://docs.libp2p.io/concepts/peer-id/)",
5655
"QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5N"),
57-
eth2Peer -> eth2Peer.getId().toBase58())
56+
eth2Peer -> eth2Peer.peer().getId().toBase58())
5857
.withOptionalField(
5958
"enr",
6059
string(
61-
"Ethereum node record. Not currently populated. "
62-
+ "[Read more](https://eips.ethereum.org/EIPS/eip-778)",
60+
"Ethereum node record. " + "[Read more](https://eips.ethereum.org/EIPS/eip-778)",
6361
"enr:-IS4QHCYrYZbAKWCBRlAy5zzaDZXJBGkcnh4MHcBFZntXNFrdvJjX04jRzjzCBOonrk"
6462
+ "Tfj499SZuOh8R33Ls8RRcy5wBgmlkgnY0gmlwhH8AAAGJc2VjcDI1NmsxoQPKY0yuDUmstAHYp"
6563
+ "Ma2_oxVtw0RW_QAdpzBQA8yWM0xOIN1ZHCCdl8"),
66-
eth2Peer -> Optional.empty())
64+
Eth2PeerWithEnr::enr)
6765
.withField(
6866
"last_seen_p2p_address",
6967
string(
7068
"Multiaddr used in last peer connection. "
7169
+ "[Read more](https://docs.libp2p.io/reference/glossary/#multiaddr)",
7270
"/ip4/7.7.7.7/tcp/4242/p2p/QmYyQSo1c1Ym7orWxLYvCrM2EmxFTANf8wXmmE7DWjhx5N"),
73-
eth2Peer -> eth2Peer.getAddress().toExternalForm())
71+
eth2Peer -> eth2Peer.peer().getAddress().toExternalForm())
7472
.withField(
7573
"state",
7674
STATE_TYPE,
77-
eth2Peer -> eth2Peer.isConnected() ? State.connected : State.disconnected)
75+
eth2Peer -> eth2Peer.peer().isConnected() ? State.connected : State.disconnected)
7876
.withField(
7977
"direction",
8078
DIRECTION_TYPE,
8179
eth2Peer ->
82-
eth2Peer.connectionInitiatedLocally() ? Direction.outbound : Direction.inbound)
80+
eth2Peer.peer().connectionInitiatedLocally()
81+
? Direction.outbound
82+
: Direction.inbound)
8383
.build();
8484

8585
private static final SerializableTypeDefinition<Integer> PEERS_META_TYPE =
@@ -118,20 +118,20 @@ public GetPeers(final DataProvider provider) {
118118
}
119119

120120
@Override
121-
public void handleRequest(RestApiRequest request) throws JsonProcessingException {
122-
request.respondOk(new PeersData(network.getEth2Peers()), NO_CACHE);
121+
public void handleRequest(final RestApiRequest request) throws JsonProcessingException {
122+
request.respondOk(new PeersData(network.getEth2PeersWithEnr()), NO_CACHE);
123123
}
124124

125125
static class PeersData {
126-
private final List<Eth2Peer> peers;
126+
private final List<Eth2PeerWithEnr> peers;
127127
private final Integer count;
128128

129-
PeersData(final List<Eth2Peer> peers) {
129+
PeersData(final List<Eth2PeerWithEnr> peers) {
130130
this.peers = peers;
131131
this.count = peers.size();
132132
}
133133

134-
public List<Eth2Peer> getPeers() {
134+
public List<Eth2PeerWithEnr> getPeers() {
135135
return peers;
136136
}
137137

data/beaconrestapi/src/test/java/tech/pegasys/teku/beaconrestapi/handlers/v1/node/GetPeerByIdTest.java

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
package tech.pegasys.teku.beaconrestapi.handlers.v1.node;
1515

1616
import static org.assertj.core.api.Assertions.assertThat;
17+
import static org.mockito.ArgumentMatchers.any;
1718
import static org.mockito.ArgumentMatchers.eq;
1819
import static org.mockito.Mockito.mock;
1920
import static org.mockito.Mockito.when;
@@ -26,17 +27,25 @@
2627

2728
import com.fasterxml.jackson.core.JsonProcessingException;
2829
import java.util.Optional;
30+
import org.apache.tuweni.units.bigints.UInt256;
2931
import org.junit.jupiter.api.BeforeEach;
3032
import org.junit.jupiter.api.Test;
33+
import tech.pegasys.teku.api.peer.Eth2PeerWithEnr;
3134
import tech.pegasys.teku.beaconrestapi.AbstractMigratedBeaconHandlerTest;
3235
import tech.pegasys.teku.infrastructure.http.HttpErrorResponse;
3336
import tech.pegasys.teku.networking.eth2.peers.Eth2Peer;
37+
import tech.pegasys.teku.networking.p2p.discovery.DiscoveryNetwork;
38+
import tech.pegasys.teku.networking.p2p.discovery.DiscoveryService;
3439
import tech.pegasys.teku.networking.p2p.mock.MockNodeId;
3540
import tech.pegasys.teku.networking.p2p.network.PeerAddress;
41+
import tech.pegasys.teku.networking.p2p.peer.NodeId;
3642

3743
public class GetPeerByIdTest extends AbstractMigratedBeaconHandlerTest {
3844
private final MockNodeId peerId = new MockNodeId(123456);
45+
private final NodeId peerNodeId = mock(NodeId.class);
3946
private final Eth2Peer peer = mock(Eth2Peer.class);
47+
private static final String ENR_STUB = "enr:test";
48+
private final Eth2PeerWithEnr peerWithEnr = new Eth2PeerWithEnr(peer, Optional.of(ENR_STUB));
4049

4150
@BeforeEach
4251
void setUp() {
@@ -50,21 +59,29 @@ void setUp() {
5059

5160
@Test
5261
public void shouldReturnNotFoundIfPeerNotFound() throws Exception {
53-
when(network.getEth2PeerById(peerId.toBase58())).thenReturn(Optional.empty());
54-
62+
when(eth2P2PNetwork.parseNodeId(peerId.toBase58())).thenReturn(peerNodeId);
63+
when(eth2P2PNetwork.getPeer(eq(peerNodeId))).thenReturn(Optional.empty());
5564
handler.handleRequest(request);
65+
5666
assertThat(request.getResponseCode()).isEqualTo(SC_NOT_FOUND);
5767
assertThat(request.getResponseBody())
5868
.isEqualTo(new HttpErrorResponse(SC_NOT_FOUND, "Peer not found"));
5969
}
6070

6171
@Test
6272
public void shouldReturnPeerIfFound() throws Exception {
63-
when(network.getEth2PeerById(eq(peerId.toBase58()))).thenReturn(Optional.of(peer));
73+
when(eth2P2PNetwork.parseNodeId(peerId.toBase58())).thenReturn(peerNodeId);
74+
when(eth2P2PNetwork.getPeer(eq(peerNodeId))).thenReturn(Optional.of(peer));
75+
when(peer.getDiscoveryNodeId()).thenReturn(Optional.of(UInt256.ONE));
76+
final DiscoveryNetwork<?> discoveryNetwork = mock(DiscoveryNetwork.class);
77+
when(eth2P2PNetwork.getDiscoveryNetwork()).thenReturn(Optional.of(discoveryNetwork));
78+
final DiscoveryService discoveryService = mock(DiscoveryService.class);
79+
when(discoveryNetwork.getDiscoveryService()).thenReturn(discoveryService);
80+
when(discoveryService.lookupEnr(any())).thenReturn(Optional.of(ENR_STUB));
6481
handler.handleRequest(request);
6582

6683
assertThat(request.getResponseCode()).isEqualTo(SC_OK);
67-
assertThat(request.getResponseBody()).isEqualTo(peer);
84+
assertThat(request.getResponseBody()).isEqualTo(peerWithEnr);
6885
}
6986

7087
@Test
@@ -79,10 +96,11 @@ void metadata_shouldHandle500() throws JsonProcessingException {
7996

8097
@Test
8198
void metadata_shouldHandle200() throws JsonProcessingException {
82-
final String data = getResponseStringFromMetadata(handler, SC_OK, peer);
99+
final String data = getResponseStringFromMetadata(handler, SC_OK, peerWithEnr);
83100
assertThat(data)
84101
.isEqualTo(
85102
"{\"data\":{\"peer_id\":\"1111111111111111111111111111177em\","
103+
+ "\"enr\":\"enr:test\","
86104
+ "\"last_seen_p2p_address\":\"1111111111111111111111111111177em\",\"state\":\"connected\",\"direction\":\"inbound\"}}");
87105
}
88106
}

data/beaconrestapi/src/test/java/tech/pegasys/teku/beaconrestapi/handlers/v1/node/GetPeersTest.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,11 @@
2424

2525
import com.fasterxml.jackson.core.JsonProcessingException;
2626
import java.util.List;
27+
import java.util.Optional;
2728
import org.junit.jupiter.api.BeforeEach;
2829
import org.junit.jupiter.api.Test;
2930
import tech.pegasys.teku.api.NetworkDataProvider;
31+
import tech.pegasys.teku.api.peer.Eth2PeerWithEnr;
3032
import tech.pegasys.teku.beaconrestapi.AbstractMigratedBeaconHandlerTest;
3133
import tech.pegasys.teku.beaconrestapi.handlers.v1.node.GetPeers.PeersData;
3234
import tech.pegasys.teku.infrastructure.restapi.endpoints.CacheLength;
@@ -42,7 +44,10 @@ public class GetPeersTest extends AbstractMigratedBeaconHandlerTest {
4244
private final Eth2Peer peer2 = mock(Eth2Peer.class);
4345

4446
private final NetworkDataProvider networkDataProvider = mock(NetworkDataProvider.class);
45-
private final List<Eth2Peer> data = List.of(peer1, peer2);
47+
private final List<Eth2PeerWithEnr> data =
48+
List.of(
49+
new Eth2PeerWithEnr(peer1, Optional.empty()),
50+
new Eth2PeerWithEnr(peer2, Optional.empty()));
4651
private final GetPeers.PeersData peersData = new PeersData(data);
4752

4853
@BeforeEach
@@ -62,7 +67,7 @@ void setup() {
6267
@Test
6368
public void shouldReturnListOfPeers() throws Exception {
6469

65-
when(networkDataProvider.getEth2Peers()).thenReturn(data);
70+
when(networkDataProvider.getEth2PeersWithEnr()).thenReturn(data);
6671

6772
handler.handleRequest(request);
6873
assertThat(request.getResponseCode()).isEqualTo(SC_OK);

data/provider/src/main/java/tech/pegasys/teku/api/NetworkDataProvider.java

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,36 @@
1515

1616
import java.util.List;
1717
import java.util.Optional;
18+
import java.util.function.Function;
19+
import org.apache.tuweni.units.bigints.UInt256;
20+
import tech.pegasys.teku.api.peer.Eth2PeerWithEnr;
1821
import tech.pegasys.teku.api.response.v1.node.Direction;
1922
import tech.pegasys.teku.api.response.v1.node.Peer;
2023
import tech.pegasys.teku.api.response.v1.node.State;
2124
import tech.pegasys.teku.networking.eth2.Eth2P2PNetwork;
2225
import tech.pegasys.teku.networking.eth2.peers.Eth2Peer;
26+
import tech.pegasys.teku.networking.p2p.discovery.DiscoveryNetwork;
27+
import tech.pegasys.teku.networking.p2p.discovery.DiscoveryService;
2328
import tech.pegasys.teku.networking.p2p.peer.NodeId;
2429
import tech.pegasys.teku.spec.datastructures.networking.libp2p.rpc.metadata.MetadataMessage;
2530

2631
public class NetworkDataProvider {
32+
final Function<Optional<UInt256>, Optional<String>> enrLookupFunction;
33+
2734
private final Eth2P2PNetwork network;
2835

2936
public NetworkDataProvider(final Eth2P2PNetwork network) {
3037
this.network = network;
38+
this.enrLookupFunction =
39+
maybeNodeId -> {
40+
if (maybeNodeId.isEmpty()) {
41+
return Optional.empty();
42+
}
43+
final Optional<DiscoveryService> maybeDiscoveryService =
44+
network.getDiscoveryNetwork().map(DiscoveryNetwork::getDiscoveryService);
45+
return maybeDiscoveryService.flatMap(
46+
discoveryService -> discoveryService.lookupEnr(maybeNodeId.get()));
47+
};
3148
}
3249

3350
/**
@@ -87,18 +104,28 @@ public List<Eth2Peer> getEth2Peers() {
87104
return network.streamPeers().toList();
88105
}
89106

90-
public List<Eth2Peer> getPeerScores() {
91-
return network.streamPeers().toList();
107+
public List<Eth2PeerWithEnr> getEth2PeersWithEnr() {
108+
return network
109+
.streamPeers()
110+
.map(
111+
eth2Peer ->
112+
new Eth2PeerWithEnr(
113+
eth2Peer, enrLookupFunction.apply(eth2Peer.getDiscoveryNodeId())))
114+
.toList();
92115
}
93116

94-
public Optional<Peer> getPeerById(final String peerId) {
95-
final NodeId nodeId = network.parseNodeId(peerId);
96-
return network.getPeer(nodeId).map(this::toPeer);
117+
public List<Eth2Peer> getPeerScores() {
118+
return network.streamPeers().toList();
97119
}
98120

99-
public Optional<Eth2Peer> getEth2PeerById(final String peerId) {
121+
public Optional<Eth2PeerWithEnr> getEth2PeerById(final String peerId) {
100122
final NodeId nodeId = network.parseNodeId(peerId);
101-
return network.getPeer(nodeId);
123+
return network
124+
.getPeer(nodeId)
125+
.map(
126+
eth2Peer ->
127+
new Eth2PeerWithEnr(
128+
eth2Peer, enrLookupFunction.apply(eth2Peer.getDiscoveryNodeId())));
102129
}
103130

104131
private Peer toPeer(final Eth2Peer eth2Peer) {
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/*
2+
* Copyright Consensys Software Inc., 2024
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
5+
* the License. You may obtain a copy of the License at
6+
*
7+
* http://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
10+
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
11+
* specific language governing permissions and limitations under the License.
12+
*/
13+
14+
package tech.pegasys.teku.api.peer;
15+
16+
import java.util.Optional;
17+
import tech.pegasys.teku.networking.eth2.peers.Eth2Peer;
18+
19+
public record Eth2PeerWithEnr(Eth2Peer peer, Optional<String> enr) {}

gradle/versions.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ dependencyManagement {
158158

159159
// discovery includes tuweni libraries under a different name so version resolution doesn't work
160160
// exclude them here and leave them to be included on the classpath by the version we use
161-
dependency('tech.pegasys.discovery:discovery:24.6.0') {
161+
dependency('tech.pegasys.discovery:discovery:24.9.1') {
162162
exclude 'org.apache.tuweni:bytes'
163163
exclude 'org.apache.tuweni:crypto'
164164
exclude 'org.apache.tuweni:units'

0 commit comments

Comments
 (0)