Added support for EIP-868 (#1721)

* #1695 - Added support for EIP-868

Signed-off-by: David Mechler <david.mechler@consensys.net>

* Fix broken test

Signed-off-by: David Mechler <david.mechler@consensys.net>

* #1695 - Added tests for new functionality

Signed-off-by: David Mechler <david.mechler@consensys.net>

* #1695 - spotlessApply

Signed-off-by: David Mechler <david.mechler@consensys.net>

* Update changelog

Signed-off-by: David Mechler <david.mechler@consensys.net>
pull/1737/head
David Mechler 4 years ago committed by GitHub
parent 850706ed94
commit 98383c5777
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      CHANGELOG.md
  2. 11
      ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/DiscoveryPeer.java
  3. 9
      ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/PeerDiscoveryAgent.java
  4. 4
      ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/internal/DiscoveryProtocolLogger.java
  5. 69
      ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/internal/ENRRequestPacketData.java
  6. 75
      ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/internal/ENRResponsePacketData.java
  7. 4
      ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/internal/PacketType.java
  8. 40
      ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/internal/PeerDiscoveryController.java
  9. 43
      ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/internal/PingPacketData.java
  10. 34
      ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/discovery/internal/PongPacketData.java
  11. 12
      ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/discovery/PeerDiscoveryAgentTest.java
  12. 252
      ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/discovery/PeerDiscoveryPacketPcapSedesTest.java
  13. 39
      ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/discovery/PeerDiscoveryTestHelper.java
  14. 90
      ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/discovery/internal/ENRRequestPacketDataTest.java
  15. 87
      ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/discovery/internal/ENRResponsePacketDataTest.java
  16. 4
      ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/discovery/internal/MockPacketDataFactory.java
  17. 9
      ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/discovery/internal/PacketTest.java
  18. 137
      ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/discovery/internal/PeerDiscoveryControllerTest.java
  19. 7
      ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/discovery/internal/PeerDiscoveryTableRefreshTest.java
  20. 22
      ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/discovery/internal/PingPacketDataTest.java
  21. 14
      ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/discovery/internal/PongPacketDataTest.java

@ -3,6 +3,8 @@
## 20.10.4
### Additions and Improvements
* Implemented [EIP-778](https://eips.ethereum.org/EIPS/eip-778): Ethereum Node Records (ENR) [\#1680](https://github.com/hyperledger/besu/pull/1680)
* Implemented [EIP-868](https://eips.ethereum.org/EIPS/eip-868): Simple Subroutines for the EVM [\#1721](https://github.com/hyperledger/besu/pull/1721)
### Bug Fixes

@ -24,6 +24,7 @@ import org.hyperledger.besu.ethereum.rlp.RLPOutput;
import java.util.Optional;
import org.apache.tuweni.bytes.Bytes;
import org.ethereum.beacon.discovery.schema.NodeRecord;
/**
* Represents an Ethereum node that we interacting with through the discovery and wire protocols.
@ -39,6 +40,8 @@ public class DiscoveryPeer extends DefaultPeer {
private long lastSeen = 0;
private long lastAttemptedConnection = 0;
private NodeRecord nodeRecord;
private DiscoveryPeer(final EnodeURL enode, final Endpoint endpoint) {
super(enode);
this.endpoint = endpoint;
@ -126,6 +129,14 @@ public class DiscoveryPeer extends DefaultPeer {
return endpoint;
}
public Optional<NodeRecord> getNodeRecord() {
return Optional.ofNullable(nodeRecord);
}
public void setNodeRecord(final NodeRecord nodeRecord) {
this.nodeRecord = nodeRecord;
}
public boolean discoveryEndpointMatches(final DiscoveryPeer peer) {
return peer.getEndpoint().getHost().equals(endpoint.getHost())
&& peer.getEndpoint().getUdpPort() == endpoint.getUdpPort();

@ -160,8 +160,10 @@ public abstract class PeerDiscoveryAgent {
this.localNode = Optional.of(ourNode);
isActive = true;
LOG.info("P2P peer discovery agent started and listening on {}", localAddress);
addLocalNodeRecord(id, advertisedAddress, tcpPort, discoveryPort)
.ifPresent(
nodeRecord -> localNode.ifPresent(peer -> peer.setNodeRecord(nodeRecord)));
startController(ourNode);
addLocalNodeRecord(id, advertisedAddress, tcpPort, discoveryPort);
return discoveryPort;
});
} else {
@ -170,7 +172,7 @@ public abstract class PeerDiscoveryAgent {
}
}
private void addLocalNodeRecord(
private Optional<NodeRecord> addLocalNodeRecord(
final Bytes nodeId,
final String advertisedAddress,
final Integer tcpPort,
@ -206,7 +208,10 @@ public abstract class PeerDiscoveryAgent {
keyValueStorageTransaction.put(
Bytes.wrap(SEQ_NO_STORE_KEY.getBytes(UTF_8)).toArray(), nodeRecord.serialize().toArray());
keyValueStorageTransaction.commit();
return Optional.of(nodeRecord);
}
return existingNodeRecord;
}
public void addPeerRequirement(final PeerRequirement peerRequirement) {

@ -74,6 +74,10 @@ public class DiscoveryProtocolLogger {
return "FINDN";
case NEIGHBORS:
return "NEIGH";
case ENR_REQUEST:
return "ENRREQ";
case ENR_RESPONSE:
return "ENRRESP";
}
return null;
}

@ -0,0 +1,69 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.p2p.discovery.internal;
import static com.google.common.base.Preconditions.checkArgument;
import org.hyperledger.besu.ethereum.rlp.RLPInput;
import org.hyperledger.besu.ethereum.rlp.RLPOutput;
public class ENRRequestPacketData implements PacketData {
/* Fixed value that represents we're using v5 of the P2P discovery protocol. */
private static final int VERSION = 5;
/* In seconds after epoch. */
private final long expiration;
private ENRRequestPacketData(final long expiration) {
checkArgument(expiration >= 0, "expiration cannot be negative");
this.expiration = expiration;
}
public static ENRRequestPacketData create() {
return create(PacketData.defaultExpiration());
}
static ENRRequestPacketData create(final long expirationSec) {
return new ENRRequestPacketData(expirationSec);
}
public static ENRRequestPacketData readFrom(final RLPInput in) {
in.enterList();
// The first element signifies the "version", but this value is ignored as of EIP-8
in.readBigIntegerScalar();
final long expiration = in.readLongScalar();
in.leaveListLenient();
return new ENRRequestPacketData(expiration);
}
@Override
public void writeTo(final RLPOutput out) {
out.startList();
out.writeIntScalar(VERSION);
out.writeLongScalar(expiration);
out.endList();
}
public long getExpiration() {
return expiration;
}
@Override
public String toString() {
return "ENRRequestPacketData{" + "expiration=" + expiration + '}';
}
}

@ -0,0 +1,75 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.p2p.discovery.internal;
import static com.google.common.base.Preconditions.checkArgument;
import org.hyperledger.besu.ethereum.rlp.RLPInput;
import org.hyperledger.besu.ethereum.rlp.RLPOutput;
import org.apache.tuweni.bytes.Bytes;
import org.ethereum.beacon.discovery.schema.IdentitySchemaInterpreter;
import org.ethereum.beacon.discovery.schema.NodeRecord;
import org.ethereum.beacon.discovery.schema.NodeRecordFactory;
public class ENRResponsePacketData implements PacketData {
/* The hash of the entire ENRRequest packet being replied to. */
private final Bytes requestHash;
/* The node record. */
private final NodeRecord enr;
private ENRResponsePacketData(final Bytes requestHash, final NodeRecord enr) {
checkArgument(requestHash != null, "request hash cannot be null");
checkArgument(enr != null, "enr cannot be null");
this.requestHash = requestHash;
this.enr = enr;
}
static ENRResponsePacketData create(final Bytes requestHash, final NodeRecord enr) {
return new ENRResponsePacketData(requestHash, enr);
}
public static ENRResponsePacketData readFrom(final RLPInput in) {
in.enterList();
final Bytes requestHash = in.readBytes();
final NodeRecord enr =
new NodeRecordFactory(IdentitySchemaInterpreter.V4).fromBytes(in.readBytes());
in.leaveListLenient();
return new ENRResponsePacketData(requestHash, enr);
}
@Override
public void writeTo(final RLPOutput out) {
out.startList();
out.writeBytes(requestHash);
out.writeBytes(enr.serialize());
out.endList();
}
public Bytes getRequestHash() {
return requestHash;
}
public NodeRecord getEnr() {
return enr;
}
@Override
public String toString() {
return "ENRResponsePacketData{" + "requestHash=" + requestHash + ", enr=" + enr + '}';
}
}

@ -26,7 +26,9 @@ public enum PacketType {
PING(0x01, PingPacketData::readFrom),
PONG(0x02, PongPacketData::readFrom),
FIND_NEIGHBORS(0x03, FindNeighborsPacketData::readFrom),
NEIGHBORS(0x04, NeighborsPacketData::readFrom);
NEIGHBORS(0x04, NeighborsPacketData::readFrom),
ENR_REQUEST(0x05, ENRRequestPacketData::readFrom),
ENR_RESPONSE(0x06, ENRResponsePacketData::readFrom);
private static final int MAX_VALUE = 0x7F;
private static final int BYTE_MASK = 0xFF;

@ -58,6 +58,7 @@ import com.google.common.cache.CacheBuilder;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.tuweni.bytes.Bytes;
import org.ethereum.beacon.discovery.schema.NodeRecord;
/**
* This component is the entrypoint for managing the lifecycle of peers.
@ -333,6 +334,23 @@ public class PeerDiscoveryController {
respondToFindNeighbors(fn, peer);
}
break;
case ENR_REQUEST:
if (PeerDiscoveryStatus.BONDED.equals(peer.getStatus())) {
packet
.getPacketData(ENRRequestPacketData.class)
.ifPresent(p -> respondToENRRequest(p, packet.getHash(), peer));
}
break;
case ENR_RESPONSE:
// Currently there is no use case where an ENRResponse will be sent otherwise
// logic can be added here to query and store the response ENRs
packet
.getPacketData(ENRResponsePacketData.class)
.filter(p -> p.getEnr().getNodeId().equals(sender.getId()))
.ifPresent(p -> LOG.debug("Received NodeRecord: {}", p.getEnr().asEnr()));
break;
}
}
@ -447,7 +465,10 @@ public class PeerDiscoveryController {
final Consumer<PeerInteractionState> action =
interaction -> {
final PingPacketData data =
PingPacketData.create(localPeer.getEndpoint(), peer.getEndpoint());
PingPacketData.create(
localPeer.getEndpoint(),
peer.getEndpoint(),
localPeer.getNodeRecord().map(NodeRecord::getSeq).orElse(null));
createPacket(
PacketType.PING,
data,
@ -542,7 +563,11 @@ public class PeerDiscoveryController {
return;
}
// We don't care about the `from` field of the ping, we pong to the `sender`
final PongPacketData data = PongPacketData.create(sender.getEndpoint(), pingHash);
final PongPacketData data =
PongPacketData.create(
sender.getEndpoint(),
pingHash,
localPeer.getNodeRecord().map(NodeRecord::getSeq).orElse(null));
sendPacket(sender, PacketType.PONG, data);
}
@ -559,6 +584,17 @@ public class PeerDiscoveryController {
sendPacket(sender, PacketType.NEIGHBORS, data);
}
private void respondToENRRequest(
final ENRRequestPacketData enrRequestPacketData,
final Bytes requestHash,
final DiscoveryPeer sender) {
if (enrRequestPacketData.getExpiration() >= Instant.now().getEpochSecond()) {
final ENRResponsePacketData data =
ENRResponsePacketData.create(requestHash, localPeer.getNodeRecord().orElse(null));
sendPacket(sender, PacketType.ENR_RESPONSE, data);
}
}
// Dispatches an event to a set of observers.
private void dispatchPeerBondedEvent(
final Subscribers<PeerBondedObserver> observers,

@ -22,6 +22,8 @@ import org.hyperledger.besu.ethereum.rlp.RLPOutput;
import java.util.Optional;
import org.apache.tuweni.units.bigints.UInt64;
public class PingPacketData implements PacketData {
/* Fixed value that represents we're using v5 of the P2P discovery protocol. */
@ -36,22 +38,34 @@ public class PingPacketData implements PacketData {
/* In seconds after epoch. */
private final long expiration;
/* Current sequence number of the sending node’s record */
private final UInt64 enrSeq;
private PingPacketData(
final Optional<Endpoint> maybeFrom, final Endpoint to, final long expiration) {
final Optional<Endpoint> maybeFrom,
final Endpoint to,
final long expiration,
final UInt64 enrSeq) {
checkArgument(to != null, "destination endpoint cannot be null");
checkArgument(expiration >= 0, "expiration cannot be negative");
this.maybeFrom = maybeFrom;
this.to = to;
this.expiration = expiration;
this.enrSeq = enrSeq;
}
public static PingPacketData create(final Endpoint from, final Endpoint to) {
return create(from, to, PacketData.defaultExpiration());
public static PingPacketData create(final Endpoint from, final Endpoint to, final UInt64 enrSeq) {
checkArgument(
enrSeq != null && UInt64.ZERO.compareTo(enrSeq) < 0, "enrSeq cannot be null or negative");
return create(from, to, PacketData.defaultExpiration(), enrSeq);
}
static PingPacketData create(final Endpoint from, final Endpoint to, final long expirationSec) {
return new PingPacketData(Optional.of(from), to, expirationSec);
static PingPacketData create(
final Endpoint from, final Endpoint to, final long expirationSec, final UInt64 enrSeq) {
checkArgument(
enrSeq != null && UInt64.ZERO.compareTo(enrSeq) < 0, "enrSeq cannot be null or negative");
return new PingPacketData(Optional.of(from), to, expirationSec, enrSeq);
}
public static PingPacketData readFrom(final RLPInput in) {
@ -61,8 +75,12 @@ public class PingPacketData implements PacketData {
final Optional<Endpoint> from = Endpoint.maybeDecodeStandalone(in);
final Endpoint to = Endpoint.decodeStandalone(in);
final long expiration = in.readLongScalar();
UInt64 enrSeq = null;
if (!in.isEndOfCurrentList()) {
enrSeq = UInt64.fromBytes(in.readBytes());
}
in.leaveListLenient();
return new PingPacketData(from, to, expiration);
return new PingPacketData(from, to, expiration, enrSeq);
}
@Override
@ -77,6 +95,13 @@ public class PingPacketData implements PacketData {
.encodeStandalone(out);
to.encodeStandalone(out);
out.writeLongScalar(expiration);
out.writeBytes(
getEnrSeq()
.orElseThrow(
() ->
new IllegalStateException(
"Attempting to serialize invalid PING packet. Missing 'enrSeq' field"))
.toBytes());
out.endList();
}
@ -92,6 +117,10 @@ public class PingPacketData implements PacketData {
return expiration;
}
public Optional<UInt64> getEnrSeq() {
return Optional.ofNullable(enrSeq);
}
@Override
public String toString() {
return "PingPacketData{"
@ -101,6 +130,8 @@ public class PingPacketData implements PacketData {
+ to
+ ", expiration="
+ expiration
+ ", enrSeq="
+ enrSeq
+ '}';
}
}

@ -18,7 +18,10 @@ import org.hyperledger.besu.ethereum.p2p.discovery.Endpoint;
import org.hyperledger.besu.ethereum.rlp.RLPInput;
import org.hyperledger.besu.ethereum.rlp.RLPOutput;
import java.util.Optional;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.units.bigints.UInt64;
public class PongPacketData implements PacketData {
@ -31,14 +34,20 @@ public class PongPacketData implements PacketData {
/* In seconds after epoch. */
private final long expiration;
private PongPacketData(final Endpoint to, final Bytes pingHash, final long expiration) {
/* Current sequence number of the sending node’s record */
private final UInt64 enrSeq;
private PongPacketData(
final Endpoint to, final Bytes pingHash, final long expiration, final UInt64 enrSeq) {
this.to = to;
this.pingHash = pingHash;
this.expiration = expiration;
this.enrSeq = enrSeq;
}
public static PongPacketData create(final Endpoint to, final Bytes pingHash) {
return new PongPacketData(to, pingHash, PacketData.defaultExpiration());
public static PongPacketData create(
final Endpoint to, final Bytes pingHash, final UInt64 enrSeq) {
return new PongPacketData(to, pingHash, PacketData.defaultExpiration(), enrSeq);
}
public static PongPacketData readFrom(final RLPInput in) {
@ -46,8 +55,12 @@ public class PongPacketData implements PacketData {
final Endpoint to = Endpoint.decodeStandalone(in);
final Bytes hash = in.readBytes();
final long expiration = in.readLongScalar();
UInt64 enrSeq = null;
if (!in.isEndOfCurrentList()) {
enrSeq = UInt64.fromBytes(in.readBytes());
}
in.leaveListLenient();
return new PongPacketData(to, hash, expiration);
return new PongPacketData(to, hash, expiration, enrSeq);
}
@Override
@ -56,6 +69,13 @@ public class PongPacketData implements PacketData {
to.encodeStandalone(out);
out.writeBytes(pingHash);
out.writeLongScalar(expiration);
out.writeBytes(
getEnrSeq()
.orElseThrow(
() ->
new IllegalStateException(
"Attempting to serialize invalid PONG packet. Missing 'enrSeq' field"))
.toBytes());
out.endList();
}
@ -68,6 +88,8 @@ public class PongPacketData implements PacketData {
+ pingHash
+ ", expiration="
+ expiration
+ ", enrSeq="
+ enrSeq
+ '}';
}
@ -82,4 +104,8 @@ public class PongPacketData implements PacketData {
public long getExpiration() {
return expiration;
}
public Optional<UInt64> getEnrSeq() {
return Optional.ofNullable(enrSeq);
}
}

@ -67,6 +67,18 @@ public class PeerDiscoveryAgentTest {
.hasMessageContaining("Bootnodes must have discovery enabled");
}
@Test
public void testNodeRecordCreated() {
final MockPeerDiscoveryAgent agent = helper.startDiscoveryAgent(Collections.emptyList());
assertThat(agent.getAdvertisedPeer().isPresent()).isTrue();
assertThat(agent.getAdvertisedPeer().get().getNodeRecord().isPresent()).isTrue();
assertThat(agent.getAdvertisedPeer().get().getNodeRecord().get().getNodeId()).isNotNull();
assertThat(agent.getAdvertisedPeer().get().getNodeRecord().get().getIdentityScheme())
.isNotNull();
assertThat(agent.getAdvertisedPeer().get().getNodeRecord().get().getSignature()).isNotNull();
assertThat(agent.getAdvertisedPeer().get().getNodeRecord().get().getSeq()).isNotNull();
}
@Test
public void neighborsPacketFromUnbondedPeerIsDropped() {
// Start an agent with no bootstrap peers.

@ -16,126 +16,204 @@ package org.hyperledger.besu.ethereum.p2p.discovery;
import static org.assertj.core.api.Assertions.assertThat;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.ENRRequestPacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.ENRResponsePacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.FindNeighborsPacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.NeighborsPacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.Packet;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.PacketType;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.PingPacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.PongPacketData;
import org.hyperledger.besu.util.NetworkUtility;
import java.io.IOException;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import com.google.common.net.InetAddresses;
import io.pkts.Pcap;
import io.pkts.protocol.Protocol;
import io.vertx.core.buffer.Buffer;
import org.apache.tuweni.units.bigints.UInt64;
import org.assertj.core.api.Condition;
import org.bouncycastle.util.encoders.Hex;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class PeerDiscoveryPacketPcapSedesTest {
private static final String pingHexData =
"dcb59003d39fe8cf9bdc72fb83cd5f83493d8e564f9bca0ef496479b928786f7fd56981f3b"
+ "1b921edc5b28417f964179a731d690ea6a0595292dad3a45f8d1ae535efe5545290a8855086722f3f22ad821146172e865c8bdc0a78d14"
+ "4d76fef80001e505cb847f00000182765f82765fc9847f00000282765f80845fda127a880000000000000003";
private static final String pongHexData =
"53cec0d27af44bdc0471d34c4eb631f74b502df7b5513a80a054f0d619f0417d6ba4fd4d6f"
+ "b83994b95c6d0ae8b175b068a6bffc397e2b408e797069b9370ce47b153dd884b60108e686546a775ed5f85e71059a9c5791e266bd949d"
+ "0dcfba380102f83bcb84b4b57a1a82040182765fa046896547d3b4259aa1a67bd26e7ec58ab4be650c5552ef0360caf9dae489d53b845b"
+ "872dc8880000000000000003";
private static final String findNeighborsHexData =
"3b4c3be981427a8e9739dcd4ea3cf29fe1faa104b8381cb7c26053c4b711015b3"
+ "919213819e30284bb82ec90081098ff4af02e8d9aa12692d4a0511fe92a3c137c3b65dddc309a0384ddb60074be46735c798710f04b95a"
+ "868a1fdbac9328bc70003f847b840986165a2febf6b2b69383bfe10bfeafe1e0d63eac2387d340e51f402bf98860323dd8603800b661ce"
+ "df5823e1a478f4f78e6661c957ed1db1b146d521cf60675845fda14be";
private static final String neighborsHexData =
"fa4484fd625113e9bf1d38218d98ce8c28f31d722f38b0bb1bc8296c82c741e8490ac"
+ "82ea9afcb582f393cd5b7ad7fc72990015d3cc58f7f1527b6a60f671767458bc4cd4c00a08ab0eb56c85b5ab739bfda68b7cf24cdbb99d"
+ "3dddbd4e0c6840004f8a5f89ef84d847f00000182765f82765fb840233590850744c0d95e3fd325a2b694de5d3a0f1e0c7e304358253f5"
+ "725d25734a2e08bb1c2ac4ccccd527660b8f1a265c0dae4ef6adda8b5f07a742239bbd1fff84d847f00000182765f82765fb840841d92a"
+ "de4223b36e213e03197fecc1250f34c52e1e1ec8cdff5b9cbe005f95567daa9fd96a64c0e3e3a8d55157bf9d87f1c4666cdae79b37bfa5"
+ "c1835353475845fda165c";
private static final String enrResquestHexData =
"08218a2075159e8e48d5bc0561b3c9b107c4459454b82b2a0df818222178a7128e4"
+ "3e951a7ee1669b5486458919bb6c5f9e910a400222eeabe12191d234b5ce94b5614cbf19c23ed4de001ffcdd009cbb38fa0ac475e272ae"
+ "593ecc5e4cff2e90105c605845fda17cc";
private static final String enrResponseHexData =
"f354dd86f7651dad9a4ab3845c985a0a603a049b8b9dd563d26ad4ecbd6d9d066c0"
+ "f448523df89a396d2ee1bf3758d34d750ae4e46956824a11b9fcb39a757551f70be6f7a6692638c16cf903e241e6c777125ce377dd7bd0"
+ "7139d4845a2d8f50106f8e7a008218a2075159e8e48d5bc0561b3c9b107c4459454b82b2a0df818222178a712b8c4f8c2b860000000000"
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
+ "000000000000000000000000000000000000000000000000000000000000000000000000003826964827634826970893132372e302e302"
+ "e3189736563703235366b31b840fbe12329d5d99e3d46cba2d1f9d8d397a4f2955253396f6e0459f3f14bb29c0e4f37d8bac890ff9bfb4"
+ "12879257ba2378a0b48bed6b81647c6972d323212d051";
private final Instant timestamp;
private final byte[] data;
@Test
public void testUDPPingSerializeDeserialize() {
final byte[] data = Hex.decode(pingHexData);
final Packet packet = Packet.decode(Buffer.buffer(data));
assertThat(packet.getType()).isNotNull();
assertThat(packet.getNodeId()).isNotNull();
assertThat(packet.getNodeId().toArray()).hasSize(64);
assertThat(packet.getType()).isEqualTo(PacketType.PING);
assertThat(packet.getPacketData(PingPacketData.class)).isPresent();
final PingPacketData pingPacketData = packet.getPacketData(PingPacketData.class).orElse(null);
assertThat(pingPacketData).isNotNull();
assertThat(pingPacketData.getTo()).isNotNull();
assertThat(pingPacketData.getFrom()).isNotNull();
assertThat(pingPacketData.getTo().getHost()).satisfies(validInetAddressCondition);
assertThat(pingPacketData.getFrom().map(Endpoint::getHost))
.hasValueSatisfying(validInetAddressCondition);
assertThat(pingPacketData.getTo().getUdpPort()).isPositive();
assertThat(pingPacketData.getFrom().get().getUdpPort()).isPositive();
pingPacketData.getTo().getTcpPort().ifPresent(p -> assertThat(p).isPositive());
pingPacketData.getFrom().get().getTcpPort().ifPresent(p -> assertThat(p).isPositive());
assertThat(pingPacketData.getExpiration()).isPositive();
assertThat(pingPacketData.getEnrSeq().isPresent()).isTrue();
assertThat(pingPacketData.getEnrSeq().get()).isGreaterThan(UInt64.ZERO);
public PeerDiscoveryPacketPcapSedesTest(final Instant instant, final byte[] data) {
this.timestamp = instant;
this.data = data;
final byte[] encoded = packet.encode().getBytes();
assertThat(encoded).isEqualTo(data);
}
@Parameterized.Parameters(name = "{index}: {0}")
public static Collection<Object[]> parameters() throws IOException {
final Pcap pcap =
Pcap.openStream(
PeerDiscoveryPacketPcapSedesTest.class
.getClassLoader()
.getResourceAsStream("udp.pcap"));
final List<Object[]> parameters = new ArrayList<>();
pcap.loop(
packet -> {
if (packet.hasProtocol(Protocol.UDP)) {
final byte[] data = packet.getPacket(Protocol.UDP).getPayload().getArray();
parameters.add(
new Object[] {Instant.ofEpochMilli(packet.getArrivalTime() / 1000), data});
}
return true;
});
return parameters;
@Test
public void testUDPPongSerializeDeserialize() {
final byte[] data = Hex.decode(pongHexData);
final Packet packet = Packet.decode(Buffer.buffer(data));
assertThat(packet.getType()).isNotNull();
assertThat(packet.getNodeId()).isNotNull();
assertThat(packet.getNodeId().toArray()).hasSize(64);
assertThat(packet.getType()).isEqualTo(PacketType.PONG);
assertThat(packet.getPacketData(PongPacketData.class)).isPresent();
final PongPacketData pongPacketData = packet.getPacketData(PongPacketData.class).orElse(null);
assertThat(pongPacketData).isNotNull();
assertThat(pongPacketData.getTo()).isNotNull();
assertThat(pongPacketData.getTo().getHost()).satisfies(validInetAddressCondition);
assertThat(pongPacketData.getTo().getUdpPort()).isPositive();
pongPacketData.getTo().getTcpPort().ifPresent(p -> assertThat(p).isPositive());
assertThat(pongPacketData.getPingHash().toArray()).hasSize(32);
assertThat(pongPacketData.getExpiration()).isPositive();
assertThat(pongPacketData.getEnrSeq().isPresent()).isTrue();
assertThat(pongPacketData.getEnrSeq().get()).isGreaterThan(UInt64.ZERO);
final byte[] encoded = packet.encode().getBytes();
assertThat(encoded).isEqualTo(data);
}
@Test
public void serializeDeserialize() {
public void testUDPFindNeighborsSerializeDeserialize() {
final byte[] data = Hex.decode(findNeighborsHexData);
final Packet packet = Packet.decode(Buffer.buffer(data));
final Instant timestamp = Instant.ofEpochSecond(1608127678L);
assertThat(packet.getType()).isNotNull();
assertThat(packet.getNodeId()).isNotNull();
assertThat(packet.getNodeId().toArray()).hasSize(64);
assertThat(packet.getType()).isEqualTo(PacketType.FIND_NEIGHBORS);
assertThat(packet.getPacketData(FindNeighborsPacketData.class)).isPresent();
final FindNeighborsPacketData findNeighborsPacketData =
packet.getPacketData(FindNeighborsPacketData.class).orElse(null);
assertThat(findNeighborsPacketData).isNotNull();
assertThat(findNeighborsPacketData.getExpiration())
.isBetween(timestamp.getEpochSecond() - 10000, timestamp.getEpochSecond() + 10000);
assertThat(findNeighborsPacketData.getTarget().toArray()).hasSize(64);
assertThat(packet.getNodeId().toArray()).hasSize(64);
final byte[] encoded = packet.encode().getBytes();
assertThat(encoded).isEqualTo(data);
}
switch (packet.getType()) {
case PING:
assertThat(packet.getPacketData(PingPacketData.class)).isPresent();
final PingPacketData ping = packet.getPacketData(PingPacketData.class).get();
assertThat(ping.getTo()).isNotNull();
assertThat(ping.getFrom()).isNotNull();
assertThat(ping.getTo().getHost()).satisfies(validInetAddressCondition);
assertThat(ping.getFrom().map(Endpoint::getHost))
.hasValueSatisfying(validInetAddressCondition);
assertThat(ping.getTo().getUdpPort()).isPositive();
assertThat(ping.getFrom().get().getUdpPort()).isPositive();
ping.getTo().getTcpPort().ifPresent(p -> assertThat(p).isPositive());
ping.getFrom().get().getTcpPort().ifPresent(p -> assertThat(p).isPositive());
assertThat(ping.getExpiration()).isPositive();
// because of the version upgrade the ping packet won't re-serialize, so we're done
return;
case PONG:
assertThat(packet.getPacketData(PongPacketData.class)).isPresent();
final PongPacketData pong = packet.getPacketData(PongPacketData.class).get();
assertThat(pong.getTo()).isNotNull();
assertThat(pong.getTo().getHost()).satisfies(validInetAddressCondition);
assertThat(pong.getTo().getUdpPort()).isPositive();
pong.getTo().getTcpPort().ifPresent(p -> assertThat(p).isPositive());
assertThat(pong.getPingHash().toArray()).hasSize(32);
assertThat(pong.getExpiration()).isPositive();
break;
case FIND_NEIGHBORS:
assertThat(packet.getPacketData(FindNeighborsPacketData.class)).isPresent();
final FindNeighborsPacketData data =
packet.getPacketData(FindNeighborsPacketData.class).get();
assertThat(data.getExpiration())
.isBetween(timestamp.getEpochSecond() - 10000, timestamp.getEpochSecond() + 10000);
assertThat(data.getTarget().toArray()).hasSize(64);
assertThat(packet.getNodeId().toArray()).hasSize(64);
break;
case NEIGHBORS:
assertThat(packet.getPacketData(NeighborsPacketData.class)).isPresent();
final NeighborsPacketData neighbors = packet.getPacketData(NeighborsPacketData.class).get();
assertThat(neighbors.getExpiration()).isPositive();
assertThat(neighbors.getNodes()).isNotEmpty();
for (final DiscoveryPeer p : neighbors.getNodes()) {
assertThat(NetworkUtility.isValidPort(p.getEndpoint().getUdpPort())).isTrue();
assertThat(p.getEndpoint().getHost()).satisfies(validInetAddressCondition);
assertThat(p.getId().toArray()).hasSize(64);
}
break;
@Test
public void testUDPNeighborsSerializeDeserialize() {
final byte[] data = Hex.decode(neighborsHexData);
final Packet packet = Packet.decode(Buffer.buffer(data));
assertThat(packet.getType()).isNotNull();
assertThat(packet.getNodeId()).isNotNull();
assertThat(packet.getNodeId().toArray()).hasSize(64);
assertThat(packet.getType()).isEqualTo(PacketType.NEIGHBORS);
assertThat(packet.getPacketData(NeighborsPacketData.class)).isPresent();
final NeighborsPacketData neighborsPacketData =
packet.getPacketData(NeighborsPacketData.class).orElse(null);
assertThat(neighborsPacketData).isNotNull();
assertThat(neighborsPacketData.getExpiration()).isPositive();
assertThat(neighborsPacketData.getNodes()).isNotEmpty();
for (final DiscoveryPeer p : neighborsPacketData.getNodes()) {
assertThat(NetworkUtility.isValidPort(p.getEndpoint().getUdpPort())).isTrue();
assertThat(p.getEndpoint().getHost()).satisfies(validInetAddressCondition);
assertThat(p.getId().toArray()).hasSize(64);
}
final byte[] encoded = packet.encode().getBytes();
assertThat(encoded).isEqualTo(data);
}
@Test
public void testUDPENRRequestSerializeDeserialize() {
final byte[] data = Hex.decode(enrResquestHexData);
final Packet packet = Packet.decode(Buffer.buffer(data));
assertThat(packet.getType()).isNotNull();
assertThat(packet.getNodeId()).isNotNull();
assertThat(packet.getNodeId().toArray()).hasSize(64);
assertThat(packet.getType()).isEqualTo(PacketType.ENR_REQUEST);
final ENRRequestPacketData enrRequestPacketData =
packet.getPacketData(ENRRequestPacketData.class).orElse(null);
assertThat(enrRequestPacketData).isNotNull();
assertThat(enrRequestPacketData.getExpiration()).isPositive();
final byte[] encoded = packet.encode().getBytes();
assertThat(encoded).isEqualTo(data);
}
@Test
public void testUDPENRResponseSerializeDeserialize() {
final byte[] data = Hex.decode(enrResponseHexData);
final Packet packet = Packet.decode(Buffer.buffer(data));
assertThat(packet.getType()).isNotNull();
assertThat(packet.getNodeId()).isNotNull();
assertThat(packet.getNodeId().toArray()).hasSize(64);
assertThat(packet.getType()).isEqualTo(PacketType.ENR_RESPONSE);
final ENRResponsePacketData enrResponsePacketData =
packet.getPacketData(ENRResponsePacketData.class).orElse(null);
assertThat(enrResponsePacketData).isNotNull();
assertThat(enrResponsePacketData.getEnr()).isNotNull();
assertThat(enrResponsePacketData.getEnr().getSeq()).isGreaterThan(UInt64.ZERO);
assertThat(enrResponsePacketData.getEnr().getSignature()).isNotNull();
assertThat(enrResponsePacketData.getRequestHash()).isNotNull();
assertThat(enrResponsePacketData.getRequestHash().toArray()).hasSize(32);
final byte[] encoded = packet.encode().getBytes();
assertThat(encoded).isEqualTo(data);
}
private final Condition<String> validInetAddressCondition =
new Condition<>(InetAddresses::isInetAddress, "checks for valid InetAddresses");
}

@ -43,6 +43,9 @@ import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.units.bigints.UInt64;
import org.ethereum.beacon.discovery.schema.IdentitySchemaInterpreter;
import org.ethereum.beacon.discovery.schema.NodeRecord;
public class PeerDiscoveryTestHelper {
private static final String LOOPBACK_IP_ADDR = "127.0.0.1";
@ -75,12 +78,17 @@ public class PeerDiscoveryTestHelper {
public DiscoveryPeer createDiscoveryPeer(final NodeKey nodeKey) {
final Bytes peerId = nodeKey.getPublicKey().getEncodedBytes();
final int port = nextAvailablePort.incrementAndGet();
return DiscoveryPeer.fromEnode(
EnodeURL.builder()
.nodeId(peerId)
.ipAddress(LOOPBACK_IP_ADDR)
.discoveryAndListeningPorts(port)
.build());
DiscoveryPeer discoveryPeer =
DiscoveryPeer.fromEnode(
EnodeURL.builder()
.nodeId(peerId)
.ipAddress(LOOPBACK_IP_ADDR)
.discoveryAndListeningPorts(port)
.build());
discoveryPeer.setNodeRecord(
NodeRecord.fromValues(IdentitySchemaInterpreter.V4, UInt64.ONE, Collections.emptyList()));
return discoveryPeer;
}
public Packet createPingPacket(
@ -89,14 +97,16 @@ public class PeerDiscoveryTestHelper {
PacketType.PING,
PingPacketData.create(
fromAgent.getAdvertisedPeer().get().getEndpoint(),
toAgent.getAdvertisedPeer().get().getEndpoint()),
toAgent.getAdvertisedPeer().get().getEndpoint(),
UInt64.ONE),
fromAgent.getNodeKey());
}
public Packet createPongPacket(final MockPeerDiscoveryAgent toAgent, final Hash pingHash) {
return Packet.create(
PacketType.PONG,
PongPacketData.create(toAgent.getAdvertisedPeer().get().getEndpoint(), pingHash),
PongPacketData.create(
toAgent.getAdvertisedPeer().get().getEndpoint(), pingHash, UInt64.ONE),
toAgent.getNodeKey());
}
@ -199,6 +209,8 @@ public class PeerDiscoveryTestHelper {
private String advertisedHost = "127.0.0.1";
private OptionalInt bindPort = OptionalInt.empty();
private NodeKey nodeKey = NodeKeyUtils.generate();
private NodeRecord nodeRecord =
NodeRecord.fromValues(IdentitySchemaInterpreter.V4, UInt64.ONE, Collections.emptyList());
private AgentBuilder(
final Map<Bytes, MockPeerDiscoveryAgent> agents, final AtomicInteger nextAvailablePort) {
@ -256,6 +268,11 @@ public class PeerDiscoveryTestHelper {
return this;
}
public AgentBuilder nodeRecord(final NodeRecord nodeRecord) {
this.nodeRecord = nodeRecord;
return this;
}
public MockPeerDiscoveryAgent build() {
final int port = bindPort.orElseGet(nextAvailablePort::incrementAndGet);
final DiscoveryConfiguration config = new DiscoveryConfiguration();
@ -265,7 +282,11 @@ public class PeerDiscoveryTestHelper {
config.setBindPort(port);
config.setActive(active);
return new MockPeerDiscoveryAgent(nodeKey, config, peerPermissions, agents, natService);
MockPeerDiscoveryAgent mockPeerDiscoveryAgent =
new MockPeerDiscoveryAgent(nodeKey, config, peerPermissions, agents, natService);
mockPeerDiscoveryAgent.getAdvertisedPeer().ifPresent(peer -> peer.setNodeRecord(nodeRecord));
return mockPeerDiscoveryAgent;
}
}
}

@ -0,0 +1,90 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.p2p.discovery.internal;
import static org.assertj.core.api.Assertions.assertThat;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
import org.hyperledger.besu.ethereum.rlp.RLP;
import java.time.Instant;
import org.apache.tuweni.bytes.Bytes;
import org.junit.Test;
public class ENRRequestPacketDataTest {
@Test
public void serializeDeserialize() {
final long currentTimeSec = Instant.now().getEpochSecond();
final ENRRequestPacketData packet = ENRRequestPacketData.create();
final Bytes serialized = RLP.encode(packet::writeTo);
final ENRRequestPacketData deserialized = ENRRequestPacketData.readFrom(RLP.input(serialized));
assertThat(deserialized.getExpiration()).isGreaterThan(currentTimeSec);
}
@Test
public void readFrom() {
final int version = 4;
final long time = System.currentTimeMillis();
final BytesValueRLPOutput out = new BytesValueRLPOutput();
out.startList();
out.writeIntScalar(version);
out.writeLongScalar(time);
out.endList();
final Bytes serialized = out.encoded();
final ENRRequestPacketData deserialized = ENRRequestPacketData.readFrom(RLP.input(serialized));
assertThat(deserialized.getExpiration()).isEqualTo(time);
}
@Test
public void readFrom_withExtraFields() {
final int version = 4;
final long time = System.currentTimeMillis();
final BytesValueRLPOutput out = new BytesValueRLPOutput();
out.startList();
out.writeIntScalar(version);
out.writeLongScalar(time);
// Add extra field
out.writeLongScalar(11);
out.endList();
final Bytes serialized = out.encoded();
final ENRRequestPacketData deserialized = ENRRequestPacketData.readFrom(RLP.input(serialized));
assertThat(deserialized.getExpiration()).isEqualTo(time);
}
@Test
public void readFrom_unknownVersion() {
final int version = 99;
final long time = System.currentTimeMillis();
final BytesValueRLPOutput out = new BytesValueRLPOutput();
out.startList();
out.writeIntScalar(version);
out.writeLongScalar(time);
out.endList();
final Bytes serialized = out.encoded();
final ENRRequestPacketData deserialized = ENRRequestPacketData.readFrom(RLP.input(serialized));
assertThat(deserialized.getExpiration()).isEqualTo(time);
}
}

@ -0,0 +1,87 @@
/*
* Copyright ConsenSys AG.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.p2p.discovery.internal;
import static org.assertj.core.api.Assertions.assertThat;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
import org.hyperledger.besu.ethereum.rlp.RLP;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.units.bigints.UInt64;
import org.ethereum.beacon.discovery.schema.EnrField;
import org.ethereum.beacon.discovery.schema.IdentitySchema;
import org.ethereum.beacon.discovery.schema.IdentitySchemaInterpreter;
import org.ethereum.beacon.discovery.schema.NodeRecord;
import org.ethereum.beacon.discovery.schema.NodeRecordFactory;
import org.junit.Test;
public class ENRResponsePacketDataTest {
@Test
public void serializeDeserialize() {
final Bytes requestHash = Bytes.fromHexStringLenient("0x1234");
final NodeRecord nodeRecord =
new NodeRecordFactory(IdentitySchemaInterpreter.V4)
.createFromValues(UInt64.ONE, new EnrField(EnrField.ID, IdentitySchema.V4));
final ENRResponsePacketData packet = ENRResponsePacketData.create(requestHash, nodeRecord);
final Bytes serialized = RLP.encode(packet::writeTo);
final ENRResponsePacketData deserialized =
ENRResponsePacketData.readFrom(RLP.input(serialized));
assertThat(deserialized.getRequestHash()).isEqualTo(requestHash);
assertThat(deserialized.getEnr()).isEqualTo(nodeRecord);
}
@Test
public void readFrom() {
final Bytes requestHash = Bytes.fromHexStringLenient("0x1234");
final NodeRecord nodeRecord =
new NodeRecordFactory(IdentitySchemaInterpreter.V4)
.createFromValues(UInt64.ONE, new EnrField(EnrField.ID, IdentitySchema.V4));
BytesValueRLPOutput out = new BytesValueRLPOutput();
out.startList();
out.writeBytes(requestHash);
out.writeBytes(nodeRecord.serialize());
out.endList();
final Bytes encoded = out.encoded();
final ENRResponsePacketData deserialized = ENRResponsePacketData.readFrom(RLP.input(encoded));
assertThat(deserialized.getRequestHash()).isEqualTo(requestHash);
assertThat(deserialized.getEnr()).isEqualTo(nodeRecord);
}
@Test
public void readFrom_withExtraFields() {
final Bytes requestHash = Bytes.fromHexStringLenient("0x1234");
final NodeRecord nodeRecord =
new NodeRecordFactory(IdentitySchemaInterpreter.V4)
.createFromValues(UInt64.ONE, new EnrField(EnrField.ID, IdentitySchema.V4));
BytesValueRLPOutput out = new BytesValueRLPOutput();
out.startList();
out.writeBytes(requestHash);
out.writeBytes(nodeRecord.serialize());
// Add random fields
out.writeLong(1234L);
out.endList();
final Bytes encoded = out.encoded();
final ENRResponsePacketData deserialized = ENRResponsePacketData.readFrom(RLP.input(encoded));
assertThat(deserialized.getRequestHash()).isEqualTo(requestHash);
assertThat(deserialized.getEnr()).isEqualTo(nodeRecord);
}
}

@ -26,6 +26,7 @@ import java.util.Optional;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.apache.tuweni.units.bigints.UInt64;
class MockPacketDataFactory {
@ -46,7 +47,8 @@ class MockPacketDataFactory {
static Packet mockPongPacket(final DiscoveryPeer from, final Bytes pingHash) {
final Packet packet = mock(Packet.class);
final PongPacketData pongPacketData = PongPacketData.create(from.getEndpoint(), pingHash);
final PongPacketData pongPacketData =
PongPacketData.create(from.getEndpoint(), pingHash, UInt64.ONE);
when(packet.getPacketData(any())).thenReturn(Optional.of(pongPacketData));
final Bytes id = from.getId();
when(packet.getNodeId()).thenReturn(id);

@ -22,13 +22,14 @@ import java.util.Optional;
import io.vertx.core.buffer.Buffer;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.units.bigints.UInt64;
import org.bouncycastle.util.encoders.Hex;
import org.junit.Test;
public class PacketTest {
private static final String VALID_PONG_PACKET =
"a1581c1705e744976d0341011c4490b3ab0b48283407ae5cf7526b948717489613ad897c4cf167117196d21352c15bcbaec23227b22eb92a15f5cd4b0a4ef98124a679935c16bd334fbd26be55ba4344843ac4710a3f3e3684d719d48c4980660002f2cb84b4b57a1a82040182765fa046896547d3b4259aa1a67bd26e7ec58ab4be650c5552ef0360caf9dae489d53b845b872dc8";
"53cec0d27af44bdc0471d34c4eb631f74b502df7b5513a80a054f0d619f0417d6ba4fd4d6fb83994b95c6d0ae8b175b068a6bffc397e2b408e797069b9370ce47b153dd884b60108e686546a775ed5f85e71059a9c5791e266bd949d0dcfba380102f83bcb84b4b57a1a82040182765fa046896547d3b4259aa1a67bd26e7ec58ab4be650c5552ef0360caf9dae489d53b845b872dc8880000000000000003";
@Test
public void shouldDecodeValidPongPacket() {
@ -43,14 +44,16 @@ public class PacketTest {
Bytes.fromHexString(
"0x46896547d3b4259aa1a67bd26e7ec58ab4be650c5552ef0360caf9dae489d53b"));
assertThat(packetData.getExpiration()).isEqualTo(1535585736);
assertThat(packetData.getEnrSeq().isPresent()).isTrue();
assertThat(packetData.getEnrSeq().get()).isEqualTo(UInt64.valueOf(3L));
assertThat(packet.getNodeId())
.isEqualTo(
Bytes.fromHexString(
"0x669f45b66acf3b804c26ce13cfdd1f7e3d0ff4ed85060841b9af3af6dbfbacd05181e1c9363161446a307f3ca24e707856a01e4bf1eed5e1aefc14011a5c1c1c"));
"0xfbe12329d5d99e3d46cba2d1f9d8d397a4f2955253396f6e0459f3f14bb29c0e4f37d8bac890ff9bfb412879257ba2378a0b48bed6b81647c6972d323212d051"));
assertThat(packet.getHash())
.isEqualTo(
Bytes.fromHexString(
"0xa1581c1705e744976d0341011c4490b3ab0b48283407ae5cf7526b9487174896"));
"0x53cec0d27af44bdc0471d34c4eb631f74b502df7b5513a80a054f0d619f0417d"));
}
@Test

@ -60,7 +60,10 @@ import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.apache.tuweni.bytes.MutableBytes;
import org.apache.tuweni.units.bigints.UInt256;
import org.apache.tuweni.units.bigints.UInt64;
import org.assertj.core.api.Assertions;
import org.ethereum.beacon.discovery.schema.IdentitySchemaInterpreter;
import org.ethereum.beacon.discovery.schema.NodeRecord;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@ -121,7 +124,7 @@ public class PeerDiscoveryControllerTest {
// Mock the creation of the PING packet, so that we can control the hash,
// which gets validated when receiving the PONG.
final PingPacketData mockPing =
PingPacketData.create(localPeer.getEndpoint(), peers.get(0).getEndpoint());
PingPacketData.create(localPeer.getEndpoint(), peers.get(0).getEndpoint(), UInt64.ONE);
final Packet mockPacket = Packet.create(PacketType.PING, mockPing, nodeKeys.get(0));
mockPingPacketCreation(mockPacket);
@ -192,7 +195,7 @@ public class PeerDiscoveryControllerTest {
// Mock the creation of the PING packet, so that we can control the hash,
// which gets validated when receiving the PONG.
final PingPacketData mockPing =
PingPacketData.create(localPeer.getEndpoint(), peers.get(0).getEndpoint());
PingPacketData.create(localPeer.getEndpoint(), peers.get(0).getEndpoint(), UInt64.ONE);
final Packet mockPacket = Packet.create(PacketType.PING, mockPing, nodeKeys.get(0));
mockPingPacketCreation(mockPacket);
@ -210,7 +213,7 @@ public class PeerDiscoveryControllerTest {
// Simulate a PONG message from peer 0.
final PongPacketData packetData =
PongPacketData.create(localPeer.getEndpoint(), mockPacket.getHash());
PongPacketData.create(localPeer.getEndpoint(), mockPacket.getHash(), UInt64.ONE);
final Packet packet = Packet.create(PacketType.PONG, packetData, nodeKeys.get(0));
controller.onMessage(packet, peers.get(0));
@ -246,7 +249,7 @@ public class PeerDiscoveryControllerTest {
// Mock the creation of the PING packet, so that we can control the hash,
// which gets validated when receiving the PONG.
final PingPacketData mockPing =
PingPacketData.create(localPeer.getEndpoint(), peers.get(0).getEndpoint());
PingPacketData.create(localPeer.getEndpoint(), peers.get(0).getEndpoint(), UInt64.ONE);
final Packet mockPacket = Packet.create(PacketType.PING, mockPing, nodeKeys.get(0));
mockPingPacketCreation(mockPacket);
@ -281,7 +284,7 @@ public class PeerDiscoveryControllerTest {
// Setup ping to be sent to discoPeer
final List<NodeKey> nodeKeys = PeerDiscoveryTestHelper.generateNodeKeys(1);
final PingPacketData pingPacketData =
PingPacketData.create(localEndpoint, discoPeer.getEndpoint());
PingPacketData.create(localEndpoint, discoPeer.getEndpoint(), UInt64.ONE);
final Packet discoPeerPing = Packet.create(PacketType.PING, pingPacketData, nodeKeys.get(0));
mockPingPacketCreation(discoPeer, discoPeerPing);
@ -312,7 +315,8 @@ public class PeerDiscoveryControllerTest {
PingPacketData.create(
localEndpoint,
discoPeer.getEndpoint(),
Instant.now().getEpochSecond() - PacketData.DEFAULT_EXPIRATION_PERIOD_SEC);
Instant.now().getEpochSecond() - PacketData.DEFAULT_EXPIRATION_PERIOD_SEC,
UInt64.ONE);
final Packet discoPeerPing = Packet.create(PacketType.PING, pingPacketData, nodeKeys.get(0));
mockPingPacketCreation(discoPeer, discoPeerPing);
@ -340,7 +344,7 @@ public class PeerDiscoveryControllerTest {
// Mock the creation of the PING packet, so that we can control the hash, which gets validated
// when receiving the PONG.
final PingPacketData mockPing =
PingPacketData.create(localPeer.getEndpoint(), peers.get(0).getEndpoint());
PingPacketData.create(localPeer.getEndpoint(), peers.get(0).getEndpoint(), UInt64.ONE);
final Packet mockPacket = Packet.create(PacketType.PING, mockPing, nodeKeys.get(0));
mockPingPacketCreation(mockPacket);
@ -355,7 +359,7 @@ public class PeerDiscoveryControllerTest {
// Simulate PONG messages from all peers
for (int i = 0; i < 3; i++) {
final PongPacketData packetData =
PongPacketData.create(localPeer.getEndpoint(), mockPacket.getHash());
PongPacketData.create(localPeer.getEndpoint(), mockPacket.getHash(), UInt64.ONE);
final Packet packet0 = Packet.create(PacketType.PONG, packetData, nodeKeys.get(i));
controller.onMessage(packet0, peers.get(i));
}
@ -400,7 +404,7 @@ public class PeerDiscoveryControllerTest {
// when
// processing the PONG.
final PingPacketData mockPing =
PingPacketData.create(localPeer.getEndpoint(), peers.get(0).getEndpoint());
PingPacketData.create(localPeer.getEndpoint(), peers.get(0).getEndpoint(), UInt64.ONE);
final Packet mockPacket = Packet.create(PacketType.PING, mockPing, nodeKeys.get(0));
mockPingPacketCreation(mockPacket);
@ -414,7 +418,7 @@ public class PeerDiscoveryControllerTest {
// Send a PONG packet from peer 1, with an incorrect hash.
final PongPacketData packetData =
PongPacketData.create(localPeer.getEndpoint(), Bytes.fromHexString("1212"));
PongPacketData.create(localPeer.getEndpoint(), Bytes.fromHexString("1212"), UInt64.ONE);
final Packet packet = Packet.create(PacketType.PONG, packetData, nodeKeys.get(1));
controller.onMessage(packet, peers.get(1));
@ -449,7 +453,7 @@ public class PeerDiscoveryControllerTest {
// when
// processing the PONG.
final PingPacketData mockPing =
PingPacketData.create(localPeer.getEndpoint(), peers.get(0).getEndpoint());
PingPacketData.create(localPeer.getEndpoint(), peers.get(0).getEndpoint(), UInt64.ONE);
final Packet mockPacket = Packet.create(PacketType.PING, mockPing, nodeKeys.get(0));
mockPingPacketCreation(mockPacket);
controller.setRetryDelayFunction((prev) -> 999999999L);
@ -492,7 +496,8 @@ public class PeerDiscoveryControllerTest {
private void respondWithPong(
final DiscoveryPeer discoveryPeer, final NodeKey nodeKey, final Bytes hash) {
final PongPacketData packetData0 = PongPacketData.create(localPeer.getEndpoint(), hash);
final PongPacketData packetData0 =
PongPacketData.create(localPeer.getEndpoint(), hash, UInt64.ONE);
final Packet pongPacket0 = Packet.create(PacketType.PONG, packetData0, nodeKey);
controller.onMessage(pongPacket0, discoveryPeer);
}
@ -514,7 +519,7 @@ public class PeerDiscoveryControllerTest {
// Mock the creation of the PING packet, so that we can control the hash, which gets validated
// when processing the PONG.
final PingPacketData pingPacketData =
PingPacketData.create(localPeer.getEndpoint(), peers.get(0).getEndpoint());
PingPacketData.create(localPeer.getEndpoint(), peers.get(0).getEndpoint(), UInt64.ONE);
final Packet pingPacket = Packet.create(PacketType.PING, pingPacketData, nodeKeys.get(0));
mockPingPacketCreation(pingPacket);
@ -540,7 +545,7 @@ public class PeerDiscoveryControllerTest {
.hasSize(1);
final PongPacketData pongPacketData =
PongPacketData.create(localPeer.getEndpoint(), pingPacket.getHash());
PongPacketData.create(localPeer.getEndpoint(), pingPacket.getHash(), UInt64.ONE);
final Packet pongPacket = Packet.create(PacketType.PONG, pongPacketData, nodeKeys.get(1));
controller.onMessage(pongPacket, peers.get(1));
@ -574,7 +579,7 @@ public class PeerDiscoveryControllerTest {
// Send a PONG packet from peer[2], to transition it to the BONDED state.
final PongPacketData packetData2 =
PongPacketData.create(localPeer.getEndpoint(), pingPacket.getHash());
PongPacketData.create(localPeer.getEndpoint(), pingPacket.getHash(), UInt64.ONE);
final Packet pongPacket2 = Packet.create(PacketType.PONG, packetData2, nodeKeys.get(2));
controller.onMessage(pongPacket2, peers.get(2));
@ -657,7 +662,8 @@ public class PeerDiscoveryControllerTest {
// Setup ping to be sent to discoPeer
List<NodeKey> nodeKeys = PeerDiscoveryTestHelper.generateNodeKeys(1);
PingPacketData pingPacketData = PingPacketData.create(localEndpoint, discoPeer.getEndpoint());
PingPacketData pingPacketData =
PingPacketData.create(localEndpoint, discoPeer.getEndpoint(), UInt64.ONE);
final Packet discoPeerPing = Packet.create(PacketType.PING, pingPacketData, nodeKeys.get(0));
mockPingPacketCreation(discoPeer, discoPeerPing);
@ -674,13 +680,13 @@ public class PeerDiscoveryControllerTest {
// Setup ping to be sent to otherPeer after neighbors packet is received
nodeKeys = PeerDiscoveryTestHelper.generateNodeKeys(1);
pingPacketData = PingPacketData.create(localEndpoint, otherPeer.getEndpoint());
pingPacketData = PingPacketData.create(localEndpoint, otherPeer.getEndpoint(), UInt64.ONE);
final Packet pingPacket = Packet.create(PacketType.PING, pingPacketData, nodeKeys.get(0));
mockPingPacketCreation(otherPeer, pingPacket);
// Setup ping to be sent to otherPeer2 after neighbors packet is received
nodeKeys = PeerDiscoveryTestHelper.generateNodeKeys(1);
pingPacketData = PingPacketData.create(localEndpoint, otherPeer2.getEndpoint());
pingPacketData = PingPacketData.create(localEndpoint, otherPeer2.getEndpoint(), UInt64.ONE);
final Packet pingPacket2 = Packet.create(PacketType.PING, pingPacketData, nodeKeys.get(0));
mockPingPacketCreation(otherPeer2, pingPacket2);
@ -735,7 +741,8 @@ public class PeerDiscoveryControllerTest {
// Setup ping to be sent to discoPeer
List<NodeKey> nodeKeys = PeerDiscoveryTestHelper.generateNodeKeys(1);
PingPacketData pingPacketData = PingPacketData.create(localEndpoint, discoPeer.getEndpoint());
PingPacketData pingPacketData =
PingPacketData.create(localEndpoint, discoPeer.getEndpoint(), UInt64.ONE);
final Packet discoPeerPing = Packet.create(PacketType.PING, pingPacketData, nodeKeys.get(0));
mockPingPacketCreation(discoPeer, discoPeerPing);
@ -751,13 +758,13 @@ public class PeerDiscoveryControllerTest {
// Setup ping to be sent to otherPeer after neighbors packet is received
nodeKeys = PeerDiscoveryTestHelper.generateNodeKeys(1);
pingPacketData = PingPacketData.create(localEndpoint, otherPeer.getEndpoint());
pingPacketData = PingPacketData.create(localEndpoint, otherPeer.getEndpoint(), UInt64.ONE);
final Packet pingPacket = Packet.create(PacketType.PING, pingPacketData, nodeKeys.get(0));
mockPingPacketCreation(otherPeer, pingPacket);
// Setup ping to be sent to otherPeer2 after neighbors packet is received
nodeKeys = PeerDiscoveryTestHelper.generateNodeKeys(1);
pingPacketData = PingPacketData.create(localEndpoint, otherPeer2.getEndpoint());
pingPacketData = PingPacketData.create(localEndpoint, otherPeer2.getEndpoint(), UInt64.ONE);
final Packet pingPacket2 = Packet.create(PacketType.PING, pingPacketData, nodeKeys.get(0));
mockPingPacketCreation(otherPeer2, pingPacket2);
@ -790,7 +797,7 @@ public class PeerDiscoveryControllerTest {
// Setup ping to be sent to discoPeer
final List<NodeKey> nodeKeys = PeerDiscoveryTestHelper.generateNodeKeys(1);
final PingPacketData pingPacketData =
PingPacketData.create(localEndpoint, discoPeer.getEndpoint());
PingPacketData.create(localEndpoint, discoPeer.getEndpoint(), UInt64.ONE);
final Packet discoPeerPing = Packet.create(PacketType.PING, pingPacketData, nodeKeys.get(0));
mockPingPacketCreation(discoPeer, discoPeerPing);
@ -830,7 +837,7 @@ public class PeerDiscoveryControllerTest {
// Setup ping to be sent to discoPeer
final List<NodeKey> nodeKeys = PeerDiscoveryTestHelper.generateNodeKeys(1);
final PingPacketData pingPacketData =
PingPacketData.create(localEndpoint, discoPeer.getEndpoint());
PingPacketData.create(localEndpoint, discoPeer.getEndpoint(), UInt64.ONE);
final Packet discoPeerPing = Packet.create(PacketType.PING, pingPacketData, nodeKeys.get(0));
mockPingPacketCreation(discoPeer, discoPeerPing);
@ -869,7 +876,7 @@ public class PeerDiscoveryControllerTest {
// Setup ping to be sent to discoPeer
final List<NodeKey> nodeKeys = PeerDiscoveryTestHelper.generateNodeKeys(1);
final PingPacketData pingPacketData =
PingPacketData.create(localEndpoint, discoPeer.getEndpoint());
PingPacketData.create(localEndpoint, discoPeer.getEndpoint(), UInt64.ONE);
final Packet discoPeerPing = Packet.create(PacketType.PING, pingPacketData, nodeKeys.get(0));
mockPingPacketCreation(discoPeer, discoPeerPing);
@ -912,7 +919,7 @@ public class PeerDiscoveryControllerTest {
// Setup ping to be sent to discoPeer
final List<NodeKey> nodeKeys = PeerDiscoveryTestHelper.generateNodeKeys(1);
final PingPacketData pingPacketData =
PingPacketData.create(localEndpoint, discoPeer.getEndpoint());
PingPacketData.create(localEndpoint, discoPeer.getEndpoint(), UInt64.ONE);
final Packet discoPeerPing = Packet.create(PacketType.PING, pingPacketData, nodeKeys.get(0));
mockPingPacketCreation(discoPeer, discoPeerPing);
@ -941,7 +948,7 @@ public class PeerDiscoveryControllerTest {
// Mock the creation of the PING packet to control hash for PONG.
final List<NodeKey> nodeKeys = PeerDiscoveryTestHelper.generateNodeKeys(1);
final PingPacketData pingPacketData =
PingPacketData.create(localPeer.getEndpoint(), peers.get(0).getEndpoint());
PingPacketData.create(localPeer.getEndpoint(), peers.get(0).getEndpoint(), UInt64.ONE);
final Packet pingPacket = Packet.create(PacketType.PING, pingPacketData, nodeKeys.get(0));
final OutboundMessageHandler outboundMessageHandler = mock(OutboundMessageHandler.class);
@ -980,7 +987,7 @@ public class PeerDiscoveryControllerTest {
// Mock the creation of PING packets to control hash PONG packets.
final List<NodeKey> nodeKeys = PeerDiscoveryTestHelper.generateNodeKeys(1);
final PingPacketData pingPacketData =
PingPacketData.create(localPeer.getEndpoint(), peers.get(0).getEndpoint());
PingPacketData.create(localPeer.getEndpoint(), peers.get(0).getEndpoint(), UInt64.ONE);
final Packet pingPacket = Packet.create(PacketType.PING, pingPacketData, nodeKeys.get(0));
mockPingPacketCreation(pingPacket);
@ -1039,7 +1046,7 @@ public class PeerDiscoveryControllerTest {
// Mock the creation of the PING packet to control hash for PONG.
final List<NodeKey> nodeKeys = PeerDiscoveryTestHelper.generateNodeKeys(1);
final PingPacketData pingPacketData =
PingPacketData.create(localPeer.getEndpoint(), peers.get(0).getEndpoint());
PingPacketData.create(localPeer.getEndpoint(), peers.get(0).getEndpoint(), UInt64.ONE);
final Packet pingPacket = Packet.create(PacketType.PING, pingPacketData, nodeKeys.get(0));
final OutboundMessageHandler outboundMessageHandler = mock(OutboundMessageHandler.class);
@ -1078,6 +1085,8 @@ public class PeerDiscoveryControllerTest {
.nodeId(Peer.randomId())
.discoveryAndListeningPorts(30303)
.build());
localNode.setNodeRecord(
NodeRecord.fromValues(IdentitySchemaInterpreter.V4, UInt64.ONE, Collections.emptyList()));
controller =
getControllerBuilder()
@ -1116,6 +1125,8 @@ public class PeerDiscoveryControllerTest {
.nodeId(Peer.randomId())
.discoveryAndListeningPorts(30303)
.build());
localNode.setNodeRecord(
NodeRecord.fromValues(IdentitySchemaInterpreter.V4, UInt64.ONE, Collections.emptyList()));
controller =
getControllerBuilder()
@ -1160,6 +1171,8 @@ public class PeerDiscoveryControllerTest {
.nodeId(Peer.randomId())
.discoveryAndListeningPorts(30303)
.build());
localNode.setNodeRecord(
NodeRecord.fromValues(IdentitySchemaInterpreter.V4, UInt64.ONE, Collections.emptyList()));
controller =
getControllerBuilder()
@ -1206,6 +1219,8 @@ public class PeerDiscoveryControllerTest {
.nodeId(Peer.randomId())
.discoveryAndListeningPorts(30303)
.build());
localNode.setNodeRecord(
NodeRecord.fromValues(IdentitySchemaInterpreter.V4, UInt64.ONE, Collections.emptyList()));
controller =
getControllerBuilder()
@ -1244,6 +1259,8 @@ public class PeerDiscoveryControllerTest {
.nodeId(Peer.randomId())
.discoveryAndListeningPorts(30303)
.build());
localNode.setNodeRecord(
NodeRecord.fromValues(IdentitySchemaInterpreter.V4, UInt64.ONE, Collections.emptyList()));
controller =
getControllerBuilder()
@ -1266,11 +1283,72 @@ public class PeerDiscoveryControllerTest {
verify(controller, never()).dropPeer(any());
}
@Test
public void shouldRespondToENRRequest() {
final List<DiscoveryPeer> peers = createPeersInLastBucket(localPeer, 1);
// Mock the creation of the PING packet to control hash for PONG.
final List<NodeKey> nodeKeys = PeerDiscoveryTestHelper.generateNodeKeys(1);
final PingPacketData pingPacketData =
PingPacketData.create(localPeer.getEndpoint(), peers.get(0).getEndpoint(), UInt64.ONE);
final Packet pingPacket = Packet.create(PacketType.PING, pingPacketData, nodeKeys.get(0));
final OutboundMessageHandler outboundMessageHandler = mock(OutboundMessageHandler.class);
controller =
getControllerBuilder()
.peers(peers.get(0))
.outboundMessageHandler(outboundMessageHandler)
.build();
mockPingPacketCreation(pingPacket);
controller.start();
verify(outboundMessageHandler, times(1)).send(any(), matchPacketOfType(PacketType.PING));
final Packet pongPacket =
MockPacketDataFactory.mockPongPacket(peers.get(0), pingPacket.getHash());
controller.onMessage(pongPacket, peers.get(0));
assertThat(controller.streamDiscoveredPeers())
.filteredOn(p -> p.getStatus() == PeerDiscoveryStatus.BONDED)
.contains(peers.get(0));
final ENRRequestPacketData enrRequestPacketData = ENRRequestPacketData.create();
final Packet enrRequestPacket =
Packet.create(PacketType.ENR_REQUEST, enrRequestPacketData, nodeKeys.get(0));
controller.onMessage(enrRequestPacket, peers.get(0));
verify(outboundMessageHandler, times(1))
.send(any(), matchPacketOfType(PacketType.FIND_NEIGHBORS));
verify(outboundMessageHandler, times(1))
.send(any(), matchPacketOfType(PacketType.ENR_RESPONSE));
}
@Test
public void shouldNotRespondToENRRequestForNonBondedPeer() {
final List<NodeKey> nodeKeys = PeerDiscoveryTestHelper.generateNodeKeys(1);
final List<DiscoveryPeer> peers = helper.createDiscoveryPeers(nodeKeys);
final OutboundMessageHandler outboundMessageHandler = mock(OutboundMessageHandler.class);
controller =
getControllerBuilder()
.peers(peers.get(0))
.outboundMessageHandler(outboundMessageHandler)
.build();
final ENRRequestPacketData enrRequestPacketData = ENRRequestPacketData.create();
final Packet packet =
Packet.create(PacketType.ENR_REQUEST, enrRequestPacketData, nodeKeys.get(0));
controller.onMessage(packet, peers.get(0));
assertThat(controller.streamDiscoveredPeers())
.filteredOn(p -> p.getStatus() == PeerDiscoveryStatus.BONDED)
.hasSize(0);
verify(outboundMessageHandler, times(0))
.send(eq(peers.get(0)), matchPacketOfType(PacketType.ENR_REQUEST));
}
private static Packet mockPingPacket(final DiscoveryPeer from, final DiscoveryPeer to) {
final Packet packet = mock(Packet.class);
final PingPacketData pingPacketData =
PingPacketData.create(from.getEndpoint(), to.getEndpoint());
PingPacketData.create(from.getEndpoint(), to.getEndpoint(), UInt64.ONE);
when(packet.getPacketData(any())).thenReturn(Optional.of(pingPacketData));
final Bytes id = from.getId();
when(packet.getNodeId()).thenReturn(id);
@ -1304,6 +1382,7 @@ public class PeerDiscoveryControllerTest {
.ipAddress("127.0.0.1")
.discoveryAndListeningPorts(100 + counter.incrementAndGet())
.build()));
doReturn(keccak).when(peer).keccak256();
newPeers.add(peer);
}

@ -38,6 +38,7 @@ import java.util.function.Consumer;
import java.util.stream.Collectors;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.units.bigints.UInt64;
import org.assertj.core.api.Assertions;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
@ -74,7 +75,7 @@ public class PeerDiscoveryTableRefreshTest {
controller.start();
final PingPacketData mockPing =
PingPacketData.create(localPeer.getEndpoint(), remotePeer.getEndpoint());
PingPacketData.create(localPeer.getEndpoint(), remotePeer.getEndpoint(), UInt64.ONE);
final Packet mockPingPacket = Packet.create(PacketType.PING, mockPing, localKeyPair);
doAnswer(
@ -88,13 +89,13 @@ public class PeerDiscoveryTableRefreshTest {
// Send a PING, so as to add a Peer in the controller.
final PingPacketData ping =
PingPacketData.create(remotePeer.getEndpoint(), localPeer.getEndpoint());
PingPacketData.create(remotePeer.getEndpoint(), localPeer.getEndpoint(), UInt64.ONE);
final Packet pingPacket = Packet.create(PacketType.PING, ping, remoteKeyPair);
controller.onMessage(pingPacket, remotePeer);
// Answer localPeer PING to complete bonding
final PongPacketData pong =
PongPacketData.create(localPeer.getEndpoint(), mockPingPacket.getHash());
PongPacketData.create(localPeer.getEndpoint(), mockPingPacket.getHash(), UInt64.ONE);
final Packet pongPacket = Packet.create(PacketType.PONG, pong, remoteKeyPair);
controller.onMessage(pongPacket, remotePeer);

@ -24,6 +24,7 @@ import java.time.Instant;
import java.util.Optional;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.units.bigints.UInt64;
import org.junit.Test;
public class PingPacketDataTest {
@ -34,13 +35,16 @@ public class PingPacketDataTest {
final Endpoint from = new Endpoint("127.0.0.1", 30303, Optional.of(30303));
final Endpoint to = new Endpoint("127.0.0.2", 30303, Optional.empty());
final PingPacketData packet = PingPacketData.create(from, to);
final UInt64 enrSeq = UInt64.ONE;
final PingPacketData packet = PingPacketData.create(from, to, enrSeq);
final Bytes serialized = RLP.encode(packet::writeTo);
final PingPacketData deserialized = PingPacketData.readFrom(RLP.input(serialized));
assertThat(deserialized.getFrom()).contains(from);
assertThat(deserialized.getTo()).isEqualTo(to);
assertThat(deserialized.getExpiration()).isGreaterThan(currentTimeSec);
assertThat(deserialized.getEnrSeq().isPresent()).isTrue();
assertThat(deserialized.getEnrSeq().get()).isEqualTo(enrSeq);
}
@Test
@ -49,6 +53,7 @@ public class PingPacketDataTest {
final Endpoint from = new Endpoint("127.0.0.1", 30303, Optional.of(30303));
final Endpoint to = new Endpoint("127.0.0.2", 30303, Optional.empty());
final long time = System.currentTimeMillis();
final UInt64 enrSeq = UInt64.ONE;
final BytesValueRLPOutput out = new BytesValueRLPOutput();
out.startList();
@ -56,6 +61,7 @@ public class PingPacketDataTest {
from.encodeStandalone(out);
to.encodeStandalone(out);
out.writeLongScalar(time);
out.writeBytes(enrSeq.toBytes());
out.endList();
final Bytes serialized = out.encoded();
@ -64,6 +70,8 @@ public class PingPacketDataTest {
assertThat(deserialized.getFrom()).contains(from);
assertThat(deserialized.getTo()).isEqualTo(to);
assertThat(deserialized.getExpiration()).isEqualTo(time);
assertThat(deserialized.getEnrSeq().isPresent()).isTrue();
assertThat(deserialized.getEnrSeq().get()).isEqualTo(enrSeq);
}
@Test
@ -72,6 +80,7 @@ public class PingPacketDataTest {
final Endpoint from = new Endpoint("127.0.0.1", 30303, Optional.of(30303));
final Endpoint to = new Endpoint("127.0.0.2", 30303, Optional.empty());
final long time = System.currentTimeMillis();
final UInt64 enrSeq = UInt64.ONE;
final BytesValueRLPOutput out = new BytesValueRLPOutput();
out.startList();
@ -79,6 +88,7 @@ public class PingPacketDataTest {
from.encodeStandalone(out);
to.encodeStandalone(out);
out.writeLongScalar(time);
out.writeBytes(enrSeq.toBytes());
// Add extra field
out.writeLongScalar(11);
out.endList();
@ -89,6 +99,8 @@ public class PingPacketDataTest {
assertThat(deserialized.getFrom()).contains(from);
assertThat(deserialized.getTo()).isEqualTo(to);
assertThat(deserialized.getExpiration()).isEqualTo(time);
assertThat(deserialized.getEnrSeq().isPresent()).isTrue();
assertThat(deserialized.getEnrSeq().get()).isEqualTo(enrSeq);
}
@Test
@ -97,6 +109,7 @@ public class PingPacketDataTest {
final Endpoint from = new Endpoint("127.0.0.1", 30303, Optional.of(30303));
final Endpoint to = new Endpoint("127.0.0.2", 30303, Optional.empty());
final long time = System.currentTimeMillis();
final UInt64 enrSeq = UInt64.ONE;
final BytesValueRLPOutput out = new BytesValueRLPOutput();
out.startList();
@ -104,6 +117,7 @@ public class PingPacketDataTest {
from.encodeStandalone(out);
to.encodeStandalone(out);
out.writeLongScalar(time);
out.writeBytes(enrSeq.toBytes());
out.endList();
final Bytes serialized = out.encoded();
@ -112,6 +126,8 @@ public class PingPacketDataTest {
assertThat(deserialized.getFrom()).contains(from);
assertThat(deserialized.getTo()).isEqualTo(to);
assertThat(deserialized.getExpiration()).isEqualTo(time);
assertThat(deserialized.getEnrSeq().isPresent()).isTrue();
assertThat(deserialized.getEnrSeq().get()).isEqualTo(enrSeq);
}
@Test
@ -120,6 +136,7 @@ public class PingPacketDataTest {
final Endpoint from = new Endpoint("0.1.2.1", 1, Optional.of(1));
final Endpoint to = new Endpoint("127.0.0.2", 30303, Optional.empty());
final long time = System.currentTimeMillis();
final UInt64 enrSeq = UInt64.ONE;
final BytesValueRLPOutput out = new BytesValueRLPOutput();
out.startList();
@ -127,6 +144,7 @@ public class PingPacketDataTest {
from.encodeStandalone(out);
to.encodeStandalone(out);
out.writeLongScalar(time);
out.writeBytes(enrSeq.toBytes());
out.endList();
final Bytes serialized = out.encoded();
@ -135,5 +153,7 @@ public class PingPacketDataTest {
assertThat(deserialized.getFrom()).contains(from);
assertThat(deserialized.getTo()).isEqualTo(to);
assertThat(deserialized.getExpiration()).isEqualTo(time);
assertThat(deserialized.getEnrSeq().isPresent()).isTrue();
assertThat(deserialized.getEnrSeq().get()).isEqualTo(enrSeq);
}
}

@ -25,6 +25,7 @@ import java.util.Optional;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.apache.tuweni.units.bigints.UInt64;
import org.junit.Test;
public class PongPacketDataTest {
@ -34,14 +35,17 @@ public class PongPacketDataTest {
final long currentTimeSec = Instant.now().getEpochSecond();
final Endpoint to = new Endpoint("127.0.0.2", 30303, Optional.empty());
final Bytes32 hash = Bytes32.fromHexStringLenient("0x1234");
final UInt64 enrSeq = UInt64.ONE;
final PongPacketData packet = PongPacketData.create(to, hash);
final PongPacketData packet = PongPacketData.create(to, hash, enrSeq);
final Bytes serialized = RLP.encode(packet::writeTo);
final PongPacketData deserialized = PongPacketData.readFrom(RLP.input(serialized));
assertThat(deserialized.getTo()).isEqualTo(to);
assertThat(deserialized.getPingHash()).isEqualTo(hash);
assertThat(deserialized.getExpiration()).isGreaterThan(currentTimeSec);
assertThat(deserialized.getEnrSeq().isPresent()).isTrue();
assertThat(deserialized.getEnrSeq().get()).isEqualTo(enrSeq);
}
@Test
@ -49,12 +53,14 @@ public class PongPacketDataTest {
final long time = System.currentTimeMillis();
final Endpoint to = new Endpoint("127.0.0.2", 30303, Optional.empty());
final Bytes32 hash = Bytes32.fromHexStringLenient("0x1234");
final UInt64 enrSeq = UInt64.ONE;
BytesValueRLPOutput out = new BytesValueRLPOutput();
out.startList();
to.encodeStandalone(out);
out.writeBytes(hash);
out.writeLongScalar(time);
out.writeBytes(enrSeq.toBytes());
out.endList();
final Bytes encoded = out.encoded();
@ -62,6 +68,8 @@ public class PongPacketDataTest {
assertThat(deserialized.getTo()).isEqualTo(to);
assertThat(deserialized.getPingHash()).isEqualTo(hash);
assertThat(deserialized.getExpiration()).isEqualTo(time);
assertThat(deserialized.getEnrSeq().isPresent()).isTrue();
assertThat(deserialized.getEnrSeq().get()).isEqualTo(enrSeq);
}
@Test
@ -69,12 +77,14 @@ public class PongPacketDataTest {
final long time = System.currentTimeMillis();
final Endpoint to = new Endpoint("127.0.0.2", 30303, Optional.empty());
final Bytes32 hash = Bytes32.fromHexStringLenient("0x1234");
final UInt64 enrSeq = UInt64.ONE;
BytesValueRLPOutput out = new BytesValueRLPOutput();
out.startList();
to.encodeStandalone(out);
out.writeBytes(hash);
out.writeLongScalar(time);
out.writeBytes(enrSeq.toBytes());
// Add random fields
out.writeLong(1234L);
out.endList();
@ -84,5 +94,7 @@ public class PongPacketDataTest {
assertThat(deserialized.getTo()).isEqualTo(to);
assertThat(deserialized.getPingHash()).isEqualTo(hash);
assertThat(deserialized.getExpiration()).isEqualTo(time);
assertThat(deserialized.getEnrSeq().isPresent()).isTrue();
assertThat(deserialized.getEnrSeq().get()).isEqualTo(enrSeq);
}
}

Loading…
Cancel
Save