Merge branch 'hyperledger:main' into zkbesu

This commit is contained in:
Joshua Fernandes
2025-02-14 07:32:46 +10:00
committed by GitHub
120 changed files with 5458 additions and 2455 deletions

View File

@@ -2,6 +2,7 @@
## Unreleased
### Breaking Changes
### Upcoming Breaking Changes
- `MetricSystem::createLabelledGauge` is deprecated and will be removed in a future release, replace it with `MetricSystem::createLabelledSuppliedGauge`
- k8s (KUBERNETES) Nat method is now deprecated and will be removed in a future release. Use docker or none instead.
@@ -14,9 +15,11 @@
- Smart-contract-based (onchain) permissioning
- Proof of Work consensus
- Fast Sync
- Transaction indexing will be disabled by default in a future release for snap sync and checkpoint sync modes. This will break RPCs that use transaction hash for historical queries.
### Additions and Improvements
- Add TLS/mTLS options and configure the GraphQL HTTP service[#7910](https://github.com/hyperledger/besu/pull/7910)
- Allow plugins to propose transactions during block creation [#8268](https://github.com/hyperledger/besu/pull/8268)
- Update `eth_getLogs` to return a `Block not found` error when the requested block is not found. [#8290](https://github.com/hyperledger/besu/pull/8290)
### Bug fixes
- Upgrade Netty to version 4.1.118 to fix CVE-2025-24970 [#8275](https://github.com/hyperledger/besu/pull/8275)
- Add missing RPC method `debug_accountRange` to `RpcMethod.java` and implemented its handler. [#8153](https://github.com/hyperledger/besu/issues/8153)

View File

@@ -72,6 +72,8 @@ public class SynchronizerOptions implements CLIOptions<SynchronizerConfiguration
"--Xsnapsync-synchronizer-bytecode-count-per-request";
private static final String SNAP_TRIENODE_COUNT_PER_REQUEST_FLAG =
"--Xsnapsync-synchronizer-trienode-count-per-request";
private static final String SNAP_TRANSACTION_INDEXING_ENABLED_FLAG =
"--Xsnapsync-synchronizer-transaction-indexing-enabled";
private static final String SNAP_FLAT_ACCOUNT_HEALED_COUNT_PER_REQUEST_FLAG =
"--Xsnapsync-synchronizer-flat-account-healed-count-per-request";
@@ -319,6 +321,15 @@ public class SynchronizerOptions implements CLIOptions<SynchronizerConfiguration
"Temporary feature toggle to enable using the new peertask system (default: ${DEFAULT-VALUE})")
private final Boolean isPeerTaskSystemEnabled = false;
@CommandLine.Option(
names = SNAP_TRANSACTION_INDEXING_ENABLED_FLAG,
hidden = true,
paramLabel = "<Boolean>",
arity = "0..1",
description = "Enable transaction indexing during snap sync. (default: ${DEFAULT-VALUE})")
private Boolean snapTransactionIndexingEnabled =
SnapSyncConfiguration.DEFAULT_SNAP_SYNC_TRANSACTION_INDEXING_ENABLED;
private SynchronizerOptions() {}
/**
@@ -399,6 +410,8 @@ public class SynchronizerOptions implements CLIOptions<SynchronizerConfiguration
options.checkpointPostMergeSyncEnabled = config.isCheckpointPostMergeEnabled();
options.snapsyncServerEnabled = config.getSnapSyncConfiguration().isSnapServerEnabled();
options.snapsyncBftEnabled = config.getSnapSyncConfiguration().isSnapSyncBftEnabled();
options.snapTransactionIndexingEnabled =
config.getSnapSyncConfiguration().isSnapSyncTransactionIndexingEnabled();
return options;
}
@@ -432,6 +445,7 @@ public class SynchronizerOptions implements CLIOptions<SynchronizerConfiguration
.localFlatStorageCountToHealPerRequest(snapsyncFlatStorageHealedCountPerRequest)
.isSnapServerEnabled(snapsyncServerEnabled)
.isSnapSyncBftEnabled(snapsyncBftEnabled)
.isSnapSyncTransactionIndexingEnabled(snapTransactionIndexingEnabled)
.build());
builder.checkpointPostMergeEnabled(checkpointPostMergeSyncEnabled);
builder.isPeerTaskSystemEnabled(isPeerTaskSystemEnabled);
@@ -491,7 +505,9 @@ public class SynchronizerOptions implements CLIOptions<SynchronizerConfiguration
SNAP_SERVER_ENABLED_FLAG,
OptionParser.format(snapsyncServerEnabled),
SNAP_SYNC_BFT_ENABLED_FLAG,
OptionParser.format(snapsyncBftEnabled));
OptionParser.format(snapsyncBftEnabled),
SNAP_TRANSACTION_INDEXING_ENABLED_FLAG,
OptionParser.format(snapTransactionIndexingEnabled));
return value;
}
}

View File

@@ -78,6 +78,7 @@ public class SynchronizerOptionsTest
.bytecodeCountPerRequest(
SnapSyncConfiguration.DEFAULT_BYTECODE_COUNT_PER_REQUEST + 2)
.isSnapServerEnabled(Boolean.TRUE)
.isSnapSyncTransactionIndexingEnabled(Boolean.TRUE)
.build());
}

View File

@@ -73,8 +73,18 @@ public class EthGetLogs implements JsonRpcMethod {
.getBlockHash()
.map(
blockHash ->
blockchain.matchingLogs(
blockHash, filter.getLogsQuery(), requestContext::isAlive))
blockchain
.getBlockHeaderByHash(blockHash)
.map(
blockHeader ->
blockchain.matchingLogs(
blockHeader.getBlockHash(),
filter.getLogsQuery(),
requestContext::isAlive))
.orElseThrow(
() ->
new InvalidJsonRpcParameters(
"Block not found", RpcErrorType.BLOCK_NOT_FOUND)))
.orElseGet(
() -> {
final long fromBlockNumber;

View File

@@ -12,7 +12,10 @@
"response": {
"jsonrpc": "2.0",
"id": 406,
"result" : [ ]
"error" : {
"code": -32000,
"message": "Block not found"
}
},
"statusCode": 200
}

View File

@@ -417,13 +417,20 @@ public class DefaultBlockchain implements MutableBlockchain {
@Override
public synchronized void appendBlock(final Block block, final List<TransactionReceipt> receipts) {
if (numberOfBlocksToCache != 0) cacheBlockData(block, receipts);
appendBlockHelper(new BlockWithReceipts(block, receipts), false);
appendBlockHelper(new BlockWithReceipts(block, receipts), false, true);
}
@Override
public synchronized void appendBlockWithoutIndexingTransactions(
final Block block, final List<TransactionReceipt> receipts) {
if (numberOfBlocksToCache != 0) cacheBlockData(block, receipts);
appendBlockHelper(new BlockWithReceipts(block, receipts), false, false);
}
@Override
public synchronized void storeBlock(final Block block, final List<TransactionReceipt> receipts) {
if (numberOfBlocksToCache != 0) cacheBlockData(block, receipts);
appendBlockHelper(new BlockWithReceipts(block, receipts), true);
appendBlockHelper(new BlockWithReceipts(block, receipts), true, true);
}
private void cacheBlockData(final Block block, final List<TransactionReceipt> receipts) {
@@ -447,7 +454,9 @@ public class DefaultBlockchain implements MutableBlockchain {
}
private void appendBlockHelper(
final BlockWithReceipts blockWithReceipts, final boolean storeOnly) {
final BlockWithReceipts blockWithReceipts,
final boolean storeOnly,
final boolean transactionIndexing) {
if (!blockShouldBeProcessed(blockWithReceipts.getBlock(), blockWithReceipts.getReceipts())) {
return;
@@ -469,7 +478,7 @@ public class DefaultBlockchain implements MutableBlockchain {
if (storeOnly) {
blockAddedEvent = handleStoreOnly(blockWithReceipts);
} else {
blockAddedEvent = updateCanonicalChainData(updater, blockWithReceipts);
blockAddedEvent = updateCanonicalChainData(updater, blockWithReceipts, transactionIndexing);
if (blockAddedEvent.isNewCanonicalHead()) {
updateCacheForNewCanonicalHead(block, td);
}
@@ -521,7 +530,9 @@ public class DefaultBlockchain implements MutableBlockchain {
}
private BlockAddedEvent updateCanonicalChainData(
final BlockchainStorage.Updater updater, final BlockWithReceipts blockWithReceipts) {
final Updater updater,
final BlockWithReceipts blockWithReceipts,
final boolean transactionIndexing) {
final Block newBlock = blockWithReceipts.getBlock();
final Hash chainHead = blockchainStorage.getChainHead().orElse(null);
@@ -532,7 +543,7 @@ public class DefaultBlockchain implements MutableBlockchain {
try {
if (newBlock.getHeader().getParentHash().equals(chainHead) || chainHead == null) {
return handleNewHead(updater, blockWithReceipts);
return handleNewHead(updater, blockWithReceipts, transactionIndexing);
} else if (blockChoiceRule.compare(newBlock.getHeader(), chainHeader) > 0) {
// New block represents a chain reorganization
return handleChainReorg(updater, blockWithReceipts);
@@ -553,14 +564,18 @@ public class DefaultBlockchain implements MutableBlockchain {
}
private BlockAddedEvent handleNewHead(
final Updater updater, final BlockWithReceipts blockWithReceipts) {
final Updater updater,
final BlockWithReceipts blockWithReceipts,
final boolean transactionIndexing) {
// This block advances the chain, update the chain head
final Hash newBlockHash = blockWithReceipts.getHash();
updater.putBlockHash(blockWithReceipts.getNumber(), newBlockHash);
updater.setChainHead(newBlockHash);
indexTransactionsForBlock(
updater, newBlockHash, blockWithReceipts.getBlock().getBody().getTransactions());
if (transactionIndexing) {
indexTransactionsForBlock(
updater, newBlockHash, blockWithReceipts.getBlock().getBody().getTransactions());
}
gasUsedCounter.inc(blockWithReceipts.getHeader().getGasUsed());
numberOfTransactionsCounter.inc(
blockWithReceipts.getBlock().getBody().getTransactions().size());
@@ -745,7 +760,7 @@ public class DefaultBlockchain implements MutableBlockchain {
try {
final BlockWithReceipts blockWithReceipts = getBlockWithReceipts(blockHeader).get();
BlockAddedEvent newHeadEvent = handleNewHead(updater, blockWithReceipts);
BlockAddedEvent newHeadEvent = handleNewHead(updater, blockWithReceipts, true);
updateCacheForNewCanonicalHead(
blockWithReceipts.getBlock(), calculateTotalDifficulty(blockHeader));
updater.commit();

View File

@@ -37,6 +37,18 @@ public interface MutableBlockchain extends Blockchain {
*/
void appendBlock(Block block, List<TransactionReceipt> receipts);
/**
* Adds a block to the blockchain without indexing transactions.
*
* <p>Block must be connected to the existing blockchain (its parent must already be stored),
* otherwise an {@link IllegalArgumentException} is thrown. Blocks representing forks are allowed
* as long as they are connected.
*
* @param block The block to append.
* @param receipts The list of receipts associated with this block's transactions.
*/
void appendBlockWithoutIndexingTransactions(Block block, List<TransactionReceipt> receipts);
/**
* Adds a block to the blockchain, without updating the chain state.
*

View File

@@ -71,6 +71,8 @@ public interface BlockImporter {
* @param receipts The receipts associated with this block.
* @param headerValidationMode Determines the validation to perform on this header.
* @param ommerValidationMode Determines the validation to perform on ommer headers.
* @param bodyValidationMode Determines the validation to perform on the block's body.
* @param importWithTxIndexing Whether to import the block with transaction indexing.
* @return {@code BlockImportResult}
* @see BlockImportResult
*/
@@ -80,5 +82,6 @@ public interface BlockImporter {
List<TransactionReceipt> receipts,
HeaderValidationMode headerValidationMode,
HeaderValidationMode ommerValidationMode,
BodyValidationMode bodyValidationMode);
BodyValidationMode bodyValidationMode,
boolean importWithTxIndexing);
}

View File

@@ -15,7 +15,7 @@
package org.hyperledger.besu.ethereum.mainnet;
import org.hyperledger.besu.ethereum.mainnet.feemarket.BaseFeeMarket;
import org.hyperledger.besu.evm.gascalculator.CancunGasCalculator;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
public class CancunTargetingGasLimitCalculator extends LondonTargetingGasLimitCalculator {
@@ -25,8 +25,10 @@ public class CancunTargetingGasLimitCalculator extends LondonTargetingGasLimitCa
private final long maxBlobGasPerBlock;
public CancunTargetingGasLimitCalculator(
final long londonForkBlock, final BaseFeeMarket feeMarket) {
this(londonForkBlock, feeMarket, DEFAULT_MAX_BLOBS_PER_BLOCK_CANCUN);
final long londonForkBlock,
final BaseFeeMarket feeMarket,
final GasCalculator gasCalculator) {
this(londonForkBlock, feeMarket, gasCalculator, DEFAULT_MAX_BLOBS_PER_BLOCK_CANCUN);
}
/**
@@ -34,10 +36,12 @@ public class CancunTargetingGasLimitCalculator extends LondonTargetingGasLimitCa
* 131072 * 6 = 786432 = 0xC0000
*/
public CancunTargetingGasLimitCalculator(
final long londonForkBlock, final BaseFeeMarket feeMarket, final int maxBlobsPerBlock) {
final long londonForkBlock,
final BaseFeeMarket feeMarket,
final GasCalculator gasCalculator,
final int maxBlobsPerBlock) {
super(londonForkBlock, feeMarket);
final CancunGasCalculator cancunGasCalculator = new CancunGasCalculator();
this.maxBlobGasPerBlock = cancunGasCalculator.getBlobGasPerBlob() * maxBlobsPerBlock;
this.maxBlobGasPerBlock = gasCalculator.getBlobGasPerBlob() * maxBlobsPerBlock;
}
@Override

View File

@@ -63,11 +63,16 @@ public class MainnetBlockImporter implements BlockImporter {
final List<TransactionReceipt> receipts,
final HeaderValidationMode headerValidationMode,
final HeaderValidationMode ommerValidationMode,
final BodyValidationMode bodyValidationMode) {
final BodyValidationMode bodyValidationMode,
final boolean importWithTxIndexing) {
if (blockValidator.validateBlockForSyncing(
context, block, receipts, headerValidationMode, ommerValidationMode, bodyValidationMode)) {
context.getBlockchain().appendBlock(block, receipts);
if (importWithTxIndexing) {
context.getBlockchain().appendBlock(block, receipts);
} else {
context.getBlockchain().appendBlockWithoutIndexingTransactions(block, receipts);
}
return new BlockImportResult(true);
}

View File

@@ -717,7 +717,10 @@ public abstract class MainnetProtocolSpecs {
.gasLimitCalculatorBuilder(
feeMarket ->
new CancunTargetingGasLimitCalculator(
londonForkBlockNumber, (BaseFeeMarket) feeMarket, cancunBlobSchedule.getMax()))
londonForkBlockNumber,
(BaseFeeMarket) feeMarket,
cancunGasCalcSupplier.get(),
cancunBlobSchedule.getMax()))
// EVM changes to support EIP-1153: TSTORE and EIP-5656: MCOPY
.evmBuilder(
(gasCalculator, jdCacheConfig) ->
@@ -854,7 +857,10 @@ public abstract class MainnetProtocolSpecs {
.gasLimitCalculatorBuilder(
feeMarket ->
new PragueTargetingGasLimitCalculator(
londonForkBlockNumber, (BaseFeeMarket) feeMarket, pragueBlobSchedule.getMax()))
londonForkBlockNumber,
(BaseFeeMarket) feeMarket,
pragueGasCalcSupplier.get(),
pragueBlobSchedule.getMax()))
// EIP-3074 AUTH and AUTHCALL
.evmBuilder(
(gasCalculator, jdCacheConfig) ->
@@ -950,7 +956,10 @@ public abstract class MainnetProtocolSpecs {
.gasLimitCalculatorBuilder(
feeMarket ->
new OsakaTargetingGasLimitCalculator(
londonForkBlockNumber, (BaseFeeMarket) feeMarket, maxBlobsPerBlock))
londonForkBlockNumber,
(BaseFeeMarket) feeMarket,
osakaGasCalcSupplier.get(),
maxBlobsPerBlock))
// EIP-7692 EOF v1 EVM and opcodes
.evmBuilder(
(gasCalculator, jdCacheConfig) ->

View File

@@ -481,6 +481,7 @@ public class MainnetTransactionProcessor {
}
}
// TODO SLD are the log correct following EIP-7623?
if (LOG.isTraceEnabled()) {
LOG.trace(
"Gas used by transaction: {}, by message call/contract creation: {}",

View File

@@ -15,6 +15,7 @@
package org.hyperledger.besu.ethereum.mainnet;
import org.hyperledger.besu.ethereum.mainnet.feemarket.BaseFeeMarket;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
public class OsakaTargetingGasLimitCalculator extends PragueTargetingGasLimitCalculator {
@@ -22,8 +23,10 @@ public class OsakaTargetingGasLimitCalculator extends PragueTargetingGasLimitCal
private static final int DEFAULT_MAX_BLOBS_PER_BLOCK_OSAKA = 12;
public OsakaTargetingGasLimitCalculator(
final long londonForkBlock, final BaseFeeMarket feeMarket) {
super(londonForkBlock, feeMarket, DEFAULT_MAX_BLOBS_PER_BLOCK_OSAKA);
final long londonForkBlock,
final BaseFeeMarket feeMarket,
final GasCalculator gasCalculator) {
super(londonForkBlock, feeMarket, gasCalculator, DEFAULT_MAX_BLOBS_PER_BLOCK_OSAKA);
}
/**
@@ -31,7 +34,10 @@ public class OsakaTargetingGasLimitCalculator extends PragueTargetingGasLimitCal
* CancunGasCalculator.BLOB_GAS_PER_BLOB * 12 blobs = 131072 * 12 = 1572864 = 0x180000
*/
public OsakaTargetingGasLimitCalculator(
final long londonForkBlock, final BaseFeeMarket feeMarket, final int maxBlobsPerBlock) {
super(londonForkBlock, feeMarket, maxBlobsPerBlock);
final long londonForkBlock,
final BaseFeeMarket feeMarket,
final GasCalculator gasCalculator,
final int maxBlobsPerBlock) {
super(londonForkBlock, feeMarket, gasCalculator, maxBlobsPerBlock);
}
}

View File

@@ -15,6 +15,7 @@
package org.hyperledger.besu.ethereum.mainnet;
import org.hyperledger.besu.ethereum.mainnet.feemarket.BaseFeeMarket;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
public class PragueTargetingGasLimitCalculator extends CancunTargetingGasLimitCalculator {
@@ -22,8 +23,10 @@ public class PragueTargetingGasLimitCalculator extends CancunTargetingGasLimitCa
private static final int DEFAULT_MAX_BLOBS_PER_BLOCK_PRAGUE = 9;
public PragueTargetingGasLimitCalculator(
final long londonForkBlock, final BaseFeeMarket feeMarket) {
super(londonForkBlock, feeMarket, DEFAULT_MAX_BLOBS_PER_BLOCK_PRAGUE);
final long londonForkBlock,
final BaseFeeMarket feeMarket,
final GasCalculator gasCalculator) {
super(londonForkBlock, feeMarket, gasCalculator, DEFAULT_MAX_BLOBS_PER_BLOCK_PRAGUE);
}
/**
@@ -31,7 +34,10 @@ public class PragueTargetingGasLimitCalculator extends CancunTargetingGasLimitCa
* CancunGasCalculator.BLOB_GAS_PER_BLOB * 9 blobs = 131072 * 9 = 1179648 = 0x120000
*/
public PragueTargetingGasLimitCalculator(
final long londonForkBlock, final BaseFeeMarket feeMarket, final int maxBlobsPerBlock) {
super(londonForkBlock, feeMarket, maxBlobsPerBlock);
final long londonForkBlock,
final BaseFeeMarket feeMarket,
final GasCalculator gasCalculator,
final int maxBlobsPerBlock) {
super(londonForkBlock, feeMarket, gasCalculator, maxBlobsPerBlock);
}
}

View File

@@ -17,6 +17,7 @@ package org.hyperledger.besu.ethereum.mainnet;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import org.hyperledger.besu.evm.gascalculator.CancunGasCalculator;
import java.util.Optional;
@@ -27,7 +28,53 @@ class CancunTargetingGasLimitCalculatorTest {
@Test
void currentBlobGasLimitIs6Blobs() {
var cancunTargetingGasLimitCalculator =
new CancunTargetingGasLimitCalculator(0L, FeeMarket.cancun(0L, Optional.empty()));
new org.hyperledger.besu.ethereum.mainnet.CancunTargetingGasLimitCalculator(
0L, FeeMarket.cancun(0L, Optional.empty()), new CancunGasCalculator());
assertThat(cancunTargetingGasLimitCalculator.currentBlobGasLimit()).isEqualTo(0xC0000);
}
@Test
void shouldUseCancunCalculatorBlobGasPerBlob() {
// should use CancunGasCalculator's blob gas per blob to calculate the gas limit
final long blobGasPerBlob = new CancunGasCalculator().getBlobGasPerBlob();
assertThat(blobGasPerBlob).isEqualTo(131072);
int maxBlobs = 10;
var cancunTargetingGasLimitCalculator =
new CancunTargetingGasLimitCalculator(
0L, FeeMarket.cancun(0L, Optional.empty()), new CancunGasCalculator(), maxBlobs);
// if maxBlobs = 10, then the gas limit would be 131072 * 10 = 1310720
assertThat(cancunTargetingGasLimitCalculator.currentBlobGasLimit())
.isEqualTo(blobGasPerBlob * maxBlobs);
assertThat(cancunTargetingGasLimitCalculator.currentBlobGasLimit()).isEqualTo(1310720);
}
@Test
void shouldUseFutureForkCalculatorBlobGasPerBlob() {
// if a future fork changes the blob gas per blob
// even if we still use the CancunTargetingGasLimitCalculator
// it should use TestFutureForkCalculator's blob gas per blob to calculate the blob gas limit
final long blobGasPerBlob = new TestFutureGasCalculator().getBlobGasPerBlob();
assertThat(blobGasPerBlob).isEqualTo(262144);
int maxBlobs = 10;
var cancunTargetingGasLimitCalculator =
new CancunTargetingGasLimitCalculator(
0L, FeeMarket.cancun(0L, Optional.empty()), new TestFutureGasCalculator(), maxBlobs);
// if maxBlobs = 10, then the gas limit would be 262144 * 10 = 2621440
assertThat(cancunTargetingGasLimitCalculator.currentBlobGasLimit())
.isEqualTo(blobGasPerBlob * maxBlobs);
assertThat(cancunTargetingGasLimitCalculator.currentBlobGasLimit()).isEqualTo(2621440);
}
private static class TestFutureGasCalculator extends CancunGasCalculator {
private static final long TEST_BLOB_GAS_PER_BLOB_FUTURE = 262144;
public TestFutureGasCalculator() {
super(0, 7);
}
@Override
public long getBlobGasPerBlob() {
return TEST_BLOB_GAS_PER_BLOB_FUTURE;
}
}
}

View File

@@ -17,6 +17,7 @@ package org.hyperledger.besu.ethereum.mainnet;
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import org.hyperledger.besu.evm.gascalculator.PragueGasCalculator;
import java.util.Optional;
@@ -27,7 +28,23 @@ class PragueTargetingGasLimitCalculatorTest {
@Test
void currentBlobGasLimitIs9BlobsByDefault() {
var pragueTargetingGasLimitCalculator =
new PragueTargetingGasLimitCalculator(0L, FeeMarket.cancun(0L, Optional.empty()));
new PragueTargetingGasLimitCalculator(
0L, FeeMarket.cancun(0L, Optional.empty()), new PragueGasCalculator());
assertThat(pragueTargetingGasLimitCalculator.currentBlobGasLimit()).isEqualTo(0x120000);
}
@Test
void shouldUsePragueCalculatorBlobGasPerBlob() {
// should use PragueGasCalculator's blob gas per blob to calculate the gas limit
final long blobGasPerBlob = new PragueGasCalculator().getBlobGasPerBlob();
assertThat(blobGasPerBlob).isEqualTo(131072); // same as Cancun
int maxBlobs = 10;
var pragueTargetingGasLimitCalculator =
new PragueTargetingGasLimitCalculator(
0L, FeeMarket.cancun(0L, Optional.empty()), new PragueGasCalculator(), maxBlobs);
// if maxBlobs = 10, then the gas limit would be 131072 * 10 = 1310720
assertThat(pragueTargetingGasLimitCalculator.currentBlobGasLimit())
.isEqualTo(blobGasPerBlob * maxBlobs);
assertThat(pragueTargetingGasLimitCalculator.currentBlobGasLimit()).isEqualTo(1310720);
}
}

View File

@@ -150,7 +150,8 @@ public class FastSyncDownloadPipelineFactory implements DownloadPipelineFactory
attachedValidationPolicy,
ommerValidationPolicy,
ethContext,
fastSyncState.getPivotBlockHeader().get());
fastSyncState.getPivotBlockHeader().get(),
syncConfig.getSnapSyncConfiguration().isSnapSyncTransactionIndexingEnabled());
return PipelineBuilder.createPipelineFrom(
"fetchCheckpoints",

View File

@@ -47,6 +47,7 @@ public class ImportBlocksStep implements Consumer<List<BlockWithReceipts>> {
private OptionalLong logStartBlock = OptionalLong.empty();
private final BlockHeader pivotHeader;
private final BodyValidationMode bodyValidationMode;
private final boolean transactionIndexingEnabled;
public ImportBlocksStep(
final ProtocolSchedule protocolSchedule,
@@ -54,17 +55,19 @@ public class ImportBlocksStep implements Consumer<List<BlockWithReceipts>> {
final ValidationPolicy headerValidationPolicy,
final ValidationPolicy ommerValidationPolicy,
final EthContext ethContext,
final BlockHeader pivotHeader) {
final BlockHeader pivotHeader,
final boolean transactionIndexingEnabled) {
this.protocolSchedule = protocolSchedule;
this.protocolContext = protocolContext;
this.headerValidationPolicy = headerValidationPolicy;
this.ommerValidationPolicy = ommerValidationPolicy;
this.ethContext = ethContext;
this.pivotHeader = pivotHeader;
bodyValidationMode =
this.bodyValidationMode =
protocolSchedule.anyMatch(scheduledProtocolSpec -> scheduledProtocolSpec.spec().isPoS())
? BodyValidationMode.NONE
: BodyValidationMode.LIGHT;
this.transactionIndexingEnabled = transactionIndexingEnabled;
}
@Override
@@ -125,7 +128,8 @@ public class ImportBlocksStep implements Consumer<List<BlockWithReceipts>> {
blockWithReceipts.getReceipts(),
headerValidationPolicy.getValidationModeForNextBlock(),
ommerValidationPolicy.getValidationModeForNextBlock(),
bodyValidationMode);
bodyValidationMode,
transactionIndexingEnabled);
return blockImportResult.isImported();
}
}

View File

@@ -39,6 +39,7 @@ public class SnapSyncConfiguration {
public static final Boolean DEFAULT_SNAP_SERVER_ENABLED = Boolean.FALSE;
public static final Boolean DEFAULT_SNAP_SYNC_BFT_ENABLED = Boolean.FALSE;
public static final Boolean DEFAULT_SNAP_SYNC_TRANSACTION_INDEXING_ENABLED = Boolean.TRUE;
public static SnapSyncConfiguration getDefault() {
return ImmutableSnapSyncConfiguration.builder().build();
@@ -88,4 +89,9 @@ public class SnapSyncConfiguration {
public Boolean isSnapSyncBftEnabled() {
return DEFAULT_SNAP_SYNC_BFT_ENABLED;
}
@Value.Default
public Boolean isSnapSyncTransactionIndexingEnabled() {
return DEFAULT_SNAP_SYNC_TRANSACTION_INDEXING_ENABLED;
}
}

View File

@@ -73,7 +73,8 @@ public class ImportBlocksStepTest {
validationPolicy,
ommerValidationPolicy,
null,
pivotHeader);
pivotHeader,
true);
}
@Test
@@ -91,13 +92,23 @@ public class ImportBlocksStepTest {
blockWithReceipts.getReceipts(),
FULL,
LIGHT,
BodyValidationMode.LIGHT))
BodyValidationMode.LIGHT,
true))
.thenReturn(new BlockImportResult(true));
}
importBlocksStep.accept(blocksWithReceipts);
for (final BlockWithReceipts blockWithReceipts : blocksWithReceipts) {
verify(protocolSchedule).getByBlockHeader(blockWithReceipts.getHeader());
verify(blockImporter)
.importBlockForSyncing(
protocolContext,
blockWithReceipts.getBlock(),
blockWithReceipts.getReceipts(),
FULL,
LIGHT,
BodyValidationMode.LIGHT,
true);
}
verify(validationPolicy, times(blocks.size())).getValidationModeForNextBlock();
}
@@ -113,9 +124,50 @@ public class ImportBlocksStepTest {
blockWithReceipts.getReceipts(),
FULL,
LIGHT,
BodyValidationMode.LIGHT))
BodyValidationMode.LIGHT,
true))
.thenReturn(new BlockImportResult(false));
assertThatThrownBy(() -> importBlocksStep.accept(singletonList(blockWithReceipts)))
.isInstanceOf(InvalidBlockException.class);
}
@Test
public void shouldImportBlockWithoutTxIndexingWhenNotEnabled() {
ImportBlocksStep importBlocksStep =
new ImportBlocksStep(
protocolSchedule,
protocolContext,
validationPolicy,
ommerValidationPolicy,
null,
pivotHeader,
false);
final Block block = gen.block();
final BlockWithReceipts blockWithReceipts = new BlockWithReceipts(block, gen.receipts(block));
when(blockImporter.importBlockForSyncing(
protocolContext,
blockWithReceipts.getBlock(),
blockWithReceipts.getReceipts(),
FULL,
LIGHT,
BodyValidationMode.LIGHT,
false))
.thenReturn(new BlockImportResult(true));
importBlocksStep.accept(List.of(blockWithReceipts));
verify(protocolSchedule).getByBlockHeader(blockWithReceipts.getHeader());
verify(validationPolicy, times(1)).getValidationModeForNextBlock();
verify(blockImporter)
.importBlockForSyncing(
protocolContext,
blockWithReceipts.getBlock(),
blockWithReceipts.getReceipts(),
FULL,
LIGHT,
BodyValidationMode.LIGHT,
false);
}
}

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());
}
}

Some files were not shown because too many files have changed in this diff Show More