Refactor packet classes for dependency injection (#8271)

* Apply expiration checks in PacketData classes

Signed-off-by: Matilda Clerke <matilda.clerke@consensys.net>

* Fix broken unit tests

Signed-off-by: Matilda Clerke <matilda.clerke@consensys.net>

* spotless

Signed-off-by: Matilda Clerke <matilda.clerke@consensys.net>

* Fix failing test

Signed-off-by: Matilda Clerke <matilda.clerke@consensys.net>

* Refactor to use clock to validate expiry

Signed-off-by: Matilda Clerke <matilda.clerke@consensys.net>

* Refactor Packet and PacketData

Signed-off-by: Matilda Clerke <matilda.clerke@consensys.net>

* Move java.inject version to platform/build.gradle

Signed-off-by: Matilda Clerke <matilda.clerke@consensys.net>

* Update copyright notices on Packet and PacketData classes

Signed-off-by: Matilda Clerke <matilda.clerke@consensys.net>

* Update PeerDiscoveryControllerTest to avoid use of Thread.sleep

Signed-off-by: Matilda Clerke <matilda.clerke@consensys.net>

* Update copyright noticed on PeerDiscoveryControllerTest and MockPacketDataFactory

Signed-off-by: Matilda Clerke <matilda.clerke@consensys.net>

* Remove old PacketTest

Signed-off-by: Matilda Clerke <matilda.clerke@consensys.net>

* Reorder platform/build.gradle api platform dependencies to alphabetical order

Signed-off-by: Matilda Clerke <matilda.clerke@consensys.net>

---------

Signed-off-by: Matilda Clerke <matilda.clerke@consensys.net>
This commit is contained in:
Matilda-Clerke
2025-02-13 13:09:58 +11:00
committed by GitHub
parent fa19459bb2
commit 90015fca42
100 changed files with 5194 additions and 2410 deletions

View File

@@ -40,9 +40,12 @@ dependencies {
implementation project(':util')
implementation 'com.google.guava:guava'
annotationProcessor 'com.google.dagger:dagger-compiler'
implementation 'com.google.dagger:dagger'
implementation 'dnsjava:dnsjava'
implementation 'io.netty:netty-transport-native-unix-common'
implementation 'io.vertx:vertx-core'
implementation 'javax.inject:javax.inject'
implementation 'io.tmio:tuweni-bytes'
implementation 'io.tmio:tuweni-crypto'

View File

@@ -23,12 +23,12 @@ import org.hyperledger.besu.cryptoservices.NodeKey;
import org.hyperledger.besu.ethereum.chain.VariablesStorage;
import org.hyperledger.besu.ethereum.forkid.ForkIdManager;
import org.hyperledger.besu.ethereum.p2p.config.DiscoveryConfiguration;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.Packet;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.PeerDiscoveryController;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.PeerRequirement;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.PeerTable;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.PingPacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.TimerUtil;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.Packet;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.ping.PingPacketData;
import org.hyperledger.besu.ethereum.p2p.peers.EnodeURLImpl;
import org.hyperledger.besu.ethereum.p2p.peers.Peer;
import org.hyperledger.besu.ethereum.p2p.peers.PeerId;

View File

@@ -20,12 +20,16 @@ import static org.apache.tuweni.bytes.Bytes.wrapBuffer;
import org.hyperledger.besu.cryptoservices.NodeKey;
import org.hyperledger.besu.ethereum.forkid.ForkIdManager;
import org.hyperledger.besu.ethereum.p2p.config.DiscoveryConfiguration;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.Packet;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.PeerDiscoveryController;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.PeerDiscoveryController.AsyncExecutor;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.PeerTable;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.TimerUtil;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.VertxTimerUtil;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.DaggerPacketPackage;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.Packet;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.PacketDeserializer;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.PacketPackage;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.PacketSerializer;
import org.hyperledger.besu.ethereum.p2p.permissions.PeerPermissions;
import org.hyperledger.besu.ethereum.p2p.rlpx.RlpxAgent;
import org.hyperledger.besu.ethereum.storage.StorageProvider;
@@ -65,7 +69,10 @@ public class VertxPeerDiscoveryAgent extends PeerDiscoveryAgent {
/* The vert.x UDP socket. */
private DatagramSocket socket;
public VertxPeerDiscoveryAgent(
private final PacketSerializer packetSerializer;
private final PacketDeserializer packetDeserializer;
VertxPeerDiscoveryAgent(
final Vertx vertx,
final NodeKey nodeKey,
final DiscoveryConfiguration config,
@@ -75,7 +82,9 @@ public class VertxPeerDiscoveryAgent extends PeerDiscoveryAgent {
final StorageProvider storageProvider,
final ForkIdManager forkIdManager,
final RlpxAgent rlpxAgent,
final PeerTable peerTable) {
final PeerTable peerTable,
final PacketSerializer packetSerializer,
final PacketDeserializer packetDeserializer) {
super(
nodeKey,
config,
@@ -88,6 +97,8 @@ public class VertxPeerDiscoveryAgent extends PeerDiscoveryAgent {
peerTable);
checkArgument(vertx != null, "vertx instance cannot be null");
this.vertx = vertx;
this.packetSerializer = packetSerializer;
this.packetDeserializer = packetDeserializer;
metricsSystem.createIntegerGauge(
BesuMetricCategory.NETWORK,
@@ -96,6 +107,33 @@ public class VertxPeerDiscoveryAgent extends PeerDiscoveryAgent {
pendingTaskCounter(vertx.nettyEventLoopGroup()));
}
public static VertxPeerDiscoveryAgent create(
final Vertx vertx,
final NodeKey nodeKey,
final DiscoveryConfiguration config,
final PeerPermissions peerPermissions,
final NatService natService,
final MetricsSystem metricsSystem,
final StorageProvider storageProvider,
final ForkIdManager forkIdManager,
final RlpxAgent rlpxAgent,
final PeerTable peerTable) {
PacketPackage packetPackage = DaggerPacketPackage.create();
return new VertxPeerDiscoveryAgent(
vertx,
nodeKey,
config,
peerPermissions,
natService,
metricsSystem,
storageProvider,
forkIdManager,
rlpxAgent,
peerTable,
packetPackage.packetSerializer(),
packetPackage.packetDeserializer());
}
private IntSupplier pendingTaskCounter(final EventLoopGroup eventLoopGroup) {
return () ->
StreamSupport.stream(eventLoopGroup.spliterator(), false)
@@ -172,7 +210,7 @@ public class VertxPeerDiscoveryAgent extends PeerDiscoveryAgent {
new RuntimeException("Discovery socket already closed, because Besu is closing down"));
} else {
socket.send(
packet.encode(),
packetSerializer.encode(packet),
peer.getEndpoint().getUdpPort(),
peer.getEnodeURL().getIpAsString(),
ar -> {
@@ -217,7 +255,7 @@ public class VertxPeerDiscoveryAgent extends PeerDiscoveryAgent {
.setMessage("Peer {} is unreachable, native error code {}, packet: {}, stacktrace: {}")
.addArgument(peer)
.addArgument(nativeErr::expectedErr)
.addArgument(() -> wrapBuffer(packet.encode()))
.addArgument(() -> wrapBuffer(packetSerializer.encode(packet)))
.addArgument(err)
.log();
} else {
@@ -226,7 +264,7 @@ public class VertxPeerDiscoveryAgent extends PeerDiscoveryAgent {
"Sending to peer {} failed, native error code {}, packet: {}, stacktrace: {}")
.addArgument(peer)
.addArgument(nativeErr.expectedErr())
.addArgument(wrapBuffer(packet.encode()))
.addArgument(wrapBuffer(packetSerializer.encode(packet)))
.addArgument(err)
.log();
}
@@ -234,7 +272,7 @@ public class VertxPeerDiscoveryAgent extends PeerDiscoveryAgent {
LOG.atDebug()
.setMessage("Peer {} is unreachable, packet: {}")
.addArgument(peer)
.addArgument(() -> wrapBuffer(packet.encode()))
.addArgument(() -> wrapBuffer(packetSerializer.encode(packet)))
.addArgument(err)
.log();
} else if (err instanceof SocketException
@@ -251,14 +289,14 @@ public class VertxPeerDiscoveryAgent extends PeerDiscoveryAgent {
LOG.atTrace()
.setMessage("Sending to peer {} failed, packet: {}, stacktrace: {}")
.addArgument(peer)
.addArgument(() -> wrapBuffer(packet.encode()))
.addArgument(() -> wrapBuffer(packetSerializer.encode(packet)))
.addArgument(err)
.log();
} else {
LOG.warn(
"Sending to peer {} failed, packet: {}, stacktrace: {}",
peer,
wrapBuffer(packet.encode()),
wrapBuffer(packetSerializer.encode(packet)),
err);
}
}
@@ -290,7 +328,7 @@ public class VertxPeerDiscoveryAgent extends PeerDiscoveryAgent {
vertx.<Packet>executeBlocking(
future -> {
try {
future.complete(Packet.decode(datagram.data()));
future.complete(packetDeserializer.decode(datagram.data()));
} catch (final Throwable t) {
future.fail(t);
}

View File

@@ -14,6 +14,7 @@
*/
package org.hyperledger.besu.ethereum.p2p.discovery.internal;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.Packet;
import org.hyperledger.besu.ethereum.p2p.peers.Peer;
import org.hyperledger.besu.metrics.BesuMetricCategory;
import org.hyperledger.besu.plugin.services.MetricsSystem;

View File

@@ -1,62 +0,0 @@
/*
* 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 {
/* 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();
final long expiration = in.readLongScalar();
in.leaveListLenient();
return new ENRRequestPacketData(expiration);
}
@Override
public void writeTo(final RLPOutput out) {
out.startList();
out.writeLongScalar(expiration);
out.endList();
}
public long getExpiration() {
return expiration;
}
@Override
public String toString() {
return "ENRRequestPacketData{" + "expiration=" + expiration + '}';
}
}

View File

@@ -1,74 +0,0 @@
/*
* 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.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;
}
public 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();
in.leaveListLenient();
final NodeRecord enr = NodeRecordFactory.DEFAULT.fromBytes(in.currentListAsBytes());
return new ENRResponsePacketData(requestHash, enr);
}
@Override
public void writeTo(final RLPOutput out) {
out.startList();
out.writeBytes(requestHash);
out.writeRLPBytes(enr.serialize());
out.endList();
}
public Bytes getRequestHash() {
return requestHash;
}
public NodeRecord getEnr() {
return enr;
}
@Override
public String toString() {
return "ENRResponsePacketData{" + "requestHash=" + requestHash + ", enr=" + enr + '}';
}
}

View File

@@ -1,77 +0,0 @@
/*
* 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;
public class FindNeighborsPacketData implements PacketData {
private static final int TARGET_SIZE = 64;
/* Node ID. */
private final Bytes target;
/* In seconds after epoch. */
private final long expiration;
private FindNeighborsPacketData(final Bytes target, final long expiration) {
checkArgument(target != null && target.size() == TARGET_SIZE, "target must be a valid node id");
checkArgument(expiration >= 0, "expiration must be positive");
this.target = target;
this.expiration = expiration;
}
public static FindNeighborsPacketData create(final Bytes target) {
return create(target, PacketData.defaultExpiration());
}
static FindNeighborsPacketData create(final Bytes target, final long expirationSec) {
return new FindNeighborsPacketData(target, expirationSec);
}
@Override
public void writeTo(final RLPOutput out) {
out.startList();
out.writeBytes(target);
out.writeLongScalar(expiration);
out.endList();
}
public static FindNeighborsPacketData readFrom(final RLPInput in) {
in.enterList();
final Bytes target = in.readBytes();
final long expiration = in.readLongScalar();
in.leaveListLenient();
return new FindNeighborsPacketData(target, expiration);
}
public long getExpiration() {
return expiration;
}
public Bytes getTarget() {
return target;
}
@Override
public String toString() {
return "FindNeighborsPacketData{" + "expiration=" + expiration + ", target=" + target + '}';
}
}

View File

@@ -15,6 +15,7 @@
package org.hyperledger.besu.ethereum.p2p.discovery.internal;
import org.hyperledger.besu.ethereum.p2p.discovery.DiscoveryPeer;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.Packet;
@FunctionalInterface
public interface OutboundMessageHandler {

View File

@@ -1,215 +0,0 @@
/*
* 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 static org.hyperledger.besu.crypto.Hash.keccak256;
import static org.hyperledger.besu.util.Preconditions.checkGuard;
import org.hyperledger.besu.crypto.SECPPublicKey;
import org.hyperledger.besu.crypto.SECPSignature;
import org.hyperledger.besu.crypto.SignatureAlgorithm;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.cryptoservices.NodeKey;
import org.hyperledger.besu.ethereum.p2p.discovery.PeerDiscoveryPacketDecodingException;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
import org.hyperledger.besu.ethereum.rlp.RLP;
import org.hyperledger.besu.ethereum.rlp.RLPException;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Optional;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import io.vertx.core.buffer.Buffer;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.MutableBytes;
import org.apache.tuweni.units.bigints.UInt256;
public class Packet {
private static final int PACKET_TYPE_INDEX = 97;
private static final int PACKET_DATA_INDEX = 98;
private static final int SIGNATURE_INDEX = 32;
private static final Supplier<SignatureAlgorithm> SIGNATURE_ALGORITHM =
Suppliers.memoize(SignatureAlgorithmFactory::getInstance);
private final PacketType type;
private final PacketData data;
private final Bytes hash;
private final SECPSignature signature;
private final SECPPublicKey publicKey;
private Packet(final PacketType type, final PacketData data, final NodeKey nodeKey) {
this.type = type;
this.data = data;
final Bytes typeBytes = Bytes.of(this.type.getValue());
final Bytes dataBytes = RLP.encode(this.data::writeTo);
this.signature = nodeKey.sign(keccak256(Bytes.wrap(typeBytes, dataBytes)));
this.hash = keccak256(Bytes.concatenate(encodeSignature(signature), typeBytes, dataBytes));
this.publicKey = nodeKey.getPublicKey();
}
private Packet(final PacketType packetType, final PacketData packetData, final Bytes message) {
final Bytes hash = message.slice(0, SIGNATURE_INDEX);
final Bytes encodedSignature =
message.slice(SIGNATURE_INDEX, PACKET_TYPE_INDEX - SIGNATURE_INDEX);
final Bytes signedPayload =
message.slice(PACKET_TYPE_INDEX, message.size() - PACKET_TYPE_INDEX);
// Perform hash integrity check.
final Bytes rest = message.slice(SIGNATURE_INDEX, message.size() - SIGNATURE_INDEX);
if (!Arrays.equals(keccak256(rest).toArray(), hash.toArray())) {
throw new PeerDiscoveryPacketDecodingException(
"Integrity check failed: non-matching hashes.");
}
this.type = packetType;
this.data = packetData;
this.hash = hash;
this.signature = decodeSignature(encodedSignature);
this.publicKey =
SIGNATURE_ALGORITHM
.get()
.recoverPublicKeyFromSignature(keccak256(signedPayload), this.signature)
.orElseThrow(
() ->
new PeerDiscoveryPacketDecodingException(
"Invalid packet signature, " + "cannot recover public key"));
}
public static Packet create(
final PacketType packetType, final PacketData packetData, final NodeKey nodeKey) {
return new Packet(packetType, packetData, nodeKey);
}
public static Packet decode(final Buffer message) {
checkGuard(
message.length() >= PACKET_DATA_INDEX,
PeerDiscoveryPacketDecodingException::new,
"Packet too short: expected at least %s bytes, got %s",
PACKET_DATA_INDEX,
message.length());
final byte type = message.getByte(PACKET_TYPE_INDEX);
final PacketType packetType =
PacketType.forByte(type)
.orElseThrow(
() ->
new PeerDiscoveryPacketDecodingException("Unrecognized packet type: " + type));
final PacketType.Deserializer<?> deserializer = packetType.getDeserializer();
final PacketData packetData;
try {
packetData =
deserializer.deserialize(
RLP.input(
Bytes.wrapBuffer(
message, PACKET_DATA_INDEX, message.length() - PACKET_DATA_INDEX)));
return new Packet(packetType, packetData, Bytes.wrapBuffer(message));
} catch (final RLPException e) {
throw new PeerDiscoveryPacketDecodingException("Malformed packet of type: " + packetType, e);
} catch (final IllegalArgumentException e) {
throw new PeerDiscoveryPacketDecodingException(
"Failed decoding packet of type: " + packetType, e);
}
}
public Buffer encode() {
final Bytes encodedSignature = encodeSignature(signature);
final BytesValueRLPOutput encodedData = new BytesValueRLPOutput();
data.writeTo(encodedData);
final Buffer buffer =
Buffer.buffer(hash.size() + encodedSignature.size() + 1 + encodedData.encodedSize());
hash.appendTo(buffer);
encodedSignature.appendTo(buffer);
buffer.appendByte(type.getValue());
appendEncoded(encodedData, buffer);
return buffer;
}
protected void appendEncoded(final BytesValueRLPOutput encoded, final Buffer buffer) {
final int size = encoded.encodedSize();
if (size == 0) {
return;
}
// We want to append to the buffer, and Buffer always grows to accommodate anything writing,
// so we write the last byte we know we'll need to make it resize accordingly.
final int start = buffer.length();
buffer.setByte(start + size - 1, (byte) 0);
encoded.writeEncoded(MutableBytes.wrapBuffer(buffer, start, size));
}
@SuppressWarnings("unchecked")
public <T extends PacketData> Optional<T> getPacketData(final Class<T> expectedPacketType) {
if (data == null || !data.getClass().equals(expectedPacketType)) {
return Optional.empty();
}
return Optional.of((T) data);
}
public Bytes getNodeId() {
return publicKey.getEncodedBytes();
}
public PacketType getType() {
return type;
}
public Bytes getHash() {
return hash;
}
@Override
public String toString() {
return "Packet{"
+ "type="
+ type
+ ", data="
+ data
+ ", hash="
+ hash
+ ", signature="
+ signature
+ ", publicKey="
+ publicKey
+ '}';
}
private static Bytes encodeSignature(final SECPSignature signature) {
final MutableBytes encoded = MutableBytes.create(65);
UInt256.valueOf(signature.getR()).copyTo(encoded, 0);
UInt256.valueOf(signature.getS()).copyTo(encoded, 32);
final int v = signature.getRecId();
encoded.set(64, (byte) v);
return encoded;
}
private static SECPSignature decodeSignature(final Bytes encodedSignature) {
checkArgument(
encodedSignature != null && encodedSignature.size() == 65, "encodedSignature is 65 bytes");
final BigInteger r = encodedSignature.slice(0, 32).toUnsignedBigInteger();
final BigInteger s = encodedSignature.slice(32, 32).toUnsignedBigInteger();
final int recId = encodedSignature.get(64);
return SIGNATURE_ALGORITHM.get().createSignature(r, s, (byte) recId);
}
}

View File

@@ -16,22 +16,18 @@ package org.hyperledger.besu.ethereum.p2p.discovery.internal;
import static com.google.common.base.Preconditions.checkArgument;
import org.hyperledger.besu.ethereum.rlp.RLPInput;
import java.util.Arrays;
import java.util.Optional;
import javax.annotation.concurrent.Immutable;
public enum PacketType {
PING(0x01, PingPacketData::readFrom),
PONG(0x02, PongPacketData::readFrom),
FIND_NEIGHBORS(0x03, FindNeighborsPacketData::readFrom),
NEIGHBORS(0x04, NeighborsPacketData::readFrom),
ENR_REQUEST(0x05, ENRRequestPacketData::readFrom),
ENR_RESPONSE(0x06, ENRResponsePacketData::readFrom);
PING(0x01),
PONG(0x02),
FIND_NEIGHBORS(0x03),
NEIGHBORS(0x04),
ENR_REQUEST(0x05),
ENR_RESPONSE(0x06);
private static final int MAX_VALUE = 0x7F;
private static final int BYTE_MASK = 0xFF;
private static final byte MAX_VALUE = 0x7F;
private static final PacketType[] INDEX = new PacketType[PacketType.MAX_VALUE];
@@ -40,29 +36,17 @@ public enum PacketType {
}
private final byte value;
private final Deserializer<?> deserializer;
public static Optional<PacketType> forByte(final byte b) {
return b >= MAX_VALUE || b < 0 ? Optional.empty() : Optional.ofNullable(INDEX[b]);
}
PacketType(final int value, final Deserializer<?> deserializer) {
checkArgument(value <= MAX_VALUE, "Packet type ID must be in range [0x00, 0x80)");
this.deserializer = deserializer;
this.value = (byte) (value & BYTE_MASK);
PacketType(final int value) {
checkArgument(value >= 0 && value <= MAX_VALUE, "Packet type ID must be in range [0x00, 0x80)");
this.value = (byte) value;
}
public byte getValue() {
return value;
}
public Deserializer<?> getDeserializer() {
return deserializer;
}
@FunctionalInterface
@Immutable
public interface Deserializer<T extends PacketData> {
T deserialize(RLPInput in);
}
}

View File

@@ -23,6 +23,23 @@ import static java.util.concurrent.TimeUnit.SECONDS;
import org.hyperledger.besu.cryptoservices.NodeKey;
import org.hyperledger.besu.ethereum.p2p.discovery.DiscoveryPeer;
import org.hyperledger.besu.ethereum.p2p.discovery.PeerDiscoveryStatus;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.DaggerPacketPackage;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.Packet;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.PacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.PacketFactory;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.PacketPackage;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.enrrequest.EnrRequestPacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.enrrequest.EnrRequestPacketDataFactory;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.enrresponse.EnrResponsePacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.enrresponse.EnrResponsePacketDataFactory;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.findneighbors.FindNeighborsPacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.findneighbors.FindNeighborsPacketDataFactory;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.neighbors.NeighborsPacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.neighbors.NeighborsPacketDataFactory;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.ping.PingPacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.ping.PingPacketDataFactory;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.pong.PongPacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.pong.PongPacketDataFactory;
import org.hyperledger.besu.ethereum.p2p.peers.Peer;
import org.hyperledger.besu.ethereum.p2p.peers.PeerId;
import org.hyperledger.besu.ethereum.p2p.permissions.PeerPermissions;
@@ -147,7 +164,15 @@ public class PeerDiscoveryController {
private RecursivePeerRefreshState recursivePeerRefreshState;
private final boolean includeBootnodesOnPeerRefresh;
private PeerDiscoveryController(
private final PacketFactory packetFactory;
private final PingPacketDataFactory pingPacketDataFactory;
private final PongPacketDataFactory pongPacketDataFactory;
private final FindNeighborsPacketDataFactory findNeighborsPacketDataFactory;
private final NeighborsPacketDataFactory neighborsPacketDataFactory;
private final EnrRequestPacketDataFactory enrRequestPacketDataFactory;
private final EnrResponsePacketDataFactory enrResponsePacketDataFactory;
PeerDiscoveryController(
final NodeKey nodeKey,
final DiscoveryPeer localPeer,
final PeerTable peerTable,
@@ -163,7 +188,14 @@ public class PeerDiscoveryController {
final Optional<Cache<Bytes, Packet>> maybeCacheForEnrRequests,
final boolean filterOnEnrForkId,
final RlpxAgent rlpxAgent,
final boolean includeBootnodesOnPeerRefresh) {
final boolean includeBootnodesOnPeerRefresh,
final PacketFactory packetFactory,
final PingPacketDataFactory pingPacketDataFactory,
final PongPacketDataFactory pongPacketDataFactory,
final FindNeighborsPacketDataFactory findNeighborsPacketDataFactory,
final NeighborsPacketDataFactory neighborsPacketDataFactory,
final EnrRequestPacketDataFactory enrRequestPacketDataFactory,
final EnrResponsePacketDataFactory enrResponsePacketDataFactory) {
this.timerUtil = timerUtil;
this.nodeKey = nodeKey;
this.localPeer = localPeer;
@@ -179,6 +211,14 @@ public class PeerDiscoveryController {
this.rlpxAgent = rlpxAgent;
this.includeBootnodesOnPeerRefresh = includeBootnodesOnPeerRefresh;
this.packetFactory = packetFactory;
this.pingPacketDataFactory = pingPacketDataFactory;
this.pongPacketDataFactory = pongPacketDataFactory;
this.findNeighborsPacketDataFactory = findNeighborsPacketDataFactory;
this.neighborsPacketDataFactory = neighborsPacketDataFactory;
this.enrRequestPacketDataFactory = enrRequestPacketDataFactory;
this.enrResponsePacketDataFactory = enrResponsePacketDataFactory;
metricsSystem.createIntegerGauge(
BesuMetricCategory.NETWORK,
"discovery_inflight_interactions_current",
@@ -377,8 +417,8 @@ public class PeerDiscoveryController {
matchInteraction(packet)
.ifPresent(
interaction -> {
final Optional<ENRResponsePacketData> packetData =
packet.getPacketData(ENRResponsePacketData.class);
final Optional<EnrResponsePacketData> packetData =
packet.getPacketData(EnrResponsePacketData.class);
final NodeRecord enr = packetData.get().getEnr();
peer.setNodeRecord(enr);
});
@@ -389,7 +429,7 @@ public class PeerDiscoveryController {
private void processEnrRequest(final DiscoveryPeer peer, final Packet packet) {
LOG.trace("ENR_REQUEST received from bonded peer Id: {}", peer.getId());
packet
.getPacketData(ENRRequestPacketData.class)
.getPacketData(EnrRequestPacketData.class)
.ifPresent(p -> respondToENRRequest(p, packet.getHash(), peer));
}
@@ -526,7 +566,7 @@ public class PeerDiscoveryController {
final Consumer<PeerInteractionState> action =
interaction -> {
final PingPacketData data =
PingPacketData.create(
pingPacketDataFactory.create(
Optional.of(localPeer.getEndpoint()),
peer.getEndpoint(),
localPeer.getNodeRecord().map(NodeRecord::getSeq).orElse(null));
@@ -564,7 +604,7 @@ public class PeerDiscoveryController {
void requestENR(final DiscoveryPeer peer) {
final Consumer<PeerInteractionState> action =
interaction -> {
final ENRRequestPacketData data = ENRRequestPacketData.create();
final EnrRequestPacketData data = enrRequestPacketDataFactory.create();
createPacket(
PacketType.ENR_REQUEST,
data,
@@ -575,7 +615,7 @@ public class PeerDiscoveryController {
final Predicate<Packet> newFilter =
packet ->
packet
.getPacketData(ENRResponsePacketData.class)
.getPacketData(EnrResponsePacketData.class)
.map(enr -> enr.getRequestHash().equals(enrHash))
.orElse(false);
interaction.updateFilter(newFilter);
@@ -610,7 +650,7 @@ public class PeerDiscoveryController {
// Creating packets is quite expensive because they have to be cryptographically signed
// So ensure the work is done on a worker thread to avoid blocking the vertx event thread.
workerExecutor
.execute(() -> Packet.create(type, data, nodeKey))
.execute(() -> packetFactory.create(type, data, nodeKey))
.thenAccept(handler)
.exceptionally(
error -> {
@@ -628,7 +668,7 @@ public class PeerDiscoveryController {
private void findNodes(final DiscoveryPeer peer, final Bytes target) {
final Consumer<PeerInteractionState> action =
interaction -> {
final FindNeighborsPacketData data = FindNeighborsPacketData.create(target);
final FindNeighborsPacketData data = findNeighborsPacketDataFactory.create(target);
sendPacket(peer, PacketType.FIND_NEIGHBORS, data);
};
final PeerInteractionState interaction =
@@ -665,7 +705,7 @@ public class PeerDiscoveryController {
}
// We don't care about the `from` field of the ping, we pong to the `sender`
final PongPacketData data =
PongPacketData.create(
pongPacketDataFactory.create(
sender.getEndpoint(),
pingHash,
localPeer.getNodeRecord().map(NodeRecord::getSeq).orElse(null));
@@ -684,17 +724,17 @@ public class PeerDiscoveryController {
// 88 * 13 = 1144 bytes
// To fit under 1280 bytes, we must return just 13 peers maximum.
final List<DiscoveryPeer> peers = peerTable.nearestBondedPeers(packetData.getTarget(), 13);
final PacketData data = NeighborsPacketData.create(peers);
final PacketData data = neighborsPacketDataFactory.create(peers);
sendPacket(sender, PacketType.NEIGHBORS, data);
}
private void respondToENRRequest(
final ENRRequestPacketData enrRequestPacketData,
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));
final EnrResponsePacketData data =
enrResponsePacketDataFactory.create(requestHash, localPeer.getNodeRecord().orElse(null));
sendPacket(sender, PacketType.ENR_RESPONSE, data);
}
}
@@ -850,6 +890,20 @@ public class PeerDiscoveryController {
CacheBuilder.newBuilder().maximumSize(50).expireAfterWrite(10, SECONDS).build();
private RlpxAgent rlpxAgent;
// set defaults for all PacketPackage classes, allowing calling code to override if needed
private final PacketPackage packetPackage = DaggerPacketPackage.create();
private PacketFactory packetFactory = packetPackage.packetFactory();
private PingPacketDataFactory pingPacketDataFactory = packetPackage.pingPacketDataFactory();
private PongPacketDataFactory pongPacketDataFactory = packetPackage.pongPacketDataFactory();
private FindNeighborsPacketDataFactory findNeighborsPacketDataFactory =
packetPackage.findNeighborsPacketDataFactory();
private NeighborsPacketDataFactory neighborsPacketDataFactory =
packetPackage.neighborsPacketDataFactory();
private EnrRequestPacketDataFactory enrRequestPacketDataFactory =
packetPackage.enrRequestPacketDataFactory();
private EnrResponsePacketDataFactory enrResponsePacketDataFactory =
packetPackage.enrResponsePacketDataFactory();
private Builder() {}
public PeerDiscoveryController build() {
@@ -871,7 +925,14 @@ public class PeerDiscoveryController {
Optional.of(cachedEnrRequests),
filterOnEnrForkId,
rlpxAgent,
includeBootnodesOnPeerRefresh);
includeBootnodesOnPeerRefresh,
packetFactory,
pingPacketDataFactory,
pongPacketDataFactory,
findNeighborsPacketDataFactory,
neighborsPacketDataFactory,
enrRequestPacketDataFactory,
enrResponsePacketDataFactory);
}
private void validate() {
@@ -980,5 +1041,44 @@ public class PeerDiscoveryController {
this.includeBootnodesOnPeerRefresh = includeBootnodesOnPeerRefresh;
return this;
}
public Builder setPacketFactory(final PacketFactory packetFactory) {
this.packetFactory = packetFactory;
return this;
}
public Builder setPingPacketDataFactory(final PingPacketDataFactory pingPacketDataFactory) {
this.pingPacketDataFactory = pingPacketDataFactory;
return this;
}
public Builder setPongPacketDataFactory(final PongPacketDataFactory pongPacketDataFactory) {
this.pongPacketDataFactory = pongPacketDataFactory;
return this;
}
public Builder setFindNeighborsPacketDataFactory(
final FindNeighborsPacketDataFactory findNeighborsPacketDataFactory) {
this.findNeighborsPacketDataFactory = findNeighborsPacketDataFactory;
return this;
}
public Builder setNeighborsPacketDataFactory(
final NeighborsPacketDataFactory neighborsPacketDataFactory) {
this.neighborsPacketDataFactory = neighborsPacketDataFactory;
return this;
}
public Builder setEnrRequestPacketDataFactory(
final EnrRequestPacketDataFactory enrRequestPacketDataFactory) {
this.enrRequestPacketDataFactory = enrRequestPacketDataFactory;
return this;
}
public Builder setEnrResponsePacketDataFactory(
final EnrResponsePacketDataFactory enrResponsePacketDataFactory) {
this.enrResponsePacketDataFactory = enrResponsePacketDataFactory;
return this;
}
}
}

View File

@@ -1,202 +0,0 @@
/*
* 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.p2p.discovery.Endpoint;
import org.hyperledger.besu.ethereum.rlp.MalformedRLPInputException;
import org.hyperledger.besu.ethereum.rlp.RLPInput;
import org.hyperledger.besu.ethereum.rlp.RLPOutput;
import java.util.Optional;
import org.apache.tuweni.units.bigints.UInt64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class PingPacketData implements PacketData {
/* Fixed value that represents we're using v5 of the P2P discovery protocol. */
private static final int VERSION = 5;
/* Source. If the field is garbage this is empty and we might need to recover it another way. From our bonded peers, for example. */
private final Optional<Endpoint> maybeFrom;
/* Destination. */
private final Endpoint to;
/* In seconds after epoch. */
private final long expiration;
/* Current sequence number of the sending nodes record */
private final UInt64 enrSeq;
private static final Logger LOG = LoggerFactory.getLogger(PingPacketData.class);
private PingPacketData(
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 Optional<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 Optional<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(from, to, expirationSec, enrSeq);
}
public static PingPacketData readFrom(final RLPInput in) {
in.enterList();
// The first element signifies the "version", but this value is ignored as of EIP-8
in.readBigIntegerScalar();
Optional<Endpoint> from = Optional.empty();
Optional<Endpoint> to = Optional.empty();
if (in.nextIsList()) {
to = Endpoint.maybeDecodeStandalone(in);
// https://github.com/ethereum/devp2p/blob/master/discv4.md#ping-packet-0x01
if (in.nextIsList()) { // if there are two, the first is the from address, next is the to
// address
from = to;
to = Endpoint.maybeDecodeStandalone(in);
}
} else {
throw new DevP2PException("missing address in ping packet");
}
final long expiration = in.readLongScalar();
UInt64 enrSeq = null;
if (!in.isEndOfCurrentList()) {
try {
enrSeq = UInt64.valueOf(in.readBigIntegerScalar());
LOG.trace("read PING enr as long scalar");
} catch (MalformedRLPInputException malformed) {
LOG.trace("failed to read PING enr as scalar, trying to read bytes instead");
enrSeq = UInt64.fromBytes(in.readBytes());
}
}
in.leaveListLenient();
return new PingPacketData(from, to.get(), expiration, enrSeq);
}
/**
* Used by test classes to read legacy encodes of Pings which used a byte array for the ENR field
*
* @deprecated Only to be used by internal tests to confirm backward compatibility.
* @param in input stream to read from
* @return PingPacketData parsed from input, using legacy encode.
*/
@Deprecated
public static PingPacketData legacyReadFrom(final RLPInput in) { // only for testing, do not use
in.enterList();
// The first element signifies the "version", but this value is ignored as of EIP-8
in.readBigIntegerScalar();
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, enrSeq);
}
@Override
public void writeTo(final RLPOutput out) {
out.startList();
out.writeIntScalar(VERSION);
if (maybeFrom.isPresent()) {
maybeFrom.get().encodeStandalone(out);
}
to.encodeStandalone(out);
out.writeLongScalar(expiration);
out.writeBigIntegerScalar(enrSeq.toBigInteger());
out.endList();
}
/**
* @deprecated Only to be used by internal tests to confirm backward compatibility.
* @param out stream to write to
*/
@Deprecated
public void legacyWriteTo(final RLPOutput out) {
out.startList();
out.writeIntScalar(VERSION);
maybeFrom
.orElseThrow(
() ->
new IllegalStateException(
"Attempting to serialize invalid PING packet. Missing 'from' field"))
.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();
}
public Optional<Endpoint> getFrom() {
return maybeFrom;
}
public Endpoint getTo() {
return to;
}
public long getExpiration() {
return expiration;
}
public Optional<UInt64> getEnrSeq() {
return Optional.ofNullable(enrSeq);
}
@Override
public String toString() {
return "PingPacketData{"
+ "from="
+ maybeFrom.map(Object::toString).orElse("INVALID")
+ ", to="
+ to
+ ", expiration="
+ expiration
+ ", enrSeq="
+ enrSeq
+ '}';
}
}

View File

@@ -1,156 +0,0 @@
/*
* 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 org.hyperledger.besu.ethereum.p2p.discovery.Endpoint;
import org.hyperledger.besu.ethereum.rlp.MalformedRLPInputException;
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;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class PongPacketData implements PacketData {
/* Destination. */
private final Endpoint to;
/* Hash of the PING packet. */
private final Bytes pingHash;
/* In seconds after epoch. */
private final long expiration;
/* Current sequence number of the sending nodes record */
private final UInt64 enrSeq;
private static final Logger LOG = LoggerFactory.getLogger(PongPacketData.class);
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, final UInt64 enrSeq) {
return new PongPacketData(to, pingHash, PacketData.defaultExpiration(), enrSeq);
}
public static PongPacketData readFrom(final RLPInput in) {
in.enterList();
final Endpoint to = Endpoint.decodeStandalone(in);
final Bytes hash = in.readBytes();
final long expiration = in.readLongScalar();
UInt64 enrSeq = null;
if (!in.isEndOfCurrentList()) {
try {
enrSeq = UInt64.valueOf(in.readBigIntegerScalar());
LOG.trace("read PONG enr from scalar");
} catch (final MalformedRLPInputException malformed) {
LOG.trace("failed to read PONG enr from scalar, trying as byte array");
enrSeq = UInt64.fromBytes(in.readBytes());
}
}
in.leaveListLenient();
return new PongPacketData(to, hash, expiration, enrSeq);
}
@Override
public void writeTo(final RLPOutput out) {
out.startList();
to.encodeStandalone(out);
out.writeBytes(pingHash);
out.writeLongScalar(expiration);
out.writeBigIntegerScalar(enrSeq.toBigInteger());
out.endList();
}
/**
* Used by test classes to read legacy encodes of Pongs which used a byte array for the ENR field
*
* @deprecated Only to be used by internal tests to confirm backward compatibility.
* @param in input stream being read from
* @return PongPacketData parsed from input, using legacy encode
*/
@Deprecated
public static PongPacketData legacyReadFrom(final RLPInput in) {
in.enterList();
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, enrSeq);
}
/**
* @deprecated Only to be used by internal tests to confirm backward compatibility.
* @param out output stream being written to
*/
@Deprecated
public void legacyWriteTo(final RLPOutput out) {
out.startList();
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();
}
@Override
public String toString() {
return "PongPacketData{"
+ "to="
+ to
+ ", pingHash="
+ pingHash
+ ", expiration="
+ expiration
+ ", enrSeq="
+ enrSeq
+ '}';
}
public Endpoint getTo() {
return to;
}
public Bytes getPingHash() {
return pingHash;
}
public long getExpiration() {
return expiration;
}
public Optional<UInt64> getEnrSeq() {
return Optional.ofNullable(enrSeq);
}
}

View File

@@ -0,0 +1,92 @@
/*
* Copyright contributors to Besu.
*
* 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.packet;
import org.hyperledger.besu.crypto.SECPPublicKey;
import org.hyperledger.besu.crypto.SECPSignature;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.PacketType;
import java.util.Optional;
import org.apache.tuweni.bytes.Bytes;
public class Packet {
public static final int PACKET_TYPE_INDEX = 97;
public static final int PACKET_DATA_INDEX = 98;
public static final int SIGNATURE_INDEX = 32;
private final PacketType type;
private final PacketData data;
private final Bytes hash;
private final SECPSignature signature;
private final SECPPublicKey publicKey;
Packet(
final PacketType type,
final PacketData data,
final Bytes hash,
final SECPSignature signature,
final SECPPublicKey publicKey) {
this.type = type;
this.data = data;
this.hash = hash;
this.signature = signature;
this.publicKey = publicKey;
}
public PacketData getPacketData() {
return data;
}
@SuppressWarnings("unchecked")
public <T extends PacketData> Optional<T> getPacketData(final Class<T> expectedPacketType) {
if (data == null || !data.getClass().equals(expectedPacketType)) {
return Optional.empty();
}
return Optional.of((T) data);
}
public Bytes getNodeId() {
return publicKey.getEncodedBytes();
}
public PacketType getType() {
return type;
}
public Bytes getHash() {
return hash;
}
public SECPSignature getSignature() {
return signature;
}
@Override
public String toString() {
return "Packet{"
+ "type="
+ type
+ ", data="
+ data
+ ", hash="
+ hash
+ ", signature="
+ signature
+ ", publicKey="
+ publicKey
+ '}';
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright ConsenSys AG.
* Copyright contributors to Besu.
*
* 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
@@ -12,9 +12,7 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.p2p.discovery.internal;
import org.hyperledger.besu.ethereum.rlp.RLPOutput;
package org.hyperledger.besu.ethereum.p2p.discovery.internal.packet;
import java.time.Instant;
@@ -26,13 +24,6 @@ public interface PacketData {
*/
long DEFAULT_EXPIRATION_PERIOD_SEC = 60;
/**
* Serializes the implementing packet data onto the provided RLP output buffer.
*
* @param out The RLP output buffer.
*/
void writeTo(RLPOutput out);
static long defaultExpiration() {
return Instant.now().getEpochSecond() + DEFAULT_EXPIRATION_PERIOD_SEC;
}

View File

@@ -0,0 +1,21 @@
/*
* Copyright contributors to Besu.
*
* 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.packet;
import org.hyperledger.besu.ethereum.rlp.RLPInput;
public interface PacketDataDeserializer<T extends PacketData> {
T readFrom(RLPInput in);
}

View File

@@ -0,0 +1,106 @@
/*
* Copyright contributors to Besu.
*
* 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.packet;
import static org.hyperledger.besu.util.Preconditions.checkGuard;
import org.hyperledger.besu.ethereum.p2p.discovery.PeerDiscoveryPacketDecodingException;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.PacketType;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.enrrequest.EnrRequestPacketDataRlpReader;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.enrresponse.EnrResponsePacketDataRlpReader;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.findneighbors.FindNeighborsPacketDataRlpReader;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.neighbors.NeighborsPacketDataRlpReader;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.ping.PingPacketDataRlpReader;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.pong.PongPacketDataRlpReader;
import org.hyperledger.besu.ethereum.rlp.RLP;
import org.hyperledger.besu.ethereum.rlp.RLPException;
import javax.inject.Inject;
import javax.inject.Singleton;
import io.vertx.core.buffer.Buffer;
import org.apache.tuweni.bytes.Bytes;
@Singleton
public class PacketDeserializer {
private final PingPacketDataRlpReader pingPacketDataRlpReader;
private final PongPacketDataRlpReader pongPacketDataRlpReader;
private final FindNeighborsPacketDataRlpReader findNeighborsPacketDataRlpReader;
private final NeighborsPacketDataRlpReader neighborsPacketDataRlpReader;
private final EnrRequestPacketDataRlpReader enrRequestPacketDataRlpReader;
private final EnrResponsePacketDataRlpReader enrResponsePacketDataRlpReader;
private final PacketFactory packetFactory;
public @Inject PacketDeserializer(
final PingPacketDataRlpReader pingPacketDataRlpReader,
final PongPacketDataRlpReader pongPacketDataRlpReader,
final FindNeighborsPacketDataRlpReader findNeighborsPacketDataRlpReader,
final NeighborsPacketDataRlpReader neighborsPacketDataRlpReader,
final EnrRequestPacketDataRlpReader enrRequestPacketDataRlpReader,
final EnrResponsePacketDataRlpReader enrResponsePacketDataRlpReader,
final PacketFactory packetFactory) {
this.pingPacketDataRlpReader = pingPacketDataRlpReader;
this.pongPacketDataRlpReader = pongPacketDataRlpReader;
this.findNeighborsPacketDataRlpReader = findNeighborsPacketDataRlpReader;
this.neighborsPacketDataRlpReader = neighborsPacketDataRlpReader;
this.enrRequestPacketDataRlpReader = enrRequestPacketDataRlpReader;
this.enrResponsePacketDataRlpReader = enrResponsePacketDataRlpReader;
this.packetFactory = packetFactory;
}
public Packet decode(final Buffer message) {
checkGuard(
message.length() >= Packet.PACKET_DATA_INDEX,
PeerDiscoveryPacketDecodingException::new,
"Packet too short: expected at least %s bytes, got %s",
Packet.PACKET_DATA_INDEX,
message.length());
final byte type = message.getByte(Packet.PACKET_TYPE_INDEX);
final PacketType packetType =
PacketType.forByte(type)
.orElseThrow(
() ->
new PeerDiscoveryPacketDecodingException("Unrecognized packet type: " + type));
final PacketDataDeserializer<? extends PacketData> deserializer =
switch (packetType) {
case PING -> pingPacketDataRlpReader;
case PONG -> pongPacketDataRlpReader;
case FIND_NEIGHBORS -> findNeighborsPacketDataRlpReader;
case NEIGHBORS -> neighborsPacketDataRlpReader;
case ENR_REQUEST -> enrRequestPacketDataRlpReader;
case ENR_RESPONSE -> enrResponsePacketDataRlpReader;
};
final PacketData packetData;
try {
packetData =
deserializer.readFrom(
RLP.input(
Bytes.wrapBuffer(
message,
Packet.PACKET_DATA_INDEX,
message.length() - Packet.PACKET_DATA_INDEX)));
} catch (final RLPException e) {
throw new PeerDiscoveryPacketDecodingException("Malformed packet of type: " + packetType, e);
} catch (final IllegalArgumentException e) {
throw new PeerDiscoveryPacketDecodingException(
"Failed decoding packet of type: " + packetType, e);
}
return packetFactory.create(packetType, packetData, Bytes.wrapBuffer(message));
}
}

View File

@@ -0,0 +1,143 @@
/*
* Copyright contributors to Besu.
*
* 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.packet;
import org.hyperledger.besu.crypto.Hash;
import org.hyperledger.besu.crypto.SECPPublicKey;
import org.hyperledger.besu.crypto.SECPSignature;
import org.hyperledger.besu.crypto.SignatureAlgorithm;
import org.hyperledger.besu.cryptoservices.NodeKey;
import org.hyperledger.besu.ethereum.p2p.discovery.PeerDiscoveryPacketDecodingException;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.PacketType;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.enrrequest.EnrRequestPacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.enrrequest.EnrRequestPacketDataRlpWriter;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.enrresponse.EnrResponsePacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.enrresponse.EnrResponsePacketDataRlpWriter;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.findneighbors.FindNeighborsPacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.findneighbors.FindNeighborsPacketDataRlpWriter;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.neighbors.NeighborsPacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.neighbors.NeighborsPacketDataRlpWriter;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.ping.PingPacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.ping.PingPacketDataRlpWriter;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.pong.PongPacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.pong.PongPacketDataRlpWriter;
import org.hyperledger.besu.ethereum.rlp.RLP;
import java.math.BigInteger;
import java.util.Arrays;
import javax.inject.Inject;
import javax.inject.Singleton;
import com.google.common.base.Preconditions;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
@Singleton
public class PacketFactory {
private final PingPacketDataRlpWriter pingPacketDataRlpWriter;
private final PongPacketDataRlpWriter pongPacketDataRlpWriter;
private final FindNeighborsPacketDataRlpWriter findNeighborsPacketDataRlpWriter;
private final NeighborsPacketDataRlpWriter neighborsPacketDataRlpWriter;
private final EnrRequestPacketDataRlpWriter enrRequestPacketDataRlpWriter;
private final EnrResponsePacketDataRlpWriter enrResponsePacketDataRlpWriter;
private final SignatureAlgorithm signatureAlgorithm;
private final PacketSignatureEncoder packetSignatureEncoder;
public @Inject PacketFactory(
final PingPacketDataRlpWriter pingPacketDataRlpWriter,
final PongPacketDataRlpWriter pongPacketDataRlpWriter,
final FindNeighborsPacketDataRlpWriter findNeighborsPacketDataRlpWriter,
final NeighborsPacketDataRlpWriter neighborsPacketDataRlpWriter,
final EnrRequestPacketDataRlpWriter enrRequestPacketDataRlpWriter,
final EnrResponsePacketDataRlpWriter enrResponsePacketDataRlpWriter,
final SignatureAlgorithm signatureAlgorithm,
final PacketSignatureEncoder packetSignatureEncoder) {
this.pingPacketDataRlpWriter = pingPacketDataRlpWriter;
this.pongPacketDataRlpWriter = pongPacketDataRlpWriter;
this.findNeighborsPacketDataRlpWriter = findNeighborsPacketDataRlpWriter;
this.neighborsPacketDataRlpWriter = neighborsPacketDataRlpWriter;
this.enrRequestPacketDataRlpWriter = enrRequestPacketDataRlpWriter;
this.enrResponsePacketDataRlpWriter = enrResponsePacketDataRlpWriter;
this.signatureAlgorithm = signatureAlgorithm;
this.packetSignatureEncoder = packetSignatureEncoder;
}
public Packet create(final PacketType type, final PacketData data, final NodeKey nodeKey) {
final Bytes typeBytes = Bytes.of(type.getValue());
final Bytes dataBytes =
RLP.encode(
(rlpOutput) -> {
switch (type) {
case PING -> pingPacketDataRlpWriter.writeTo((PingPacketData) data, rlpOutput);
case PONG -> pongPacketDataRlpWriter.writeTo((PongPacketData) data, rlpOutput);
case FIND_NEIGHBORS ->
findNeighborsPacketDataRlpWriter.writeTo(
(FindNeighborsPacketData) data, rlpOutput);
case NEIGHBORS ->
neighborsPacketDataRlpWriter.writeTo((NeighborsPacketData) data, rlpOutput);
case ENR_REQUEST ->
enrRequestPacketDataRlpWriter.writeTo((EnrRequestPacketData) data, rlpOutput);
case ENR_RESPONSE ->
enrResponsePacketDataRlpWriter.writeTo((EnrResponsePacketData) data, rlpOutput);
}
});
SECPSignature signature = nodeKey.sign(Hash.keccak256(Bytes.wrap(typeBytes, dataBytes)));
Bytes32 hash =
Hash.keccak256(
Bytes.concatenate(
packetSignatureEncoder.encodeSignature(signature), typeBytes, dataBytes));
SECPPublicKey publicKey = nodeKey.getPublicKey();
return new Packet(type, data, hash, signature, publicKey);
}
public Packet create(
final PacketType packetType, final PacketData packetData, final Bytes message) {
final Bytes hash = message.slice(0, Packet.SIGNATURE_INDEX);
final Bytes encodedSignature =
message.slice(Packet.SIGNATURE_INDEX, Packet.PACKET_TYPE_INDEX - Packet.SIGNATURE_INDEX);
final Bytes signedPayload =
message.slice(Packet.PACKET_TYPE_INDEX, message.size() - Packet.PACKET_TYPE_INDEX);
// Perform hash integrity check.
final Bytes rest =
message.slice(Packet.SIGNATURE_INDEX, message.size() - Packet.SIGNATURE_INDEX);
if (!Arrays.equals(Hash.keccak256(rest).toArray(), hash.toArray())) {
throw new PeerDiscoveryPacketDecodingException(
"Integrity check failed: non-matching hashes.");
}
SECPSignature signature = decodeSignature(encodedSignature);
SECPPublicKey publicKey =
signatureAlgorithm
.recoverPublicKeyFromSignature(Hash.keccak256(signedPayload), signature)
.orElseThrow(
() ->
new PeerDiscoveryPacketDecodingException(
"Invalid packet signature, cannot recover public key"));
return new Packet(packetType, packetData, hash, signature, publicKey);
}
private SECPSignature decodeSignature(final Bytes encodedSignature) {
Preconditions.checkArgument(
encodedSignature != null && encodedSignature.size() == 65,
"encodedSignature is not 65 bytes");
final BigInteger r = encodedSignature.slice(0, 32).toUnsignedBigInteger();
final BigInteger s = encodedSignature.slice(32, 32).toUnsignedBigInteger();
final int recId = encodedSignature.get(64);
return signatureAlgorithm.createSignature(r, s, (byte) recId);
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright contributors to Besu.
*
* 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.packet;
import org.hyperledger.besu.crypto.SignatureAlgorithm;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import java.time.Clock;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
import org.ethereum.beacon.discovery.schema.NodeRecordFactory;
@Module
abstract class PacketModule {
@Provides
@Singleton
static SignatureAlgorithm signatureAlgorithm() {
return SignatureAlgorithmFactory.getInstance();
}
@Provides
@Singleton
static Clock clock() {
return Clock.systemUTC();
}
@Provides
@Singleton
static NodeRecordFactory nodeRecordFactory() {
return NodeRecordFactory.DEFAULT;
}
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright contributors to Besu.
*
* 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.packet;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.enrrequest.EnrRequestPacketDataFactory;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.enrresponse.EnrResponsePacketDataFactory;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.findneighbors.FindNeighborsPacketDataFactory;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.neighbors.NeighborsPacketDataFactory;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.ping.PingPacketDataFactory;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.pong.PongPacketDataFactory;
import javax.inject.Singleton;
import dagger.Component;
@Singleton
@Component(modules = PacketModule.class)
public interface PacketPackage {
PacketFactory packetFactory();
PacketSerializer packetSerializer();
PacketDeserializer packetDeserializer();
PingPacketDataFactory pingPacketDataFactory();
PongPacketDataFactory pongPacketDataFactory();
FindNeighborsPacketDataFactory findNeighborsPacketDataFactory();
NeighborsPacketDataFactory neighborsPacketDataFactory();
EnrRequestPacketDataFactory enrRequestPacketDataFactory();
EnrResponsePacketDataFactory enrResponsePacketDataFactory();
}

View File

@@ -0,0 +1,111 @@
/*
* Copyright contributors to Besu.
*
* 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.packet;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.enrrequest.EnrRequestPacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.enrrequest.EnrRequestPacketDataRlpWriter;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.enrresponse.EnrResponsePacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.enrresponse.EnrResponsePacketDataRlpWriter;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.findneighbors.FindNeighborsPacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.findneighbors.FindNeighborsPacketDataRlpWriter;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.neighbors.NeighborsPacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.neighbors.NeighborsPacketDataRlpWriter;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.ping.PingPacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.ping.PingPacketDataRlpWriter;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.pong.PongPacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.pong.PongPacketDataRlpWriter;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
import javax.inject.Inject;
import javax.inject.Singleton;
import io.vertx.core.buffer.Buffer;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.MutableBytes;
@Singleton
public class PacketSerializer {
private final PacketSignatureEncoder packetSignatureEncoder;
private final PingPacketDataRlpWriter pingPacketDataRlpWriter;
private final PongPacketDataRlpWriter pongPacketDataRlpWriter;
private final FindNeighborsPacketDataRlpWriter findNeighborsPacketDataRlpWriter;
private final NeighborsPacketDataRlpWriter neighborsPacketDataRlpWriter;
private final EnrRequestPacketDataRlpWriter enrRequestPacketDataRlpWriter;
private final EnrResponsePacketDataRlpWriter enrResponsePacketDataRlpWriter;
public @Inject PacketSerializer(
final PacketSignatureEncoder packetSignatureEncoder,
final PingPacketDataRlpWriter pingPacketDataRlpWriter,
final PongPacketDataRlpWriter pongPacketDataRlpWriter,
final FindNeighborsPacketDataRlpWriter findNeighborsPacketDataRlpWriter,
final NeighborsPacketDataRlpWriter neighborsPacketDataRlpWriter,
final EnrRequestPacketDataRlpWriter enrRequestPacketDataRlpWriter,
final EnrResponsePacketDataRlpWriter enrResponsePacketDataRlpWriter) {
this.packetSignatureEncoder = packetSignatureEncoder;
this.pingPacketDataRlpWriter = pingPacketDataRlpWriter;
this.pongPacketDataRlpWriter = pongPacketDataRlpWriter;
this.findNeighborsPacketDataRlpWriter = findNeighborsPacketDataRlpWriter;
this.neighborsPacketDataRlpWriter = neighborsPacketDataRlpWriter;
this.enrRequestPacketDataRlpWriter = enrRequestPacketDataRlpWriter;
this.enrResponsePacketDataRlpWriter = enrResponsePacketDataRlpWriter;
}
public Buffer encode(final Packet packet) {
final Bytes encodedSignature = packetSignatureEncoder.encodeSignature(packet.getSignature());
final BytesValueRLPOutput encodedData = new BytesValueRLPOutput();
switch (packet.getType()) {
case PING ->
pingPacketDataRlpWriter.writeTo(
packet.getPacketData(PingPacketData.class).orElseThrow(), encodedData);
case PONG ->
pongPacketDataRlpWriter.writeTo(
packet.getPacketData(PongPacketData.class).orElseThrow(), encodedData);
case FIND_NEIGHBORS ->
findNeighborsPacketDataRlpWriter.writeTo(
packet.getPacketData(FindNeighborsPacketData.class).orElseThrow(), encodedData);
case NEIGHBORS ->
neighborsPacketDataRlpWriter.writeTo(
packet.getPacketData(NeighborsPacketData.class).orElseThrow(), encodedData);
case ENR_REQUEST ->
enrRequestPacketDataRlpWriter.writeTo(
packet.getPacketData(EnrRequestPacketData.class).orElseThrow(), encodedData);
case ENR_RESPONSE ->
enrResponsePacketDataRlpWriter.writeTo(
packet.getPacketData(EnrResponsePacketData.class).orElseThrow(), encodedData);
}
final Buffer buffer =
Buffer.buffer(
packet.getHash().size() + encodedSignature.size() + 1 + encodedData.encodedSize());
packet.getHash().appendTo(buffer);
encodedSignature.appendTo(buffer);
buffer.appendByte(packet.getType().getValue());
appendEncoded(encodedData, buffer);
return buffer;
}
private void appendEncoded(final BytesValueRLPOutput encoded, final Buffer buffer) {
final int size = encoded.encodedSize();
if (size == 0) {
return;
}
// We want to append to the buffer, and Buffer always grows to accommodate anything writing,
// so we write the last byte we know we'll need to make it resize accordingly.
final int start = buffer.length();
buffer.setByte(start + size - 1, (byte) 0);
encoded.writeEncoded(MutableBytes.wrapBuffer(buffer, start, size));
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright contributors to Besu.
*
* 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.packet;
import org.hyperledger.besu.crypto.SECPSignature;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.MutableBytes;
import org.apache.tuweni.units.bigints.UInt256;
@Singleton
public class PacketSignatureEncoder {
public @Inject PacketSignatureEncoder() {}
public Bytes encodeSignature(final SECPSignature signature) {
final MutableBytes encoded = MutableBytes.create(65);
UInt256.valueOf(signature.getR()).copyTo(encoded, 0);
UInt256.valueOf(signature.getS()).copyTo(encoded, 32);
final int v = signature.getRecId();
encoded.set(64, (byte) v);
return encoded;
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright contributors to Besu.
*
* 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.packet.enrrequest;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.PacketData;
public class EnrRequestPacketData implements PacketData {
/* In seconds after epoch. */
private final long expiration;
EnrRequestPacketData(final long expiration) {
this.expiration = expiration;
}
public long getExpiration() {
return expiration;
}
@Override
public String toString() {
return "EnrRequestPacketData{" + "expiration=" + expiration + '}';
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright contributors to Besu.
*
* 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.packet.enrrequest;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.PacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.validation.ExpiryValidator;
import java.time.Clock;
import javax.inject.Inject;
import javax.inject.Singleton;
@Singleton
public class EnrRequestPacketDataFactory {
private final ExpiryValidator expiryValidator;
private final Clock clock;
public @Inject EnrRequestPacketDataFactory(
final ExpiryValidator expiryValidator, final Clock clock) {
this.expiryValidator = expiryValidator;
this.clock = clock;
}
public EnrRequestPacketData create(final long expiration) {
expiryValidator.validate(expiration);
return new EnrRequestPacketData(expiration);
}
public EnrRequestPacketData create() {
return new EnrRequestPacketData(getDefaultExpirationTime());
}
private long getDefaultExpirationTime() {
return clock.instant().getEpochSecond() + PacketData.DEFAULT_EXPIRATION_PERIOD_SEC;
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright contributors to Besu.
*
* 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.packet.enrrequest;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.PacketDataDeserializer;
import org.hyperledger.besu.ethereum.rlp.RLPInput;
import javax.inject.Inject;
import javax.inject.Singleton;
@Singleton
public class EnrRequestPacketDataRlpReader implements PacketDataDeserializer<EnrRequestPacketData> {
private final EnrRequestPacketDataFactory enrRequestPacketDataFactory;
public @Inject EnrRequestPacketDataRlpReader(
final EnrRequestPacketDataFactory enrRequestPacketDataFactory) {
this.enrRequestPacketDataFactory = enrRequestPacketDataFactory;
}
@Override
public EnrRequestPacketData readFrom(final RLPInput in) {
in.enterList();
final long expiration = in.readLongScalar();
in.leaveListLenient();
return enrRequestPacketDataFactory.create(expiration);
}
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright contributors to Besu.
*
* 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.packet.enrrequest;
import org.hyperledger.besu.ethereum.rlp.RLPOutput;
import javax.inject.Inject;
import javax.inject.Singleton;
@Singleton
public class EnrRequestPacketDataRlpWriter {
public @Inject EnrRequestPacketDataRlpWriter() {}
public void writeTo(final EnrRequestPacketData enrRequestPacketData, final RLPOutput out) {
out.startList();
out.writeLongScalar(enrRequestPacketData.getExpiration());
out.endList();
}
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright contributors to Besu.
*
* 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.packet.enrresponse;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.PacketData;
import org.apache.tuweni.bytes.Bytes;
import org.ethereum.beacon.discovery.schema.NodeRecord;
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;
EnrResponsePacketData(final Bytes requestHash, final NodeRecord enr) {
this.requestHash = requestHash;
this.enr = enr;
}
public Bytes getRequestHash() {
return requestHash;
}
public NodeRecord getEnr() {
return enr;
}
@Override
public String toString() {
return "EnrResponsePacketData{" + "requestHash=" + requestHash + ", enr=" + enr + '}';
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright contributors to Besu.
*
* 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.packet.enrresponse;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.validation.NodeRecordValidator;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.validation.RequestHashValidator;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.apache.tuweni.bytes.Bytes;
import org.ethereum.beacon.discovery.schema.NodeRecord;
@Singleton
public class EnrResponsePacketDataFactory {
private final RequestHashValidator requestHashValidator;
private final NodeRecordValidator nodeRecordValidator;
public @Inject EnrResponsePacketDataFactory(
final RequestHashValidator requestHashValidator,
final NodeRecordValidator nodeRecordValidator) {
this.requestHashValidator = requestHashValidator;
this.nodeRecordValidator = nodeRecordValidator;
}
public EnrResponsePacketData create(final Bytes requestHash, final NodeRecord enr) {
requestHashValidator.validate(requestHash);
nodeRecordValidator.validate(enr);
return new EnrResponsePacketData(requestHash, enr);
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright contributors to Besu.
*
* 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.packet.enrresponse;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.PacketDataDeserializer;
import org.hyperledger.besu.ethereum.rlp.RLPInput;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.apache.tuweni.bytes.Bytes;
import org.ethereum.beacon.discovery.schema.NodeRecord;
import org.ethereum.beacon.discovery.schema.NodeRecordFactory;
@Singleton
public class EnrResponsePacketDataRlpReader
implements PacketDataDeserializer<EnrResponsePacketData> {
private final NodeRecordFactory nodeRecordFactory;
private final EnrResponsePacketDataFactory enrResponsePacketDataFactory;
public @Inject EnrResponsePacketDataRlpReader(
final NodeRecordFactory nodeRecordFactory,
final EnrResponsePacketDataFactory enrResponsePacketDataFactory) {
this.nodeRecordFactory = nodeRecordFactory;
this.enrResponsePacketDataFactory = enrResponsePacketDataFactory;
}
@Override
public EnrResponsePacketData readFrom(final RLPInput in) {
in.enterList();
final Bytes requestHash = in.readBytes();
in.leaveListLenient();
final NodeRecord enr = nodeRecordFactory.fromBytes(in.currentListAsBytes());
return enrResponsePacketDataFactory.create(requestHash, enr);
}
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright contributors to Besu.
*
* 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.packet.enrresponse;
import org.hyperledger.besu.ethereum.rlp.RLPOutput;
import javax.inject.Inject;
import javax.inject.Singleton;
@Singleton
public class EnrResponsePacketDataRlpWriter {
public @Inject EnrResponsePacketDataRlpWriter() {}
public void writeTo(final EnrResponsePacketData enrResponsePacketData, final RLPOutput out) {
out.startList();
out.writeBytes(enrResponsePacketData.getRequestHash());
out.writeRLPBytes(enrResponsePacketData.getEnr().serialize());
out.endList();
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright contributors to Besu.
*
* 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.packet.findneighbors;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.PacketData;
import org.apache.tuweni.bytes.Bytes;
public class FindNeighborsPacketData implements PacketData {
public static final int TARGET_SIZE = 64;
/* Node ID. */
private final Bytes target;
/* In seconds after epoch. */
private final long expiration;
FindNeighborsPacketData(final Bytes target, final long expiration) {
this.target = target;
this.expiration = expiration;
}
public Bytes getTarget() {
return target;
}
public long getExpiration() {
return expiration;
}
@Override
public String toString() {
return "FindNeighborsPacketData{" + "expiration=" + expiration + ", target=" + target + '}';
}
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright contributors to Besu.
*
* 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.packet.findneighbors;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.PacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.validation.ExpiryValidator;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.validation.TargetValidator;
import java.time.Clock;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.apache.tuweni.bytes.Bytes;
@Singleton
public class FindNeighborsPacketDataFactory {
private final TargetValidator targetValidator;
private final ExpiryValidator expiryValidator;
private final Clock clock;
public @Inject FindNeighborsPacketDataFactory(
final TargetValidator targetValidator,
final ExpiryValidator expiryValidator,
final Clock clock) {
this.targetValidator = targetValidator;
this.expiryValidator = expiryValidator;
this.clock = clock;
}
public FindNeighborsPacketData create(final Bytes target, final long expiration) {
targetValidator.validate(target);
expiryValidator.validate(expiration);
return new FindNeighborsPacketData(target, expiration);
}
public FindNeighborsPacketData create(final Bytes target) {
targetValidator.validate(target);
return new FindNeighborsPacketData(target, getDefaultExpirationTime());
}
private long getDefaultExpirationTime() {
return clock.instant().getEpochSecond() + PacketData.DEFAULT_EXPIRATION_PERIOD_SEC;
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright contributors to Besu.
*
* 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.packet.findneighbors;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.PacketDataDeserializer;
import org.hyperledger.besu.ethereum.rlp.RLPInput;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.apache.tuweni.bytes.Bytes;
@Singleton
public class FindNeighborsPacketDataRlpReader
implements PacketDataDeserializer<FindNeighborsPacketData> {
private final FindNeighborsPacketDataFactory findNeighborsPacketDataFactory;
public @Inject FindNeighborsPacketDataRlpReader(
final FindNeighborsPacketDataFactory findNeighborsPacketDataFactory) {
this.findNeighborsPacketDataFactory = findNeighborsPacketDataFactory;
}
@Override
public FindNeighborsPacketData readFrom(final RLPInput in) {
in.enterList();
final Bytes target = in.readBytes();
final long expiration = in.readLongScalar();
in.leaveListLenient();
return findNeighborsPacketDataFactory.create(target, expiration);
}
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright contributors to Besu.
*
* 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.packet.findneighbors;
import org.hyperledger.besu.ethereum.rlp.RLPOutput;
import javax.inject.Inject;
import javax.inject.Singleton;
@Singleton
public class FindNeighborsPacketDataRlpWriter {
public @Inject FindNeighborsPacketDataRlpWriter() {}
public void writeTo(final FindNeighborsPacketData findNeighborsPacketData, final RLPOutput out) {
out.startList();
out.writeBytes(findNeighborsPacketData.getTarget());
out.writeLongScalar(findNeighborsPacketData.getExpiration());
out.endList();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright ConsenSys AG.
* Copyright contributors to Besu.
*
* 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
@@ -12,13 +12,10 @@
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.p2p.discovery.internal;
import static com.google.common.base.Preconditions.checkArgument;
package org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.neighbors;
import org.hyperledger.besu.ethereum.p2p.discovery.DiscoveryPeer;
import org.hyperledger.besu.ethereum.rlp.RLPInput;
import org.hyperledger.besu.ethereum.rlp.RLPOutput;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.PacketData;
import java.util.List;
@@ -29,34 +26,11 @@ public class NeighborsPacketData implements PacketData {
/* In seconds after epoch. */
private final long expiration;
private NeighborsPacketData(final List<DiscoveryPeer> peers, final long expiration) {
checkArgument(peers != null, "peer list cannot be null");
checkArgument(expiration >= 0, "expiration must be positive");
NeighborsPacketData(final List<DiscoveryPeer> peers, final long expiration) {
this.peers = peers;
this.expiration = expiration;
}
public static NeighborsPacketData create(final List<DiscoveryPeer> peers) {
return new NeighborsPacketData(peers, PacketData.defaultExpiration());
}
public static NeighborsPacketData readFrom(final RLPInput in) {
in.enterList();
final List<DiscoveryPeer> peers = in.readList(DiscoveryPeer::readFrom);
final long expiration = in.readLongScalar();
in.leaveListLenient();
return new NeighborsPacketData(peers, expiration);
}
@Override
public void writeTo(final RLPOutput out) {
out.startList();
out.writeList(peers, DiscoveryPeer::writeTo);
out.writeLongScalar(expiration);
out.endList();
}
public List<DiscoveryPeer> getNodes() {
return peers;
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright contributors to Besu.
*
* 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.packet.neighbors;
import org.hyperledger.besu.ethereum.p2p.discovery.DiscoveryPeer;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.PacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.validation.DiscoveryPeersValidator;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.validation.ExpiryValidator;
import java.time.Clock;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Singleton;
@Singleton
public class NeighborsPacketDataFactory {
private final DiscoveryPeersValidator discoveryPeersValidator;
private final ExpiryValidator expiryValidator;
private final Clock clock;
public @Inject NeighborsPacketDataFactory(
final DiscoveryPeersValidator discoveryPeersValidator,
final ExpiryValidator expiryValidator,
final Clock clock) {
this.discoveryPeersValidator = discoveryPeersValidator;
this.expiryValidator = expiryValidator;
this.clock = clock;
}
public NeighborsPacketData create(final List<DiscoveryPeer> peers, final long expiration) {
discoveryPeersValidator.validate(peers);
expiryValidator.validate(expiration);
return new NeighborsPacketData(peers, expiration);
}
public NeighborsPacketData create(final List<DiscoveryPeer> peers) {
discoveryPeersValidator.validate(peers);
return new NeighborsPacketData(peers, getDefaultExpirationTime());
}
private long getDefaultExpirationTime() {
return clock.instant().getEpochSecond() + PacketData.DEFAULT_EXPIRATION_PERIOD_SEC;
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright contributors to Besu.
*
* 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.packet.neighbors;
import org.hyperledger.besu.ethereum.p2p.discovery.DiscoveryPeer;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.PacketDataDeserializer;
import org.hyperledger.besu.ethereum.rlp.RLPInput;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Singleton;
@Singleton
public class NeighborsPacketDataRlpReader implements PacketDataDeserializer<NeighborsPacketData> {
private final NeighborsPacketDataFactory neighborsPacketDataFactory;
public @Inject NeighborsPacketDataRlpReader(
final NeighborsPacketDataFactory neighborsPacketDataFactory) {
this.neighborsPacketDataFactory = neighborsPacketDataFactory;
}
@Override
public NeighborsPacketData readFrom(final RLPInput in) {
in.enterList();
final List<DiscoveryPeer> peers = in.readList(DiscoveryPeer::readFrom);
final long expiration = in.readLongScalar();
in.leaveListLenient();
return neighborsPacketDataFactory.create(peers, expiration);
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright contributors to Besu.
*
* 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.packet.neighbors;
import org.hyperledger.besu.ethereum.p2p.discovery.DiscoveryPeer;
import org.hyperledger.besu.ethereum.rlp.RLPOutput;
import javax.inject.Inject;
import javax.inject.Singleton;
@Singleton
public class NeighborsPacketDataRlpWriter {
public @Inject NeighborsPacketDataRlpWriter() {}
public void writeTo(final NeighborsPacketData neighborsPacketData, final RLPOutput out) {
out.startList();
out.writeList(neighborsPacketData.getNodes(), DiscoveryPeer::writeTo);
out.writeLongScalar(neighborsPacketData.getExpiration());
out.endList();
}
}

View File

@@ -0,0 +1,81 @@
/*
* Copyright contributors to Besu.
*
* 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.packet.ping;
import org.hyperledger.besu.ethereum.p2p.discovery.Endpoint;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.PacketData;
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. */
public static final int VERSION = 5;
/* Source. If the field is garbage this is empty and we might need to recover it another way. From our bonded peers, for example. */
private final Optional<Endpoint> maybeFrom;
/* Destination. */
private final Endpoint to;
/* In seconds after epoch. */
private final long expiration;
/* Current sequence number of the sending nodes record */
private final UInt64 enrSeq;
PingPacketData(
final Optional<Endpoint> maybeFrom,
final Endpoint to,
final long expiration,
final UInt64 enrSeq) {
this.maybeFrom = maybeFrom;
this.to = to;
this.expiration = expiration;
this.enrSeq = enrSeq;
}
public Optional<Endpoint> getFrom() {
return maybeFrom;
}
public Endpoint getTo() {
return to;
}
public long getExpiration() {
return expiration;
}
public Optional<UInt64> getEnrSeq() {
return Optional.ofNullable(enrSeq);
}
@Override
public String toString() {
return "PingPacketData{"
+ "from="
+ maybeFrom.map(Object::toString).orElse("INVALID")
+ ", to="
+ to
+ ", expiration="
+ expiration
+ ", enrSeq="
+ enrSeq
+ '}';
}
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright contributors to Besu.
*
* 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.packet.ping;
import org.hyperledger.besu.ethereum.p2p.discovery.Endpoint;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.PacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.validation.EndpointValidator;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.validation.ExpiryValidator;
import java.time.Clock;
import java.util.Optional;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.apache.tuweni.units.bigints.UInt64;
@Singleton
public class PingPacketDataFactory {
private final EndpointValidator endpointValidator;
private final ExpiryValidator expiryValidator;
private final Clock clock;
public @Inject PingPacketDataFactory(
final EndpointValidator endpointValidator,
final ExpiryValidator expiryValidator,
final Clock clock) {
this.endpointValidator = endpointValidator;
this.expiryValidator = expiryValidator;
this.clock = clock;
}
public PingPacketData create(
final Optional<Endpoint> maybeFrom,
final Endpoint to,
final long expiration,
final UInt64 enrSeq) {
endpointValidator.validate(to, "destination endpoint cannot be null");
expiryValidator.validate(expiration);
return new PingPacketData(maybeFrom, to, expiration, enrSeq);
}
public PingPacketData create(
final Optional<Endpoint> maybeFrom, final Endpoint to, final UInt64 enrSeq) {
endpointValidator.validate(to, "destination endpoint cannot be null");
return new PingPacketData(maybeFrom, to, getDefaultExpirationTime(), enrSeq);
}
private long getDefaultExpirationTime() {
return clock.instant().getEpochSecond() + PacketData.DEFAULT_EXPIRATION_PERIOD_SEC;
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright contributors to Besu.
*
* 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.packet.ping;
import org.hyperledger.besu.ethereum.p2p.discovery.Endpoint;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.DevP2PException;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.PacketDataDeserializer;
import org.hyperledger.besu.ethereum.rlp.MalformedRLPInputException;
import org.hyperledger.besu.ethereum.rlp.RLPInput;
import java.util.Optional;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.apache.tuweni.units.bigints.UInt64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Singleton
public class PingPacketDataRlpReader implements PacketDataDeserializer<PingPacketData> {
private static final Logger LOG = LoggerFactory.getLogger(PingPacketDataRlpReader.class);
private final PingPacketDataFactory pingPacketDataFactory;
public @Inject PingPacketDataRlpReader(final PingPacketDataFactory pingPacketDataFactory) {
this.pingPacketDataFactory = pingPacketDataFactory;
}
@Override
public PingPacketData readFrom(final RLPInput in) {
in.enterList();
// The first element signifies the "version", but this value is ignored as of EIP-8
in.readBigIntegerScalar();
Optional<Endpoint> from = Optional.empty();
Optional<Endpoint> to = Optional.empty();
if (in.nextIsList()) {
to = Endpoint.maybeDecodeStandalone(in);
// https://github.com/ethereum/devp2p/blob/master/discv4.md#ping-packet-0x01
if (in.nextIsList()) { // if there are two, the first is the from address, next is the to
// address
from = to;
to = Endpoint.maybeDecodeStandalone(in);
}
} else {
throw new DevP2PException("missing address in ping packet");
}
final long expiration = in.readLongScalar();
UInt64 enrSeq = null;
if (!in.isEndOfCurrentList()) {
try {
enrSeq = UInt64.valueOf(in.readBigIntegerScalar());
LOG.trace("read PING enr as long scalar");
} catch (MalformedRLPInputException malformed) {
LOG.trace("failed to read PING enr as scalar, trying to read bytes instead");
enrSeq = UInt64.fromBytes(in.readBytes());
}
}
in.leaveListLenient();
return pingPacketDataFactory.create(from, to.get(), expiration, enrSeq);
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright contributors to Besu.
*
* 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.packet.ping;
import org.hyperledger.besu.ethereum.rlp.RLPOutput;
import javax.inject.Inject;
import javax.inject.Singleton;
@Singleton
public class PingPacketDataRlpWriter {
public @Inject PingPacketDataRlpWriter() {}
public void writeTo(final PingPacketData pingPacketData, final RLPOutput out) {
out.startList();
out.writeIntScalar(PingPacketData.VERSION);
if (pingPacketData.getFrom().isPresent()) {
pingPacketData.getFrom().get().encodeStandalone(out);
}
pingPacketData.getTo().encodeStandalone(out);
out.writeLongScalar(pingPacketData.getExpiration());
out.writeBigIntegerScalar(
pingPacketData.getEnrSeq().map(seq -> seq.toBigInteger()).orElseThrow());
out.endList();
}
}

View File

@@ -0,0 +1,76 @@
/*
* Copyright contributors to Besu.
*
* 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.packet.pong;
import org.hyperledger.besu.ethereum.p2p.discovery.Endpoint;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.PacketData;
import java.util.Optional;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.units.bigints.UInt64;
public class PongPacketData implements PacketData {
/* Destination. */
private final Endpoint to;
/* Hash of the PING packet. */
private final Bytes pingHash;
/* In seconds after epoch. */
private final long expiration;
/* Current sequence number of the sending nodes record */
private final UInt64 enrSeq;
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 Endpoint getTo() {
return to;
}
public Bytes getPingHash() {
return pingHash;
}
public long getExpiration() {
return expiration;
}
public Optional<UInt64> getEnrSeq() {
return Optional.ofNullable(enrSeq);
}
@Override
public String toString() {
return "PongPacketData{"
+ "to="
+ to
+ ", pingHash="
+ pingHash
+ ", expiration="
+ expiration
+ ", enrSeq="
+ enrSeq
+ '}';
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright contributors to Besu.
*
* 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.packet.pong;
import org.hyperledger.besu.ethereum.p2p.discovery.Endpoint;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.PacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.validation.ExpiryValidator;
import java.time.Clock;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.units.bigints.UInt64;
@Singleton
public class PongPacketDataFactory {
private final ExpiryValidator expiryValidator;
private final Clock clock;
public @Inject PongPacketDataFactory(final ExpiryValidator expiryValidator, final Clock clock) {
this.expiryValidator = expiryValidator;
this.clock = clock;
}
public PongPacketData create(
final Endpoint to, final Bytes pingHash, final long expiration, final UInt64 enrSeq) {
expiryValidator.validate(expiration);
return new PongPacketData(to, pingHash, expiration, enrSeq);
}
public PongPacketData create(final Endpoint to, final Bytes pingHash, final UInt64 enrSeq) {
return new PongPacketData(to, pingHash, getDefaultExpirationTime(), enrSeq);
}
private long getDefaultExpirationTime() {
return clock.instant().getEpochSecond() + PacketData.DEFAULT_EXPIRATION_PERIOD_SEC;
}
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright contributors to Besu.
*
* 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.packet.pong;
import org.hyperledger.besu.ethereum.p2p.discovery.Endpoint;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.PacketDataDeserializer;
import org.hyperledger.besu.ethereum.rlp.MalformedRLPInputException;
import org.hyperledger.besu.ethereum.rlp.RLPInput;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.units.bigints.UInt64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Singleton
public class PongPacketDataRlpReader implements PacketDataDeserializer<PongPacketData> {
private static final Logger LOG = LoggerFactory.getLogger(PongPacketData.class);
private final PongPacketDataFactory pongPacketDataFactory;
public @Inject PongPacketDataRlpReader(final PongPacketDataFactory pongPacketDataFactory) {
this.pongPacketDataFactory = pongPacketDataFactory;
}
@Override
public PongPacketData readFrom(final RLPInput in) {
in.enterList();
final Endpoint to = Endpoint.decodeStandalone(in);
final Bytes hash = in.readBytes();
final long expiration = in.readLongScalar();
UInt64 enrSeq = null;
if (!in.isEndOfCurrentList()) {
try {
enrSeq = UInt64.valueOf(in.readBigIntegerScalar());
LOG.trace("read PONG enr from scalar");
} catch (final MalformedRLPInputException malformed) {
LOG.trace("failed to read PONG enr from scalar, trying as byte array");
enrSeq = UInt64.fromBytes(in.readBytes());
}
}
in.leaveListLenient();
return pongPacketDataFactory.create(to, hash, expiration, enrSeq);
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright contributors to Besu.
*
* 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.packet.pong;
import org.hyperledger.besu.ethereum.rlp.RLPOutput;
import javax.inject.Inject;
import javax.inject.Singleton;
@Singleton
public class PongPacketDataRlpWriter {
public @Inject PongPacketDataRlpWriter() {}
public void writeTo(final PongPacketData pongPacketData, final RLPOutput out) {
out.startList();
pongPacketData.getTo().encodeStandalone(out);
out.writeBytes(pongPacketData.getPingHash());
out.writeLongScalar(pongPacketData.getExpiration());
out.writeBigIntegerScalar(
pongPacketData.getEnrSeq().map(seq -> seq.toBigInteger()).orElseThrow());
out.endList();
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright contributors to Besu.
*
* 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.packet.validation;
import static com.google.common.base.Preconditions.checkArgument;
import org.hyperledger.besu.ethereum.p2p.discovery.DiscoveryPeer;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Singleton;
@Singleton
public class DiscoveryPeersValidator {
public @Inject DiscoveryPeersValidator() {}
public void validate(final List<DiscoveryPeer> peers) {
checkArgument(peers != null, "peer list cannot be null");
}
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright contributors to Besu.
*
* 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.packet.validation;
import static com.google.common.base.Preconditions.checkArgument;
import org.hyperledger.besu.ethereum.p2p.discovery.Endpoint;
import javax.inject.Inject;
import javax.inject.Singleton;
@Singleton
public class EndpointValidator {
public @Inject EndpointValidator() {}
public void validate(final Endpoint endpoint, final String message) {
checkArgument(endpoint != null, message);
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright contributors to Besu.
*
* 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.packet.validation;
import static com.google.common.base.Preconditions.checkArgument;
import java.time.Clock;
import javax.inject.Inject;
import javax.inject.Singleton;
@Singleton
public class ExpiryValidator {
private final Clock clock;
public @Inject ExpiryValidator(final Clock clock) {
this.clock = clock;
}
public void validate(final long expiry) {
checkArgument(expiry >= 0, "expiration cannot be negative");
checkArgument(expiry >= clock.instant().getEpochSecond(), "expiration cannot be in the past");
}
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright contributors to Besu.
*
* 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.packet.validation;
import static com.google.common.base.Preconditions.checkArgument;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.ethereum.beacon.discovery.schema.NodeRecord;
@Singleton
public class NodeRecordValidator {
public @Inject NodeRecordValidator() {}
public void validate(final NodeRecord nodeRecord) {
checkArgument(nodeRecord != null, "node record cannot be null");
}
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright contributors to Besu.
*
* 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.packet.validation;
import static com.google.common.base.Preconditions.checkArgument;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.apache.tuweni.bytes.Bytes;
@Singleton
public class RequestHashValidator {
public @Inject RequestHashValidator() {}
public void validate(final Bytes requestHash) {
checkArgument(requestHash != null, "request hash cannot be null");
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright contributors to Besu.
*
* 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.packet.validation;
import static com.google.common.base.Preconditions.checkArgument;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.findneighbors.FindNeighborsPacketData;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.apache.tuweni.bytes.Bytes;
@Singleton
public class TargetValidator {
public @Inject TargetValidator() {}
public void validate(final Bytes target) {
checkArgument(
target != null && target.size() == FindNeighborsPacketData.TARGET_SIZE,
"target must be a valid node id");
}
}

View File

@@ -585,7 +585,7 @@ public class DefaultP2PNetwork implements P2PNetwork {
final ForkIdManager forkIdManager =
new ForkIdManager(blockchain, blockNumberForks, timestampForks, this.legacyForkIdEnabled);
return new VertxPeerDiscoveryAgent(
return VertxPeerDiscoveryAgent.create(
vertx,
nodeKey,
config.getDiscovery(),

View File

@@ -31,13 +31,14 @@ import org.hyperledger.besu.cryptoservices.NodeKey;
import org.hyperledger.besu.cryptoservices.NodeKeyUtils;
import org.hyperledger.besu.ethereum.forkid.ForkId;
import org.hyperledger.besu.ethereum.p2p.discovery.PeerDiscoveryTestHelper.AgentBuilder;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.FindNeighborsPacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.MockPeerDiscoveryAgent;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.MockPeerDiscoveryAgent.IncomingPacket;
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.packet.DaggerPacketPackage;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.Packet;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.PacketPackage;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.findneighbors.FindNeighborsPacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.neighbors.NeighborsPacketData;
import org.hyperledger.besu.ethereum.p2p.peers.DefaultPeer;
import org.hyperledger.besu.ethereum.p2p.peers.EnodeURLImpl;
import org.hyperledger.besu.ethereum.p2p.peers.Peer;
@@ -59,6 +60,7 @@ import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.apache.tuweni.units.bigints.UInt64;
import org.ethereum.beacon.discovery.schema.NodeRecord;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public class PeerDiscoveryAgentTest {
@@ -66,7 +68,14 @@ public class PeerDiscoveryAgentTest {
private static final int BROADCAST_TCP_PORT = 30303;
private static final Supplier<SignatureAlgorithm> SIGNATURE_ALGORITHM =
Suppliers.memoize(SignatureAlgorithmFactory::getInstance);
private final PeerDiscoveryTestHelper helper = new PeerDiscoveryTestHelper();
private PeerDiscoveryTestHelper helper;
private PacketPackage packetPackage;
@BeforeEach
public void beforeTest() {
helper = new PeerDiscoveryTestHelper();
packetPackage = DaggerPacketPackage.create();
}
@Test
public void createAgentWithInvalidBootnodes() {
@@ -177,8 +186,9 @@ public class PeerDiscoveryAgentTest {
// Generate an out-of-band NEIGHBORS message.
final List<DiscoveryPeer> peers = helper.createDiscoveryPeers(5);
final NeighborsPacketData data = NeighborsPacketData.create(peers);
final Packet packet = Packet.create(PacketType.NEIGHBORS, data, otherNode.getNodeKey());
final NeighborsPacketData data = packetPackage.neighborsPacketDataFactory().create(peers);
final Packet packet =
packetPackage.packetFactory().create(PacketType.NEIGHBORS, data, otherNode.getNodeKey());
helper.sendMessageBetweenAgents(otherNode, agent, packet);
assertThat(agent.streamDiscoveredPeers()).isEmpty();
@@ -210,16 +220,20 @@ public class PeerDiscoveryAgentTest {
final MockPeerDiscoveryAgent testAgent = helper.startDiscoveryAgent();
// Send a PING so we can exchange messages with the latter agent.
Packet packet = helper.createPingPacket(testAgent, agent);
Packet packet = helper.createPingPacket(testAgent, agent, packetPackage);
helper.sendMessageBetweenAgents(testAgent, agent, packet);
// Send a FIND_NEIGHBORS message.
assertThat(otherAgents.get(0).getAdvertisedPeer().isPresent()).isTrue();
packet =
Packet.create(
PacketType.FIND_NEIGHBORS,
FindNeighborsPacketData.create(otherAgents.get(0).getAdvertisedPeer().get().getId()),
testAgent.getNodeKey());
packetPackage
.packetFactory()
.create(
PacketType.FIND_NEIGHBORS,
packetPackage
.findNeighborsPacketDataFactory()
.create(otherAgents.get(0).getAdvertisedPeer().get().getId()),
testAgent.getNodeKey());
helper.sendMessageBetweenAgents(testAgent, agent, packet);
// Check response packet
@@ -238,7 +252,8 @@ public class PeerDiscoveryAgentTest {
neighborsPacket.packet.getPacketData(NeighborsPacketData.class).get();
assertThat(neighbors).isNotNull();
assertThat(neighbors.getNodes()).hasSize(13);
assertThat(neighborsPacket.packet.encode().length()).isLessThanOrEqualTo(1280); // under max MTU
assertThat(packetPackage.packetSerializer().encode(neighborsPacket.packet).length())
.isLessThanOrEqualTo(1280); // under max MTU
// Assert that after removing those 13 items we're left with either 7 or 8.
// If we are left with 8, the test peer was returned as an item, assert that this is the case.
@@ -260,7 +275,7 @@ public class PeerDiscoveryAgentTest {
final MockPeerDiscoveryAgent agent2 = helper.startDiscoveryAgent("192.168.0.1");
// Send a PING so we can exchange messages
Packet packet = helper.createPingPacket(agent2, agent1);
Packet packet = helper.createPingPacket(agent2, agent1, packetPackage);
helper.sendMessageBetweenAgents(agent2, agent1, packet);
// Agent 1's peers should have endpoints that match the custom advertised value...
@@ -860,33 +875,41 @@ public class PeerDiscoveryAgentTest {
when(mock(Packet.class).getPacketData(any()))
.thenReturn(
Optional.of(
PingPacketData.create(Optional.of(emptyIPv4), endpointLocal, UInt64.ONE)))
packetPackage
.pingPacketDataFactory()
.create(Optional.of(emptyIPv4), endpointLocal, UInt64.ONE)))
.getMock();
Packet mockEmptyIPv6 =
when(mock(Packet.class).getPacketData(any()))
.thenReturn(
Optional.of(
PingPacketData.create(Optional.of(emptyIPv6), endpointLocal, UInt64.ONE)))
packetPackage
.pingPacketDataFactory()
.create(Optional.of(emptyIPv6), endpointLocal, UInt64.ONE)))
.getMock();
Packet mockLocal =
when(mock(Packet.class).getPacketData(any()))
.thenReturn(
Optional.of(
PingPacketData.create(Optional.of(endpointLocal), endpointLocal, UInt64.ONE)))
packetPackage
.pingPacketDataFactory()
.create(Optional.of(endpointLocal), endpointLocal, UInt64.ONE)))
.getMock();
Packet mockBroadcast =
when(mock(Packet.class).getPacketData(any()))
.thenReturn(
Optional.of(
PingPacketData.create(
Optional.of(endpointBroadcast), endpointLocal, UInt64.ONE)))
packetPackage
.pingPacketDataFactory()
.create(Optional.of(endpointBroadcast), endpointLocal, UInt64.ONE)))
.getMock();
Packet mockWellFormed =
when(mock(Packet.class).getPacketData(any()))
.thenReturn(
Optional.of(
PingPacketData.create(
Optional.of(endpointRoutable), endpointLocal, UInt64.ONE)))
packetPackage
.pingPacketDataFactory()
.create(Optional.of(endpointRoutable), endpointLocal, UInt64.ONE)))
.getMock();
// assert a pingpacketdata with empty ipv4 address reverts to the udp source host
@@ -920,14 +943,18 @@ public class PeerDiscoveryAgentTest {
protected void bondViaIncomingPing(
final MockPeerDiscoveryAgent agent, final MockPeerDiscoveryAgent otherNode) {
final Packet pingPacket = helper.createPingPacket(otherNode, agent);
final Packet pingPacket = helper.createPingPacket(otherNode, agent, packetPackage);
helper.sendMessageBetweenAgents(otherNode, agent, pingPacket);
}
protected void requestNeighbors(
final MockPeerDiscoveryAgent fromAgent, final MockPeerDiscoveryAgent toAgent) {
final FindNeighborsPacketData data = FindNeighborsPacketData.create(Peer.randomId());
final Packet packet = Packet.create(PacketType.FIND_NEIGHBORS, data, fromAgent.getNodeKey());
final FindNeighborsPacketData data =
packetPackage.findNeighborsPacketDataFactory().create(Peer.randomId());
final Packet packet =
packetPackage
.packetFactory()
.create(PacketType.FIND_NEIGHBORS, data, fromAgent.getNodeKey());
helper.sendMessageBetweenAgents(fromAgent, toAgent, packet);
}
}

View File

@@ -17,22 +17,31 @@ package org.hyperledger.besu.ethereum.p2p.discovery;
import static java.util.Collections.emptyList;
import static org.assertj.core.api.Assertions.assertThat;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.FindNeighborsPacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.MockPeerDiscoveryAgent;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.MockPeerDiscoveryAgent.IncomingPacket;
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.PongPacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.DaggerPacketPackage;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.Packet;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.PacketPackage;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.findneighbors.FindNeighborsPacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.pong.PongPacketData;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public class PeerDiscoveryBondingTest {
private final PeerDiscoveryTestHelper helper = new PeerDiscoveryTestHelper();
private PacketPackage packetPackage;
@BeforeEach
public void beforeTest() {
packetPackage = DaggerPacketPackage.create();
}
@Test
public void pongSentUponPing() {
@@ -77,8 +86,12 @@ public class PeerDiscoveryBondingTest {
// ignored because
// we haven't bonded.
final MockPeerDiscoveryAgent otherNode = helper.startDiscoveryAgent();
final FindNeighborsPacketData data = FindNeighborsPacketData.create(otherNode.getId());
final Packet packet = Packet.create(PacketType.FIND_NEIGHBORS, data, otherNode.getNodeKey());
final FindNeighborsPacketData data =
packetPackage.findNeighborsPacketDataFactory().create(otherNode.getId());
final Packet packet =
packetPackage
.packetFactory()
.create(PacketType.FIND_NEIGHBORS, data, otherNode.getNodeKey());
helper.sendMessageBetweenAgents(otherNode, agent, packet);
// No responses received

View File

@@ -21,9 +21,9 @@ import static org.assertj.core.api.Assertions.assertThat;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.MockPeerDiscoveryAgent;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.MockPeerDiscoveryAgent.IncomingPacket;
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.packet.Packet;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.ping.PingPacketData;
import org.hyperledger.besu.ethereum.p2p.peers.Peer;
import java.util.List;

View File

@@ -1,215 +0,0 @@
/*
* 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;
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.time.Instant;
import com.google.common.net.InetAddresses;
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.jupiter.api.Test;
public class PeerDiscoveryPacketPcapSedesTest {
private static final String pingHexData =
"dd49244b390b8ee964c7320742acfbb893ab47f051a323c0d908bd360ae83bd92d1e6dd370af2e5d21c4ce2b054baa1be66fe879d"
+ "76ca851c12803a26a382d6e56e33071ba98ae22374fd37be02aa8573aac89f955ae21e96f154d82d0f6b2e20101df05cb"
+ "84b4b57a1982040182765fcb84b4b57a1a82040182765f8460e5f20603";
private static final String pongHexData =
"3b3d56e4fcdcf714d6c29b0d521e9f4ec0dd50c73c0bbb9b44758a0a7e416ff30a3ea99de3e78a5cff82ad0a0b82030f78c6eff5a"
+ "1b2a030276277b6e9b6ad7c35db1a6d5f83586a203b4537d82edc6b2820849a485e55aa06cfc680dc63c22d0002f3cb84"
+ "b4b57a1a82040182765fa046896547d3b4259aa1a67bd26e7ec58ab4be650c5552ef0360caf9dae489d53b8460e5e7b603";
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 =
"fed7cfd0a60b51d027d14d5bd1d4c5bc4ea289940c0d38f2ff8e72522e33e39be040ef1acbe25e2c40523821c0c536e17e0f7204a08260b842dc"
+ "a830513a2f9e5169a3f711ecb5a512fdd56e5edfd7d8fdaa0e6982020dbd2f76949ef84d1a840005c58460146486";
private static final String enrResponseHexData =
"9c85a6d16e5222dc48df51654cc36fb372b5429646ca8fd85a7f79ea420dba326f2cc84456b6c7fa1e6dd4ed6e3e89934e6f4415d58b40899996"
+ "ee461a8e147f62ff33177680cffe061d048183091b4254dd1edf05f7e92d1117b23035d94f7d0106f8a7a0fed7cfd0a60b51d027d14d5b"
+ "d1d4c5bc4ea289940c0d38f2ff8e72522e33e39bf884b8407098ad865b00a582051940cb9cf36836572411a47278783077011599ed5cd1"
+ "6b76f2635f4e234738f30813a89eb9137e3e3df5266e3a1f11df72ecf1145ccb9c01826964827634826970847f00000189736563703235"
+ "366b31a103ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd31388375647082765f";
@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);
final byte[] encoded = packet.encode().getBytes();
assertThat(encoded).isEqualTo(data);
}
@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 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);
}
@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");
}

View File

@@ -1,130 +0,0 @@
/*
* 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;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.assertj.core.data.Offset.offset;
import org.hyperledger.besu.cryptoservices.NodeKey;
import org.hyperledger.besu.cryptoservices.NodeKeyUtils;
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.PacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.PacketType;
import org.hyperledger.besu.ethereum.rlp.RLP;
import org.hyperledger.besu.ethereum.rlp.RLPException;
import org.hyperledger.besu.ethereum.rlp.RLPInput;
import java.util.List;
import java.util.Random;
import io.vertx.core.buffer.Buffer;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.MutableBytes;
import org.junit.jupiter.api.Test;
public class PeerDiscoveryPacketSedesTest {
private final PeerDiscoveryTestHelper helper = new PeerDiscoveryTestHelper();
@Test
public void serializeDeserializeEntirePacket() {
final byte[] r = new byte[64];
new Random().nextBytes(r);
final Bytes target = Bytes.wrap(r);
final NodeKey nodeKey = NodeKeyUtils.generate();
final FindNeighborsPacketData packetData = FindNeighborsPacketData.create(target);
final Packet packet = Packet.create(PacketType.FIND_NEIGHBORS, packetData, nodeKey);
final Buffer encoded = packet.encode();
assertThat(encoded).isNotNull();
final Packet decoded = Packet.decode(encoded);
assertThat(decoded.getType()).isEqualTo(PacketType.FIND_NEIGHBORS);
assertThat(decoded.getNodeId()).isEqualTo(nodeKey.getPublicKey().getEncodedBytes());
assertThat(decoded.getPacketData(NeighborsPacketData.class)).isNotPresent();
assertThat(decoded.getPacketData(FindNeighborsPacketData.class)).isPresent();
}
@Test
public void serializeDeserializeFindNeighborsPacketData() {
final byte[] r = new byte[64];
new Random().nextBytes(r);
final Bytes target = Bytes.wrap(r);
final FindNeighborsPacketData packet = FindNeighborsPacketData.create(target);
final Bytes serialized = RLP.encode(packet::writeTo);
assertThat(serialized).isNotNull();
final FindNeighborsPacketData deserialized =
FindNeighborsPacketData.readFrom(RLP.input(serialized));
assertThat(deserialized.getTarget()).isEqualTo(target);
// Fuzziness: allow a skew of 2 seconds between the time the message was generated until the
// assertion.
assertThat(deserialized.getExpiration()).isCloseTo(PacketData.defaultExpiration(), offset(2L));
}
@Test
public void neighborsPacketData() {
final List<DiscoveryPeer> peers = helper.createDiscoveryPeers(5);
final NeighborsPacketData packet = NeighborsPacketData.create(peers);
final Bytes serialized = RLP.encode(packet::writeTo);
assertThat(serialized).isNotNull();
final NeighborsPacketData deserialized = NeighborsPacketData.readFrom(RLP.input(serialized));
assertThat(deserialized.getNodes()).isEqualTo(peers);
// Fuzziness: allow a skew of 2 seconds between the time the message was generated until the
// assertion.
assertThat(deserialized.getExpiration()).isCloseTo(PacketData.defaultExpiration(), offset(2L));
}
@Test
public void deserializeDifferentPacketData() {
final byte[] r = new byte[64];
new Random().nextBytes(r);
final Bytes target = Bytes.wrap(r);
final FindNeighborsPacketData packet = FindNeighborsPacketData.create(target);
final Bytes serialized = RLP.encode(packet::writeTo);
assertThat(serialized).isNotNull();
final RLPInput input = RLP.input(serialized);
assertThatThrownBy(() -> NeighborsPacketData.readFrom(input)).isInstanceOf(RLPException.class);
}
@Test
public void integrityCheckFailsUnmatchedHash() {
final byte[] r = new byte[64];
new Random().nextBytes(r);
final Bytes target = Bytes.wrap(r);
final NodeKey nodeKey = NodeKeyUtils.generate();
final FindNeighborsPacketData data = FindNeighborsPacketData.create(target);
final Packet packet = Packet.create(PacketType.FIND_NEIGHBORS, data, nodeKey);
final Bytes encoded = Bytes.wrapBuffer(packet.encode());
final MutableBytes garbled = encoded.mutableCopy();
final int i = garbled.size() - 1;
// Change one bit in the last byte, which belongs to the payload, hence the hash will not match
// any longer.
garbled.set(i, (byte) (garbled.get(i) + 0x01));
final Buffer input = Buffer.buffer(garbled.toArray());
assertThatThrownBy(() -> Packet.decode(input))
.isInstanceOf(PeerDiscoveryPacketDecodingException.class);
}
}

View File

@@ -27,10 +27,9 @@ import org.hyperledger.besu.ethereum.forkid.ForkId;
import org.hyperledger.besu.ethereum.forkid.ForkIdManager;
import org.hyperledger.besu.ethereum.p2p.config.DiscoveryConfiguration;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.MockPeerDiscoveryAgent;
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.ethereum.p2p.discovery.internal.packet.Packet;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.PacketPackage;
import org.hyperledger.besu.ethereum.p2p.peers.EnodeURLImpl;
import org.hyperledger.besu.ethereum.p2p.peers.Peer;
import org.hyperledger.besu.ethereum.p2p.permissions.PeerPermissions;
@@ -100,22 +99,34 @@ public class PeerDiscoveryTestHelper {
}
public Packet createPingPacket(
final MockPeerDiscoveryAgent fromAgent, final MockPeerDiscoveryAgent toAgent) {
return Packet.create(
PacketType.PING,
PingPacketData.create(
Optional.of(fromAgent.getAdvertisedPeer().get().getEndpoint()),
toAgent.getAdvertisedPeer().get().getEndpoint(),
UInt64.ONE),
fromAgent.getNodeKey());
final MockPeerDiscoveryAgent fromAgent,
final MockPeerDiscoveryAgent toAgent,
final PacketPackage packetPackage) {
return packetPackage
.packetFactory()
.create(
PacketType.PING,
packetPackage
.pingPacketDataFactory()
.create(
Optional.of(fromAgent.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, UInt64.ONE),
toAgent.getNodeKey());
public Packet createPongPacket(
final MockPeerDiscoveryAgent toAgent,
final Hash pingHash,
final PacketPackage packetPackage) {
return packetPackage
.packetFactory()
.create(
PacketType.PONG,
packetPackage
.pongPacketDataFactory()
.create(toAgent.getAdvertisedPeer().get().getEndpoint(), pingHash, UInt64.ONE),
toAgent.getNodeKey());
}
public AgentBuilder agentBuilder() {

View File

@@ -18,26 +18,36 @@ import static org.assertj.core.api.Assertions.assertThat;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.MockPeerDiscoveryAgent;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.Packet;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.DaggerPacketPackage;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.Packet;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.PacketPackage;
import java.util.Collections;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public class PeerDiscoveryTimestampsTest {
private final PeerDiscoveryTestHelper helper = new PeerDiscoveryTestHelper();
private PacketPackage packetPackage;
@BeforeEach
public void beforeTest() {
packetPackage = DaggerPacketPackage.create();
}
@Test
public void lastSeenAndFirstDiscoveredTimestampsUpdatedOnMessage() {
final MockPeerDiscoveryAgent agent = helper.startDiscoveryAgent(Collections.emptyList());
final MockPeerDiscoveryAgent testAgent = helper.startDiscoveryAgent();
final Packet testAgentPing = helper.createPingPacket(testAgent, agent);
final Packet testAgentPing = helper.createPingPacket(testAgent, agent, packetPackage);
helper.sendMessageBetweenAgents(testAgent, agent, testAgentPing);
final Packet agentPing = helper.createPingPacket(agent, testAgent);
final Packet agentPing = helper.createPingPacket(agent, testAgent, packetPackage);
helper.sendMessageBetweenAgents(agent, testAgent, agentPing);
final Packet pong = helper.createPongPacket(agent, Hash.hash(agentPing.getHash()));
final Packet pong =
helper.createPongPacket(agent, Hash.hash(agentPing.getHash()), packetPackage);
helper.sendMessageBetweenAgents(testAgent, agent, pong);
long firstDiscovered;

View File

@@ -1,69 +0,0 @@
/*
* 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.jupiter.api.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 long time = System.currentTimeMillis();
final BytesValueRLPOutput out = new BytesValueRLPOutput();
out.startList();
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 long time = System.currentTimeMillis();
final BytesValueRLPOutput out = new BytesValueRLPOutput();
out.startList();
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);
}
}

View File

@@ -1,228 +0,0 @@
/*
* 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.bytes.Bytes32;
import org.apache.tuweni.crypto.SECP256K1;
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.NodeRecord;
import org.ethereum.beacon.discovery.schema.NodeRecordFactory;
import org.ethereum.beacon.discovery.util.Functions;
import org.junit.jupiter.api.Test;
public class ENRResponsePacketDataTest {
@Test
public void serializeDeserialize() {
final Bytes requestHash = Bytes.fromHexStringLenient("0x1234");
final Bytes nodeId =
Bytes.fromHexString("a448f24c6d18e575453db13171562b71999873db5b286df957af199ec94617f7");
final SECP256K1.SecretKey privateKey =
SECP256K1.SecretKey.fromBytes(
Bytes32.fromHexString(
"b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291"));
NodeRecord nodeRecord =
NodeRecordFactory.DEFAULT.createFromValues(
UInt64.ONE,
new EnrField(EnrField.ID, IdentitySchema.V4),
new EnrField(EnrField.IP_V4, Bytes.fromHexString("0x7F000001")),
new EnrField(EnrField.IP_V6, Bytes.fromHexString("0x00000001")),
new EnrField(EnrField.UDP, 30303),
new EnrField(EnrField.UDP_V6, 30303),
new EnrField(EnrField.TCP, 8080),
new EnrField(EnrField.TCP_V6, 8080),
new EnrField(
EnrField.PKEY_SECP256K1,
Functions.deriveCompressedPublicKeyFromPrivate(privateKey)));
nodeRecord.sign(privateKey);
assertThat(nodeRecord.getNodeId()).isEqualTo(nodeId);
assertThat(nodeRecord.asEnr())
.isEqualTo(
"enr:-KS4QHWjNgmcnxf-dwC_paPSLEi1N-eW0Swoa4lLNOLe09UOEZ4qoDy3a8dl8wmprsu84JMFzvdc-WZrrqW"
+ "efDWpf3IBgmlkgnY0gmlwhH8AAAGDaXA2hAAAAAGJc2VjcDI1NmsxoQPKY0yuDUmstAHYpMa2_oxVtw0R"
+ "W_QAdpzBQA8yWM0xOIN0Y3CCH5CEdGNwNoIfkIN1ZHCCdl-EdWRwNoJ2Xw");
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 Bytes nodeId =
Bytes.fromHexString("a448f24c6d18e575453db13171562b71999873db5b286df957af199ec94617f7");
final SECP256K1.SecretKey privateKey =
SECP256K1.SecretKey.fromBytes(
Bytes32.fromHexString(
"b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291"));
NodeRecord nodeRecord =
NodeRecordFactory.DEFAULT.createFromValues(
UInt64.ONE,
new EnrField(EnrField.ID, IdentitySchema.V4),
new EnrField(EnrField.IP_V4, Bytes.fromHexString("0x7F000001")),
new EnrField(EnrField.UDP, 30303),
new EnrField(
EnrField.PKEY_SECP256K1,
Functions.deriveCompressedPublicKeyFromPrivate(privateKey)));
nodeRecord.sign(privateKey);
assertThat(nodeRecord.getNodeId()).isEqualTo(nodeId);
assertThat(nodeRecord.asEnr())
.isEqualTo(
"enr:-IS4QHCYrYZbAKWCBRlAy5zzaDZXJBGkcnh4MHcBFZntXNFrdvJjX04jRzjzCBOonrkTfj499SZuOh8R33L"
+ "s8RRcy5wBgmlkgnY0gmlwhH8AAAGJc2VjcDI1NmsxoQPKY0yuDUmstAHYpMa2_oxVtw0RW_QAdpzBQA8y"
+ "WM0xOIN1ZHCCdl8");
BytesValueRLPOutput out = new BytesValueRLPOutput();
out.startList();
out.writeBytes(requestHash);
out.writeRLPBytes(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 writeTo() {
final Bytes requestHash = Bytes.fromHexStringLenient("0x1234");
final Bytes nodeId =
Bytes.fromHexString("a448f24c6d18e575453db13171562b71999873db5b286df957af199ec94617f7");
final SECP256K1.SecretKey privateKey =
SECP256K1.SecretKey.fromBytes(
Bytes32.fromHexString(
"b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291"));
NodeRecord nodeRecord =
NodeRecordFactory.DEFAULT.createFromValues(
UInt64.ONE,
new EnrField(EnrField.ID, IdentitySchema.V4),
new EnrField(EnrField.IP_V4, Bytes.fromHexString("0x7F000001")),
new EnrField(EnrField.UDP, 30303),
new EnrField(
EnrField.PKEY_SECP256K1,
Functions.deriveCompressedPublicKeyFromPrivate(privateKey)));
nodeRecord.sign(privateKey);
assertThat(nodeRecord.getNodeId()).isEqualTo(nodeId);
assertThat(nodeRecord.asEnr())
.isEqualTo(
"enr:-IS4QHCYrYZbAKWCBRlAy5zzaDZXJBGkcnh4MHcBFZntXNFrdvJjX04jRzjzCBOonrkTfj499SZuOh8R33L"
+ "s8RRcy5wBgmlkgnY0gmlwhH8AAAGJc2VjcDI1NmsxoQPKY0yuDUmstAHYpMa2_oxVtw0RW_QAdpzBQA8y"
+ "WM0xOIN1ZHCCdl8");
BytesValueRLPOutput out = new BytesValueRLPOutput();
final ENRResponsePacketData packet = ENRResponsePacketData.create(requestHash, nodeRecord);
packet.writeTo(out);
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 Bytes nodeId =
Bytes.fromHexString("a448f24c6d18e575453db13171562b71999873db5b286df957af199ec94617f7");
final SECP256K1.SecretKey privateKey =
SECP256K1.SecretKey.fromBytes(
Bytes32.fromHexString(
"b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291"));
NodeRecord nodeRecord =
NodeRecordFactory.DEFAULT.createFromValues(
UInt64.ONE,
new EnrField(EnrField.ID, IdentitySchema.V4),
new EnrField(EnrField.IP_V4, Bytes.fromHexString("0x7F000001")),
new EnrField(EnrField.UDP, 30303),
new EnrField(
EnrField.PKEY_SECP256K1,
Functions.deriveCompressedPublicKeyFromPrivate(privateKey)),
new EnrField("foo", Bytes.fromHexString("0x1234")));
nodeRecord.sign(privateKey);
assertThat(nodeRecord.getNodeId()).isEqualTo(nodeId);
assertThat(nodeRecord.asEnr())
.isEqualTo(
"enr:-Iu4QDokK026ShDdi-PmzNgTr-oaQEslAfoLuphwEznSx0xsVwD0KZV1m7k4enZpf0aEQmCYWZOgD4kptYE"
+ "Fo2QKX28Bg2Zvb4ISNIJpZIJ2NIJpcIR_AAABiXNlY3AyNTZrMaEDymNMrg1JrLQB2KTGtv6MVbcNEVv0"
+ "AHacwUAPMljNMTiDdWRwgnZf");
BytesValueRLPOutput out = new BytesValueRLPOutput();
out.startList();
out.writeBytes(requestHash);
out.writeRLPBytes(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);
}
@Test
public void readFrom_invalidSignature() {
final Bytes requestHash = Bytes.fromHexStringLenient("0x1234");
final SECP256K1.SecretKey privateKey =
SECP256K1.SecretKey.fromBytes(
Bytes32.fromHexString(
"b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f292"));
NodeRecord nodeRecord =
NodeRecordFactory.DEFAULT.createFromValues(
UInt64.ONE,
new EnrField(EnrField.ID, IdentitySchema.V4),
new EnrField(EnrField.IP_V4, Bytes.fromHexString("0x7F000001")),
new EnrField(EnrField.UDP, 30303),
new EnrField(
EnrField.PKEY_SECP256K1,
Functions.deriveCompressedPublicKeyFromPrivate(privateKey)));
nodeRecord.sign(privateKey);
nodeRecord.set(EnrField.UDP, 1234);
BytesValueRLPOutput out = new BytesValueRLPOutput();
out.startList();
out.writeBytes(requestHash);
out.writeRLPBytes(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);
assertThat(deserialized.getEnr().isValid()).isFalse();
}
}

View File

@@ -1,80 +0,0 @@
/*
* 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.p2p.peers.Peer;
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.jupiter.api.Test;
public class FindNeighborsPacketDataTest {
@Test
public void serializeDeserialize() {
final long timeSec = Instant.now().getEpochSecond();
final Bytes target = Peer.randomId();
final FindNeighborsPacketData packet = FindNeighborsPacketData.create(target);
final Bytes serialized = RLP.encode(packet::writeTo);
final FindNeighborsPacketData deserialized =
FindNeighborsPacketData.readFrom(RLP.input(serialized));
assertThat(deserialized.getTarget()).isEqualTo(target);
assertThat(deserialized.getExpiration()).isGreaterThan(timeSec);
}
@Test
public void readFrom() {
final long time = System.currentTimeMillis();
final Bytes target = Peer.randomId();
BytesValueRLPOutput out = new BytesValueRLPOutput();
out.startList();
out.writeBytes(target);
out.writeLongScalar(time);
out.endList();
final Bytes encoded = out.encoded();
final FindNeighborsPacketData deserialized =
FindNeighborsPacketData.readFrom(RLP.input(encoded));
assertThat(deserialized.getTarget()).isEqualTo(target);
assertThat(deserialized.getExpiration()).isEqualTo(time);
}
@Test
public void readFrom_withExtraFields() {
final long time = System.currentTimeMillis();
final Bytes target = Peer.randomId();
BytesValueRLPOutput out = new BytesValueRLPOutput();
out.startList();
out.writeBytes(target);
out.writeLongScalar(time);
// Add extra list elements
out.writeLong(123L);
out.endList();
final Bytes encoded = out.encoded();
final FindNeighborsPacketData deserialized =
FindNeighborsPacketData.readFrom(RLP.input(encoded));
assertThat(deserialized.getTarget()).isEqualTo(target);
assertThat(deserialized.getExpiration()).isEqualTo(time);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright ConsenSys AG.
* Copyright contributors to Besu.
*
* 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
@@ -19,8 +19,21 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import org.hyperledger.besu.ethereum.p2p.discovery.DiscoveryPeer;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.DaggerPacketPackage;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.Packet;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.PacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.PacketPackage;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.findneighbors.FindNeighborsPacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.findneighbors.FindNeighborsPacketDataFactory;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.neighbors.NeighborsPacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.pong.PongPacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.validation.ExpiryValidator;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.validation.TargetValidator;
import org.hyperledger.besu.ethereum.p2p.peers.Peer;
import java.time.Clock;
import java.time.Instant;
import java.time.ZoneId;
import java.util.Arrays;
import java.util.Optional;
@@ -29,11 +42,13 @@ import org.apache.tuweni.bytes.Bytes32;
import org.apache.tuweni.units.bigints.UInt64;
class MockPacketDataFactory {
private static final PacketPackage PACKET_PACKAGE = DaggerPacketPackage.create();
static Packet mockNeighborsPacket(final DiscoveryPeer from, final DiscoveryPeer... neighbors) {
final Packet packet = mock(Packet.class);
final NeighborsPacketData packetData = NeighborsPacketData.create(Arrays.asList(neighbors));
final NeighborsPacketData packetData =
PACKET_PACKAGE.neighborsPacketDataFactory().create(Arrays.asList(neighbors));
when(packet.getPacketData(any())).thenReturn(Optional.of(packetData));
final Bytes id = from.getId();
@@ -48,7 +63,7 @@ class MockPacketDataFactory {
final Packet packet = mock(Packet.class);
final PongPacketData pongPacketData =
PongPacketData.create(from.getEndpoint(), pingHash, UInt64.ONE);
PACKET_PACKAGE.pongPacketDataFactory().create(from.getEndpoint(), pingHash, UInt64.ONE);
when(packet.getPacketData(any())).thenReturn(Optional.of(pongPacketData));
final Bytes id = from.getId();
when(packet.getNodeId()).thenReturn(id);
@@ -68,7 +83,12 @@ class MockPacketDataFactory {
Bytes.fromHexString(
"0x0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f40");
final FindNeighborsPacketData packetData = FindNeighborsPacketData.create(target, exparationMs);
Clock fixedClock = Clock.fixed(Instant.ofEpochSecond(exparationMs - 1), ZoneId.of("UTC"));
FindNeighborsPacketDataFactory findNeighborsPacketDataFactory =
new FindNeighborsPacketDataFactory(
new TargetValidator(), new ExpiryValidator(fixedClock), fixedClock);
final FindNeighborsPacketData packetData =
findNeighborsPacketDataFactory.create(target, exparationMs);
when(packet.getPacketData(any())).thenReturn(Optional.of(packetData));
final Bytes id = from.getId();
when(packet.getNodeId()).thenReturn(id);

View File

@@ -22,6 +22,11 @@ import org.hyperledger.besu.ethereum.forkid.ForkIdManager;
import org.hyperledger.besu.ethereum.p2p.config.DiscoveryConfiguration;
import org.hyperledger.besu.ethereum.p2p.discovery.DiscoveryPeer;
import org.hyperledger.besu.ethereum.p2p.discovery.PeerDiscoveryAgent;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.DaggerPacketPackage;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.Packet;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.PacketDeserializer;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.PacketPackage;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.PacketSerializer;
import org.hyperledger.besu.ethereum.p2p.permissions.PeerPermissions;
import org.hyperledger.besu.ethereum.p2p.rlpx.RlpxAgent;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
@@ -45,6 +50,8 @@ public class MockPeerDiscoveryAgent extends PeerDiscoveryAgent {
// The set of known agents operating on the network
private final Map<Bytes, MockPeerDiscoveryAgent> agentNetwork;
private final Deque<IncomingPacket> incomingPackets = new ArrayDeque<>();
private final PacketSerializer packetSerializer;
private final PacketDeserializer packetDeserializer;
private boolean isRunning = false;
public MockPeerDiscoveryAgent(
@@ -66,12 +73,15 @@ public class MockPeerDiscoveryAgent extends PeerDiscoveryAgent {
rlpxAgent,
new PeerTable(nodeKey.getPublicKey().getEncodedBytes()));
this.agentNetwork = agentNetwork;
PacketPackage packetPackage = DaggerPacketPackage.create();
this.packetSerializer = packetPackage.packetSerializer();
this.packetDeserializer = packetPackage.packetDeserializer();
}
public void processIncomingPacket(final MockPeerDiscoveryAgent fromAgent, final Packet packet) {
// Cycle packet through encode / decode to make clone of any data
// This ensures that any data passed between agents is not shared
final Packet packetClone = Packet.decode(packet.encode());
final Packet packetClone = packetDeserializer.decode(packetSerializer.encode(packet));
incomingPackets.add(new IncomingPacket(fromAgent, packetClone));
handleIncomingPacket(fromAgent.getAdvertisedPeer().get().getEndpoint(), packetClone);
}
@@ -155,7 +165,7 @@ public class MockPeerDiscoveryAgent extends PeerDiscoveryAgent {
LOG.warn(
"Sending to peer {} failed, packet: {}, stacktrace: {}",
peer,
wrapBuffer(packet.encode()),
wrapBuffer(packetSerializer.encode(packet)),
err);
}

View File

@@ -1,82 +0,0 @@
/*
* 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 static org.hyperledger.besu.ethereum.p2p.peers.PeerTestHelper.enode;
import org.hyperledger.besu.ethereum.p2p.discovery.DiscoveryPeer;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
import org.hyperledger.besu.ethereum.rlp.RLP;
import java.time.Instant;
import java.util.Arrays;
import java.util.List;
import org.apache.tuweni.bytes.Bytes;
import org.junit.jupiter.api.Test;
public class NeighborsPacketDataTest {
@Test
public void serializeDeserialize() {
final long timeSec = Instant.now().getEpochSecond();
final List<DiscoveryPeer> peers =
Arrays.asList(DiscoveryPeer.fromEnode(enode()), DiscoveryPeer.fromEnode(enode()));
final NeighborsPacketData packet = NeighborsPacketData.create(peers);
final Bytes serialized = RLP.encode(packet::writeTo);
final NeighborsPacketData deserialized = NeighborsPacketData.readFrom(RLP.input(serialized));
assertThat(deserialized.getNodes()).isEqualTo(peers);
assertThat(deserialized.getExpiration()).isGreaterThan(timeSec);
}
@Test
public void readFrom() {
final long timeSec = Instant.now().getEpochSecond();
final List<DiscoveryPeer> peers =
Arrays.asList(DiscoveryPeer.fromEnode(enode()), DiscoveryPeer.fromEnode(enode()));
BytesValueRLPOutput out = new BytesValueRLPOutput();
out.startList();
out.writeList(peers, DiscoveryPeer::writeTo);
out.writeLongScalar(timeSec);
out.endList();
Bytes encoded = out.encoded();
final NeighborsPacketData deserialized = NeighborsPacketData.readFrom(RLP.input(encoded));
assertThat(deserialized.getNodes()).isEqualTo(peers);
assertThat(deserialized.getExpiration()).isEqualTo(timeSec);
}
@Test
public void readFrom_extraFields() {
final long time = System.currentTimeMillis();
final List<DiscoveryPeer> peers =
Arrays.asList(DiscoveryPeer.fromEnode(enode()), DiscoveryPeer.fromEnode(enode()));
BytesValueRLPOutput out = new BytesValueRLPOutput();
out.startList();
out.writeList(peers, DiscoveryPeer::writeTo);
out.writeLongScalar(time);
out.endList();
Bytes encoded = out.encoded();
final NeighborsPacketData deserialized = NeighborsPacketData.readFrom(RLP.input(encoded));
assertThat(deserialized.getNodes()).isEqualTo(peers);
assertThat(deserialized.getExpiration()).isEqualTo(time);
}
}

View File

@@ -1,78 +0,0 @@
/*
* 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 static org.assertj.core.api.Assertions.assertThatThrownBy;
import org.hyperledger.besu.ethereum.p2p.discovery.Endpoint;
import org.hyperledger.besu.ethereum.p2p.discovery.PeerDiscoveryPacketDecodingException;
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.jupiter.api.Test;
public class PacketTest {
private static final String VALID_PONG_PACKET =
"3b3d56e4fcdcf714d6c29b0d521e9f4ec0dd50c73c0bbb9b44758a0a7e416ff30a3ea99de3e78a5cff82ad0a0b82030f78c6eff5a1b2a030276277b6e9b6ad7c35db1a6d5f83586a203b4537d82edc6b2820849a485e55aa06cfc680dc63c22d0002f3cb84b4b57a1a82040182765fa046896547d3b4259aa1a67bd26e7ec58ab4be650c5552ef0360caf9dae489d53b8460e5e7b603";
private static final String INVALID_SIGNATURE_PACKET =
"43f91d11b3338b4dbdf16db4f9fa25d7b4e2db81e6fd63f8f6884dfaea851e106f8f692c77169b387bde7c38832cf2d37a9b97b1553d07587ebe251ee21ee36e0ed54fd9218e3feea3bd13ca6982b25c204d5186e7ec5373ea664c91d42467b30102f3cb842f3ee37b82040e82765fa04139782abaccbc8fd290a7fde1ff138943fa9659f7bd67f97c97b09893d1ee8a84607806e108";
@Test
public void shouldDecodeValidPongPacket() {
final Packet packet = decode(VALID_PONG_PACKET);
final PongPacketData packetData = packet.getPacketData(PongPacketData.class).get();
assertThat(packet.getType()).isSameAs(PacketType.PONG);
assertThat(packetData.getTo())
.isEqualTo(new Endpoint("180.181.122.26", 1025, Optional.of(30303)));
assertThat(packetData.getPingHash())
.isEqualTo(
Bytes.fromHexString(
"0x46896547d3b4259aa1a67bd26e7ec58ab4be650c5552ef0360caf9dae489d53b"));
assertThat(packetData.getExpiration()).isEqualTo(1625679798);
assertThat(packetData.getEnrSeq().isPresent()).isTrue();
assertThat(packetData.getEnrSeq().get()).isEqualTo(UInt64.valueOf(3L));
assertThat(packet.getNodeId())
.isEqualTo(
Bytes.fromHexString(
"0x8cee393bcb969168690845905292da56f5eed661a2f332632c61be3d9171763825f8d520041d6dbf41b4d749a78ff73b6da286a5d7e88c52ac4dae26b9df4602"));
assertThat(packet.getHash())
.isEqualTo(
Bytes.fromHexString(
"0x3b3d56e4fcdcf714d6c29b0d521e9f4ec0dd50c73c0bbb9b44758a0a7e416ff3"));
}
@Test
public void shouldRoundTripPacket() {
final Packet packet = decode(VALID_PONG_PACKET);
assertThat(Hex.toHexString(packet.encode().getBytes())).isEqualTo(VALID_PONG_PACKET);
}
@Test
public void invalidSignatureShouldThrowPeerDiscoveryPacketDecodingException() {
assertThatThrownBy(() -> decode(INVALID_SIGNATURE_PACKET))
.isInstanceOf(PeerDiscoveryPacketDecodingException.class);
}
private Packet decode(final String hexData) {
return Packet.decode(Buffer.buffer(Hex.decode(hexData)));
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright ConsenSys AG.
* Copyright contributors to Besu.
*
* 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
@@ -39,6 +39,19 @@ import org.hyperledger.besu.ethereum.p2p.discovery.DiscoveryPeer;
import org.hyperledger.besu.ethereum.p2p.discovery.Endpoint;
import org.hyperledger.besu.ethereum.p2p.discovery.PeerDiscoveryStatus;
import org.hyperledger.besu.ethereum.p2p.discovery.PeerDiscoveryTestHelper;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.DaggerPacketPackage;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.Packet;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.PacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.PacketPackage;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.enrrequest.EnrRequestPacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.enrresponse.EnrResponsePacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.findneighbors.FindNeighborsPacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.neighbors.NeighborsPacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.ping.PingPacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.ping.PingPacketDataFactory;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.pong.PongPacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.validation.EndpointValidator;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.validation.ExpiryValidator;
import org.hyperledger.besu.ethereum.p2p.peers.EnodeURLImpl;
import org.hyperledger.besu.ethereum.p2p.peers.Peer;
import org.hyperledger.besu.ethereum.p2p.permissions.PeerPermissions;
@@ -47,7 +60,9 @@ import org.hyperledger.besu.ethereum.p2p.permissions.PeerPermissionsDenylist;
import org.hyperledger.besu.ethereum.p2p.rlpx.RlpxAgent;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import java.time.Clock;
import java.time.Instant;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -94,6 +109,7 @@ public class PeerDiscoveryControllerTest {
private NodeKey localNodeKey;
private final AtomicInteger counter = new AtomicInteger(1);
private final PeerDiscoveryTestHelper helper = new PeerDiscoveryTestHelper();
private PacketPackage packetPackage;
private static Long longDelayFunction(final Long prev) {
return 999999999L;
@@ -109,6 +125,7 @@ public class PeerDiscoveryControllerTest {
localNodeKey = nodeKeys.get(0);
localPeer = helper.createDiscoveryPeer(localNodeKey);
peerTable = new PeerTable(localPeer.getId());
packetPackage = DaggerPacketPackage.create();
}
@AfterEach
@@ -138,9 +155,14 @@ 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(
Optional.ofNullable(localPeer.getEndpoint()), peers.get(0).getEndpoint(), UInt64.ONE);
final Packet mockPacket = Packet.create(PacketType.PING, mockPing, nodeKeys.get(0));
packetPackage
.pingPacketDataFactory()
.create(
Optional.ofNullable(localPeer.getEndpoint()),
peers.get(0).getEndpoint(),
UInt64.ONE);
final Packet mockPacket =
packetPackage.packetFactory().create(PacketType.PING, mockPing, nodeKeys.get(0));
mockPingPacketCreation(mockPacket);
controller.start();
@@ -210,9 +232,14 @@ 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(
Optional.ofNullable(localPeer.getEndpoint()), peers.get(0).getEndpoint(), UInt64.ONE);
final Packet mockPacket = Packet.create(PacketType.PING, mockPing, nodeKeys.get(0));
packetPackage
.pingPacketDataFactory()
.create(
Optional.ofNullable(localPeer.getEndpoint()),
peers.get(0).getEndpoint(),
UInt64.ONE);
final Packet mockPacket =
packetPackage.packetFactory().create(PacketType.PING, mockPing, nodeKeys.get(0));
mockPingPacketCreation(mockPacket);
controller.start();
@@ -229,8 +256,11 @@ public class PeerDiscoveryControllerTest {
// Simulate a PONG message from peer 0.
final PongPacketData packetData =
PongPacketData.create(localPeer.getEndpoint(), mockPacket.getHash(), UInt64.ONE);
final Packet packet = Packet.create(PacketType.PONG, packetData, nodeKeys.get(0));
packetPackage
.pongPacketDataFactory()
.create(localPeer.getEndpoint(), mockPacket.getHash(), UInt64.ONE);
final Packet packet =
packetPackage.packetFactory().create(PacketType.PONG, packetData, nodeKeys.get(0));
controller.onMessage(packet, peers.get(0));
// Invoke timers again
@@ -265,9 +295,14 @@ 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(
Optional.ofNullable(localPeer.getEndpoint()), peers.get(0).getEndpoint(), UInt64.ONE);
final Packet mockPacket = Packet.create(PacketType.PING, mockPing, nodeKeys.get(0));
packetPackage
.pingPacketDataFactory()
.create(
Optional.ofNullable(localPeer.getEndpoint()),
peers.get(0).getEndpoint(),
UInt64.ONE);
final Packet mockPacket =
packetPackage.packetFactory().create(PacketType.PING, mockPing, nodeKeys.get(0));
mockPingPacketCreation(mockPacket);
controller.start();
@@ -301,9 +336,11 @@ public class PeerDiscoveryControllerTest {
// Setup ping to be sent to discoPeer
final List<NodeKey> nodeKeys = PeerDiscoveryTestHelper.generateNodeKeys(1);
final PingPacketData pingPacketData =
PingPacketData.create(
Optional.ofNullable(localEndpoint), discoPeer.getEndpoint(), UInt64.ONE);
final Packet discoPeerPing = Packet.create(PacketType.PING, pingPacketData, nodeKeys.get(0));
packetPackage
.pingPacketDataFactory()
.create(Optional.ofNullable(localEndpoint), discoPeer.getEndpoint(), UInt64.ONE);
final Packet discoPeerPing =
packetPackage.packetFactory().create(PacketType.PING, pingPacketData, nodeKeys.get(0));
mockPingPacketCreation(discoPeer, discoPeerPing);
controller.onMessage(discoPeerPing, discoPeer);
@@ -313,7 +350,7 @@ public class PeerDiscoveryControllerTest {
}
@Test
public void shouldNotRespondToExpiredPingRequest() {
public void shouldNotRespondToExpiredPingRequest() throws InterruptedException {
final List<DiscoveryPeer> peers = createPeersInLastBucket(localPeer, 1);
final DiscoveryPeer discoPeer = peers.get(0);
@@ -329,13 +366,18 @@ public class PeerDiscoveryControllerTest {
// Setup ping to be sent to discoPeer
final List<NodeKey> nodeKeys = PeerDiscoveryTestHelper.generateNodeKeys(1);
Clock fixedClock = Clock.fixed(Instant.ofEpochSecond(123), ZoneId.of("UTC"));
PingPacketDataFactory pingPacketDataFactory =
new PingPacketDataFactory(
new EndpointValidator(), new ExpiryValidator(fixedClock), fixedClock);
final PingPacketData pingPacketData =
PingPacketData.create(
pingPacketDataFactory.create(
Optional.ofNullable(localEndpoint),
discoPeer.getEndpoint(),
Instant.now().getEpochSecond() - PacketData.DEFAULT_EXPIRATION_PERIOD_SEC,
fixedClock.instant().getEpochSecond() + 1,
UInt64.ONE);
final Packet discoPeerPing = Packet.create(PacketType.PING, pingPacketData, nodeKeys.get(0));
final Packet discoPeerPing =
packetPackage.packetFactory().create(PacketType.PING, pingPacketData, nodeKeys.getFirst());
mockPingPacketCreation(discoPeer, discoPeerPing);
controller.onMessage(discoPeerPing, discoPeer);
@@ -362,9 +404,14 @@ 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(
Optional.ofNullable(localPeer.getEndpoint()), peers.get(0).getEndpoint(), UInt64.ONE);
final Packet mockPacket = Packet.create(PacketType.PING, mockPing, nodeKeys.get(0));
packetPackage
.pingPacketDataFactory()
.create(
Optional.ofNullable(localPeer.getEndpoint()),
peers.get(0).getEndpoint(),
UInt64.ONE);
final Packet mockPacket =
packetPackage.packetFactory().create(PacketType.PING, mockPing, nodeKeys.get(0));
mockPingPacketCreation(mockPacket);
controller.start();
@@ -378,8 +425,11 @@ 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(), UInt64.ONE);
final Packet packet0 = Packet.create(PacketType.PONG, packetData, nodeKeys.get(i));
packetPackage
.pongPacketDataFactory()
.create(localPeer.getEndpoint(), mockPacket.getHash(), UInt64.ONE);
final Packet packet0 =
packetPackage.packetFactory().create(PacketType.PONG, packetData, nodeKeys.get(i));
controller.onMessage(packet0, peers.get(i));
}
@@ -423,9 +473,14 @@ public class PeerDiscoveryControllerTest {
// when
// processing the PONG.
final PingPacketData mockPing =
PingPacketData.create(
Optional.ofNullable(localPeer.getEndpoint()), peers.get(0).getEndpoint(), UInt64.ONE);
final Packet mockPacket = Packet.create(PacketType.PING, mockPing, nodeKeys.get(0));
packetPackage
.pingPacketDataFactory()
.create(
Optional.ofNullable(localPeer.getEndpoint()),
peers.get(0).getEndpoint(),
UInt64.ONE);
final Packet mockPacket =
packetPackage.packetFactory().create(PacketType.PING, mockPing, nodeKeys.get(0));
mockPingPacketCreation(mockPacket);
controller.start();
@@ -438,8 +493,11 @@ public class PeerDiscoveryControllerTest {
// Send a PONG packet from peer 1, with an incorrect hash.
final PongPacketData packetData =
PongPacketData.create(localPeer.getEndpoint(), Bytes.fromHexString("1212"), UInt64.ONE);
final Packet packet = Packet.create(PacketType.PONG, packetData, nodeKeys.get(1));
packetPackage
.pongPacketDataFactory()
.create(localPeer.getEndpoint(), Bytes.fromHexString("1212"), UInt64.ONE);
final Packet packet =
packetPackage.packetFactory().create(PacketType.PONG, packetData, nodeKeys.get(1));
controller.onMessage(packet, peers.get(1));
// No FIND_NEIGHBORS packet was sent for peer 1.
@@ -472,9 +530,14 @@ 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 mockPing =
PingPacketData.create(
Optional.ofNullable(localPeer.getEndpoint()), peers.get(0).getEndpoint(), UInt64.ONE);
final Packet mockPacket = Packet.create(PacketType.PING, mockPing, nodeKeys.get(0));
packetPackage
.pingPacketDataFactory()
.create(
Optional.ofNullable(localPeer.getEndpoint()),
peers.get(0).getEndpoint(),
UInt64.ONE);
final Packet mockPacket =
packetPackage.packetFactory().create(PacketType.PING, mockPing, nodeKeys.get(0));
mockPingPacketCreation(mockPacket);
controller.setRetryDelayFunction(PeerDiscoveryControllerTest::longDelayFunction);
controller.start();
@@ -530,11 +593,14 @@ 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 mockPing =
PingPacketData.create(
Optional.ofNullable(localPeer.getEndpoint()),
peerThatTimesOut.getEndpoint(),
UInt64.ONE);
final Packet mockPacket = Packet.create(PacketType.PING, mockPing, nodeKey);
packetPackage
.pingPacketDataFactory()
.create(
Optional.ofNullable(localPeer.getEndpoint()),
peerThatTimesOut.getEndpoint(),
UInt64.ONE);
final Packet mockPacket =
packetPackage.packetFactory().create(PacketType.PING, mockPing, nodeKey);
mockPingPacketCreation(mockPacket);
controller.setRetryDelayFunction(PeerDiscoveryControllerTest::longDelayFunction);
controller.start();
@@ -574,8 +640,9 @@ public class PeerDiscoveryControllerTest {
private void respondWithPong(
final DiscoveryPeer discoveryPeer, final NodeKey nodeKey, final Bytes hash) {
final PongPacketData packetData0 =
PongPacketData.create(localPeer.getEndpoint(), hash, UInt64.ONE);
final Packet pongPacket0 = Packet.create(PacketType.PONG, packetData0, nodeKey);
packetPackage.pongPacketDataFactory().create(localPeer.getEndpoint(), hash, UInt64.ONE);
final Packet pongPacket0 =
packetPackage.packetFactory().create(PacketType.PONG, packetData0, nodeKey);
controller.onMessage(pongPacket0, discoveryPeer);
}
@@ -596,9 +663,14 @@ 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(
Optional.ofNullable(localPeer.getEndpoint()), peers.get(0).getEndpoint(), UInt64.ONE);
final Packet pingPacket = Packet.create(PacketType.PING, pingPacketData, nodeKeys.get(0));
packetPackage
.pingPacketDataFactory()
.create(
Optional.ofNullable(localPeer.getEndpoint()),
peers.get(0).getEndpoint(),
UInt64.ONE);
final Packet pingPacket =
packetPackage.packetFactory().create(PacketType.PING, pingPacketData, nodeKeys.get(0));
mockPingPacketCreation(pingPacket);
@@ -623,8 +695,11 @@ public class PeerDiscoveryControllerTest {
.hasSize(1);
final PongPacketData pongPacketData =
PongPacketData.create(localPeer.getEndpoint(), pingPacket.getHash(), UInt64.ONE);
final Packet pongPacket = Packet.create(PacketType.PONG, pongPacketData, nodeKeys.get(1));
packetPackage
.pongPacketDataFactory()
.create(localPeer.getEndpoint(), pingPacket.getHash(), UInt64.ONE);
final Packet pongPacket =
packetPackage.packetFactory().create(PacketType.PONG, pongPacketData, nodeKeys.get(1));
controller.onMessage(pongPacket, peers.get(1));
// Now after we got that pong we should have sent a find neighbours message...
@@ -633,9 +708,9 @@ public class PeerDiscoveryControllerTest {
// Simulate a NEIGHBORS message from peer[0] listing peer[2].
final NeighborsPacketData neighbors0 =
NeighborsPacketData.create(Collections.singletonList(peers.get(2)));
packetPackage.neighborsPacketDataFactory().create(Collections.singletonList(peers.get(2)));
final Packet neighborsPacket0 =
Packet.create(PacketType.NEIGHBORS, neighbors0, nodeKeys.get(0));
packetPackage.packetFactory().create(PacketType.NEIGHBORS, neighbors0, nodeKeys.get(0));
controller.onMessage(neighborsPacket0, peers.get(0));
// Assert that we're bonded with the third peer.
@@ -647,9 +722,9 @@ public class PeerDiscoveryControllerTest {
// Simulate bonding and neighbors packet from the second bootstrap peer, with peer[2] reported
// in the peer list.
final NeighborsPacketData neighbors1 =
NeighborsPacketData.create(Collections.singletonList(peers.get(2)));
packetPackage.neighborsPacketDataFactory().create(Collections.singletonList(peers.get(2)));
final Packet neighborsPacket1 =
Packet.create(PacketType.NEIGHBORS, neighbors1, nodeKeys.get(1));
packetPackage.packetFactory().create(PacketType.NEIGHBORS, neighbors1, nodeKeys.get(1));
controller.onMessage(neighborsPacket1, peers.get(1));
verify(outboundMessageHandler, times(1))
@@ -657,8 +732,11 @@ 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(), UInt64.ONE);
final Packet pongPacket2 = Packet.create(PacketType.PONG, packetData2, nodeKeys.get(2));
packetPackage
.pongPacketDataFactory()
.create(localPeer.getEndpoint(), pingPacket.getHash(), UInt64.ONE);
final Packet pongPacket2 =
packetPackage.packetFactory().create(PacketType.PONG, packetData2, nodeKeys.get(2));
controller.onMessage(pongPacket2, peers.get(2));
// Assert we're now bonded with peer[2].
@@ -741,9 +819,11 @@ public class PeerDiscoveryControllerTest {
// Setup ping to be sent to discoPeer
List<NodeKey> nodeKeys = PeerDiscoveryTestHelper.generateNodeKeys(1);
PingPacketData pingPacketData =
PingPacketData.create(
Optional.ofNullable(localEndpoint), discoPeer.getEndpoint(), UInt64.ONE);
final Packet discoPeerPing = Packet.create(PacketType.PING, pingPacketData, nodeKeys.get(0));
packetPackage
.pingPacketDataFactory()
.create(Optional.ofNullable(localEndpoint), discoPeer.getEndpoint(), UInt64.ONE);
final Packet discoPeerPing =
packetPackage.packetFactory().create(PacketType.PING, pingPacketData, nodeKeys.get(0));
mockPingPacketCreation(discoPeer, discoPeerPing);
controller.start();
@@ -760,17 +840,21 @@ public class PeerDiscoveryControllerTest {
// Setup ping to be sent to otherPeer after neighbors packet is received
nodeKeys = PeerDiscoveryTestHelper.generateNodeKeys(1);
pingPacketData =
PingPacketData.create(
Optional.ofNullable(localEndpoint), otherPeer.getEndpoint(), UInt64.ONE);
final Packet pingPacket = Packet.create(PacketType.PING, pingPacketData, nodeKeys.get(0));
packetPackage
.pingPacketDataFactory()
.create(Optional.ofNullable(localEndpoint), otherPeer.getEndpoint(), UInt64.ONE);
final Packet pingPacket =
packetPackage.packetFactory().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(
Optional.ofNullable(localEndpoint), otherPeer2.getEndpoint(), UInt64.ONE);
final Packet pingPacket2 = Packet.create(PacketType.PING, pingPacketData, nodeKeys.get(0));
packetPackage
.pingPacketDataFactory()
.create(Optional.ofNullable(localEndpoint), otherPeer2.getEndpoint(), UInt64.ONE);
final Packet pingPacket2 =
packetPackage.packetFactory().create(PacketType.PING, pingPacketData, nodeKeys.get(0));
mockPingPacketCreation(otherPeer2, pingPacket2);
final Packet neighborsPacket =
@@ -825,9 +909,11 @@ public class PeerDiscoveryControllerTest {
// Setup ping to be sent to discoPeer
List<NodeKey> nodeKeys = PeerDiscoveryTestHelper.generateNodeKeys(1);
PingPacketData pingPacketData =
PingPacketData.create(
Optional.ofNullable(localEndpoint), discoPeer.getEndpoint(), UInt64.ONE);
final Packet discoPeerPing = Packet.create(PacketType.PING, pingPacketData, nodeKeys.get(0));
packetPackage
.pingPacketDataFactory()
.create(Optional.ofNullable(localEndpoint), discoPeer.getEndpoint(), UInt64.ONE);
final Packet discoPeerPing =
packetPackage.packetFactory().create(PacketType.PING, pingPacketData, nodeKeys.get(0));
mockPingPacketCreation(discoPeer, discoPeerPing);
controller.start();
@@ -843,17 +929,21 @@ public class PeerDiscoveryControllerTest {
// Setup ping to be sent to otherPeer after neighbors packet is received
nodeKeys = PeerDiscoveryTestHelper.generateNodeKeys(1);
pingPacketData =
PingPacketData.create(
Optional.ofNullable(localEndpoint), otherPeer.getEndpoint(), UInt64.ONE);
final Packet pingPacket = Packet.create(PacketType.PING, pingPacketData, nodeKeys.get(0));
packetPackage
.pingPacketDataFactory()
.create(Optional.ofNullable(localEndpoint), otherPeer.getEndpoint(), UInt64.ONE);
final Packet pingPacket =
packetPackage.packetFactory().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(
Optional.ofNullable(localEndpoint), otherPeer2.getEndpoint(), UInt64.ONE);
final Packet pingPacket2 = Packet.create(PacketType.PING, pingPacketData, nodeKeys.get(0));
packetPackage
.pingPacketDataFactory()
.create(Optional.ofNullable(localEndpoint), otherPeer2.getEndpoint(), UInt64.ONE);
final Packet pingPacket2 =
packetPackage.packetFactory().create(PacketType.PING, pingPacketData, nodeKeys.get(0));
mockPingPacketCreation(otherPeer2, pingPacket2);
// Denylist peer
@@ -885,9 +975,11 @@ public class PeerDiscoveryControllerTest {
// Setup ping to be sent to discoPeer
final List<NodeKey> nodeKeys = PeerDiscoveryTestHelper.generateNodeKeys(1);
final PingPacketData pingPacketData =
PingPacketData.create(
Optional.ofNullable(localEndpoint), discoPeer.getEndpoint(), UInt64.ONE);
final Packet discoPeerPing = Packet.create(PacketType.PING, pingPacketData, nodeKeys.get(0));
packetPackage
.pingPacketDataFactory()
.create(Optional.ofNullable(localEndpoint), discoPeer.getEndpoint(), UInt64.ONE);
final Packet discoPeerPing =
packetPackage.packetFactory().create(PacketType.PING, pingPacketData, nodeKeys.get(0));
mockPingPacketCreation(discoPeer, discoPeerPing);
controller.start();
@@ -926,9 +1018,11 @@ public class PeerDiscoveryControllerTest {
// Setup ping to be sent to discoPeer
final List<NodeKey> nodeKeys = PeerDiscoveryTestHelper.generateNodeKeys(1);
final PingPacketData pingPacketData =
PingPacketData.create(
Optional.ofNullable(localEndpoint), discoPeer.getEndpoint(), UInt64.ONE);
final Packet discoPeerPing = Packet.create(PacketType.PING, pingPacketData, nodeKeys.get(0));
packetPackage
.pingPacketDataFactory()
.create(Optional.ofNullable(localEndpoint), discoPeer.getEndpoint(), UInt64.ONE);
final Packet discoPeerPing =
packetPackage.packetFactory().create(PacketType.PING, pingPacketData, nodeKeys.get(0));
mockPingPacketCreation(discoPeer, discoPeerPing);
controller.start();
@@ -949,7 +1043,7 @@ public class PeerDiscoveryControllerTest {
}
@Test
public void shouldNotRespondToExpiredNeighborsRequest() {
public void shouldNotRespondToExpiredNeighborsRequest() throws InterruptedException {
final List<DiscoveryPeer> peers = createPeersInLastBucket(localPeer, 1);
final DiscoveryPeer discoPeer = peers.get(0);
@@ -966,9 +1060,11 @@ public class PeerDiscoveryControllerTest {
// Setup ping to be sent to discoPeer
final List<NodeKey> nodeKeys = PeerDiscoveryTestHelper.generateNodeKeys(1);
final PingPacketData pingPacketData =
PingPacketData.create(
Optional.ofNullable(localEndpoint), discoPeer.getEndpoint(), UInt64.ONE);
final Packet discoPeerPing = Packet.create(PacketType.PING, pingPacketData, nodeKeys.get(0));
packetPackage
.pingPacketDataFactory()
.create(Optional.ofNullable(localEndpoint), discoPeer.getEndpoint(), UInt64.ONE);
final Packet discoPeerPing =
packetPackage.packetFactory().create(PacketType.PING, pingPacketData, nodeKeys.get(0));
mockPingPacketCreation(discoPeer, discoPeerPing);
controller.start();
@@ -982,8 +1078,7 @@ public class PeerDiscoveryControllerTest {
.send(eq(discoPeer), matchPacketOfType(PacketType.FIND_NEIGHBORS));
final Packet findNeighborsPacket =
MockPacketDataFactory.mockFindNeighborsPacket(
discoPeer, Instant.now().getEpochSecond() - PacketData.DEFAULT_EXPIRATION_PERIOD_SEC);
MockPacketDataFactory.mockFindNeighborsPacket(discoPeer, 123);
controller.onMessage(findNeighborsPacket, discoPeer);
verify(outboundMessageHandler, times(0))
@@ -1010,9 +1105,11 @@ public class PeerDiscoveryControllerTest {
// Setup ping to be sent to discoPeer
final List<NodeKey> nodeKeys = PeerDiscoveryTestHelper.generateNodeKeys(1);
final PingPacketData pingPacketData =
PingPacketData.create(
Optional.ofNullable(localEndpoint), discoPeer.getEndpoint(), UInt64.ONE);
final Packet discoPeerPing = Packet.create(PacketType.PING, pingPacketData, nodeKeys.get(0));
packetPackage
.pingPacketDataFactory()
.create(Optional.ofNullable(localEndpoint), discoPeer.getEndpoint(), UInt64.ONE);
final Packet discoPeerPing =
packetPackage.packetFactory().create(PacketType.PING, pingPacketData, nodeKeys.get(0));
mockPingPacketCreation(discoPeer, discoPeerPing);
controller.start();
@@ -1040,9 +1137,14 @@ 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(
Optional.ofNullable(localPeer.getEndpoint()), peers.get(0).getEndpoint(), UInt64.ONE);
final Packet pingPacket = Packet.create(PacketType.PING, pingPacketData, nodeKeys.get(0));
packetPackage
.pingPacketDataFactory()
.create(
Optional.ofNullable(localPeer.getEndpoint()),
peers.get(0).getEndpoint(),
UInt64.ONE);
final Packet pingPacket =
packetPackage.packetFactory().create(PacketType.PING, pingPacketData, nodeKeys.get(0));
final OutboundMessageHandler outboundMessageHandler = mock(OutboundMessageHandler.class);
controller =
@@ -1080,9 +1182,14 @@ 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(
Optional.ofNullable(localPeer.getEndpoint()), peers.get(0).getEndpoint(), UInt64.ONE);
final Packet pingPacket = Packet.create(PacketType.PING, pingPacketData, nodeKeys.get(0));
packetPackage
.pingPacketDataFactory()
.create(
Optional.ofNullable(localPeer.getEndpoint()),
peers.get(0).getEndpoint(),
UInt64.ONE);
final Packet pingPacket =
packetPackage.packetFactory().create(PacketType.PING, pingPacketData, nodeKeys.get(0));
mockPingPacketCreation(pingPacket);
controller.start();
@@ -1140,9 +1247,14 @@ 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(
Optional.ofNullable(localPeer.getEndpoint()), peers.get(0).getEndpoint(), UInt64.ONE);
final Packet pingPacket = Packet.create(PacketType.PING, pingPacketData, nodeKeys.get(0));
packetPackage
.pingPacketDataFactory()
.create(
Optional.ofNullable(localPeer.getEndpoint()),
peers.get(0).getEndpoint(),
UInt64.ONE);
final Packet pingPacket =
packetPackage.packetFactory().create(PacketType.PING, pingPacketData, nodeKeys.get(0));
final OutboundMessageHandler outboundMessageHandler = mock(OutboundMessageHandler.class);
controller =
@@ -1385,9 +1497,14 @@ 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(
Optional.ofNullable(localPeer.getEndpoint()), peers.get(0).getEndpoint(), UInt64.ONE);
final Packet pingPacket = Packet.create(PacketType.PING, pingPacketData, nodeKeys.get(0));
packetPackage
.pingPacketDataFactory()
.create(
Optional.ofNullable(localPeer.getEndpoint()),
peers.get(0).getEndpoint(),
UInt64.ONE);
final Packet pingPacket =
packetPackage.packetFactory().create(PacketType.PING, pingPacketData, nodeKeys.get(0));
final OutboundMessageHandler outboundMessageHandler = mock(OutboundMessageHandler.class);
controller =
@@ -1406,9 +1523,12 @@ public class PeerDiscoveryControllerTest {
.filteredOn(p -> p.getStatus() == PeerDiscoveryStatus.BONDED)
.contains(peers.get(0));
final ENRRequestPacketData enrRequestPacketData = ENRRequestPacketData.create();
final EnrRequestPacketData enrRequestPacketData =
packetPackage.enrRequestPacketDataFactory().create();
final Packet enrRequestPacket =
Packet.create(PacketType.ENR_REQUEST, enrRequestPacketData, nodeKeys.get(0));
packetPackage
.packetFactory()
.create(PacketType.ENR_REQUEST, enrRequestPacketData, nodeKeys.get(0));
controller.onMessage(enrRequestPacket, peers.get(0));
verify(outboundMessageHandler, times(1))
.send(any(), matchPacketOfType(PacketType.FIND_NEIGHBORS));
@@ -1427,9 +1547,12 @@ public class PeerDiscoveryControllerTest {
.outboundMessageHandler(outboundMessageHandler)
.build();
final ENRRequestPacketData enrRequestPacketData = ENRRequestPacketData.create();
final EnrRequestPacketData enrRequestPacketData =
packetPackage.enrRequestPacketDataFactory().create();
final Packet packet =
Packet.create(PacketType.ENR_REQUEST, enrRequestPacketData, nodeKeys.get(0));
packetPackage
.packetFactory()
.create(PacketType.ENR_REQUEST, enrRequestPacketData, nodeKeys.get(0));
controller.onMessage(packet, peers.get(0));
@@ -1454,22 +1577,34 @@ 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(
Optional.ofNullable(localPeer.getEndpoint()), peers.get(0).getEndpoint(), UInt64.ONE);
final Packet mockPacket = Packet.create(PacketType.PING, mockPing, nodeKeys.get(0));
packetPackage
.pingPacketDataFactory()
.create(
Optional.ofNullable(localPeer.getEndpoint()),
peers.get(0).getEndpoint(),
UInt64.ONE);
final Packet mockPacket =
packetPackage.packetFactory().create(PacketType.PING, mockPing, nodeKeys.get(0));
mockPingPacketCreation(mockPacket);
controller.start();
final PongPacketData pongRequestPacketData =
PongPacketData.create(localPeer.getEndpoint(), mockPacket.getHash(), UInt64.ONE);
packetPackage
.pongPacketDataFactory()
.create(localPeer.getEndpoint(), mockPacket.getHash(), UInt64.ONE);
final ENRRequestPacketData enrRequestPacketData = ENRRequestPacketData.create();
final EnrRequestPacketData enrRequestPacketData =
packetPackage.enrRequestPacketDataFactory().create();
final Packet enrPacket =
Packet.create(PacketType.ENR_REQUEST, enrRequestPacketData, nodeKeys.get(0));
packetPackage
.packetFactory()
.create(PacketType.ENR_REQUEST, enrRequestPacketData, nodeKeys.get(0));
final Packet pongPacket =
Packet.create(PacketType.PONG, pongRequestPacketData, nodeKeys.get(0));
packetPackage
.packetFactory()
.create(PacketType.PONG, pongRequestPacketData, nodeKeys.get(0));
controller.onMessage(enrPacket, peers.get(0));
verify(outboundMessageHandler, never()).send(any(), matchPacketOfType(PacketType.ENR_RESPONSE));
@@ -1508,22 +1643,34 @@ 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(
Optional.ofNullable(localPeer.getEndpoint()), peers.get(0).getEndpoint(), UInt64.ONE);
final Packet mockPacket = Packet.create(PacketType.PING, mockPing, nodeKeys.get(0));
packetPackage
.pingPacketDataFactory()
.create(
Optional.ofNullable(localPeer.getEndpoint()),
peers.get(0).getEndpoint(),
UInt64.ONE);
final Packet mockPacket =
packetPackage.packetFactory().create(PacketType.PING, mockPing, nodeKeys.get(0));
mockPingPacketCreation(mockPacket);
controller.start();
final PongPacketData pongRequestPacketData =
PongPacketData.create(localPeer.getEndpoint(), mockPacket.getHash(), UInt64.ONE);
packetPackage
.pongPacketDataFactory()
.create(localPeer.getEndpoint(), mockPacket.getHash(), UInt64.ONE);
final ENRRequestPacketData enrRequestPacketData = ENRRequestPacketData.create();
final EnrRequestPacketData enrRequestPacketData =
packetPackage.enrRequestPacketDataFactory().create();
final Packet enrPacket =
Packet.create(PacketType.ENR_REQUEST, enrRequestPacketData, nodeKeys.get(0));
packetPackage
.packetFactory()
.create(PacketType.ENR_REQUEST, enrRequestPacketData, nodeKeys.get(0));
final Packet pongPacket =
Packet.create(PacketType.PONG, pongRequestPacketData, nodeKeys.get(0));
packetPackage
.packetFactory()
.create(PacketType.PONG, pongRequestPacketData, nodeKeys.get(0));
controller.onMessage(enrPacket, peers.get(0));
enrs.cleanUp();
@@ -1606,28 +1753,37 @@ 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(
Optional.ofNullable(localPeer.getEndpoint()), sender.getEndpoint(), UInt64.ONE);
final Packet mockPacket = Packet.create(PacketType.PING, mockPing, nodeKeys.get(0));
packetPackage
.pingPacketDataFactory()
.create(Optional.ofNullable(localPeer.getEndpoint()), sender.getEndpoint(), UInt64.ONE);
final Packet mockPacket =
packetPackage.packetFactory().create(PacketType.PING, mockPing, nodeKeys.get(0));
mockPingPacketCreation(mockPacket);
controller.start();
final PongPacketData pongRequestPacketData =
PongPacketData.create(localPeer.getEndpoint(), mockPacket.getHash(), UInt64.ONE);
packetPackage
.pongPacketDataFactory()
.create(localPeer.getEndpoint(), mockPacket.getHash(), UInt64.ONE);
final Packet pongPacket =
Packet.create(PacketType.PONG, pongRequestPacketData, nodeKeys.get(0));
packetPackage
.packetFactory()
.create(PacketType.PONG, pongRequestPacketData, nodeKeys.get(0));
controller.onMessage(pongPacket, sender);
final NodeRecord nodeRecord = createNodeRecord(nodeKeys.get(0), sendForkId);
final ENRResponsePacketData enrResponsePacketData =
ENRResponsePacketData.create(
packetTypeBytesHashMap.get(PacketType.ENR_REQUEST), nodeRecord);
final EnrResponsePacketData enrResponsePacketData =
packetPackage
.enrResponsePacketDataFactory()
.create(packetTypeBytesHashMap.get(PacketType.ENR_REQUEST), nodeRecord);
final Packet enrPacket =
Packet.create(PacketType.ENR_RESPONSE, enrResponsePacketData, nodeKeys.get(0));
packetPackage
.packetFactory()
.create(PacketType.ENR_RESPONSE, enrResponsePacketData, nodeKeys.get(0));
return enrPacket;
}
@@ -1675,12 +1831,13 @@ public class PeerDiscoveryControllerTest {
return nodeRecord;
}
private static Packet mockPingPacket(final DiscoveryPeer from, final DiscoveryPeer to) {
private Packet mockPingPacket(final DiscoveryPeer from, final DiscoveryPeer to) {
final Packet packet = mock(Packet.class);
final PingPacketData pingPacketData =
PingPacketData.create(
Optional.ofNullable(from.getEndpoint()), to.getEndpoint(), UInt64.ONE);
packetPackage
.pingPacketDataFactory()
.create(Optional.ofNullable(from.getEndpoint()), to.getEndpoint(), UInt64.ONE);
when(packet.getPacketData(any())).thenReturn(Optional.of(pingPacketData));
final Bytes id = from.getId();
when(packet.getNodeId()).thenReturn(id);

View File

@@ -28,6 +28,12 @@ import org.hyperledger.besu.cryptoservices.NodeKey;
import org.hyperledger.besu.ethereum.p2p.discovery.DiscoveryPeer;
import org.hyperledger.besu.ethereum.p2p.discovery.PeerDiscoveryStatus;
import org.hyperledger.besu.ethereum.p2p.discovery.PeerDiscoveryTestHelper;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.DaggerPacketPackage;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.Packet;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.PacketPackage;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.findneighbors.FindNeighborsPacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.ping.PingPacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.pong.PongPacketData;
import org.hyperledger.besu.ethereum.p2p.rlpx.RlpxAgent;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
@@ -41,11 +47,18 @@ import java.util.function.Consumer;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.units.bigints.UInt64;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
public class PeerDiscoveryTableRefreshTest {
private final PeerDiscoveryTestHelper helper = new PeerDiscoveryTestHelper();
private PacketPackage packetPackage;
@BeforeEach
public void beforeTest() {
packetPackage = DaggerPacketPackage.create();
}
@Test
public void tableRefreshSingleNode() {
@@ -79,9 +92,12 @@ public class PeerDiscoveryTableRefreshTest {
controller.start();
final PingPacketData mockPing =
PingPacketData.create(
Optional.ofNullable(localPeer.getEndpoint()), remotePeer.getEndpoint(), UInt64.ONE);
final Packet mockPingPacket = Packet.create(PacketType.PING, mockPing, localKeyPair);
packetPackage
.pingPacketDataFactory()
.create(
Optional.ofNullable(localPeer.getEndpoint()), remotePeer.getEndpoint(), UInt64.ONE);
final Packet mockPingPacket =
packetPackage.packetFactory().create(PacketType.PING, mockPing, localKeyPair);
doAnswer(
invocation -> {
@@ -94,15 +110,21 @@ public class PeerDiscoveryTableRefreshTest {
// Send a PING, so as to add a Peer in the controller.
final PingPacketData ping =
PingPacketData.create(
Optional.ofNullable(remotePeer.getEndpoint()), localPeer.getEndpoint(), UInt64.ONE);
final Packet pingPacket = Packet.create(PacketType.PING, ping, remoteKeyPair);
packetPackage
.pingPacketDataFactory()
.create(
Optional.ofNullable(remotePeer.getEndpoint()), localPeer.getEndpoint(), UInt64.ONE);
final Packet pingPacket =
packetPackage.packetFactory().create(PacketType.PING, ping, remoteKeyPair);
controller.onMessage(pingPacket, remotePeer);
// Answer localPeer PING to complete bonding
final PongPacketData pong =
PongPacketData.create(localPeer.getEndpoint(), mockPingPacket.getHash(), UInt64.ONE);
final Packet pongPacket = Packet.create(PacketType.PONG, pong, remoteKeyPair);
packetPackage
.pongPacketDataFactory()
.create(localPeer.getEndpoint(), mockPingPacket.getHash(), UInt64.ONE);
final Packet pongPacket =
packetPackage.packetFactory().create(PacketType.PONG, pong, remoteKeyPair);
controller.onMessage(pongPacket, remotePeer);
// Wait until the controller has added the newly found peer.

View File

@@ -1,286 +0,0 @@
/*
* 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.p2p.discovery.Endpoint;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
import org.hyperledger.besu.ethereum.rlp.RLP;
import org.hyperledger.besu.ethereum.rlp.RLPOutput;
import java.time.Instant;
import java.util.Optional;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.units.bigints.UInt64;
import org.junit.jupiter.api.Test;
public class PingPacketDataTest {
@Test
public void serializeDeserialize() {
final long currentTimeSec = Instant.now().getEpochSecond();
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 UInt64 enrSeq = UInt64.ONE;
final PingPacketData packet = PingPacketData.create(Optional.of(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
public void readFrom() {
final int version = 4;
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();
out.writeIntScalar(version);
from.encodeStandalone(out);
to.encodeStandalone(out);
out.writeLongScalar(time);
out.writeLongScalar(enrSeq.toLong());
out.endList();
final Bytes serialized = out.encoded();
final PingPacketData deserialized = PingPacketData.readFrom(RLP.input(serialized));
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
public void handlesNullEnr() {
final int version = 4;
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 BytesValueRLPOutput out = new BytesValueRLPOutput();
out.startList();
out.writeIntScalar(version);
from.encodeStandalone(out);
to.encodeStandalone(out);
out.writeLongScalar(time);
out.endList();
final Bytes serialized = out.encoded();
final PingPacketData deserialized = PingPacketData.readFrom(RLP.input(serialized));
assertThat(deserialized.getFrom()).contains(from);
assertThat(deserialized.getTo()).isEqualTo(to);
assertThat(deserialized.getExpiration()).isEqualTo(time);
assertThat(deserialized.getEnrSeq().isPresent()).isFalse();
}
@Test
public void handlesLegacyENREncode() {
final int version = 4;
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();
out.writeIntScalar(version);
from.encodeStandalone(out);
to.encodeStandalone(out);
out.writeLongScalar(time);
out.writeBytes(enrSeq.toBytes());
out.endList();
final Bytes serialized = out.encoded();
final PingPacketData deserialized = PingPacketData.readFrom(RLP.input(serialized));
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
public void handleOptionalSourceIP() {
final Endpoint to = new Endpoint("127.0.0.2", 30303, Optional.empty());
final long time = System.currentTimeMillis();
final UInt64 enrSeq = UInt64.ONE;
final PingPacketData anon = PingPacketData.create(Optional.empty(), to, time, enrSeq);
final BytesValueRLPOutput out = new BytesValueRLPOutput();
anon.writeTo(out);
final Bytes serialized = out.encoded();
System.out.println(serialized.toHexString());
final PingPacketData deserialized = PingPacketData.readFrom(RLP.input(serialized));
assertThat(deserialized.getFrom()).isEmpty();
assertThat(deserialized.getTo()).isEqualTo(to);
assertThat(deserialized.getExpiration()).isEqualTo(time);
assertThat(deserialized.getEnrSeq().isPresent()).isTrue();
assertThat(deserialized.getEnrSeq().get()).isEqualTo(enrSeq);
}
@Test
public void handleSourcePortNullHost() {
final Endpoint to = new Endpoint("127.0.0.2", 30303, Optional.empty());
final BytesValueRLPOutput out = new BytesValueRLPOutput();
final UInt64 enrSeq = UInt64.MAX_VALUE;
final long time = System.currentTimeMillis();
out.startList();
out.writeIntScalar(4);
((RLPOutput) out).startList();
out.writeNull();
out.writeIntScalar(30303);
out.writeNull();
((RLPOutput) out).endList();
to.encodeStandalone(out);
out.writeLongScalar(time);
out.writeBigIntegerScalar(enrSeq.toBigInteger());
out.endList();
final Bytes serialized = out.encoded();
final PingPacketData deserialized = PingPacketData.readFrom(RLP.input(serialized));
assertThat(deserialized.getFrom()).isPresent();
assertThat(deserialized.getFrom().get().getUdpPort()).isEqualTo(30303);
assertThat(deserialized.getFrom().get().getHost().isEmpty()).isTrue();
assertThat(deserialized.getFrom().get().getTcpPort().isEmpty()).isTrue();
assertThat(deserialized.getTo()).isEqualTo(to);
assertThat(deserialized.getExpiration()).isEqualTo(time);
assertThat(deserialized.getEnrSeq().isPresent()).isTrue();
assertThat(deserialized.getEnrSeq().get()).isEqualTo(enrSeq);
}
@Test
public void legacyHandlesScalarEncode() {
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 UInt64 enrSeq = UInt64.MAX_VALUE;
final PingPacketData ping = PingPacketData.create(Optional.of(from), to, enrSeq);
final BytesValueRLPOutput out = new BytesValueRLPOutput();
ping.writeTo(out);
final PingPacketData legacyPing = PingPacketData.legacyReadFrom(RLP.input(out.encoded()));
assertThat(legacyPing.getFrom().get()).isEqualTo(from);
assertThat(legacyPing.getTo()).isEqualTo(to);
assertThat(legacyPing.getEnrSeq().isPresent()).isTrue();
assertThat(legacyPing.getEnrSeq().get()).isEqualTo(enrSeq);
}
@Test
public void readFrom_withExtraFields() {
final int version = 4;
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();
out.writeIntScalar(version);
from.encodeStandalone(out);
to.encodeStandalone(out);
out.writeLongScalar(time);
out.writeLongScalar(enrSeq.toLong());
// Add extra field
out.writeLongScalar(11);
out.endList();
final Bytes serialized = out.encoded();
final PingPacketData deserialized = PingPacketData.readFrom(RLP.input(serialized));
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
public void readFrom_unknownVersion() {
final int version = 99;
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();
out.writeIntScalar(version);
from.encodeStandalone(out);
to.encodeStandalone(out);
out.writeLongScalar(time);
out.writeLongScalar(enrSeq.toLong());
out.endList();
final Bytes serialized = out.encoded();
final PingPacketData deserialized = PingPacketData.readFrom(RLP.input(serialized));
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
public void readFrom_lowPortValues() {
final int version = 4;
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();
out.writeIntScalar(version);
from.encodeStandalone(out);
to.encodeStandalone(out);
out.writeLongScalar(time);
out.writeLongScalar(enrSeq.toLong());
out.endList();
final Bytes serialized = out.encoded();
final PingPacketData deserialized = PingPacketData.readFrom(RLP.input(serialized));
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);
}
}

View File

@@ -1,169 +0,0 @@
/*
* 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.p2p.discovery.Endpoint;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
import org.hyperledger.besu.ethereum.rlp.RLP;
import java.time.Instant;
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.jupiter.api.Test;
public class PongPacketDataTest {
@Test
public void serializeDeserialize() {
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, 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
public void readFrom() {
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.writeLongScalar(enrSeq.toLong());
out.endList();
final Bytes encoded = out.encoded();
final PongPacketData deserialized = PongPacketData.readFrom(RLP.input(encoded));
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
public void handlesLegacyENREncode() {
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();
final PongPacketData deserialized = PongPacketData.readFrom(RLP.input(encoded));
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
public void legacyHandlesScalar() {
final Endpoint to = new Endpoint("127.0.0.2", 30303, Optional.empty());
final UInt64 enrSeq = UInt64.MAX_VALUE;
final Bytes32 hash = Bytes32.fromHexStringLenient("0x1234");
final PongPacketData pong = PongPacketData.create(to, hash, enrSeq);
final BytesValueRLPOutput out = new BytesValueRLPOutput();
pong.writeTo(out);
final PongPacketData legacyPong = PongPacketData.legacyReadFrom(RLP.input(out.encoded()));
assertThat(legacyPong.getTo()).isEqualTo(to);
assertThat(legacyPong.getPingHash()).isEqualTo(hash);
assertThat(legacyPong.getEnrSeq().isPresent()).isTrue();
assertThat(legacyPong.getEnrSeq().get()).isEqualTo(enrSeq);
}
@Test
public void readFrom_withExtraFields() {
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.writeLongScalar(enrSeq.toLong());
// Add random fields
out.writeLong(1234L);
out.endList();
final Bytes encoded = out.encoded();
final PongPacketData deserialized = PongPacketData.readFrom(RLP.input(encoded));
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
public void readFrom_fixedWidthSeq() {
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.writeLongScalar(enrSeq.toLong());
// Add random fields
out.writeLong(1234L);
out.endList();
final Bytes encoded = out.encoded();
final PongPacketData deserialized = PongPacketData.readFrom(RLP.input(encoded));
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);
}
}

View File

@@ -0,0 +1,269 @@
/*
* Copyright contributors to Besu.
*
* 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.packet;
import org.hyperledger.besu.crypto.SECPSignature;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.ethereum.p2p.discovery.Endpoint;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.PacketType;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.enrrequest.EnrRequestPacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.enrrequest.EnrRequestPacketDataFactory;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.enrrequest.EnrRequestPacketDataRlpReader;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.enrrequest.EnrRequestPacketDataRlpWriter;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.enrresponse.EnrResponsePacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.enrresponse.EnrResponsePacketDataFactory;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.enrresponse.EnrResponsePacketDataRlpReader;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.enrresponse.EnrResponsePacketDataRlpWriter;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.findneighbors.FindNeighborsPacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.findneighbors.FindNeighborsPacketDataFactory;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.findneighbors.FindNeighborsPacketDataRlpReader;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.findneighbors.FindNeighborsPacketDataRlpWriter;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.neighbors.NeighborsPacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.neighbors.NeighborsPacketDataFactory;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.neighbors.NeighborsPacketDataRlpReader;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.neighbors.NeighborsPacketDataRlpWriter;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.ping.PingPacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.ping.PingPacketDataFactory;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.ping.PingPacketDataRlpReader;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.ping.PingPacketDataRlpWriter;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.pong.PongPacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.pong.PongPacketDataFactory;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.pong.PongPacketDataRlpReader;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.pong.PongPacketDataRlpWriter;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.validation.DiscoveryPeersValidator;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.validation.EndpointValidator;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.validation.ExpiryValidator;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.validation.NodeRecordValidator;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.validation.RequestHashValidator;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.validation.TargetValidator;
import java.math.BigInteger;
import java.time.Clock;
import java.time.Instant;
import java.time.ZoneId;
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.ethereum.beacon.discovery.schema.IdentitySchemaInterpreter;
import org.ethereum.beacon.discovery.schema.NodeRecordFactory;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public class PacketDeserializerTest {
private final Clock clock = Clock.fixed(Instant.ofEpochSecond(123), ZoneId.of("UTC"));
private PacketDeserializer packetDeserializer;
@BeforeEach
public void beforeTest() {
PingPacketDataRlpReader pingPacketDataRlpReader =
new PingPacketDataRlpReader(
new PingPacketDataFactory(new EndpointValidator(), new ExpiryValidator(clock), clock));
PongPacketDataRlpReader pongPacketDataRlpReader =
new PongPacketDataRlpReader(new PongPacketDataFactory(new ExpiryValidator(clock), clock));
FindNeighborsPacketDataRlpReader findNeighborsPacketDataRlpReader =
new FindNeighborsPacketDataRlpReader(
new FindNeighborsPacketDataFactory(
new TargetValidator(), new ExpiryValidator(clock), clock));
NeighborsPacketDataRlpReader neighborsPacketDataRlpReader =
new NeighborsPacketDataRlpReader(
new NeighborsPacketDataFactory(
new DiscoveryPeersValidator(), new ExpiryValidator(clock), clock));
EnrRequestPacketDataRlpReader enrRequestPacketDataRlpReader =
new EnrRequestPacketDataRlpReader(
new EnrRequestPacketDataFactory(new ExpiryValidator(clock), clock));
EnrResponsePacketDataRlpReader enrResponsePacketDataRlpReader =
new EnrResponsePacketDataRlpReader(
NodeRecordFactory.DEFAULT,
new EnrResponsePacketDataFactory(
new RequestHashValidator(), new NodeRecordValidator()));
PacketFactory packetFactory =
new PacketFactory(
new PingPacketDataRlpWriter(),
new PongPacketDataRlpWriter(),
new FindNeighborsPacketDataRlpWriter(),
new NeighborsPacketDataRlpWriter(),
new EnrRequestPacketDataRlpWriter(),
new EnrResponsePacketDataRlpWriter(),
SignatureAlgorithmFactory.getInstance(),
new PacketSignatureEncoder());
packetDeserializer =
new PacketDeserializer(
pingPacketDataRlpReader,
pongPacketDataRlpReader,
findNeighborsPacketDataRlpReader,
neighborsPacketDataRlpReader,
enrRequestPacketDataRlpReader,
enrResponsePacketDataRlpReader,
packetFactory);
}
@Test
public void testDecodeForPingPacket() {
Buffer buffer = Buffer.buffer();
String packetHex =
"0xc68a00f14b2e91d2592005ca4e77247c8e0627fc5a6e6d942bd9e1af41d2f5b9000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020001d105c9840a00000182765f808201c8820315";
Bytes.fromHexString(packetHex).appendTo(buffer);
Packet packet = packetDeserializer.decode(buffer);
Assertions.assertNotNull(packet);
Assertions.assertEquals(PacketType.PING, packet.getType());
Assertions.assertEquals(
"0xc68a00f14b2e91d2592005ca4e77247c8e0627fc5a6e6d942bd9e1af41d2f5b9",
packet.getHash().toHexString());
Assertions.assertEquals(
SECPSignature.create(BigInteger.ONE, BigInteger.TWO, (byte) 0x00, BigInteger.valueOf(4)),
packet.getSignature());
PingPacketData actualPacketData = packet.getPacketData(PingPacketData.class).orElseThrow();
Assertions.assertFalse(actualPacketData.getFrom().isPresent());
Assertions.assertEquals(
new Endpoint("10.0.0.1", 30303, Optional.empty()), actualPacketData.getTo());
Assertions.assertEquals(456, actualPacketData.getExpiration());
Assertions.assertEquals(Optional.of(UInt64.valueOf(789)), actualPacketData.getEnrSeq());
}
@Test
public void testDecodeForPongPacket() {
Buffer buffer = Buffer.buffer();
String packetHex =
"0x21206a30fabe5d8fe7c4896bb25f51a9f2a22260da9164d21645fb482439736c000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020002d3c9840a00000182765f808201238201c8820315";
Bytes.fromHexString(packetHex).appendTo(buffer);
Packet packet = packetDeserializer.decode(buffer);
Assertions.assertNotNull(packet);
Assertions.assertEquals(PacketType.PONG, packet.getType());
Assertions.assertEquals(
"0x21206a30fabe5d8fe7c4896bb25f51a9f2a22260da9164d21645fb482439736c",
packet.getHash().toHexString());
Assertions.assertEquals(
SECPSignature.create(BigInteger.ONE, BigInteger.TWO, (byte) 0x00, BigInteger.valueOf(4)),
packet.getSignature());
PongPacketData actualPacketData = packet.getPacketData(PongPacketData.class).orElseThrow();
Assertions.assertEquals(
new Endpoint("10.0.0.1", 30303, Optional.empty()), actualPacketData.getTo());
Assertions.assertEquals(Bytes.fromHexString("0x0123"), actualPacketData.getPingHash());
Assertions.assertEquals(456, actualPacketData.getExpiration());
Assertions.assertEquals(Optional.of(UInt64.valueOf(789)), actualPacketData.getEnrSeq());
}
@Test
public void testDecodeForFindNeighborsPacket() {
Buffer buffer = Buffer.buffer();
String packetHex =
"0xe51a3c97707983d13de6a7aa7c54cc67e8a8f964145bb86cd0af7ee6dd6a196b000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020003f845b840cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd8201c8";
Bytes.fromHexString(packetHex).appendTo(buffer);
Packet packet = packetDeserializer.decode(buffer);
Assertions.assertNotNull(packet);
Assertions.assertEquals(PacketType.FIND_NEIGHBORS, packet.getType());
Assertions.assertEquals(
"0xe51a3c97707983d13de6a7aa7c54cc67e8a8f964145bb86cd0af7ee6dd6a196b",
packet.getHash().toHexString());
Assertions.assertEquals(
SECPSignature.create(BigInteger.ONE, BigInteger.TWO, (byte) 0x00, BigInteger.valueOf(4)),
packet.getSignature());
FindNeighborsPacketData actualPacketData =
packet.getPacketData(FindNeighborsPacketData.class).orElseThrow();
Assertions.assertEquals(Bytes.repeat((byte) 0xcd, 64), actualPacketData.getTarget());
Assertions.assertEquals(456, actualPacketData.getExpiration());
}
@Test
public void testDecodeForNeighborsPacket() {
Buffer buffer = Buffer.buffer();
String packetHex =
"0xc6dce7f54086fb7537a9c2793fd7938b057888442109304ea463017c6265e743000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020004f852f84df84b840a00000182765f80b840989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898988201c8";
Bytes.fromHexString(packetHex).appendTo(buffer);
Packet packet = packetDeserializer.decode(buffer);
Assertions.assertNotNull(packet);
Assertions.assertEquals(PacketType.NEIGHBORS, packet.getType());
Assertions.assertEquals(
"0xc6dce7f54086fb7537a9c2793fd7938b057888442109304ea463017c6265e743",
packet.getHash().toHexString());
Assertions.assertEquals(
SECPSignature.create(BigInteger.ONE, BigInteger.TWO, (byte) 0x00, BigInteger.valueOf(4)),
packet.getSignature());
NeighborsPacketData actualPacketData =
packet.getPacketData(NeighborsPacketData.class).orElseThrow();
Assertions.assertEquals(1, actualPacketData.getNodes().size());
Assertions.assertEquals(
Bytes.repeat((byte) 0x98, 64), actualPacketData.getNodes().getFirst().getId());
Assertions.assertEquals(
new Endpoint("10.0.0.1", 30303, Optional.empty()),
actualPacketData.getNodes().getFirst().getEndpoint());
Assertions.assertEquals(456, actualPacketData.getExpiration());
}
@Test
public void testDecodeForEnrRequestPacket() {
Buffer buffer = Buffer.buffer();
String packetHex =
"0xfde48f75a7340fafea894eb45f7badecd9705227902c5b48928ad3c57b1963e7000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020005c38201c8";
Bytes.fromHexString(packetHex).appendTo(buffer);
Packet packet = packetDeserializer.decode(buffer);
Assertions.assertNotNull(packet);
Assertions.assertEquals(PacketType.ENR_REQUEST, packet.getType());
Assertions.assertEquals(
"0xfde48f75a7340fafea894eb45f7badecd9705227902c5b48928ad3c57b1963e7",
packet.getHash().toHexString());
Assertions.assertEquals(
SECPSignature.create(BigInteger.ONE, BigInteger.TWO, (byte) 0x00, BigInteger.valueOf(4)),
packet.getSignature());
EnrRequestPacketData actualPacketData =
packet.getPacketData(EnrRequestPacketData.class).orElseThrow();
Assertions.assertEquals(456, actualPacketData.getExpiration());
}
@Test
public void testDecodeForEnrResponsePacket() {
Buffer buffer = Buffer.buffer();
String packetHex =
"0x9d0b57ce920728f26211e7784f626bf9a881e7e942c7243160d535a3760e8848000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020006f870821234f86bb860000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000820237826964827634";
Bytes.fromHexString(packetHex).appendTo(buffer);
Packet packet = packetDeserializer.decode(buffer);
Assertions.assertNotNull(packet);
Assertions.assertEquals(PacketType.ENR_RESPONSE, packet.getType());
Assertions.assertEquals(
"0x9d0b57ce920728f26211e7784f626bf9a881e7e942c7243160d535a3760e8848",
packet.getHash().toHexString());
Assertions.assertEquals(
SECPSignature.create(BigInteger.ONE, BigInteger.TWO, (byte) 0x00, BigInteger.valueOf(4)),
packet.getSignature());
EnrResponsePacketData actualPacketData =
packet.getPacketData(EnrResponsePacketData.class).orElseThrow();
Assertions.assertEquals(Bytes.fromHexString("0x1234"), actualPacketData.getRequestHash());
Assertions.assertEquals(
IdentitySchemaInterpreter.V4.getScheme(), actualPacketData.getEnr().getIdentityScheme());
Assertions.assertEquals(UInt64.valueOf(567), actualPacketData.getEnr().getSeq());
}
}

View File

@@ -0,0 +1,501 @@
/*
* Copyright contributors to Besu.
*
* 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.packet;
import org.hyperledger.besu.crypto.Hash;
import org.hyperledger.besu.crypto.SECPPublicKey;
import org.hyperledger.besu.crypto.SECPSignature;
import org.hyperledger.besu.crypto.SignatureAlgorithm;
import org.hyperledger.besu.cryptoservices.NodeKey;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.PacketType;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.enrrequest.EnrRequestPacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.enrrequest.EnrRequestPacketDataRlpWriter;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.enrresponse.EnrResponsePacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.enrresponse.EnrResponsePacketDataRlpWriter;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.findneighbors.FindNeighborsPacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.findneighbors.FindNeighborsPacketDataRlpWriter;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.neighbors.NeighborsPacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.neighbors.NeighborsPacketDataRlpWriter;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.ping.PingPacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.ping.PingPacketDataRlpWriter;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.pong.PongPacketData;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.pong.PongPacketDataRlpWriter;
import org.hyperledger.besu.ethereum.rlp.RLPOutput;
import java.lang.reflect.Field;
import java.util.Optional;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.platform.commons.util.ReflectionUtils;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
public class PacketFactoryTest {
private @Mock PingPacketDataRlpWriter pingPacketDataRlpWriter;
private @Mock PongPacketDataRlpWriter pongPacketDataRlpWriter;
private @Mock FindNeighborsPacketDataRlpWriter findNeighborsPacketDataRlpWriter;
private @Mock NeighborsPacketDataRlpWriter neighborsPacketDataRlpWriter;
private @Mock EnrRequestPacketDataRlpWriter enrRequestPacketDataRlpWriter;
private @Mock EnrResponsePacketDataRlpWriter enrResponsePacketDataRlpWriter;
private @Mock SignatureAlgorithm signatureAlgorithm;
private @Mock PacketSignatureEncoder packetSignatureEncoder;
private PacketFactory factory;
@BeforeEach
public void beforeTest() {
factory =
new PacketFactory(
pingPacketDataRlpWriter,
pongPacketDataRlpWriter,
findNeighborsPacketDataRlpWriter,
neighborsPacketDataRlpWriter,
enrRequestPacketDataRlpWriter,
enrResponsePacketDataRlpWriter,
signatureAlgorithm,
packetSignatureEncoder);
}
@Test
public void testCreatePingPacketWithNodeKey() throws IllegalAccessException {
final PingPacketData packetData = Mockito.mock(PingPacketData.class);
final NodeKey nodeKey = Mockito.mock(NodeKey.class);
final SECPSignature secpSignature = Mockito.mock(SECPSignature.class);
final SECPPublicKey secpPublicKey = Mockito.mock(SECPPublicKey.class);
Mockito.doAnswer(
(invocationOnMock) -> {
RLPOutput out = invocationOnMock.getArgument(1, RLPOutput.class);
out.startList();
out.writeBytes(Bytes.repeat((byte) 0xbb, 32));
out.endList();
return null;
})
.when(pingPacketDataRlpWriter)
.writeTo(Mockito.eq(packetData), Mockito.any(RLPOutput.class));
Mockito.when(nodeKey.sign(Mockito.any(Bytes32.class))).thenReturn(secpSignature);
Mockito.when(packetSignatureEncoder.encodeSignature(secpSignature))
.thenReturn(Bytes.fromHexString("0xeeeeeeeeeeeeeeee"));
Mockito.when(nodeKey.getPublicKey()).thenReturn(secpPublicKey);
Packet packet = factory.create(PacketType.PING, packetData, nodeKey);
Mockito.verify(pingPacketDataRlpWriter)
.writeTo(Mockito.eq(packetData), Mockito.any(RLPOutput.class));
ArgumentCaptor<Bytes32> typeAndDataBytesHashCaptor = ArgumentCaptor.forClass(Bytes32.class);
Mockito.verify(nodeKey).sign(typeAndDataBytesHashCaptor.capture());
Mockito.verify(packetSignatureEncoder).encodeSignature(secpSignature);
Mockito.verify(nodeKey).getPublicKey();
Mockito.verifyNoInteractions(
pongPacketDataRlpWriter,
findNeighborsPacketDataRlpWriter,
neighborsPacketDataRlpWriter,
enrRequestPacketDataRlpWriter,
enrResponsePacketDataRlpWriter,
signatureAlgorithm);
Assertions.assertEquals(
"0x1d82b79365133086f85611e56d5cf9bf91c64d901f38e415735c7b6ec3cbb3fe",
typeAndDataBytesHashCaptor.getValue().toHexString());
Assertions.assertNotNull(packet);
Assertions.assertEquals(PacketType.PING, packet.getType());
Assertions.assertEquals(packetData, packet.getPacketData());
Assertions.assertEquals(
"0x771d602d8630675967b911a12988f07ec2697902268ad412f9e1842ee32be1a6",
packet.getHash().toHexString());
Assertions.assertEquals(secpSignature, packet.getSignature());
Field publicKeyField =
ReflectionUtils.findFields(
Packet.class,
(f) -> f.getName().equals("publicKey"),
ReflectionUtils.HierarchyTraversalMode.TOP_DOWN)
.getFirst();
publicKeyField.setAccessible(true);
Assertions.assertEquals(secpPublicKey, publicKeyField.get(packet));
publicKeyField.setAccessible(false);
}
@Test
public void testCreatePongPacketWithNodeKey() throws IllegalAccessException {
final PongPacketData packetData = Mockito.mock(PongPacketData.class);
final NodeKey nodeKey = Mockito.mock(NodeKey.class);
final SECPSignature secpSignature = Mockito.mock(SECPSignature.class);
final SECPPublicKey secpPublicKey = Mockito.mock(SECPPublicKey.class);
Mockito.doAnswer(
(invocationOnMock) -> {
RLPOutput out = invocationOnMock.getArgument(1, RLPOutput.class);
out.startList();
out.writeBytes(Bytes.repeat((byte) 0xbb, 32));
out.endList();
return null;
})
.when(pongPacketDataRlpWriter)
.writeTo(Mockito.eq(packetData), Mockito.any(RLPOutput.class));
Mockito.when(nodeKey.sign(Mockito.any(Bytes32.class))).thenReturn(secpSignature);
Mockito.when(packetSignatureEncoder.encodeSignature(secpSignature))
.thenReturn(Bytes.fromHexString("0xeeeeeeeeeeeeeeee"));
Mockito.when(nodeKey.getPublicKey()).thenReturn(secpPublicKey);
Packet packet = factory.create(PacketType.PONG, packetData, nodeKey);
Mockito.verify(pongPacketDataRlpWriter)
.writeTo(Mockito.eq(packetData), Mockito.any(RLPOutput.class));
ArgumentCaptor<Bytes32> typeAndDataBytesHashCaptor = ArgumentCaptor.forClass(Bytes32.class);
Mockito.verify(nodeKey).sign(typeAndDataBytesHashCaptor.capture());
Mockito.verify(packetSignatureEncoder).encodeSignature(secpSignature);
Mockito.verify(nodeKey).getPublicKey();
Mockito.verifyNoInteractions(
pingPacketDataRlpWriter,
findNeighborsPacketDataRlpWriter,
neighborsPacketDataRlpWriter,
enrRequestPacketDataRlpWriter,
enrResponsePacketDataRlpWriter,
signatureAlgorithm);
Assertions.assertEquals(
"0xbb86992fd5aeeac25ccfbaf86830fa7ab8987d9668e33f7ef4355a6b0da3830c",
typeAndDataBytesHashCaptor.getValue().toHexString());
Assertions.assertNotNull(packet);
Assertions.assertEquals(PacketType.PONG, packet.getType());
Assertions.assertEquals(packetData, packet.getPacketData());
Assertions.assertEquals(
"0xbcafc42be6d5b78eac26e6c1a596b31606eee23824ab8a771d5ec0fbde869c8f",
packet.getHash().toHexString());
Assertions.assertEquals(secpSignature, packet.getSignature());
Field publicKeyField =
ReflectionUtils.findFields(
Packet.class,
(f) -> f.getName().equals("publicKey"),
ReflectionUtils.HierarchyTraversalMode.TOP_DOWN)
.getFirst();
publicKeyField.setAccessible(true);
Assertions.assertEquals(secpPublicKey, publicKeyField.get(packet));
publicKeyField.setAccessible(false);
}
@Test
public void testCreateFindNeighborsPacketWithNodeKey() throws IllegalAccessException {
final FindNeighborsPacketData packetData = Mockito.mock(FindNeighborsPacketData.class);
final NodeKey nodeKey = Mockito.mock(NodeKey.class);
final SECPSignature secpSignature = Mockito.mock(SECPSignature.class);
final SECPPublicKey secpPublicKey = Mockito.mock(SECPPublicKey.class);
Mockito.doAnswer(
(invocationOnMock) -> {
RLPOutput out = invocationOnMock.getArgument(1, RLPOutput.class);
out.startList();
out.writeBytes(Bytes.repeat((byte) 0xbb, 32));
out.endList();
return null;
})
.when(findNeighborsPacketDataRlpWriter)
.writeTo(Mockito.eq(packetData), Mockito.any(RLPOutput.class));
Mockito.when(nodeKey.sign(Mockito.any(Bytes32.class))).thenReturn(secpSignature);
Mockito.when(packetSignatureEncoder.encodeSignature(secpSignature))
.thenReturn(Bytes.fromHexString("0xeeeeeeeeeeeeeeee"));
Mockito.when(nodeKey.getPublicKey()).thenReturn(secpPublicKey);
Packet packet = factory.create(PacketType.FIND_NEIGHBORS, packetData, nodeKey);
Mockito.verify(findNeighborsPacketDataRlpWriter)
.writeTo(Mockito.eq(packetData), Mockito.any(RLPOutput.class));
ArgumentCaptor<Bytes32> typeAndDataBytesHashCaptor = ArgumentCaptor.forClass(Bytes32.class);
Mockito.verify(nodeKey).sign(typeAndDataBytesHashCaptor.capture());
Mockito.verify(packetSignatureEncoder).encodeSignature(secpSignature);
Mockito.verify(nodeKey).getPublicKey();
Mockito.verifyNoInteractions(
pingPacketDataRlpWriter,
pongPacketDataRlpWriter,
neighborsPacketDataRlpWriter,
enrRequestPacketDataRlpWriter,
enrResponsePacketDataRlpWriter,
signatureAlgorithm);
Assertions.assertEquals(
"0x66c5b5be0b368658d70620b67f819df01fa6f235c434f7797f749cd27c1d5694",
typeAndDataBytesHashCaptor.getValue().toHexString());
Assertions.assertNotNull(packet);
Assertions.assertEquals(PacketType.FIND_NEIGHBORS, packet.getType());
Assertions.assertEquals(packetData, packet.getPacketData());
Assertions.assertEquals(
"0xb8d306ac23f97ebc4a3a477ebb7f47230e581cc4bf4a03a2c4f12e668095fc93",
packet.getHash().toHexString());
Assertions.assertEquals(secpSignature, packet.getSignature());
Field publicKeyField =
ReflectionUtils.findFields(
Packet.class,
(f) -> f.getName().equals("publicKey"),
ReflectionUtils.HierarchyTraversalMode.TOP_DOWN)
.getFirst();
publicKeyField.setAccessible(true);
Assertions.assertEquals(secpPublicKey, publicKeyField.get(packet));
publicKeyField.setAccessible(false);
}
@Test
public void testCreateNeighborsPacketWithNodeKey() throws IllegalAccessException {
final NeighborsPacketData packetData = Mockito.mock(NeighborsPacketData.class);
final NodeKey nodeKey = Mockito.mock(NodeKey.class);
final SECPSignature secpSignature = Mockito.mock(SECPSignature.class);
final SECPPublicKey secpPublicKey = Mockito.mock(SECPPublicKey.class);
Mockito.doAnswer(
(invocationOnMock) -> {
RLPOutput out = invocationOnMock.getArgument(1, RLPOutput.class);
out.startList();
out.writeBytes(Bytes.repeat((byte) 0xbb, 32));
out.endList();
return null;
})
.when(neighborsPacketDataRlpWriter)
.writeTo(Mockito.eq(packetData), Mockito.any(RLPOutput.class));
Mockito.when(nodeKey.sign(Mockito.any(Bytes32.class))).thenReturn(secpSignature);
Mockito.when(packetSignatureEncoder.encodeSignature(secpSignature))
.thenReturn(Bytes.fromHexString("0xeeeeeeeeeeeeeeee"));
Mockito.when(nodeKey.getPublicKey()).thenReturn(secpPublicKey);
Packet packet = factory.create(PacketType.NEIGHBORS, packetData, nodeKey);
Mockito.verify(neighborsPacketDataRlpWriter)
.writeTo(Mockito.eq(packetData), Mockito.any(RLPOutput.class));
ArgumentCaptor<Bytes32> typeAndDataBytesHashCaptor = ArgumentCaptor.forClass(Bytes32.class);
Mockito.verify(nodeKey).sign(typeAndDataBytesHashCaptor.capture());
Mockito.verify(packetSignatureEncoder).encodeSignature(secpSignature);
Mockito.verify(nodeKey).getPublicKey();
Mockito.verifyNoInteractions(
pingPacketDataRlpWriter,
pongPacketDataRlpWriter,
findNeighborsPacketDataRlpWriter,
enrRequestPacketDataRlpWriter,
enrResponsePacketDataRlpWriter,
signatureAlgorithm);
Assertions.assertEquals(
"0x9bba8554a37d5ecaac3e85483f7cb4be96cfa82bb1be8fb3bb01c9bcb76d35e6",
typeAndDataBytesHashCaptor.getValue().toHexString());
Assertions.assertNotNull(packet);
Assertions.assertEquals(PacketType.NEIGHBORS, packet.getType());
Assertions.assertEquals(packetData, packet.getPacketData());
Assertions.assertEquals(
"0x16395ac0538f94709c73575da835305ccaa7c62f8e578282af5168971f3d795e",
packet.getHash().toHexString());
Assertions.assertEquals(secpSignature, packet.getSignature());
Field publicKeyField =
ReflectionUtils.findFields(
Packet.class,
(f) -> f.getName().equals("publicKey"),
ReflectionUtils.HierarchyTraversalMode.TOP_DOWN)
.getFirst();
publicKeyField.setAccessible(true);
Assertions.assertEquals(secpPublicKey, publicKeyField.get(packet));
publicKeyField.setAccessible(false);
}
@Test
public void testCreateEnrRequestPacketWithNodeKey() throws IllegalAccessException {
final EnrRequestPacketData packetData = Mockito.mock(EnrRequestPacketData.class);
final NodeKey nodeKey = Mockito.mock(NodeKey.class);
final SECPSignature secpSignature = Mockito.mock(SECPSignature.class);
final SECPPublicKey secpPublicKey = Mockito.mock(SECPPublicKey.class);
Mockito.doAnswer(
(invocationOnMock) -> {
RLPOutput out = invocationOnMock.getArgument(1, RLPOutput.class);
out.startList();
out.writeBytes(Bytes.repeat((byte) 0xbb, 32));
out.endList();
return null;
})
.when(enrRequestPacketDataRlpWriter)
.writeTo(Mockito.eq(packetData), Mockito.any(RLPOutput.class));
Mockito.when(nodeKey.sign(Mockito.any(Bytes32.class))).thenReturn(secpSignature);
Mockito.when(packetSignatureEncoder.encodeSignature(secpSignature))
.thenReturn(Bytes.fromHexString("0xeeeeeeeeeeeeeeee"));
Mockito.when(nodeKey.getPublicKey()).thenReturn(secpPublicKey);
Packet packet = factory.create(PacketType.ENR_REQUEST, packetData, nodeKey);
Mockito.verify(enrRequestPacketDataRlpWriter)
.writeTo(Mockito.eq(packetData), Mockito.any(RLPOutput.class));
ArgumentCaptor<Bytes32> typeAndDataBytesHashCaptor = ArgumentCaptor.forClass(Bytes32.class);
Mockito.verify(nodeKey).sign(typeAndDataBytesHashCaptor.capture());
Mockito.verify(packetSignatureEncoder).encodeSignature(secpSignature);
Mockito.verify(nodeKey).getPublicKey();
Mockito.verifyNoInteractions(
pingPacketDataRlpWriter,
pongPacketDataRlpWriter,
findNeighborsPacketDataRlpWriter,
neighborsPacketDataRlpWriter,
enrResponsePacketDataRlpWriter,
signatureAlgorithm);
Assertions.assertEquals(
"0x4005740364c45fe432c2332c80aa989c9ade2c020785634c6f8650189378da89",
typeAndDataBytesHashCaptor.getValue().toHexString());
Assertions.assertNotNull(packet);
Assertions.assertEquals(PacketType.ENR_REQUEST, packet.getType());
Assertions.assertEquals(packetData, packet.getPacketData());
Assertions.assertEquals(
"0x482d0816a45ace4eb2d3dca77ae207f31b093a5264d723ac793d9065286c0454",
packet.getHash().toHexString());
Assertions.assertEquals(secpSignature, packet.getSignature());
Field publicKeyField =
ReflectionUtils.findFields(
Packet.class,
(f) -> f.getName().equals("publicKey"),
ReflectionUtils.HierarchyTraversalMode.TOP_DOWN)
.getFirst();
publicKeyField.setAccessible(true);
Assertions.assertEquals(secpPublicKey, publicKeyField.get(packet));
publicKeyField.setAccessible(false);
}
@Test
public void testCreateEnrResponsePacketWithNodeKey() throws IllegalAccessException {
final EnrResponsePacketData packetData = Mockito.mock(EnrResponsePacketData.class);
final NodeKey nodeKey = Mockito.mock(NodeKey.class);
final SECPSignature secpSignature = Mockito.mock(SECPSignature.class);
final SECPPublicKey secpPublicKey = Mockito.mock(SECPPublicKey.class);
Mockito.doAnswer(
(invocationOnMock) -> {
RLPOutput out = invocationOnMock.getArgument(1, RLPOutput.class);
out.startList();
out.writeBytes(Bytes.repeat((byte) 0xbb, 32));
out.endList();
return null;
})
.when(enrResponsePacketDataRlpWriter)
.writeTo(Mockito.eq(packetData), Mockito.any(RLPOutput.class));
Mockito.when(nodeKey.sign(Mockito.any(Bytes32.class))).thenReturn(secpSignature);
Mockito.when(packetSignatureEncoder.encodeSignature(secpSignature))
.thenReturn(Bytes.fromHexString("0xeeeeeeeeeeeeeeee"));
Mockito.when(nodeKey.getPublicKey()).thenReturn(secpPublicKey);
Packet packet = factory.create(PacketType.ENR_RESPONSE, packetData, nodeKey);
Mockito.verify(enrResponsePacketDataRlpWriter)
.writeTo(Mockito.eq(packetData), Mockito.any(RLPOutput.class));
ArgumentCaptor<Bytes32> typeAndDataBytesHashCaptor = ArgumentCaptor.forClass(Bytes32.class);
Mockito.verify(nodeKey).sign(typeAndDataBytesHashCaptor.capture());
Mockito.verify(packetSignatureEncoder).encodeSignature(secpSignature);
Mockito.verify(nodeKey).getPublicKey();
Mockito.verifyNoInteractions(
pingPacketDataRlpWriter,
pongPacketDataRlpWriter,
findNeighborsPacketDataRlpWriter,
neighborsPacketDataRlpWriter,
enrRequestPacketDataRlpWriter,
signatureAlgorithm);
Assertions.assertEquals(
"0x141c592ac04e806c77621d9a6e3885f2a0b6e9ecd2ea9e069ade4a965f037f04",
typeAndDataBytesHashCaptor.getValue().toHexString());
Assertions.assertNotNull(packet);
Assertions.assertEquals(PacketType.ENR_RESPONSE, packet.getType());
Assertions.assertEquals(packetData, packet.getPacketData());
Assertions.assertEquals(
"0xf610ac4f0ef89cdcc65a7c2369d29fc4df3f6c197fb1faab63246be3c1f55844",
packet.getHash().toHexString());
Assertions.assertEquals(secpSignature, packet.getSignature());
Field publicKeyField =
ReflectionUtils.findFields(
Packet.class,
(f) -> f.getName().equals("publicKey"),
ReflectionUtils.HierarchyTraversalMode.TOP_DOWN)
.getFirst();
publicKeyField.setAccessible(true);
Assertions.assertEquals(secpPublicKey, publicKeyField.get(packet));
publicKeyField.setAccessible(false);
}
@Test
public void testCreateFromMessage() throws IllegalAccessException {
final PacketType packetType = PacketType.PING;
final PacketData packetData = Mockito.mock(PacketData.class);
final Bytes encodedSignature =
Bytes.repeat((byte) 0x02, Packet.PACKET_TYPE_INDEX - Packet.SIGNATURE_INDEX);
final Bytes signedPayload = Bytes.repeat((byte) 0x03, 128);
final Bytes hash = Hash.keccak256(Bytes.concatenate(encodedSignature, signedPayload));
final Bytes message = Bytes.concatenate(hash, encodedSignature, signedPayload);
final SECPSignature secpSignature = Mockito.mock(SECPSignature.class);
final SECPPublicKey secpPublicKey = Mockito.mock(SECPPublicKey.class);
Mockito.when(
signatureAlgorithm.createSignature(
Bytes.repeat((byte) 0x02, 32).toUnsignedBigInteger(),
Bytes.repeat((byte) 0x02, 32).toUnsignedBigInteger(),
(byte) 0x02))
.thenReturn(secpSignature);
Mockito.when(
signatureAlgorithm.recoverPublicKeyFromSignature(
Hash.keccak256(signedPayload), secpSignature))
.thenReturn(Optional.of(secpPublicKey));
Packet packet = factory.create(packetType, packetData, message);
Mockito.verify(signatureAlgorithm)
.createSignature(
Bytes.repeat((byte) 0x02, 32).toUnsignedBigInteger(),
Bytes.repeat((byte) 0x02, 32).toUnsignedBigInteger(),
(byte) 0x02);
Mockito.verify(signatureAlgorithm)
.recoverPublicKeyFromSignature(Hash.keccak256(signedPayload), secpSignature);
Mockito.verifyNoInteractions(
pingPacketDataRlpWriter,
pongPacketDataRlpWriter,
findNeighborsPacketDataRlpWriter,
neighborsPacketDataRlpWriter,
enrRequestPacketDataRlpWriter,
enrResponsePacketDataRlpWriter,
packetSignatureEncoder);
Assertions.assertNotNull(packet);
Assertions.assertEquals(packetType, packet.getType());
Assertions.assertEquals(packetData, packet.getPacketData());
Assertions.assertEquals(hash, packet.getHash());
Assertions.assertEquals(secpSignature, packet.getSignature());
Field publicKeyField =
ReflectionUtils.findFields(
Packet.class,
(f) -> f.getName().equals("publicKey"),
ReflectionUtils.HierarchyTraversalMode.TOP_DOWN)
.getFirst();
publicKeyField.setAccessible(true);
Assertions.assertEquals(secpPublicKey, publicKeyField.get(packet));
publicKeyField.setAccessible(false);
}
}

View File

@@ -0,0 +1,241 @@
/*
* Copyright contributors to Besu.
*
* 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.packet;
import org.hyperledger.besu.crypto.SECPPublicKey;
import org.hyperledger.besu.crypto.SECPSignature;
import org.hyperledger.besu.ethereum.p2p.discovery.DiscoveryPeer;
import org.hyperledger.besu.ethereum.p2p.discovery.Endpoint;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.PacketType;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.enrrequest.EnrRequestPacketDataFactory;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.enrrequest.EnrRequestPacketDataRlpWriter;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.enrresponse.EnrResponsePacketDataFactory;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.enrresponse.EnrResponsePacketDataRlpWriter;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.findneighbors.FindNeighborsPacketDataFactory;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.findneighbors.FindNeighborsPacketDataRlpWriter;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.neighbors.NeighborsPacketDataFactory;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.neighbors.NeighborsPacketDataRlpWriter;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.ping.PingPacketDataFactory;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.ping.PingPacketDataRlpWriter;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.pong.PongPacketDataFactory;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.pong.PongPacketDataRlpWriter;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.validation.DiscoveryPeersValidator;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.validation.EndpointValidator;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.validation.ExpiryValidator;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.validation.NodeRecordValidator;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.validation.RequestHashValidator;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.validation.TargetValidator;
import java.math.BigInteger;
import java.time.Clock;
import java.time.Instant;
import java.time.ZoneId;
import java.util.List;
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.ethereum.beacon.discovery.schema.EnrField;
import org.ethereum.beacon.discovery.schema.IdentitySchemaInterpreter;
import org.ethereum.beacon.discovery.schema.NodeRecordFactory;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
public class PacketSerializerTest {
final Clock clock = Clock.fixed(Instant.ofEpochSecond(123), ZoneId.of("UTC"));
private PacketSerializer packetSerializer;
@BeforeEach
public void beforeTest() {
PacketSignatureEncoder packetSignatureEncoder = new PacketSignatureEncoder();
PingPacketDataRlpWriter pingPacketDataRlpWriter = new PingPacketDataRlpWriter();
PongPacketDataRlpWriter pongPacketDataRlpWriter = new PongPacketDataRlpWriter();
FindNeighborsPacketDataRlpWriter findNeighborsPacketDataRlpWriter =
new FindNeighborsPacketDataRlpWriter();
NeighborsPacketDataRlpWriter neighborsPacketDataRlpWriter = new NeighborsPacketDataRlpWriter();
EnrRequestPacketDataRlpWriter enrRequestPacketDataRlpWriter =
new EnrRequestPacketDataRlpWriter();
EnrResponsePacketDataRlpWriter enrResponsePacketDataRlpWriter =
new EnrResponsePacketDataRlpWriter();
packetSerializer =
new PacketSerializer(
packetSignatureEncoder,
pingPacketDataRlpWriter,
pongPacketDataRlpWriter,
findNeighborsPacketDataRlpWriter,
neighborsPacketDataRlpWriter,
enrRequestPacketDataRlpWriter,
enrResponsePacketDataRlpWriter);
}
@Test
public void testEncodeForPingPacket() {
final PacketType type = PacketType.PING;
final PingPacketDataFactory packetDataFactory =
new PingPacketDataFactory(new EndpointValidator(), new ExpiryValidator(clock), clock);
final PacketData data =
packetDataFactory.create(
Optional.empty(),
new Endpoint("10.0.0.1", 30303, Optional.empty()),
456,
UInt64.valueOf(789));
final Bytes hash =
Bytes.fromHexString("0xc68a00f14b2e91d2592005ca4e77247c8e0627fc5a6e6d942bd9e1af41d2f5b9");
final SECPSignature signature =
SECPSignature.create(BigInteger.ONE, BigInteger.TWO, (byte) 0x00, BigInteger.valueOf(4));
final SECPPublicKey publicKey = Mockito.mock(SECPPublicKey.class);
Packet packet = new Packet(type, data, hash, signature, publicKey);
Buffer result = packetSerializer.encode(packet);
Mockito.verifyNoInteractions(publicKey);
String expectedResult =
"0xc68a00f14b2e91d2592005ca4e77247c8e0627fc5a6e6d942bd9e1af41d2f5b9000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020001d105c9840a00000182765f808201c8820315";
Assertions.assertEquals(expectedResult, Bytes.wrapBuffer(result).toHexString());
}
@Test
public void testEncodeForPongPacket() {
final PacketType type = PacketType.PONG;
final PongPacketDataFactory packetDataFactory =
new PongPacketDataFactory(new ExpiryValidator(clock), clock);
final PacketData data =
packetDataFactory.create(
new Endpoint("10.0.0.1", 30303, Optional.empty()),
Bytes.fromHexString("0x0123"),
456,
UInt64.valueOf(789));
final Bytes hash =
Bytes.fromHexString("0x21206a30fabe5d8fe7c4896bb25f51a9f2a22260da9164d21645fb482439736c");
final SECPSignature signature =
SECPSignature.create(BigInteger.ONE, BigInteger.TWO, (byte) 0x00, BigInteger.valueOf(4));
final SECPPublicKey publicKey = Mockito.mock(SECPPublicKey.class);
Packet packet = new Packet(type, data, hash, signature, publicKey);
Buffer result = packetSerializer.encode(packet);
Mockito.verifyNoInteractions(publicKey);
String expectedResult =
"0x21206a30fabe5d8fe7c4896bb25f51a9f2a22260da9164d21645fb482439736c000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020002d3c9840a00000182765f808201238201c8820315";
Assertions.assertEquals(expectedResult, Bytes.wrapBuffer(result).toHexString());
}
@Test
public void testEncodeForFindNeighborsPacket() {
final PacketType type = PacketType.FIND_NEIGHBORS;
final FindNeighborsPacketDataFactory packetDataFactory =
new FindNeighborsPacketDataFactory(
new TargetValidator(), new ExpiryValidator(clock), clock);
final PacketData data = packetDataFactory.create(Bytes.repeat((byte) 0xcd, 64), 456);
final Bytes hash =
Bytes.fromHexString("0xe51a3c97707983d13de6a7aa7c54cc67e8a8f964145bb86cd0af7ee6dd6a196b");
final SECPSignature signature =
SECPSignature.create(BigInteger.ONE, BigInteger.TWO, (byte) 0x00, BigInteger.valueOf(4));
final SECPPublicKey publicKey = Mockito.mock(SECPPublicKey.class);
Packet packet = new Packet(type, data, hash, signature, publicKey);
Buffer result = packetSerializer.encode(packet);
Mockito.verifyNoInteractions(publicKey);
String expectedResult =
"0xe51a3c97707983d13de6a7aa7c54cc67e8a8f964145bb86cd0af7ee6dd6a196b000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020003f845b840cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd8201c8";
Assertions.assertEquals(expectedResult, Bytes.wrapBuffer(result).toHexString());
}
@Test
public void testEncodeForNeighborsPacket() {
final PacketType type = PacketType.NEIGHBORS;
final NeighborsPacketDataFactory packetDataFactory =
new NeighborsPacketDataFactory(
new DiscoveryPeersValidator(), new ExpiryValidator(clock), clock);
final PacketData data =
packetDataFactory.create(
List.of(
DiscoveryPeer.fromIdAndEndpoint(
Bytes.repeat((byte) 0x98, 64),
new Endpoint("10.0.0.1", 30303, Optional.empty()))),
456);
final Bytes hash =
Bytes.fromHexString("0xc6dce7f54086fb7537a9c2793fd7938b057888442109304ea463017c6265e743");
final SECPSignature signature =
SECPSignature.create(BigInteger.ONE, BigInteger.TWO, (byte) 0x00, BigInteger.valueOf(4));
final SECPPublicKey publicKey = Mockito.mock(SECPPublicKey.class);
Packet packet = new Packet(type, data, hash, signature, publicKey);
Buffer result = packetSerializer.encode(packet);
Mockito.verifyNoInteractions(publicKey);
String expectedResult =
"0xc6dce7f54086fb7537a9c2793fd7938b057888442109304ea463017c6265e743000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020004f852f84df84b840a00000182765f80b840989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898989898988201c8";
Assertions.assertEquals(expectedResult, Bytes.wrapBuffer(result).toHexString());
}
@Test
public void testEncodeForEnrRequestPacket() {
final PacketType type = PacketType.ENR_REQUEST;
final EnrRequestPacketDataFactory packetDataFactory =
new EnrRequestPacketDataFactory(new ExpiryValidator(clock), clock);
final PacketData data = packetDataFactory.create(456);
final Bytes hash =
Bytes.fromHexString("0xfde48f75a7340fafea894eb45f7badecd9705227902c5b48928ad3c57b1963e7");
final SECPSignature signature =
SECPSignature.create(BigInteger.ONE, BigInteger.TWO, (byte) 0x00, BigInteger.valueOf(4));
final SECPPublicKey publicKey = Mockito.mock(SECPPublicKey.class);
Packet packet = new Packet(type, data, hash, signature, publicKey);
Buffer result = packetSerializer.encode(packet);
Mockito.verifyNoInteractions(publicKey);
String expectedResult =
"0xfde48f75a7340fafea894eb45f7badecd9705227902c5b48928ad3c57b1963e7000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020005c38201c8";
Assertions.assertEquals(expectedResult, Bytes.wrapBuffer(result).toHexString());
}
@Test
public void testEncodeForEnrResponsePacket() {
final PacketType type = PacketType.ENR_RESPONSE;
final EnrResponsePacketDataFactory packetDataFactory =
new EnrResponsePacketDataFactory(new RequestHashValidator(), new NodeRecordValidator());
final PacketData data =
packetDataFactory.create(
Bytes.fromHexString("0x1234"),
NodeRecordFactory.DEFAULT.createFromValues(
UInt64.valueOf(567),
List.of(new EnrField("id", IdentitySchemaInterpreter.V4.getScheme()))));
final Bytes hash =
Bytes.fromHexString("0x9d0b57ce920728f26211e7784f626bf9a881e7e942c7243160d535a3760e8848");
final SECPSignature signature =
SECPSignature.create(BigInteger.ONE, BigInteger.TWO, (byte) 0x00, BigInteger.valueOf(4));
final SECPPublicKey publicKey = Mockito.mock(SECPPublicKey.class);
Packet packet = new Packet(type, data, hash, signature, publicKey);
Buffer result = packetSerializer.encode(packet);
Mockito.verifyNoInteractions(publicKey);
String expectedResult =
"0x9d0b57ce920728f26211e7784f626bf9a881e7e942c7243160d535a3760e8848000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020006f870821234f86bb860000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000820237826964827634";
Assertions.assertEquals(expectedResult, Bytes.wrapBuffer(result).toHexString());
}
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright contributors to Besu.
*
* 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.packet;
import org.hyperledger.besu.crypto.SECPSignature;
import com.google.common.base.Strings;
import org.apache.tuweni.bytes.Bytes;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
public class PacketSignatureEncoderTest {
private PacketSignatureEncoder packetSignatureEncoder;
@BeforeEach
public void beforeTest() {
packetSignatureEncoder = new PacketSignatureEncoder();
}
@Test
@SuppressWarnings("InlineMeInliner")
public void testEncodeSignature() {
final SECPSignature signature = Mockito.mock(SECPSignature.class);
Bytes r = Bytes.repeat((byte) 0x01, 32);
Bytes s = Bytes.repeat((byte) 0x02, 32);
byte recId = (byte) 0x03;
Mockito.when(signature.getR()).thenReturn(r.toBigInteger());
Mockito.when(signature.getS()).thenReturn(s.toBigInteger());
Mockito.when(signature.getRecId()).thenReturn(recId);
Bytes encodedSignature = packetSignatureEncoder.encodeSignature(signature);
Mockito.verify(signature).getR();
Mockito.verify(signature).getS();
Mockito.verify(signature).getRecId();
Assertions.assertEquals(
"0x" + Strings.repeat("01", 32) + Strings.repeat("02", 32) + "03",
encodedSignature.toHexString());
}
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright contributors to Besu.
*
* 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.packet.enrrequest;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.validation.ExpiryValidator;
import java.time.Clock;
import java.time.Instant;
import java.time.ZoneId;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
public class EnrRequestPacketDataFactoryTest {
private @Mock ExpiryValidator expiryValidator;
private final Clock clock = Clock.fixed(Instant.ofEpochSecond(123), ZoneId.of("UTC"));
private EnrRequestPacketDataFactory factory;
@BeforeEach
public void beforeTest() {
factory = new EnrRequestPacketDataFactory(expiryValidator, clock);
}
@Test
public void testCreateWithExpiry() {
final long expiration = 456;
EnrRequestPacketData enrRequestPacketData = factory.create(expiration);
Mockito.verify(expiryValidator).validate(expiration);
Assertions.assertEquals(expiration, enrRequestPacketData.getExpiration());
}
@Test
public void testCreateWithInvalidExpiry() {
final long expiration = 456;
Mockito.doThrow(new IllegalArgumentException()).when(expiryValidator).validate(expiration);
Assertions.assertThrows(IllegalArgumentException.class, () -> factory.create(expiration));
Mockito.verify(expiryValidator).validate(expiration);
}
@Test
public void testCreateWithoutExpiry() {
EnrRequestPacketData enrRequestPacketData = factory.create();
Mockito.verifyNoInteractions(expiryValidator);
Assertions.assertEquals(
clock.instant().getEpochSecond() + 60, enrRequestPacketData.getExpiration());
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright contributors to Besu.
*
* 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.packet.enrrequest;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput;
import org.apache.tuweni.bytes.Bytes;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
public class EnrRequestPacketDataRlpReaderTest {
private @Mock EnrRequestPacketDataFactory factory;
private EnrRequestPacketDataRlpReader reader;
@BeforeEach
public void beforeTest() {
reader = new EnrRequestPacketDataRlpReader(factory);
}
@Test
public void testReadFrom() {
final long expiration = 123;
final EnrRequestPacketData enrRequestPacketData = new EnrRequestPacketData(expiration);
BytesValueRLPInput in = new BytesValueRLPInput(Bytes.fromHexString("0xc17b"), false);
Mockito.when(factory.create(expiration)).thenReturn(enrRequestPacketData);
EnrRequestPacketData result = reader.readFrom(in);
Mockito.verify(factory).create(expiration);
Assertions.assertNotNull(result);
Assertions.assertEquals(expiration, result.getExpiration());
}
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright contributors to Besu.
*
* 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.packet.enrrequest;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public class EnrRequestPacketDataRlpWriterTest {
private EnrRequestPacketDataRlpWriter writer;
@BeforeEach
public void beforeTest() {
writer = new EnrRequestPacketDataRlpWriter();
}
@Test
public void testWriteTo() {
final long expiration = 123;
final EnrRequestPacketData enrRequestPacketData = new EnrRequestPacketData(expiration);
final BytesValueRLPOutput out = new BytesValueRLPOutput();
writer.writeTo(enrRequestPacketData, out);
Assertions.assertEquals("0xc17b", out.encoded().toHexString());
}
}

View File

@@ -0,0 +1,83 @@
/*
* Copyright contributors to Besu.
*
* 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.packet.enrresponse;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.validation.NodeRecordValidator;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.validation.RequestHashValidator;
import org.apache.tuweni.bytes.Bytes;
import org.ethereum.beacon.discovery.schema.NodeRecord;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
public class EnrResponsePacketDataFactoryTest {
private @Mock RequestHashValidator requestHashValidator;
private @Mock NodeRecordValidator nodeRecordValidator;
private EnrResponsePacketDataFactory factory;
@BeforeEach
public void beforeTest() {
factory = new EnrResponsePacketDataFactory(requestHashValidator, nodeRecordValidator);
}
@Test
public void testCreate() {
final Bytes requestHash = Mockito.mock(Bytes.class);
final NodeRecord enr = Mockito.mock(NodeRecord.class);
EnrResponsePacketData result = factory.create(requestHash, enr);
Mockito.verify(requestHashValidator).validate(requestHash);
Mockito.verify(nodeRecordValidator).validate(enr);
Assertions.assertEquals(requestHash, result.getRequestHash());
Assertions.assertEquals(enr, result.getEnr());
}
@Test
public void testCreateWithInvalidRequestHash() {
final Bytes requestHash = Mockito.mock(Bytes.class);
final NodeRecord enr = Mockito.mock(NodeRecord.class);
Mockito.doThrow(new IllegalArgumentException())
.when(requestHashValidator)
.validate(requestHash);
Assertions.assertThrows(IllegalArgumentException.class, () -> factory.create(requestHash, enr));
Mockito.verify(requestHashValidator).validate(requestHash);
Mockito.verifyNoInteractions(nodeRecordValidator);
}
@Test
public void testCreateWithInvalidNodeRecord() {
final Bytes requestHash = Mockito.mock(Bytes.class);
final NodeRecord enr = Mockito.mock(NodeRecord.class);
Mockito.doThrow(new IllegalArgumentException()).when(nodeRecordValidator).validate(enr);
Assertions.assertThrows(IllegalArgumentException.class, () -> factory.create(requestHash, enr));
Mockito.verify(requestHashValidator).validate(requestHash);
Mockito.verify(nodeRecordValidator).validate(enr);
}
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright contributors to Besu.
*
* 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.packet.enrresponse;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput;
import java.util.Map;
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;
import org.ethereum.beacon.discovery.schema.NodeRecordFactory;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
public class EnrResponsePacketDataRlpReaderTest {
private @Mock NodeRecordFactory nodeRecordFactory;
private @Mock EnrResponsePacketDataFactory enrResponsePacketDataFactory;
private EnrResponsePacketDataRlpReader reader;
@BeforeEach
public void beforeTest() {
reader = new EnrResponsePacketDataRlpReader(nodeRecordFactory, enrResponsePacketDataFactory);
}
@Test
public void testReadFrom() {
final Bytes requestHash = Bytes.fromHexString("0xdeadbeef");
final Bytes signature = Bytes.fromHexString("0xfeebdaed");
final NodeRecord enr =
NodeRecord.fromRawFields(
IdentitySchemaInterpreter.V4,
UInt64.valueOf(123),
signature,
Map.of("key", Bytes.fromHexString("0x4567")));
final EnrResponsePacketData enrResponsePacketData = new EnrResponsePacketData(requestHash, enr);
final BytesValueRLPInput in =
new BytesValueRLPInput(
Bytes.fromHexString("0xd384deadbeefcd84feebdaed7b836b6579824567"), false);
Mockito.when(nodeRecordFactory.fromBytes(enr.serialize())).thenReturn(enr);
Mockito.when(enrResponsePacketDataFactory.create(requestHash, enr))
.thenReturn(enrResponsePacketData);
final EnrResponsePacketData result = reader.readFrom(in);
Mockito.verify(nodeRecordFactory).fromBytes(enr.serialize());
Mockito.verify(enrResponsePacketDataFactory).create(requestHash, enr);
Assertions.assertNotNull(result);
Assertions.assertEquals(requestHash, result.getRequestHash());
Assertions.assertEquals(enr, result.getEnr());
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright contributors to Besu.
*
* 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.packet.enrresponse;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
import java.util.Map;
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;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public class EnrResponsePacketDataRlpWriterTest {
private EnrResponsePacketDataRlpWriter writer;
@BeforeEach
public void beforeTest() {
writer = new EnrResponsePacketDataRlpWriter();
}
@Test
public void testWriteTo() {
final Bytes requestHash = Bytes.fromHexString("0xdeadbeef");
final Bytes signature = Bytes.fromHexString("0xfeebdaed");
final NodeRecord enr =
NodeRecord.fromRawFields(
IdentitySchemaInterpreter.V4,
UInt64.valueOf(123),
signature,
Map.of("key", Bytes.fromHexString("0x4567")));
final EnrResponsePacketData enrResponsePacketData = new EnrResponsePacketData(requestHash, enr);
final BytesValueRLPOutput out = new BytesValueRLPOutput();
writer.writeTo(enrResponsePacketData, out);
Assertions.assertEquals(
"0xd384deadbeefcd84feebdaed7b836b6579824567", out.encoded().toHexString());
}
}

View File

@@ -0,0 +1,113 @@
/*
* Copyright contributors to Besu.
*
* 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.packet.findneighbors;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.validation.ExpiryValidator;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.validation.TargetValidator;
import java.time.Clock;
import java.time.Instant;
import java.time.ZoneId;
import org.apache.tuweni.bytes.Bytes;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
public class FindNeighborsPacketDataFactoryTest {
private @Mock TargetValidator targetValidator;
private @Mock ExpiryValidator expiryValidator;
private final Clock clock = Clock.fixed(Instant.ofEpochSecond(123), ZoneId.of("UTC"));
private FindNeighborsPacketDataFactory factory;
@BeforeEach
public void beforeTest() {
factory = new FindNeighborsPacketDataFactory(targetValidator, expiryValidator, clock);
}
@Test
public void testCreateWithExpiry() {
final Bytes target = Mockito.mock(Bytes.class);
final long expiration = 456;
FindNeighborsPacketData findNeighborsPacketData = factory.create(target, expiration);
Mockito.verify(targetValidator).validate(target);
Mockito.verify(expiryValidator).validate(expiration);
Assertions.assertEquals(target, findNeighborsPacketData.getTarget());
Assertions.assertEquals(expiration, findNeighborsPacketData.getExpiration());
}
@Test
public void testCreateWithExpiryAndInvalidTarget() {
final Bytes target = Mockito.mock(Bytes.class);
final long expiration = 456;
Mockito.doThrow(new IllegalArgumentException()).when(targetValidator).validate(target);
Assertions.assertThrows(
IllegalArgumentException.class, () -> factory.create(target, expiration));
Mockito.verify(targetValidator).validate(target);
Mockito.verifyNoInteractions(expiryValidator);
}
@Test
public void testCreateWithInvalidExpiry() {
final Bytes target = Mockito.mock(Bytes.class);
final long expiration = 456;
Mockito.doThrow(new IllegalArgumentException()).when(expiryValidator).validate(expiration);
Assertions.assertThrows(
IllegalArgumentException.class, () -> factory.create(target, expiration));
Mockito.verify(targetValidator).validate(target);
Mockito.verify(expiryValidator).validate(expiration);
}
@Test
public void testCreateWithoutExpiry() {
final Bytes target = Mockito.mock(Bytes.class);
FindNeighborsPacketData findNeighborsPacketData = factory.create(target);
Mockito.verify(targetValidator).validate(target);
Mockito.verifyNoInteractions(expiryValidator);
Assertions.assertEquals(target, findNeighborsPacketData.getTarget());
Assertions.assertEquals(
clock.instant().getEpochSecond() + 60, findNeighborsPacketData.getExpiration());
}
@Test
public void testCreateWithoutExpiryAndInvalidTarget() {
final Bytes target = Mockito.mock(Bytes.class);
Mockito.doThrow(new IllegalArgumentException()).when(targetValidator).validate(target);
Assertions.assertThrows(IllegalArgumentException.class, () -> factory.create(target));
Mockito.verify(targetValidator).validate(target);
Mockito.verifyNoInteractions(expiryValidator);
}
}

View File

@@ -0,0 +1,57 @@
/*
* Copyright contributors to Besu.
*
* 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.packet.findneighbors;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput;
import org.hyperledger.besu.ethereum.rlp.RLPInput;
import org.apache.tuweni.bytes.Bytes;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
public class FindNeighborsPacketDataRlpReaderTest {
private @Mock FindNeighborsPacketDataFactory factory;
private FindNeighborsPacketDataRlpReader reader;
@BeforeEach
public void beforeTest() {
reader = new FindNeighborsPacketDataRlpReader(factory);
}
@Test
public void testReadFrom() {
final RLPInput in = new BytesValueRLPInput(Bytes.fromHexString("0xc684deadbeef7b"), false);
final Bytes target = Bytes.fromHexString("0xdeadbeef");
final long expiry = 123;
Mockito.when(factory.create(target, expiry))
.thenReturn(new FindNeighborsPacketData(target, expiry));
FindNeighborsPacketData result = reader.readFrom(in);
Mockito.verify(factory).create(target, expiry);
Assertions.assertNotNull(result);
Assertions.assertEquals(target, result.getTarget());
Assertions.assertEquals(expiry, result.getExpiration());
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright contributors to Besu.
*
* 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.packet.findneighbors;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
import org.apache.tuweni.bytes.Bytes;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public class FindNeighborsPacketDataRlpWriterTest {
private FindNeighborsPacketDataRlpWriter writer;
@BeforeEach
public void beforeTest() {
writer = new FindNeighborsPacketDataRlpWriter();
}
@Test
public void testWriteTo() {
final Bytes target = Bytes.fromHexString("0xdeadbeef");
final long expiry = 123;
final FindNeighborsPacketData findNeighborsPacketData =
new FindNeighborsPacketData(target, expiry);
final BytesValueRLPOutput out = new BytesValueRLPOutput();
writer.writeTo(findNeighborsPacketData, out);
Assertions.assertEquals("0xc684deadbeef7b", out.encoded().toHexString());
}
}

View File

@@ -0,0 +1,114 @@
/*
* Copyright contributors to Besu.
*
* 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.packet.neighbors;
import org.hyperledger.besu.ethereum.p2p.discovery.DiscoveryPeer;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.validation.DiscoveryPeersValidator;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.validation.ExpiryValidator;
import java.time.Clock;
import java.time.Instant;
import java.time.ZoneId;
import java.util.List;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
public class NeighborsPacketDataFactoryTest {
private @Mock DiscoveryPeersValidator discoveryPeersValidator;
private @Mock ExpiryValidator expiryValidator;
private final Clock clock = Clock.fixed(Instant.ofEpochSecond(123), ZoneId.of("UTC"));
private NeighborsPacketDataFactory factory;
@BeforeEach
public void beforeTest() {
factory = new NeighborsPacketDataFactory(discoveryPeersValidator, expiryValidator, clock);
}
@Test
public void testCreateWithExpiry() {
final List<DiscoveryPeer> peers = List.of(Mockito.mock(DiscoveryPeer.class));
final long expiration = 456;
NeighborsPacketData neighborsPacketData = factory.create(peers, expiration);
Mockito.verify(discoveryPeersValidator).validate(peers);
Mockito.verify(expiryValidator).validate(expiration);
Assertions.assertEquals(peers, neighborsPacketData.getNodes());
Assertions.assertEquals(expiration, neighborsPacketData.getExpiration());
}
@Test
public void testCreateWithExpiryAndInvalidPeers() {
final List<DiscoveryPeer> peers = List.of(Mockito.mock(DiscoveryPeer.class));
final long expiration = 456;
Mockito.doThrow(new IllegalArgumentException()).when(discoveryPeersValidator).validate(peers);
Assertions.assertThrows(
IllegalArgumentException.class, () -> factory.create(peers, expiration));
Mockito.verify(discoveryPeersValidator).validate(peers);
Mockito.verifyNoInteractions(expiryValidator);
}
@Test
public void testCreateWithInvalidExpiry() {
final List<DiscoveryPeer> peers = List.of(Mockito.mock(DiscoveryPeer.class));
final long expiration = 456;
Mockito.doThrow(new IllegalArgumentException()).when(expiryValidator).validate(expiration);
Assertions.assertThrows(
IllegalArgumentException.class, () -> factory.create(peers, expiration));
Mockito.verify(discoveryPeersValidator).validate(peers);
Mockito.verify(expiryValidator).validate(expiration);
}
@Test
public void testCreateWithoutExpiry() {
final List<DiscoveryPeer> peers = List.of(Mockito.mock(DiscoveryPeer.class));
NeighborsPacketData neighborsPacketData = factory.create(peers);
Mockito.verify(discoveryPeersValidator).validate(peers);
Mockito.verifyNoInteractions(expiryValidator);
Assertions.assertEquals(peers, neighborsPacketData.getNodes());
Assertions.assertEquals(
clock.instant().getEpochSecond() + 60, neighborsPacketData.getExpiration());
}
@Test
public void testCreateWithoutExpiryWithInvalidPeers() {
final List<DiscoveryPeer> peers = List.of(Mockito.mock(DiscoveryPeer.class));
Mockito.doThrow(new IllegalArgumentException()).when(discoveryPeersValidator).validate(peers);
Assertions.assertThrows(IllegalArgumentException.class, () -> factory.create(peers));
Mockito.verify(discoveryPeersValidator).validate(peers);
Mockito.verifyNoInteractions(expiryValidator);
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright contributors to Besu.
*
* 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.packet.neighbors;
import org.hyperledger.besu.ethereum.p2p.discovery.DiscoveryPeer;
import org.hyperledger.besu.ethereum.p2p.discovery.Endpoint;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput;
import java.util.List;
import java.util.Optional;
import org.apache.tuweni.bytes.Bytes;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
public class NeighborsPacketDataRlpReaderTest {
private @Mock NeighborsPacketDataFactory factory;
private NeighborsPacketDataRlpReader reader;
@BeforeEach
public void beforeTest() {
reader = new NeighborsPacketDataRlpReader(factory);
}
@Test
public void testReadFrom() {
String idHex =
"0x0fcd214b55ac7ad8f1d179b7a8ea637271226ab8f5ee3ec6e12d2e27b90e0ed25e1dc9d2dc847141ee7cda64c4c7d937fe37d977bef14f277e7a4273920dcc20";
final List<DiscoveryPeer> peers =
List.of(
DiscoveryPeer.fromIdAndEndpoint(
Bytes.fromHexString(idHex), new Endpoint("10.0.0.1", 30303, Optional.of(123))));
final long expiration = 456;
final NeighborsPacketData neighborsPacketData = new NeighborsPacketData(peers, expiration);
Mockito.when(factory.create(peers, expiration)).thenReturn(neighborsPacketData);
String neighborsPacketDataHex =
"0xf852f84df84b840a00000182765f7bb8400fcd214b55ac7ad8f1d179b7a8ea637271226ab8f5ee3ec6e12d2e27b90e0ed25e1dc9d2dc847141ee7cda64c4c7d937fe37d977bef14f277e7a4273920dcc208201c8";
BytesValueRLPInput in =
new BytesValueRLPInput(Bytes.fromHexString(neighborsPacketDataHex), false);
NeighborsPacketData result = reader.readFrom(in);
Mockito.verify(factory).create(peers, expiration);
Assertions.assertNotNull(result);
Assertions.assertEquals(peers, result.getNodes());
Assertions.assertEquals(expiration, result.getExpiration());
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright contributors to Besu.
*
* 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.packet.neighbors;
import org.hyperledger.besu.ethereum.p2p.discovery.DiscoveryPeer;
import org.hyperledger.besu.ethereum.p2p.discovery.Endpoint;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
import java.util.List;
import java.util.Optional;
import org.apache.tuweni.bytes.Bytes;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public class NeighborsPacketDataRlpWriterTest {
private NeighborsPacketDataRlpWriter writer;
@BeforeEach
public void beforeTest() {
writer = new NeighborsPacketDataRlpWriter();
}
@Test
public void testWriteTo() {
String idHex =
"0x0fcd214b55ac7ad8f1d179b7a8ea637271226ab8f5ee3ec6e12d2e27b90e0ed25e1dc9d2dc847141ee7cda64c4c7d937fe37d977bef14f277e7a4273920dcc20";
final List<DiscoveryPeer> peers =
List.of(
DiscoveryPeer.fromIdAndEndpoint(
Bytes.fromHexString(idHex), new Endpoint("10.0.0.1", 30303, Optional.of(123))));
final long expiration = 456;
final NeighborsPacketData neighborsPacketData = new NeighborsPacketData(peers, expiration);
final BytesValueRLPOutput out = new BytesValueRLPOutput();
writer.writeTo(neighborsPacketData, out);
String expectedHex =
"0xf852f84df84b840a00000182765f7bb8400fcd214b55ac7ad8f1d179b7a8ea637271226ab8f5ee3ec6e12d2e27b90e0ed25e1dc9d2dc847141ee7cda64c4c7d937fe37d977bef14f277e7a4273920dcc208201c8";
Assertions.assertEquals(expectedHex, out.encoded().toHexString());
}
}

View File

@@ -0,0 +1,134 @@
/*
* Copyright contributors to Besu.
*
* 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.packet.ping;
import org.hyperledger.besu.ethereum.p2p.discovery.Endpoint;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.validation.EndpointValidator;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.validation.ExpiryValidator;
import java.time.Clock;
import java.time.Instant;
import java.time.ZoneId;
import java.util.Optional;
import org.apache.tuweni.units.bigints.UInt64;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
public class PingPacketDataFactoryTest {
private @Mock EndpointValidator endpointValidator;
private @Mock ExpiryValidator expiryValidator;
private final Clock clock = Clock.fixed(Instant.ofEpochSecond(123), ZoneId.of("UTC"));
private PingPacketDataFactory factory;
@BeforeEach
public void beforeTest() {
factory = new PingPacketDataFactory(endpointValidator, expiryValidator, clock);
}
@Test
public void testCreateWithExpiry() {
final Optional<Endpoint> maybeFrom = Optional.empty();
final Endpoint to = Mockito.mock(Endpoint.class);
final long expiration = 456;
final UInt64 enrSeq = UInt64.MAX_VALUE;
PingPacketData result = factory.create(maybeFrom, to, expiration, enrSeq);
Mockito.verify(endpointValidator).validate(to, "destination endpoint cannot be null");
Mockito.verify(expiryValidator).validate(expiration);
Assertions.assertEquals(maybeFrom, result.getFrom());
Assertions.assertEquals(to, result.getTo());
Assertions.assertEquals(expiration, result.getExpiration());
Assertions.assertTrue(result.getEnrSeq().isPresent());
Assertions.assertEquals(enrSeq, result.getEnrSeq().get());
}
@Test
public void testCreateWithExpiryWithInvalidToAddress() {
final Optional<Endpoint> maybeFrom = Optional.empty();
final Endpoint to = Mockito.mock(Endpoint.class);
final long expiration = 456;
final UInt64 enrSeq = UInt64.MAX_VALUE;
Mockito.doThrow(new IllegalArgumentException())
.when(endpointValidator)
.validate(to, "destination endpoint cannot be null");
Assertions.assertThrows(
IllegalArgumentException.class, () -> factory.create(maybeFrom, to, expiration, enrSeq));
Mockito.verify(endpointValidator).validate(to, "destination endpoint cannot be null");
}
@Test
public void testCreateWithExpiryWithInvalidExpiration() {
final Optional<Endpoint> maybeFrom = Optional.empty();
final Endpoint to = Mockito.mock(Endpoint.class);
final long expiration = 456;
final UInt64 enrSeq = UInt64.MAX_VALUE;
Mockito.doThrow(new IllegalArgumentException()).when(expiryValidator).validate(expiration);
Assertions.assertThrows(
IllegalArgumentException.class, () -> factory.create(maybeFrom, to, expiration, enrSeq));
Mockito.verify(endpointValidator).validate(to, "destination endpoint cannot be null");
Mockito.verify(expiryValidator).validate(expiration);
}
@Test
public void testCreateWithoutExpiry() {
final Optional<Endpoint> maybeFrom = Optional.empty();
final Endpoint to = Mockito.mock(Endpoint.class);
final UInt64 enrSeq = UInt64.MAX_VALUE;
PingPacketData result = factory.create(maybeFrom, to, enrSeq);
Mockito.verify(endpointValidator).validate(to, "destination endpoint cannot be null");
Mockito.verifyNoInteractions(expiryValidator);
Assertions.assertEquals(maybeFrom, result.getFrom());
Assertions.assertEquals(to, result.getTo());
Assertions.assertEquals(clock.instant().getEpochSecond() + 60, result.getExpiration());
Assertions.assertTrue(result.getEnrSeq().isPresent());
Assertions.assertEquals(enrSeq, result.getEnrSeq().get());
}
@Test
public void testCreateWithoutExpiryWithInvalidToAddress() {
final Optional<Endpoint> maybeFrom = Optional.empty();
final Endpoint to = Mockito.mock(Endpoint.class);
final UInt64 enrSeq = UInt64.MAX_VALUE;
Mockito.doThrow(new IllegalArgumentException())
.when(endpointValidator)
.validate(to, "destination endpoint cannot be null");
Assertions.assertThrows(
IllegalArgumentException.class, () -> factory.create(maybeFrom, to, enrSeq));
Mockito.verify(endpointValidator).validate(to, "destination endpoint cannot be null");
Mockito.verifyNoInteractions(expiryValidator);
}
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright contributors to Besu.
*
* 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.packet.ping;
import org.hyperledger.besu.ethereum.p2p.discovery.Endpoint;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput;
import java.util.Optional;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.units.bigints.UInt64;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
public class PingPacketDataRlpReaderTest {
private @Mock PingPacketDataFactory factory;
private PingPacketDataRlpReader reader;
@BeforeEach
public void beforeTest() {
reader = new PingPacketDataRlpReader(factory);
}
@Test
public void testReadFrom() {
String pingHexData = "0xdf05cb840a00000182765f8211d7cb840a00000282765f8222ce7b84075bcd15";
Endpoint from = new Endpoint("10.0.0.1", 30303, Optional.of(4567));
Endpoint to = new Endpoint("10.0.0.2", 30303, Optional.of(8910));
long expiration = 123;
UInt64 enrSeq = UInt64.valueOf(123456789);
Mockito.when(factory.create(Optional.of(from), to, expiration, enrSeq))
.thenReturn(new PingPacketData(Optional.of(from), to, expiration, enrSeq));
PingPacketData result =
reader.readFrom(new BytesValueRLPInput(Bytes.fromHexString(pingHexData), false));
Assertions.assertNotNull(result);
Assertions.assertTrue(result.getFrom().isPresent());
Assertions.assertEquals(from, result.getFrom().get());
Assertions.assertEquals(to, result.getTo());
Assertions.assertEquals(expiration, result.getExpiration());
Assertions.assertTrue(result.getEnrSeq().isPresent());
Assertions.assertEquals(enrSeq, result.getEnrSeq().get());
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright contributors to Besu.
*
* 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.packet.ping;
import org.hyperledger.besu.ethereum.p2p.discovery.Endpoint;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
import java.util.Optional;
import org.apache.tuweni.units.bigints.UInt64;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public class PingPacketDataRlpWriterTest {
private PingPacketDataRlpWriter writer;
@BeforeEach
public void beforeTest() {
writer = new PingPacketDataRlpWriter();
}
@Test
public void testWriteTo() {
Endpoint from = new Endpoint("10.0.0.1", 30303, Optional.of(4567));
Endpoint to = new Endpoint("10.0.0.2", 30303, Optional.of(8910));
long expiration = 123;
UInt64 enrSeq = UInt64.valueOf(123456789);
final PingPacketData pingPacketData =
new PingPacketData(Optional.of(from), to, expiration, enrSeq);
final BytesValueRLPOutput out = new BytesValueRLPOutput();
writer.writeTo(pingPacketData, out);
Assertions.assertEquals(
"0xdf05cb840a00000182765f8211d7cb840a00000282765f8222ce7b84075bcd15",
out.encoded().toHexString());
}
}

View File

@@ -0,0 +1,95 @@
/*
* Copyright contributors to Besu.
*
* 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.packet.pong;
import org.hyperledger.besu.ethereum.p2p.discovery.Endpoint;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.validation.ExpiryValidator;
import java.time.Clock;
import java.time.Instant;
import java.time.ZoneId;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.units.bigints.UInt64;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
public class PongPacketDataFactoryTest {
private @Mock ExpiryValidator expiryValidator;
private final Clock clock = Clock.fixed(Instant.ofEpochSecond(123), ZoneId.of("UTC"));
private PongPacketDataFactory factory;
@BeforeEach
public void beforeTest() {
factory = new PongPacketDataFactory(expiryValidator, clock);
}
@Test
public void testCreateWithExpiry() {
final Endpoint to = Mockito.mock(Endpoint.class);
final Bytes pingHash = Mockito.mock(Bytes.class);
final long expiration = 456;
final UInt64 enrSeq = UInt64.MAX_VALUE;
PongPacketData pongPacketData = factory.create(to, pingHash, expiration, enrSeq);
Mockito.verify(expiryValidator).validate(expiration);
Assertions.assertEquals(to, pongPacketData.getTo());
Assertions.assertEquals(pingHash, pongPacketData.getPingHash());
Assertions.assertEquals(expiration, pongPacketData.getExpiration());
Assertions.assertTrue(pongPacketData.getEnrSeq().isPresent());
Assertions.assertEquals(enrSeq, pongPacketData.getEnrSeq().get());
}
@Test
public void testCreateWithInvalidExpiry() {
final Endpoint to = Mockito.mock(Endpoint.class);
final Bytes pingHash = Mockito.mock(Bytes.class);
final long expiration = 456;
final UInt64 enrSeq = UInt64.MAX_VALUE;
Mockito.doThrow(IllegalArgumentException.class).when(expiryValidator).validate(expiration);
Assertions.assertThrows(
IllegalArgumentException.class, () -> factory.create(to, pingHash, expiration, enrSeq));
Mockito.verify(expiryValidator).validate(expiration);
}
@Test
public void testCreateWithoutExpiry() {
final Endpoint to = Mockito.mock(Endpoint.class);
final Bytes pingHash = Mockito.mock(Bytes.class);
final UInt64 enrSeq = UInt64.MAX_VALUE;
PongPacketData pongPacketData = factory.create(to, pingHash, enrSeq);
Mockito.verifyNoInteractions(expiryValidator);
Assertions.assertEquals(to, pongPacketData.getTo());
Assertions.assertEquals(pingHash, pongPacketData.getPingHash());
Assertions.assertEquals(clock.instant().getEpochSecond() + 60, pongPacketData.getExpiration());
Assertions.assertTrue(pongPacketData.getEnrSeq().isPresent());
Assertions.assertEquals(enrSeq, pongPacketData.getEnrSeq().get());
}
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright contributors to Besu.
*
* 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.packet.pong;
import org.hyperledger.besu.ethereum.p2p.discovery.Endpoint;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput;
import java.util.Optional;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.units.bigints.UInt64;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
public class PongPacketDataRlpReaderTest {
private @Mock PongPacketDataFactory pongPacketDataFactory;
private PongPacketDataRlpReader reader;
@BeforeEach
public void beforeTest() {
reader = new PongPacketDataRlpReader(pongPacketDataFactory);
}
@Test
public void testReadFrom() {
final String pongHexData = "0xd7cb840a00000182765f8204d284deadbeef8202378222ce";
final Endpoint to = new Endpoint("10.0.0.1", 30303, Optional.of(1234));
final Bytes pingHash = Bytes.fromHexString("0xdeadbeef");
final long expiration = 567;
final UInt64 enrSeq = UInt64.valueOf(8910);
Mockito.when(pongPacketDataFactory.create(to, pingHash, expiration, enrSeq))
.thenReturn(new PongPacketData(to, pingHash, expiration, enrSeq));
PongPacketData pongPacketData =
reader.readFrom(new BytesValueRLPInput(Bytes.fromHexString(pongHexData), false));
Assertions.assertNotNull(pongPacketData);
Assertions.assertEquals(to, pongPacketData.getTo());
Assertions.assertEquals(pingHash, pongPacketData.getPingHash());
Assertions.assertEquals(expiration, pongPacketData.getExpiration());
Assertions.assertTrue(pongPacketData.getEnrSeq().isPresent());
Assertions.assertEquals(enrSeq, pongPacketData.getEnrSeq().get());
}
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright contributors to Besu.
*
* 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.packet.pong;
import org.hyperledger.besu.ethereum.p2p.discovery.Endpoint;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
import java.util.Optional;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.units.bigints.UInt64;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public class PongPacketDataRlpWriterTest {
private PongPacketDataRlpWriter writer;
@BeforeEach
public void beforeTest() {
writer = new PongPacketDataRlpWriter();
}
@Test
public void testWriteTo() {
final Endpoint to = new Endpoint("10.0.0.1", 30303, Optional.of(1234));
final Bytes pingHash = Bytes.fromHexString("0xdeadbeef");
final long expiration = 567;
final UInt64 enrSeq = UInt64.valueOf(8910);
final PongPacketData pongPacketData = new PongPacketData(to, pingHash, expiration, enrSeq);
final BytesValueRLPOutput out = new BytesValueRLPOutput();
writer.writeTo(pongPacketData, out);
Assertions.assertEquals(
"0xd7cb840a00000182765f8204d284deadbeef8202378222ce", out.encoded().toHexString());
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright contributors to Besu.
*
* 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.packet.validation;
import org.hyperledger.besu.ethereum.p2p.discovery.DiscoveryPeer;
import java.util.List;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
public class DiscoveryPeersValidatorTest {
private DiscoveryPeersValidator discoveryPeersValidator;
@BeforeEach
public void beforeTest() {
discoveryPeersValidator = new DiscoveryPeersValidator();
}
@Test
public void testValidateWithValidPeers() {
Assertions.assertDoesNotThrow(
() -> discoveryPeersValidator.validate(List.of(Mockito.mock(DiscoveryPeer.class))));
}
@Test
public void testValidateWithInvalidPeers() {
Assertions.assertThrows(
IllegalArgumentException.class, () -> discoveryPeersValidator.validate(null));
}
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright contributors to Besu.
*
* 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.packet.validation;
import org.hyperledger.besu.ethereum.p2p.discovery.Endpoint;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
public class EndpointValidatorTest {
private EndpointValidator validator;
@BeforeEach
public void beforeTest() {
validator = new EndpointValidator();
}
@Test
public void testValidateForValidEndpoint() {
Assertions.assertDoesNotThrow(() -> validator.validate(Mockito.mock(Endpoint.class), ""));
}
@Test
public void testValidateForInvalidEndpoint() {
Assertions.assertThrows(IllegalArgumentException.class, () -> validator.validate(null, ""));
}
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright contributors to Besu.
*
* 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.packet.validation;
import java.time.Clock;
import java.time.Instant;
import java.time.ZoneId;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public class ExpiryValidatorTest {
private final Clock clock = Clock.fixed(Instant.ofEpochSecond(123), ZoneId.of("UTC"));
private ExpiryValidator validator;
@BeforeEach
public void beforeTest() {
validator = new ExpiryValidator(clock);
}
@Test
public void testValidateForValidExpiry() {
Assertions.assertDoesNotThrow(() -> validator.validate(456));
}
@Test
public void testValidateForNegativeExpiry() {
Assertions.assertThrows(IllegalArgumentException.class, () -> validator.validate(-1));
}
@Test
public void testValidateForExpiredExpiry() {
Assertions.assertThrows(IllegalArgumentException.class, () -> validator.validate(12));
}
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright contributors to Besu.
*
* 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.packet.validation;
import org.ethereum.beacon.discovery.schema.NodeRecord;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
public class NodeRecordValidatorTest {
private NodeRecordValidator validator;
@BeforeEach
public void beforeTest() {
validator = new NodeRecordValidator();
}
@Test
public void testValidateForValidNodeRecord() {
Assertions.assertDoesNotThrow(() -> validator.validate(Mockito.mock(NodeRecord.class)));
}
@Test
public void testValidateForInvalidNodeRecord() {
Assertions.assertThrows(IllegalArgumentException.class, () -> validator.validate(null));
}
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright contributors to Besu.
*
* 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.packet.validation;
import org.apache.tuweni.bytes.Bytes;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
public class RequestHashValidatorTest {
private RequestHashValidator validator;
@BeforeEach
public void beforeTest() {
validator = new RequestHashValidator();
}
@Test
public void testValidateForValidHash() {
Assertions.assertDoesNotThrow(() -> validator.validate(Mockito.mock(Bytes.class)));
}
@Test
public void testValidateForInvalidHash() {
Assertions.assertThrows(IllegalArgumentException.class, () -> validator.validate(null));
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright contributors to Besu.
*
* 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.packet.validation;
import org.hyperledger.besu.ethereum.p2p.discovery.internal.packet.findneighbors.FindNeighborsPacketData;
import org.apache.tuweni.bytes.Bytes;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
public class TargetValidatorTest {
private TargetValidator validator;
@BeforeEach
public void beforeTest() {
validator = new TargetValidator();
}
@Test
public void testValidateWithValidTarget() {
Assertions.assertDoesNotThrow(
() -> validator.validate(Bytes.repeat((byte) 0x00, FindNeighborsPacketData.TARGET_SIZE)));
}
@Test
public void testValidateWithNullTarget() {
Assertions.assertThrows(IllegalArgumentException.class, () -> validator.validate(null));
}
@Test
public void testValidateWithInvalidTargetSize() {
Assertions.assertThrows(
IllegalArgumentException.class,
() ->
validator.validate(Bytes.repeat((byte) 0x00, FindNeighborsPacketData.TARGET_SIZE + 1)));
}
}

View File

@@ -32,6 +32,7 @@ dependencies {
api platform('io.opentelemetry:opentelemetry-bom:1.43.0')
api platform('io.prometheus:prometheus-metrics-bom:1.3.4')
api platform('io.vertx:vertx-stack-depchain:4.5.10')
api platform('javax.inject:javax.inject:1')
api platform('org.apache.logging.log4j:log4j-bom:2.24.1')
api platform('org.assertj:assertj-bom:3.26.3')
api platform('org.immutables:bom:2.10.1')