mirror of
https://github.com/vacp2p/status-linea-besu.git
synced 2026-01-07 22:44:05 -05:00
Merge branch 'hyperledger:main' into zkbesu
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,6 +78,7 @@ public class SynchronizerOptionsTest
|
||||
.bytecodeCountPerRequest(
|
||||
SnapSyncConfiguration.DEFAULT_BYTECODE_COUNT_PER_REQUEST + 2)
|
||||
.isSnapServerEnabled(Boolean.TRUE)
|
||||
.isSnapSyncTransactionIndexingEnabled(Boolean.TRUE)
|
||||
.build());
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -12,7 +12,10 @@
|
||||
"response": {
|
||||
"jsonrpc": "2.0",
|
||||
"id": 406,
|
||||
"result" : [ ]
|
||||
"error" : {
|
||||
"code": -32000,
|
||||
"message": "Block not found"
|
||||
}
|
||||
},
|
||||
"statusCode": 200
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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) ->
|
||||
|
||||
@@ -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: {}",
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 + '}';
|
||||
}
|
||||
}
|
||||
@@ -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 + '}';
|
||||
}
|
||||
}
|
||||
@@ -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 + '}';
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 node’s 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
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
@@ -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 node’s 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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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 + '}';
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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 + '}';
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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 + '}';
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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 node’s 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
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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 node’s 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
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
@@ -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(),
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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() {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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)));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
Reference in New Issue
Block a user