mirror of
https://github.com/vacp2p/linea-besu.git
synced 2026-01-09 21:17:54 -05:00
Add eth65 support (#608)
* Add eth65 support Signed-off-by: Antoine Toulme <antoine@lunar-ocean.com> * Fix integration tests Signed-off-by: Antoine Toulme <antoine@lunar-ocean.com> * Fix acceptance tests Signed-off-by: Antoine Toulme <antoine@lunar-ocean.com> * add acceptance test that checks that transactions are gossiped between peers Signed-off-by: Antoine Toulme <antoine@lunar-ocean.com> * Update ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/messages/LimitedNewPooledTransactionHashesMessages.java Co-Authored-By: Danno Ferrin <danno.ferrin@shemnon.com> Signed-off-by: Antoine Toulme <antoine@lunar-ocean.com> * code review comments Signed-off-by: Antoine Toulme <antoine@lunar-ocean.com> * Code review changes Signed-off-by: Antoine Toulme <antoine@lunar-ocean.com> * Reviewing diffs Signed-off-by: Antoine Toulme <antoine@lunar-ocean.com> * smaller synchronized blocks Signed-off-by: Antoine Toulme <antoine@lunar-ocean.com> Co-authored-by: Danno Ferrin <danno.ferrin@shemnon.com>
This commit is contained in:
@@ -24,6 +24,7 @@ import org.hyperledger.besu.tests.acceptance.dsl.condition.login.LoginConditions
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.condition.net.NetConditions;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.condition.perm.PermissioningConditions;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.condition.priv.PrivConditions;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.condition.txpool.TxPoolConditions;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.condition.web3.Web3Conditions;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.contract.ContractVerifier;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.node.cluster.Cluster;
|
||||
@@ -39,6 +40,7 @@ import org.hyperledger.besu.tests.acceptance.dsl.transaction.miner.MinerTransact
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.net.NetTransactions;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.perm.PermissioningTransactions;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.privacy.PrivacyTransactions;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.txpool.TxPoolTransactions;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.web3.Web3Transactions;
|
||||
|
||||
import java.io.File;
|
||||
@@ -83,6 +85,8 @@ public class AcceptanceTestBase {
|
||||
protected final Web3Conditions web3;
|
||||
protected final PrivConditions priv;
|
||||
protected final PrivacyTransactions privacyTransactions;
|
||||
protected final TxPoolConditions txPoolConditions;
|
||||
protected final TxPoolTransactions txPoolTransactions;
|
||||
|
||||
protected AcceptanceTestBase() {
|
||||
ethTransactions = new EthTransactions();
|
||||
@@ -108,6 +112,8 @@ public class AcceptanceTestBase {
|
||||
admin = new AdminConditions(adminTransactions);
|
||||
web3 = new Web3Conditions(new Web3Transactions());
|
||||
besu = new BesuNodeFactory();
|
||||
txPoolTransactions = new TxPoolTransactions();
|
||||
txPoolConditions = new TxPoolConditions(txPoolTransactions);
|
||||
contractVerifier = new ContractVerifier(accounts.getPrimaryBenefactor());
|
||||
permissionedNodeBuilder = new PermissionedNodeBuilder();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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.tests.acceptance.dsl.condition.txpool;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import org.hyperledger.besu.ethereum.core.Hash;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.WaitUtils;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.condition.Condition;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.txpool.TxPoolTransactions;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class TxPoolConditions {
|
||||
|
||||
private final TxPoolTransactions txPoolTransactions;
|
||||
|
||||
public TxPoolConditions(final TxPoolTransactions txPoolTransactions) {
|
||||
this.txPoolTransactions = txPoolTransactions;
|
||||
}
|
||||
|
||||
public Condition inTransactionPool(final Hash txHash) {
|
||||
return node ->
|
||||
WaitUtils.waitFor(
|
||||
() -> {
|
||||
List<Map<String, String>> poolContents =
|
||||
node.execute(txPoolTransactions.getTxPoolContents());
|
||||
boolean found = false;
|
||||
for (Map<String, String> txInfo : poolContents) {
|
||||
if (Hash.fromHexString(txInfo.get("hash")).equals(txHash)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assertThat(found).isTrue();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -42,6 +42,7 @@ import org.hyperledger.besu.tests.acceptance.dsl.transaction.miner.MinerRequestF
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.net.CustomRequestFactory;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.perm.PermissioningJsonRpcRequestFactory;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.privacy.PrivacyRequestFactory;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.txpool.TxPoolRequestFactory;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
@@ -330,6 +331,7 @@ public class BesuNode implements NodeConfiguration, RunnableNode, AutoCloseable
|
||||
new PrivacyRequestFactory(web3jService),
|
||||
new CustomRequestFactory(web3jService),
|
||||
new MinerRequestFactory(web3jService),
|
||||
new TxPoolRequestFactory(web3jService),
|
||||
websocketService,
|
||||
loginRequestFactory());
|
||||
}
|
||||
|
||||
@@ -120,6 +120,11 @@ public class BesuNodeConfigurationBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
public BesuNodeConfigurationBuilder jsonRpcTxPool() {
|
||||
this.jsonRpcConfiguration.addRpcApi(RpcApis.TX_POOL);
|
||||
return this;
|
||||
}
|
||||
|
||||
public BesuNodeConfigurationBuilder jsonRpcAuthenticationConfiguration(final String authFile)
|
||||
throws URISyntaxException {
|
||||
final String authTomlPath =
|
||||
|
||||
@@ -92,7 +92,12 @@ public class BesuNodeFactory {
|
||||
|
||||
public BesuNode createArchiveNode(final String name) throws IOException {
|
||||
return create(
|
||||
new BesuNodeConfigurationBuilder().name(name).jsonRpcEnabled().webSocketEnabled().build());
|
||||
new BesuNodeConfigurationBuilder()
|
||||
.name(name)
|
||||
.jsonRpcEnabled()
|
||||
.jsonRpcTxPool()
|
||||
.webSocketEnabled()
|
||||
.build());
|
||||
}
|
||||
|
||||
public BesuNode createNode(
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.hyperledger.besu.tests.acceptance.dsl.transaction.miner.MinerRequestF
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.net.CustomRequestFactory;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.perm.PermissioningJsonRpcRequestFactory;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.privacy.PrivacyRequestFactory;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.txpool.TxPoolRequestFactory;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@@ -40,6 +41,7 @@ public class NodeRequests {
|
||||
private final Optional<WebSocketService> websocketService;
|
||||
private final LoginRequestFactory login;
|
||||
private final MinerRequestFactory miner;
|
||||
private final TxPoolRequestFactory txPool;
|
||||
|
||||
public NodeRequests(
|
||||
final Web3j netEth,
|
||||
@@ -50,6 +52,7 @@ public class NodeRequests {
|
||||
final PrivacyRequestFactory privacy,
|
||||
final CustomRequestFactory custom,
|
||||
final MinerRequestFactory miner,
|
||||
final TxPoolRequestFactory txPool,
|
||||
final Optional<WebSocketService> websocketService,
|
||||
final LoginRequestFactory login) {
|
||||
this.netEth = netEth;
|
||||
@@ -60,6 +63,7 @@ public class NodeRequests {
|
||||
this.privacy = privacy;
|
||||
this.custom = custom;
|
||||
this.miner = miner;
|
||||
this.txPool = txPool;
|
||||
this.websocketService = websocketService;
|
||||
this.login = login;
|
||||
}
|
||||
@@ -104,6 +108,10 @@ public class NodeRequests {
|
||||
return miner;
|
||||
}
|
||||
|
||||
public TxPoolRequestFactory txPool() {
|
||||
return txPool;
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
netEth.shutdown();
|
||||
websocketService.ifPresent(WebSocketService::close);
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* 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.tests.acceptance.dsl.transaction.txpool;
|
||||
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.NodeRequests;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.Transaction;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class TxPoolBesuTransaction implements Transaction<List<Map<String, String>>> {
|
||||
|
||||
@Override
|
||||
public List<Map<String, String>> execute(final NodeRequests node) {
|
||||
try {
|
||||
return node.txPool().besuTransactions().send().getResult();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.tests.acceptance.dsl.transaction.txpool;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.web3j.protocol.Web3jService;
|
||||
import org.web3j.protocol.core.Request;
|
||||
import org.web3j.protocol.core.Response;
|
||||
|
||||
public class TxPoolRequestFactory {
|
||||
|
||||
private final Web3jService web3jService;
|
||||
|
||||
public TxPoolRequestFactory(final Web3jService web3jService) {
|
||||
this.web3jService = web3jService;
|
||||
}
|
||||
|
||||
Request<?, TransactionInfoResponse> besuTransactions() {
|
||||
return new Request<>(
|
||||
"txpool_besuTransactions", null, web3jService, TransactionInfoResponse.class);
|
||||
}
|
||||
|
||||
static class TransactionInfoResponse extends Response<List<Map<String, String>>> {}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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.tests.acceptance.dsl.transaction.txpool;
|
||||
|
||||
public class TxPoolTransactions {
|
||||
|
||||
public TxPoolBesuTransaction getTxPoolContents() {
|
||||
return new TxPoolBesuTransaction();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.tests.acceptance;
|
||||
|
||||
import org.hyperledger.besu.ethereum.core.Hash;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.AcceptanceTestBase;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.account.Account;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.blockchain.Amount;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.node.Node;
|
||||
import org.hyperledger.besu.tests.acceptance.dsl.transaction.account.TransferTransaction;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class GossipTransactionAcceptanceTest extends AcceptanceTestBase {
|
||||
|
||||
private Node archiveNode1;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
archiveNode1 = besu.createArchiveNode("archiveNode1");
|
||||
cluster.start(archiveNode1, besu.createArchiveNode("archiveNode2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldGossipATransaction() {
|
||||
final Account account = accounts.createAccount("account-one");
|
||||
final Amount balance = Amount.ether(20);
|
||||
|
||||
TransferTransaction tx = accountTransactions.createTransfer(account, balance);
|
||||
|
||||
Hash txHash = archiveNode1.execute(tx);
|
||||
|
||||
cluster.verify(txPoolConditions.inTransactionPool(txHash));
|
||||
}
|
||||
}
|
||||
@@ -787,6 +787,15 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
|
||||
arity = "1")
|
||||
private final Integer txPoolMaxSize = TransactionPoolConfiguration.MAX_PENDING_TRANSACTIONS;
|
||||
|
||||
@Option(
|
||||
names = {"--tx-pool-hashes-max-size"},
|
||||
paramLabel = MANDATORY_INTEGER_FORMAT_HELP,
|
||||
description =
|
||||
"Maximum number of pending transaction hashes that will be kept in the transaction pool (default: ${DEFAULT-VALUE})",
|
||||
arity = "1")
|
||||
private final Integer pooledTransactionHashesSize =
|
||||
TransactionPoolConfiguration.MAX_PENDING_TRANSACTIONS_HASHES;
|
||||
|
||||
@Option(
|
||||
names = {"--tx-pool-retention-hours"},
|
||||
paramLabel = MANDATORY_INTEGER_FORMAT_HELP,
|
||||
@@ -1690,6 +1699,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
|
||||
return transactionPoolOptions
|
||||
.toDomainObject()
|
||||
.txPoolMaxSize(txPoolMaxSize)
|
||||
.pooledTransactionHashesSize(pooledTransactionHashesSize)
|
||||
.pendingTxRetentionPeriod(pendingTxRetentionPeriod)
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ public class EthProtocolOptions implements CLIOptions<EthProtocolConfiguration>
|
||||
private static final String MAX_GET_BODIES_FLAG = "--Xewp-max-get-bodies";
|
||||
private static final String MAX_GET_RECEIPTS_FLAG = "--Xewp-max-get-receipts";
|
||||
private static final String MAX_GET_NODE_DATA_FLAG = "--Xewp-max-get-node-data";
|
||||
private static final String MAX_GET_POOLED_TRANSACTIONS = "--Xewp-max-get-pooled-transactions";
|
||||
|
||||
@CommandLine.Option(
|
||||
hidden = true,
|
||||
@@ -64,6 +65,15 @@ public class EthProtocolOptions implements CLIOptions<EthProtocolConfiguration>
|
||||
private PositiveNumber maxGetNodeData =
|
||||
PositiveNumber.fromInt(EthProtocolConfiguration.DEFAULT_MAX_GET_NODE_DATA);
|
||||
|
||||
@CommandLine.Option(
|
||||
hidden = true,
|
||||
names = {MAX_GET_POOLED_TRANSACTIONS},
|
||||
paramLabel = "<INTEGER>",
|
||||
description =
|
||||
"Maximum request limit for Ethereum Wire Protocol GET_POOLED_TRANSACTIONS. (default: ${DEFAULT-VALUE})")
|
||||
private PositiveNumber maxGetPooledTransactions =
|
||||
PositiveNumber.fromInt(EthProtocolConfiguration.DEFAULT_MAX_GET_POOLED_TRANSACTIONS);
|
||||
|
||||
private EthProtocolOptions() {}
|
||||
|
||||
public static EthProtocolOptions create() {
|
||||
@@ -76,6 +86,7 @@ public class EthProtocolOptions implements CLIOptions<EthProtocolConfiguration>
|
||||
options.maxGetBlockBodies = PositiveNumber.fromInt(config.getMaxGetBlockBodies());
|
||||
options.maxGetReceipts = PositiveNumber.fromInt(config.getMaxGetReceipts());
|
||||
options.maxGetNodeData = PositiveNumber.fromInt(config.getMaxGetNodeData());
|
||||
options.maxGetPooledTransactions = PositiveNumber.fromInt(config.getMaxGetPooledTransactions());
|
||||
return options;
|
||||
}
|
||||
|
||||
@@ -86,6 +97,7 @@ public class EthProtocolOptions implements CLIOptions<EthProtocolConfiguration>
|
||||
.maxGetBlockBodies(maxGetBlockBodies)
|
||||
.maxGetReceipts(maxGetReceipts)
|
||||
.maxGetNodeData(maxGetNodeData)
|
||||
.maxGetPooledTransactions(maxGetPooledTransactions)
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -99,6 +111,8 @@ public class EthProtocolOptions implements CLIOptions<EthProtocolConfiguration>
|
||||
MAX_GET_RECEIPTS_FLAG,
|
||||
OptionParser.format(maxGetReceipts.getValue()),
|
||||
MAX_GET_NODE_DATA_FLAG,
|
||||
OptionParser.format(maxGetNodeData.getValue()));
|
||||
OptionParser.format(maxGetNodeData.getValue()),
|
||||
MAX_GET_POOLED_TRANSACTIONS,
|
||||
OptionParser.format(maxGetPooledTransactions.getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,11 @@ import org.hyperledger.besu.ethereum.core.PrivacyParameters;
|
||||
import org.hyperledger.besu.ethereum.core.Synchronizer;
|
||||
import org.hyperledger.besu.ethereum.eth.EthProtocol;
|
||||
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthMessages;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthPeers;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
|
||||
import org.hyperledger.besu.ethereum.eth.peervalidation.ClassicForkPeerValidator;
|
||||
import org.hyperledger.besu.ethereum.eth.peervalidation.DaoForkPeerValidator;
|
||||
import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator;
|
||||
@@ -242,13 +246,40 @@ public abstract class BesuControllerBuilder<C> {
|
||||
prunerConfiguration));
|
||||
}
|
||||
}
|
||||
|
||||
final EthPeers ethPeers = new EthPeers(getSupportedProtocol(), clock, metricsSystem);
|
||||
final EthMessages ethMessages = new EthMessages();
|
||||
final EthScheduler scheduler =
|
||||
new EthScheduler(
|
||||
syncConfig.getDownloaderParallelism(),
|
||||
syncConfig.getTransactionsParallelism(),
|
||||
syncConfig.getComputationParallelism(),
|
||||
metricsSystem);
|
||||
final EthContext ethContext = new EthContext(ethPeers, ethMessages, scheduler);
|
||||
final SyncState syncState = new SyncState(blockchain, ethPeers);
|
||||
final boolean fastSyncEnabled = syncConfig.getSyncMode().equals(SyncMode.FAST);
|
||||
final TransactionPool transactionPool =
|
||||
TransactionPoolFactory.createTransactionPool(
|
||||
protocolSchedule,
|
||||
protocolContext,
|
||||
ethContext,
|
||||
clock,
|
||||
metricsSystem,
|
||||
syncState,
|
||||
miningParameters.getMinTransactionGasPrice(),
|
||||
transactionPoolConfiguration);
|
||||
|
||||
final EthProtocolManager ethProtocolManager =
|
||||
createEthProtocolManager(
|
||||
protocolContext, fastSyncEnabled, createPeerValidators(protocolSchedule));
|
||||
final SyncState syncState =
|
||||
new SyncState(blockchain, ethProtocolManager.ethContext().getEthPeers());
|
||||
protocolContext,
|
||||
fastSyncEnabled,
|
||||
transactionPool,
|
||||
ethereumWireProtocolConfiguration,
|
||||
ethPeers,
|
||||
ethContext,
|
||||
ethMessages,
|
||||
scheduler,
|
||||
createPeerValidators(protocolSchedule));
|
||||
|
||||
final Synchronizer synchronizer =
|
||||
new DefaultSynchronizer<>(
|
||||
syncConfig,
|
||||
@@ -263,17 +294,6 @@ public abstract class BesuControllerBuilder<C> {
|
||||
clock,
|
||||
metricsSystem);
|
||||
|
||||
final TransactionPool transactionPool =
|
||||
TransactionPoolFactory.createTransactionPool(
|
||||
protocolSchedule,
|
||||
protocolContext,
|
||||
ethProtocolManager.ethContext(),
|
||||
clock,
|
||||
metricsSystem,
|
||||
syncState,
|
||||
miningParameters.getMinTransactionGasPrice(),
|
||||
transactionPoolConfiguration);
|
||||
|
||||
final MiningCoordinator miningCoordinator =
|
||||
createMiningCoordinator(
|
||||
protocolSchedule,
|
||||
@@ -343,22 +363,32 @@ public abstract class BesuControllerBuilder<C> {
|
||||
protected abstract C createConsensusContext(
|
||||
Blockchain blockchain, WorldStateArchive worldStateArchive);
|
||||
|
||||
protected String getSupportedProtocol() {
|
||||
return EthProtocol.NAME;
|
||||
}
|
||||
|
||||
protected EthProtocolManager createEthProtocolManager(
|
||||
final ProtocolContext<C> protocolContext,
|
||||
final boolean fastSyncEnabled,
|
||||
final TransactionPool transactionPool,
|
||||
final EthProtocolConfiguration ethereumWireProtocolConfiguration,
|
||||
final EthPeers ethPeers,
|
||||
final EthContext ethContext,
|
||||
final EthMessages ethMessages,
|
||||
final EthScheduler scheduler,
|
||||
final List<PeerValidator> peerValidators) {
|
||||
return new EthProtocolManager(
|
||||
protocolContext.getBlockchain(),
|
||||
protocolContext.getWorldStateArchive(),
|
||||
networkId,
|
||||
protocolContext.getWorldStateArchive(),
|
||||
transactionPool,
|
||||
ethereumWireProtocolConfiguration,
|
||||
ethPeers,
|
||||
ethMessages,
|
||||
ethContext,
|
||||
peerValidators,
|
||||
fastSyncEnabled,
|
||||
syncConfig.getDownloaderParallelism(),
|
||||
syncConfig.getTransactionsParallelism(),
|
||||
syncConfig.getComputationParallelism(),
|
||||
clock,
|
||||
metricsSystem,
|
||||
ethereumWireProtocolConfiguration,
|
||||
scheduler,
|
||||
genesisConfig.getForks());
|
||||
}
|
||||
|
||||
|
||||
@@ -31,7 +31,12 @@ import org.hyperledger.besu.ethereum.blockcreation.NoopMiningCoordinator;
|
||||
import org.hyperledger.besu.ethereum.chain.Blockchain;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||
import org.hyperledger.besu.ethereum.core.MiningParameters;
|
||||
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthMessages;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthPeers;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
|
||||
import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator;
|
||||
import org.hyperledger.besu.ethereum.eth.sync.state.SyncState;
|
||||
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
|
||||
@@ -106,23 +111,34 @@ public class IbftLegacyBesuControllerBuilder extends BesuControllerBuilder<IbftC
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getSupportedProtocol() {
|
||||
return Istanbul64Protocol.get().getName();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected EthProtocolManager createEthProtocolManager(
|
||||
final ProtocolContext<IbftContext> protocolContext,
|
||||
final boolean fastSyncEnabled,
|
||||
final TransactionPool transactionPool,
|
||||
final EthProtocolConfiguration ethereumWireProtocolConfiguration,
|
||||
final EthPeers ethPeers,
|
||||
final EthContext ethContext,
|
||||
final EthMessages ethMessages,
|
||||
final EthScheduler scheduler,
|
||||
final List<PeerValidator> peerValidators) {
|
||||
LOG.info("Operating on IBFT-1.0 network.");
|
||||
return new Istanbul64ProtocolManager(
|
||||
protocolContext.getBlockchain(),
|
||||
protocolContext.getWorldStateArchive(),
|
||||
networkId,
|
||||
protocolContext.getWorldStateArchive(),
|
||||
transactionPool,
|
||||
ethereumWireProtocolConfiguration,
|
||||
ethPeers,
|
||||
ethMessages,
|
||||
ethContext,
|
||||
peerValidators,
|
||||
fastSyncEnabled,
|
||||
syncConfig.getDownloaderParallelism(),
|
||||
syncConfig.getTransactionsParallelism(),
|
||||
syncConfig.getComputationParallelism(),
|
||||
clock,
|
||||
metricsSystem,
|
||||
ethereumWireProtocolConfiguration);
|
||||
scheduler);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,6 +130,9 @@ public class EthProtocolOptionsTest
|
||||
PositiveNumber.fromInt(EthProtocolConfiguration.DEFAULT_MAX_GET_RECEIPTS + 2))
|
||||
.maxGetNodeData(
|
||||
PositiveNumber.fromInt(EthProtocolConfiguration.DEFAULT_MAX_GET_NODE_DATA + 2))
|
||||
.maxGetPooledTransactions(
|
||||
PositiveNumber.fromInt(
|
||||
EthProtocolConfiguration.DEFAULT_MAX_GET_POOLED_TRANSACTIONS + 2))
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@@ -103,10 +103,15 @@ public class BesuEventsImplTest {
|
||||
new KeyValueStoragePrefixedKeyBlockchainStorage(
|
||||
new InMemoryKeyValueStorage(), new MainnetBlockHeaderFunctions()),
|
||||
new NoOpMetricsSystem());
|
||||
|
||||
when(mockEthContext.getEthMessages()).thenReturn(mockEthMessages);
|
||||
when(mockEthContext.getEthPeers()).thenReturn(mockEthPeers);
|
||||
when(mockEthContext.getScheduler()).thenReturn(mockEthScheduler);
|
||||
when(mockEthPeers.streamAvailablePeers()).thenReturn(Stream.empty()).thenReturn(Stream.empty());
|
||||
when(mockEthPeers.streamAvailablePeers())
|
||||
.thenReturn(Stream.empty())
|
||||
.thenReturn(Stream.empty())
|
||||
.thenReturn(Stream.empty())
|
||||
.thenReturn(Stream.empty());
|
||||
when(mockProtocolContext.getBlockchain()).thenReturn(blockchain);
|
||||
when(mockProtocolContext.getWorldStateArchive()).thenReturn(mockWorldStateArchive);
|
||||
when(mockProtocolSchedule.getByBlockNumber(anyLong())).thenReturn(mockProtocolSpec);
|
||||
@@ -118,6 +123,9 @@ public class BesuEventsImplTest {
|
||||
|
||||
blockBroadcaster = new BlockBroadcaster(mockEthContext);
|
||||
syncState = new SyncState(blockchain, mockEthPeers);
|
||||
TransactionPoolConfiguration txPoolConfig =
|
||||
TransactionPoolConfiguration.builder().txPoolMaxSize(1).build();
|
||||
|
||||
transactionPool =
|
||||
TransactionPoolFactory.createTransactionPool(
|
||||
mockProtocolSchedule,
|
||||
@@ -127,7 +135,7 @@ public class BesuEventsImplTest {
|
||||
new NoOpMetricsSystem(),
|
||||
syncState,
|
||||
Wei.ZERO,
|
||||
TransactionPoolConfiguration.builder().txPoolMaxSize(1).build());
|
||||
txPoolConfig);
|
||||
|
||||
serviceImpl = new BesuEventsImpl(blockchain, blockBroadcaster, transactionPool, syncState);
|
||||
}
|
||||
|
||||
@@ -129,6 +129,7 @@ privacy-onchain-groups-enabled=false
|
||||
tx-pool-retention-hours=999
|
||||
|
||||
tx-pool-max-size=1234
|
||||
tx-pool-hashes-max-size=10000
|
||||
Xincoming-tx-messages-keep-alive-seconds=60
|
||||
|
||||
# Revert Reason
|
||||
|
||||
@@ -122,6 +122,7 @@ public class CliqueBlockCreatorTest {
|
||||
new PendingTransactions(
|
||||
TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS,
|
||||
5,
|
||||
5,
|
||||
TestClock.fixed(),
|
||||
metricsSystem),
|
||||
protocolContext,
|
||||
@@ -153,6 +154,7 @@ public class CliqueBlockCreatorTest {
|
||||
new PendingTransactions(
|
||||
TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS,
|
||||
5,
|
||||
5,
|
||||
TestClock.fixed(),
|
||||
metricsSystem),
|
||||
protocolContext,
|
||||
@@ -183,6 +185,7 @@ public class CliqueBlockCreatorTest {
|
||||
new PendingTransactions(
|
||||
TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS,
|
||||
5,
|
||||
5,
|
||||
TestClock.fixed(),
|
||||
metricsSystem),
|
||||
protocolContext,
|
||||
@@ -216,6 +219,7 @@ public class CliqueBlockCreatorTest {
|
||||
new PendingTransactions(
|
||||
TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS,
|
||||
5,
|
||||
5,
|
||||
TestClock.fixed(),
|
||||
metricsSystem),
|
||||
protocolContext,
|
||||
|
||||
@@ -97,6 +97,7 @@ public class CliqueMinerExecutorTest {
|
||||
new PendingTransactions(
|
||||
TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS,
|
||||
1,
|
||||
5,
|
||||
TestClock.fixed(),
|
||||
metricsSystem),
|
||||
proposerKeyPair,
|
||||
@@ -134,6 +135,7 @@ public class CliqueMinerExecutorTest {
|
||||
new PendingTransactions(
|
||||
TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS,
|
||||
1,
|
||||
5,
|
||||
TestClock.fixed(),
|
||||
metricsSystem),
|
||||
proposerKeyPair,
|
||||
@@ -171,6 +173,7 @@ public class CliqueMinerExecutorTest {
|
||||
new PendingTransactions(
|
||||
TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS,
|
||||
1,
|
||||
5,
|
||||
TestClock.fixed(),
|
||||
metricsSystem),
|
||||
proposerKeyPair,
|
||||
|
||||
@@ -300,7 +300,7 @@ public class TestContextBuilder {
|
||||
|
||||
final PendingTransactions pendingTransactions =
|
||||
new PendingTransactions(
|
||||
TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS, 1, clock, metricsSystem);
|
||||
TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS, 1, 1, clock, metricsSystem);
|
||||
|
||||
final IbftBlockCreatorFactory blockCreatorFactory =
|
||||
new IbftBlockCreatorFactory(
|
||||
|
||||
@@ -87,6 +87,7 @@ public class IbftBlockCreatorTest {
|
||||
new PendingTransactions(
|
||||
TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS,
|
||||
1,
|
||||
5,
|
||||
TestClock.fixed(),
|
||||
metricsSystem);
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ package org.hyperledger.besu.consensus.ibftlegacy.protocol;
|
||||
|
||||
import org.hyperledger.besu.ethereum.eth.messages.EthPV62;
|
||||
import org.hyperledger.besu.ethereum.eth.messages.EthPV63;
|
||||
import org.hyperledger.besu.ethereum.eth.messages.EthPV65;
|
||||
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Capability;
|
||||
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.SubProtocol;
|
||||
|
||||
@@ -47,6 +48,9 @@ public class Istanbul64Protocol implements SubProtocol {
|
||||
EthPV62.GET_BLOCK_BODIES,
|
||||
EthPV62.BLOCK_BODIES,
|
||||
EthPV62.NEW_BLOCK,
|
||||
EthPV65.NEW_POOLED_TRANSACTION_HASHES,
|
||||
EthPV65.GET_POOLED_TRANSACTIONS,
|
||||
EthPV65.POOLED_TRANSACTIONS,
|
||||
EthPV63.GET_NODE_DATA,
|
||||
EthPV63.NODE_DATA,
|
||||
EthPV63.GET_RECEIPTS,
|
||||
@@ -90,6 +94,12 @@ public class Istanbul64Protocol implements SubProtocol {
|
||||
return "BlockBodies";
|
||||
case EthPV62.NEW_BLOCK:
|
||||
return "NewBlock";
|
||||
case EthPV65.NEW_POOLED_TRANSACTION_HASHES:
|
||||
return "NewPooledTransactionHashes";
|
||||
case EthPV65.GET_POOLED_TRANSACTIONS:
|
||||
return "GetPooledTransactions";
|
||||
case EthPV65.POOLED_TRANSACTIONS:
|
||||
return "PooledTransactions";
|
||||
case EthPV63.GET_NODE_DATA:
|
||||
return "GetNodeData";
|
||||
case EthPV63.NODE_DATA:
|
||||
|
||||
@@ -18,14 +18,17 @@ import static java.util.Collections.singletonList;
|
||||
|
||||
import org.hyperledger.besu.ethereum.chain.Blockchain;
|
||||
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthMessages;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthPeers;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
|
||||
import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator;
|
||||
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
|
||||
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Capability;
|
||||
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
|
||||
import org.hyperledger.besu.plugin.services.MetricsSystem;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.time.Clock;
|
||||
import java.util.List;
|
||||
|
||||
/** This allows for interoperability with Quorum, but shouldn't be used otherwise. */
|
||||
@@ -33,28 +36,28 @@ public class Istanbul64ProtocolManager extends EthProtocolManager {
|
||||
|
||||
public Istanbul64ProtocolManager(
|
||||
final Blockchain blockchain,
|
||||
final WorldStateArchive worldStateArchive,
|
||||
final BigInteger networkId,
|
||||
final WorldStateArchive worldStateArchive,
|
||||
final TransactionPool transactionPool,
|
||||
final EthProtocolConfiguration ethereumWireProtocolConfiguration,
|
||||
final EthPeers ethPeers,
|
||||
final EthMessages ethMessages,
|
||||
final EthContext ethContext,
|
||||
final List<PeerValidator> peerValidators,
|
||||
final boolean fastSyncEnabled,
|
||||
final int syncWorkers,
|
||||
final int txWorkers,
|
||||
final int computationWorkers,
|
||||
final Clock clock,
|
||||
final MetricsSystem metricsSystem,
|
||||
final EthProtocolConfiguration ethereumWireProtocolConfiguration) {
|
||||
final EthScheduler scheduler) {
|
||||
super(
|
||||
blockchain,
|
||||
worldStateArchive,
|
||||
networkId,
|
||||
worldStateArchive,
|
||||
transactionPool,
|
||||
ethereumWireProtocolConfiguration,
|
||||
ethPeers,
|
||||
ethMessages,
|
||||
ethContext,
|
||||
peerValidators,
|
||||
fastSyncEnabled,
|
||||
syncWorkers,
|
||||
txWorkers,
|
||||
computationWorkers,
|
||||
clock,
|
||||
metricsSystem,
|
||||
ethereumWireProtocolConfiguration);
|
||||
scheduler);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -101,6 +101,7 @@ public class IbftBlockCreatorTest {
|
||||
new PendingTransactions(
|
||||
TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS,
|
||||
1,
|
||||
5,
|
||||
TestClock.fixed(),
|
||||
metricsSystem),
|
||||
protContext,
|
||||
|
||||
@@ -48,6 +48,7 @@ import org.hyperledger.besu.ethereum.core.Wei;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthPeers;
|
||||
import org.hyperledger.besu.ethereum.eth.sync.state.SyncState;
|
||||
import org.hyperledger.besu.ethereum.eth.transactions.PeerPendingTransactionTracker;
|
||||
import org.hyperledger.besu.ethereum.eth.transactions.PeerTransactionTracker;
|
||||
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransactions;
|
||||
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
|
||||
@@ -73,6 +74,7 @@ import org.mockito.junit.MockitoJUnitRunner;
|
||||
public class EthGetFilterChangesIntegrationTest {
|
||||
|
||||
@Mock private TransactionBatchAddedListener batchAddedListener;
|
||||
@Mock private TransactionBatchAddedListener pendingBatchAddedListener;
|
||||
private MutableBlockchain blockchain;
|
||||
private final String ETH_METHOD = "eth_getFilterChanges";
|
||||
private final String JSON_RPC_VERSION = "2.0";
|
||||
@@ -83,10 +85,12 @@ public class EthGetFilterChangesIntegrationTest {
|
||||
new PendingTransactions(
|
||||
TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS,
|
||||
MAX_TRANSACTIONS,
|
||||
MAX_HASHES,
|
||||
TestClock.fixed(),
|
||||
metricsSystem);
|
||||
|
||||
private static final int MAX_TRANSACTIONS = 5;
|
||||
private static final int MAX_HASHES = 5;
|
||||
private static final KeyPair keyPair = KeyPair.generate();
|
||||
private final Transaction transaction = createTransaction(1);
|
||||
private FilterManager filterManager;
|
||||
@@ -100,6 +104,8 @@ public class EthGetFilterChangesIntegrationTest {
|
||||
final ProtocolContext<Void> protocolContext = executionContext.getProtocolContext();
|
||||
|
||||
PeerTransactionTracker peerTransactionTracker = mock(PeerTransactionTracker.class);
|
||||
PeerPendingTransactionTracker peerPendingTransactionTracker =
|
||||
mock(PeerPendingTransactionTracker.class);
|
||||
EthContext ethContext = mock(EthContext.class);
|
||||
EthPeers ethPeers = mock(EthPeers.class);
|
||||
when(ethContext.getEthPeers()).thenReturn(ethPeers);
|
||||
@@ -110,9 +116,11 @@ public class EthGetFilterChangesIntegrationTest {
|
||||
executionContext.getProtocolSchedule(),
|
||||
protocolContext,
|
||||
batchAddedListener,
|
||||
pendingBatchAddedListener,
|
||||
syncState,
|
||||
ethContext,
|
||||
peerTransactionTracker,
|
||||
peerPendingTransactionTracker,
|
||||
Wei.ZERO,
|
||||
metricsSystem);
|
||||
final BlockchainQueries blockchainQueries =
|
||||
|
||||
@@ -72,6 +72,7 @@ public class BlockTransactionSelectorTest {
|
||||
new PendingTransactions(
|
||||
TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS,
|
||||
5,
|
||||
5,
|
||||
TestClock.fixed(),
|
||||
metricsSystem);
|
||||
private final Blockchain blockchain = new TestBlockchain();
|
||||
|
||||
@@ -82,6 +82,7 @@ public class EthHashBlockCreatorTest {
|
||||
new PendingTransactions(
|
||||
TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS,
|
||||
1,
|
||||
5,
|
||||
TestClock.fixed(),
|
||||
metricsSystem);
|
||||
|
||||
@@ -131,6 +132,7 @@ public class EthHashBlockCreatorTest {
|
||||
new PendingTransactions(
|
||||
TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS,
|
||||
1,
|
||||
5,
|
||||
TestClock.fixed(),
|
||||
metricsSystem);
|
||||
|
||||
@@ -175,6 +177,7 @@ public class EthHashBlockCreatorTest {
|
||||
new PendingTransactions(
|
||||
TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS,
|
||||
1,
|
||||
5,
|
||||
TestClock.fixed(),
|
||||
metricsSystem);
|
||||
|
||||
@@ -235,6 +238,7 @@ public class EthHashBlockCreatorTest {
|
||||
new PendingTransactions(
|
||||
TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS,
|
||||
1,
|
||||
5,
|
||||
TestClock.fixed(),
|
||||
metricsSystem);
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ public class EthHashMinerExecutorTest {
|
||||
new PendingTransactions(
|
||||
TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS,
|
||||
1,
|
||||
5,
|
||||
TestClock.fixed(),
|
||||
metricsSystem);
|
||||
|
||||
@@ -66,6 +67,7 @@ public class EthHashMinerExecutorTest {
|
||||
new PendingTransactions(
|
||||
TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS,
|
||||
1,
|
||||
5,
|
||||
TestClock.fixed(),
|
||||
metricsSystem);
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ package org.hyperledger.besu.ethereum.core;
|
||||
import static org.assertj.core.util.Preconditions.checkArgument;
|
||||
import static org.hyperledger.besu.ethereum.core.InMemoryStorageProvider.createInMemoryBlockchain;
|
||||
import static org.hyperledger.besu.ethereum.core.InMemoryStorageProvider.createInMemoryWorldStateArchive;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import org.hyperledger.besu.config.GenesisConfigFile;
|
||||
import org.hyperledger.besu.ethereum.ProtocolContext;
|
||||
@@ -24,6 +25,7 @@ import org.hyperledger.besu.ethereum.chain.Blockchain;
|
||||
import org.hyperledger.besu.ethereum.chain.GenesisState;
|
||||
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
|
||||
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
|
||||
import org.hyperledger.besu.ethereum.mainnet.HeaderValidationMode;
|
||||
import org.hyperledger.besu.ethereum.mainnet.MainnetProtocolSchedule;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
|
||||
@@ -52,6 +54,7 @@ public class BlockchainSetupUtil<C> {
|
||||
private final ProtocolContext<C> protocolContext;
|
||||
private final ProtocolSchedule<C> protocolSchedule;
|
||||
private final WorldStateArchive worldArchive;
|
||||
private final TransactionPool transactionPool;
|
||||
private final List<Block> blocks;
|
||||
private final EthScheduler scheduler;
|
||||
private long maxBlockNumber;
|
||||
@@ -62,6 +65,7 @@ public class BlockchainSetupUtil<C> {
|
||||
final ProtocolContext<C> protocolContext,
|
||||
final ProtocolSchedule<C> protocolSchedule,
|
||||
final WorldStateArchive worldArchive,
|
||||
final TransactionPool transactionPool,
|
||||
final List<Block> blocks,
|
||||
final EthScheduler scheduler) {
|
||||
this.genesisState = genesisState;
|
||||
@@ -69,6 +73,7 @@ public class BlockchainSetupUtil<C> {
|
||||
this.protocolContext = protocolContext;
|
||||
this.protocolSchedule = protocolSchedule;
|
||||
this.worldArchive = worldArchive;
|
||||
this.transactionPool = transactionPool;
|
||||
this.blocks = blocks;
|
||||
this.scheduler = scheduler;
|
||||
}
|
||||
@@ -150,6 +155,7 @@ public class BlockchainSetupUtil<C> {
|
||||
final GenesisState genesisState = GenesisState.fromJson(genesisJson, protocolSchedule);
|
||||
final MutableBlockchain blockchain = createInMemoryBlockchain(genesisState.getBlock());
|
||||
final WorldStateArchive worldArchive = createInMemoryWorldStateArchive();
|
||||
final TransactionPool transactionPool = mock(TransactionPool.class);
|
||||
|
||||
genesisState.writeStateTo(worldArchive.getMutable());
|
||||
final ProtocolContext<T> protocolContext =
|
||||
@@ -172,6 +178,7 @@ public class BlockchainSetupUtil<C> {
|
||||
protocolContext,
|
||||
protocolSchedule,
|
||||
worldArchive,
|
||||
transactionPool,
|
||||
blocks,
|
||||
scheduler);
|
||||
} catch (final IOException | URISyntaxException ex) {
|
||||
@@ -209,6 +216,10 @@ public class BlockchainSetupUtil<C> {
|
||||
return scheduler;
|
||||
}
|
||||
|
||||
public TransactionPool getTransactionPool() {
|
||||
return transactionPool;
|
||||
}
|
||||
|
||||
private void importBlocks(final List<Block> blocks) {
|
||||
for (final Block block : blocks) {
|
||||
if (block.getHeader().getNumber() == BlockHeader.GENESIS_BLOCK_NUMBER) {
|
||||
|
||||
@@ -16,6 +16,7 @@ package org.hyperledger.besu.ethereum.eth;
|
||||
|
||||
import org.hyperledger.besu.ethereum.eth.messages.EthPV62;
|
||||
import org.hyperledger.besu.ethereum.eth.messages.EthPV63;
|
||||
import org.hyperledger.besu.ethereum.eth.messages.EthPV65;
|
||||
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Capability;
|
||||
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.SubProtocol;
|
||||
|
||||
@@ -32,6 +33,8 @@ public class EthProtocol implements SubProtocol {
|
||||
public static final Capability ETH62 = Capability.create(NAME, EthVersion.V62);
|
||||
public static final Capability ETH63 = Capability.create(NAME, EthVersion.V63);
|
||||
public static final Capability ETH64 = Capability.create(NAME, EthVersion.V64);
|
||||
public static final Capability ETH65 = Capability.create(NAME, EthVersion.V65);
|
||||
|
||||
private static final EthProtocol INSTANCE = new EthProtocol();
|
||||
|
||||
private static final List<Integer> eth62Messages =
|
||||
@@ -53,6 +56,16 @@ public class EthProtocol implements SubProtocol {
|
||||
EthPV63.GET_NODE_DATA, EthPV63.NODE_DATA, EthPV63.GET_RECEIPTS, EthPV63.RECEIPTS));
|
||||
}
|
||||
|
||||
private static final List<Integer> eth65Messages = new ArrayList<>(eth63Messages);
|
||||
|
||||
static {
|
||||
eth65Messages.addAll(
|
||||
Arrays.asList(
|
||||
EthPV65.NEW_POOLED_TRANSACTION_HASHES,
|
||||
EthPV65.GET_POOLED_TRANSACTIONS,
|
||||
EthPV65.POOLED_TRANSACTIONS));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return NAME;
|
||||
@@ -65,6 +78,9 @@ public class EthProtocol implements SubProtocol {
|
||||
return 8;
|
||||
case EthVersion.V63:
|
||||
case EthVersion.V64:
|
||||
case EthVersion
|
||||
.V65: // same number of messages in the range, eth65 defines messages in the middle of the
|
||||
// range defined by eth63.
|
||||
return 17;
|
||||
default:
|
||||
return 0;
|
||||
@@ -79,6 +95,8 @@ public class EthProtocol implements SubProtocol {
|
||||
case EthVersion.V63:
|
||||
case EthVersion.V64:
|
||||
return eth63Messages.contains(code);
|
||||
case EthVersion.V65:
|
||||
return eth65Messages.contains(code);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -103,6 +121,12 @@ public class EthProtocol implements SubProtocol {
|
||||
return "BlockBodies";
|
||||
case EthPV62.NEW_BLOCK:
|
||||
return "NewBlock";
|
||||
case EthPV65.NEW_POOLED_TRANSACTION_HASHES:
|
||||
return "NewPooledTransactionHashes";
|
||||
case EthPV65.GET_POOLED_TRANSACTIONS:
|
||||
return "GetPooledTransactions";
|
||||
case EthPV65.POOLED_TRANSACTIONS:
|
||||
return "PooledTransactions";
|
||||
case EthPV63.GET_NODE_DATA:
|
||||
return "GetNodeData";
|
||||
case EthPV63.NODE_DATA:
|
||||
@@ -124,5 +148,6 @@ public class EthProtocol implements SubProtocol {
|
||||
public static final int V62 = 62;
|
||||
public static final int V63 = 63;
|
||||
public static final int V64 = 64;
|
||||
public static final int V65 = 65;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,21 +26,25 @@ public class EthProtocolConfiguration {
|
||||
public static final int DEFAULT_MAX_GET_BLOCK_BODIES = 128;
|
||||
public static final int DEFAULT_MAX_GET_RECEIPTS = 256;
|
||||
public static final int DEFAULT_MAX_GET_NODE_DATA = 384;
|
||||
public static final int DEFAULT_MAX_GET_POOLED_TRANSACTIONS = 256;
|
||||
|
||||
private final int maxGetBlockHeaders;
|
||||
private final int maxGetBlockBodies;
|
||||
private final int maxGetReceipts;
|
||||
private final int maxGetNodeData;
|
||||
private final int maxGetPooledTransactions;
|
||||
|
||||
public EthProtocolConfiguration(
|
||||
final int maxGetBlockHeaders,
|
||||
final int maxGetBlockBodies,
|
||||
final int maxGetReceipts,
|
||||
final int maxGetNodeData) {
|
||||
final int maxGetNodeData,
|
||||
final int maxGetPooledTransactions) {
|
||||
this.maxGetBlockHeaders = maxGetBlockHeaders;
|
||||
this.maxGetBlockBodies = maxGetBlockBodies;
|
||||
this.maxGetReceipts = maxGetReceipts;
|
||||
this.maxGetNodeData = maxGetNodeData;
|
||||
this.maxGetPooledTransactions = maxGetPooledTransactions;
|
||||
}
|
||||
|
||||
public static EthProtocolConfiguration defaultConfig() {
|
||||
@@ -48,7 +52,8 @@ public class EthProtocolConfiguration {
|
||||
DEFAULT_MAX_GET_BLOCK_HEADERS,
|
||||
DEFAULT_MAX_GET_BLOCK_BODIES,
|
||||
DEFAULT_MAX_GET_RECEIPTS,
|
||||
DEFAULT_MAX_GET_NODE_DATA);
|
||||
DEFAULT_MAX_GET_NODE_DATA,
|
||||
DEFAULT_MAX_GET_POOLED_TRANSACTIONS);
|
||||
}
|
||||
|
||||
public static Builder builder() {
|
||||
@@ -71,6 +76,10 @@ public class EthProtocolConfiguration {
|
||||
return maxGetNodeData;
|
||||
}
|
||||
|
||||
public int getMaxGetPooledTransactions() {
|
||||
return maxGetPooledTransactions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) {
|
||||
@@ -83,7 +92,8 @@ public class EthProtocolConfiguration {
|
||||
return maxGetBlockHeaders == that.maxGetBlockHeaders
|
||||
&& maxGetBlockBodies == that.maxGetBlockBodies
|
||||
&& maxGetReceipts == that.maxGetReceipts
|
||||
&& maxGetNodeData == that.maxGetNodeData;
|
||||
&& maxGetNodeData == that.maxGetNodeData
|
||||
&& maxGetPooledTransactions == that.maxGetPooledTransactions;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -98,6 +108,7 @@ public class EthProtocolConfiguration {
|
||||
.add("maxGetBlockBodies", maxGetBlockBodies)
|
||||
.add("maxGetReceipts", maxGetReceipts)
|
||||
.add("maxGetNodeData", maxGetNodeData)
|
||||
.add("maxGetPooledTransactions", maxGetPooledTransactions)
|
||||
.toString();
|
||||
}
|
||||
|
||||
@@ -114,6 +125,9 @@ public class EthProtocolConfiguration {
|
||||
private PositiveNumber maxGetNodeData =
|
||||
PositiveNumber.fromInt(EthProtocolConfiguration.DEFAULT_MAX_GET_NODE_DATA);
|
||||
|
||||
private PositiveNumber maxGetPooledTransactions =
|
||||
PositiveNumber.fromInt(EthProtocolConfiguration.DEFAULT_MAX_GET_POOLED_TRANSACTIONS);
|
||||
|
||||
public Builder maxGetBlockHeaders(final PositiveNumber maxGetBlockHeaders) {
|
||||
this.maxGetBlockHeaders = maxGetBlockHeaders;
|
||||
return this;
|
||||
@@ -134,12 +148,18 @@ public class EthProtocolConfiguration {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder maxGetPooledTransactions(final PositiveNumber maxGetPooledTransactions) {
|
||||
this.maxGetPooledTransactions = maxGetPooledTransactions;
|
||||
return this;
|
||||
}
|
||||
|
||||
public EthProtocolConfiguration build() {
|
||||
return new EthProtocolConfiguration(
|
||||
maxGetBlockHeaders.getValue(),
|
||||
maxGetBlockBodies.getValue(),
|
||||
maxGetReceipts.getValue(),
|
||||
maxGetNodeData.getValue());
|
||||
maxGetNodeData.getValue(),
|
||||
maxGetPooledTransactions.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,9 +20,11 @@ import org.hyperledger.besu.ethereum.core.Difficulty;
|
||||
import org.hyperledger.besu.ethereum.core.Hash;
|
||||
import org.hyperledger.besu.ethereum.eth.messages.EthPV62;
|
||||
import org.hyperledger.besu.ethereum.eth.messages.EthPV63;
|
||||
import org.hyperledger.besu.ethereum.eth.messages.EthPV65;
|
||||
import org.hyperledger.besu.ethereum.eth.messages.GetBlockBodiesMessage;
|
||||
import org.hyperledger.besu.ethereum.eth.messages.GetBlockHeadersMessage;
|
||||
import org.hyperledger.besu.ethereum.eth.messages.GetNodeDataMessage;
|
||||
import org.hyperledger.besu.ethereum.eth.messages.GetPooledTransactionsMessage;
|
||||
import org.hyperledger.besu.ethereum.eth.messages.GetReceiptsMessage;
|
||||
import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator;
|
||||
import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection;
|
||||
@@ -67,6 +69,7 @@ public class EthPeer {
|
||||
private final RequestManager bodiesRequestManager = new RequestManager(this);
|
||||
private final RequestManager receiptsRequestManager = new RequestManager(this);
|
||||
private final RequestManager nodeDataRequestManager = new RequestManager(this);
|
||||
private final RequestManager pooledTransactionsRequestManager = new RequestManager(this);
|
||||
|
||||
private final AtomicReference<Consumer<EthPeer>> onStatusesExchanged = new AtomicReference<>();
|
||||
private final PeerReputation reputation = new PeerReputation();
|
||||
@@ -154,6 +157,8 @@ public class EthPeer {
|
||||
return sendRequest(receiptsRequestManager, messageData);
|
||||
case EthPV63.GET_NODE_DATA:
|
||||
return sendRequest(nodeDataRequestManager, messageData);
|
||||
case EthPV65.GET_POOLED_TRANSACTIONS:
|
||||
return sendRequest(pooledTransactionsRequestManager, messageData);
|
||||
default:
|
||||
connection.sendForProtocol(protocolName, messageData);
|
||||
return null;
|
||||
@@ -201,6 +206,12 @@ public class EthPeer {
|
||||
return sendRequest(nodeDataRequestManager, message);
|
||||
}
|
||||
|
||||
public RequestManager.ResponseStream getPooledTransactions(final List<Hash> hashes)
|
||||
throws PeerNotConnected {
|
||||
final GetPooledTransactionsMessage message = GetPooledTransactionsMessage.create(hashes);
|
||||
return sendRequest(pooledTransactionsRequestManager, message);
|
||||
}
|
||||
|
||||
boolean validateReceivedMessage(final EthMessage message) {
|
||||
checkArgument(message.getPeer().equals(this), "Mismatched message sent to peer for dispatch");
|
||||
switch (message.getData().getCode()) {
|
||||
@@ -228,6 +239,12 @@ public class EthPeer {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case EthPV65.POOLED_TRANSACTIONS:
|
||||
if (pooledTransactionsRequestManager.outstandingRequests() == 0) {
|
||||
LOG.warn("Unsolicited pooling transactions received.");
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Nothing to do
|
||||
}
|
||||
@@ -258,6 +275,10 @@ public class EthPeer {
|
||||
reputation.resetTimeoutCount(EthPV63.GET_NODE_DATA);
|
||||
nodeDataRequestManager.dispatchResponse(message);
|
||||
break;
|
||||
case EthPV65.POOLED_TRANSACTIONS:
|
||||
reputation.resetTimeoutCount(EthPV65.GET_POOLED_TRANSACTIONS);
|
||||
pooledTransactionsRequestManager.dispatchResponse(message);
|
||||
break;
|
||||
default:
|
||||
// Nothing to do
|
||||
}
|
||||
@@ -272,6 +293,7 @@ public class EthPeer {
|
||||
bodiesRequestManager.close();
|
||||
receiptsRequestManager.close();
|
||||
nodeDataRequestManager.close();
|
||||
pooledTransactionsRequestManager.close();
|
||||
}
|
||||
|
||||
public void registerKnownBlock(final Hash hash) {
|
||||
@@ -344,7 +366,8 @@ public class EthPeer {
|
||||
return headersRequestManager.outstandingRequests()
|
||||
+ bodiesRequestManager.outstandingRequests()
|
||||
+ receiptsRequestManager.outstandingRequests()
|
||||
+ nodeDataRequestManager.outstandingRequests();
|
||||
+ nodeDataRequestManager.outstandingRequests()
|
||||
+ pooledTransactionsRequestManager.outstandingRequests();
|
||||
}
|
||||
|
||||
public long getLastRequestTimestamp() {
|
||||
|
||||
@@ -29,6 +29,7 @@ import org.hyperledger.besu.ethereum.eth.messages.StatusMessage;
|
||||
import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator;
|
||||
import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidatorRunner;
|
||||
import org.hyperledger.besu.ethereum.eth.sync.BlockBroadcaster;
|
||||
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
|
||||
import org.hyperledger.besu.ethereum.p2p.network.ProtocolManager;
|
||||
import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection;
|
||||
import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection.PeerNotConnected;
|
||||
@@ -38,10 +39,8 @@ import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData;
|
||||
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.messages.DisconnectMessage.DisconnectReason;
|
||||
import org.hyperledger.besu.ethereum.rlp.RLPException;
|
||||
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
|
||||
import org.hyperledger.besu.plugin.services.MetricsSystem;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.time.Clock;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
@@ -54,9 +53,9 @@ import org.apache.logging.log4j.Logger;
|
||||
public class EthProtocolManager implements ProtocolManager, MinedBlockObserver {
|
||||
private static final Logger LOG = LogManager.getLogger();
|
||||
private static final List<Capability> FAST_SYNC_CAPS =
|
||||
List.of(EthProtocol.ETH63, EthProtocol.ETH64);
|
||||
List.of(EthProtocol.ETH63, EthProtocol.ETH64, EthProtocol.ETH65);
|
||||
private static final List<Capability> FULL_SYNC_CAPS =
|
||||
List.of(EthProtocol.ETH62, EthProtocol.ETH63, EthProtocol.ETH64);
|
||||
List.of(EthProtocol.ETH62, EthProtocol.ETH63, EthProtocol.ETH64, EthProtocol.ETH65);
|
||||
|
||||
private final EthScheduler scheduler;
|
||||
private final CountDownLatch shutdown;
|
||||
@@ -76,14 +75,16 @@ public class EthProtocolManager implements ProtocolManager, MinedBlockObserver {
|
||||
|
||||
public EthProtocolManager(
|
||||
final Blockchain blockchain,
|
||||
final WorldStateArchive worldStateArchive,
|
||||
final BigInteger networkId,
|
||||
final WorldStateArchive worldStateArchive,
|
||||
final TransactionPool transactionPool,
|
||||
final EthProtocolConfiguration ethereumWireProtocolConfiguration,
|
||||
final EthPeers ethPeers,
|
||||
final EthMessages ethMessages,
|
||||
final EthContext ethContext,
|
||||
final List<PeerValidator> peerValidators,
|
||||
final boolean fastSyncEnabled,
|
||||
final EthScheduler scheduler,
|
||||
final EthProtocolConfiguration ethereumWireProtocolConfiguration,
|
||||
final Clock clock,
|
||||
final MetricsSystem metricsSystem,
|
||||
final ForkIdManager forkIdManager) {
|
||||
this.networkId = networkId;
|
||||
this.peerValidators = peerValidators;
|
||||
@@ -96,9 +97,9 @@ public class EthProtocolManager implements ProtocolManager, MinedBlockObserver {
|
||||
|
||||
this.forkIdManager = forkIdManager;
|
||||
|
||||
ethPeers = new EthPeers(getSupportedProtocol(), clock, metricsSystem);
|
||||
ethMessages = new EthMessages();
|
||||
ethContext = new EthContext(ethPeers, ethMessages, scheduler);
|
||||
this.ethPeers = ethPeers;
|
||||
this.ethMessages = ethMessages;
|
||||
this.ethContext = ethContext;
|
||||
|
||||
this.blockBroadcaster = new BlockBroadcaster(ethContext);
|
||||
|
||||
@@ -108,58 +109,67 @@ public class EthProtocolManager implements ProtocolManager, MinedBlockObserver {
|
||||
}
|
||||
|
||||
// Set up request handlers
|
||||
new EthServer(blockchain, worldStateArchive, ethMessages, ethereumWireProtocolConfiguration);
|
||||
new EthServer(
|
||||
blockchain,
|
||||
worldStateArchive,
|
||||
transactionPool,
|
||||
ethMessages,
|
||||
ethereumWireProtocolConfiguration);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public EthProtocolManager(
|
||||
final Blockchain blockchain,
|
||||
final WorldStateArchive worldStateArchive,
|
||||
final BigInteger networkId,
|
||||
final WorldStateArchive worldStateArchive,
|
||||
final TransactionPool transactionPool,
|
||||
final EthProtocolConfiguration ethereumWireProtocolConfiguration,
|
||||
final EthPeers ethPeers,
|
||||
final EthMessages ethMessages,
|
||||
final EthContext ethContext,
|
||||
final List<PeerValidator> peerValidators,
|
||||
final boolean fastSyncEnabled,
|
||||
final int syncWorkers,
|
||||
final int txWorkers,
|
||||
final int computationWorkers,
|
||||
final Clock clock,
|
||||
final MetricsSystem metricsSystem,
|
||||
final EthProtocolConfiguration ethereumWireProtocolConfiguration) {
|
||||
final EthScheduler scheduler) {
|
||||
this(
|
||||
blockchain,
|
||||
worldStateArchive,
|
||||
networkId,
|
||||
worldStateArchive,
|
||||
transactionPool,
|
||||
ethereumWireProtocolConfiguration,
|
||||
ethPeers,
|
||||
ethMessages,
|
||||
ethContext,
|
||||
peerValidators,
|
||||
fastSyncEnabled,
|
||||
new EthScheduler(syncWorkers, txWorkers, computationWorkers, metricsSystem),
|
||||
ethereumWireProtocolConfiguration,
|
||||
clock,
|
||||
metricsSystem,
|
||||
scheduler,
|
||||
new ForkIdManager(blockchain, Collections.emptyList()));
|
||||
}
|
||||
|
||||
public EthProtocolManager(
|
||||
final Blockchain blockchain,
|
||||
final WorldStateArchive worldStateArchive,
|
||||
final BigInteger networkId,
|
||||
final WorldStateArchive worldStateArchive,
|
||||
final TransactionPool transactionPool,
|
||||
final EthProtocolConfiguration ethereumWireProtocolConfiguration,
|
||||
final EthPeers ethPeers,
|
||||
final EthMessages ethMessages,
|
||||
final EthContext ethContext,
|
||||
final List<PeerValidator> peerValidators,
|
||||
final boolean fastSyncEnabled,
|
||||
final int syncWorkers,
|
||||
final int txWorkers,
|
||||
final int computationWorkers,
|
||||
final Clock clock,
|
||||
final MetricsSystem metricsSystem,
|
||||
final EthProtocolConfiguration ethereumWireProtocolConfiguration,
|
||||
final EthScheduler scheduler,
|
||||
final List<Long> forks) {
|
||||
this(
|
||||
blockchain,
|
||||
worldStateArchive,
|
||||
networkId,
|
||||
worldStateArchive,
|
||||
transactionPool,
|
||||
ethereumWireProtocolConfiguration,
|
||||
ethPeers,
|
||||
ethMessages,
|
||||
ethContext,
|
||||
peerValidators,
|
||||
fastSyncEnabled,
|
||||
new EthScheduler(syncWorkers, txWorkers, computationWorkers, metricsSystem),
|
||||
ethereumWireProtocolConfiguration,
|
||||
clock,
|
||||
metricsSystem,
|
||||
scheduler,
|
||||
new ForkIdManager(blockchain, forks));
|
||||
}
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ public class EthScheduler {
|
||||
protected final ExecutorService servicesExecutor;
|
||||
protected final ExecutorService computationExecutor;
|
||||
|
||||
private final Collection<CompletableFuture<?>> serviceFutures = new ConcurrentLinkedDeque<>();
|
||||
private final Collection<CompletableFuture<?>> pendingFutures = new ConcurrentLinkedDeque<>();
|
||||
|
||||
public EthScheduler(
|
||||
final int syncWorkerCount,
|
||||
@@ -120,21 +120,28 @@ public class EthScheduler {
|
||||
syncWorkerExecutor.execute(command);
|
||||
}
|
||||
|
||||
public <T> CompletableFuture<T> scheduleSyncWorkerTask(final EthTask<T> task) {
|
||||
final CompletableFuture<T> syncFuture = task.runAsync(syncWorkerExecutor);
|
||||
pendingFutures.add(syncFuture);
|
||||
syncFuture.whenComplete((r, t) -> pendingFutures.remove(syncFuture));
|
||||
return syncFuture;
|
||||
}
|
||||
|
||||
public void scheduleTxWorkerTask(final Runnable command) {
|
||||
txWorkerExecutor.execute(command);
|
||||
}
|
||||
|
||||
public <T> CompletableFuture<T> scheduleServiceTask(final EthTask<T> task) {
|
||||
final CompletableFuture<T> serviceFuture = task.runAsync(servicesExecutor);
|
||||
serviceFutures.add(serviceFuture);
|
||||
serviceFuture.whenComplete((r, t) -> serviceFutures.remove(serviceFuture));
|
||||
pendingFutures.add(serviceFuture);
|
||||
serviceFuture.whenComplete((r, t) -> pendingFutures.remove(serviceFuture));
|
||||
return serviceFuture;
|
||||
}
|
||||
|
||||
public CompletableFuture<Void> startPipeline(final Pipeline<?> pipeline) {
|
||||
final CompletableFuture<Void> pipelineFuture = pipeline.start(servicesExecutor);
|
||||
serviceFutures.add(pipelineFuture);
|
||||
pipelineFuture.whenComplete((r, t) -> serviceFutures.remove(pipelineFuture));
|
||||
pendingFutures.add(pipelineFuture);
|
||||
pipelineFuture.whenComplete((r, t) -> pendingFutures.remove(pipelineFuture));
|
||||
return pipelineFuture;
|
||||
}
|
||||
|
||||
@@ -226,7 +233,7 @@ public class EthScheduler {
|
||||
|
||||
public void awaitStop() throws InterruptedException {
|
||||
shutdown.await();
|
||||
serviceFutures.forEach(future -> future.cancel(true));
|
||||
pendingFutures.forEach(future -> future.cancel(true));
|
||||
if (!syncWorkerExecutor.awaitTermination(30, TimeUnit.SECONDS)) {
|
||||
LOG.error("{} worker executor did not shutdown cleanly.", this.getClass().getSimpleName());
|
||||
}
|
||||
|
||||
@@ -18,18 +18,23 @@ import org.hyperledger.besu.ethereum.chain.Blockchain;
|
||||
import org.hyperledger.besu.ethereum.core.BlockBody;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||
import org.hyperledger.besu.ethereum.core.Hash;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
|
||||
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
|
||||
import org.hyperledger.besu.ethereum.eth.messages.BlockBodiesMessage;
|
||||
import org.hyperledger.besu.ethereum.eth.messages.BlockHeadersMessage;
|
||||
import org.hyperledger.besu.ethereum.eth.messages.EthPV62;
|
||||
import org.hyperledger.besu.ethereum.eth.messages.EthPV63;
|
||||
import org.hyperledger.besu.ethereum.eth.messages.EthPV65;
|
||||
import org.hyperledger.besu.ethereum.eth.messages.GetBlockBodiesMessage;
|
||||
import org.hyperledger.besu.ethereum.eth.messages.GetBlockHeadersMessage;
|
||||
import org.hyperledger.besu.ethereum.eth.messages.GetNodeDataMessage;
|
||||
import org.hyperledger.besu.ethereum.eth.messages.GetPooledTransactionsMessage;
|
||||
import org.hyperledger.besu.ethereum.eth.messages.GetReceiptsMessage;
|
||||
import org.hyperledger.besu.ethereum.eth.messages.NodeDataMessage;
|
||||
import org.hyperledger.besu.ethereum.eth.messages.PooledTransactionsMessage;
|
||||
import org.hyperledger.besu.ethereum.eth.messages.ReceiptsMessage;
|
||||
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
|
||||
import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection.PeerNotConnected;
|
||||
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData;
|
||||
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.messages.DisconnectMessage.DisconnectReason;
|
||||
@@ -52,16 +57,19 @@ class EthServer {
|
||||
|
||||
private final Blockchain blockchain;
|
||||
private final WorldStateArchive worldStateArchive;
|
||||
private final TransactionPool transactionPool;
|
||||
private final EthMessages ethMessages;
|
||||
private final EthProtocolConfiguration ethereumWireProtocolConfiguration;
|
||||
|
||||
EthServer(
|
||||
final Blockchain blockchain,
|
||||
final WorldStateArchive worldStateArchive,
|
||||
final TransactionPool transactionPool,
|
||||
final EthMessages ethMessages,
|
||||
final EthProtocolConfiguration ethereumWireProtocolConfiguration) {
|
||||
this.blockchain = blockchain;
|
||||
this.worldStateArchive = worldStateArchive;
|
||||
this.transactionPool = transactionPool;
|
||||
this.ethMessages = ethMessages;
|
||||
this.ethereumWireProtocolConfiguration = ethereumWireProtocolConfiguration;
|
||||
this.setupListeners();
|
||||
@@ -72,6 +80,7 @@ class EthServer {
|
||||
ethMessages.subscribe(EthPV62.GET_BLOCK_BODIES, this::handleGetBlockBodies);
|
||||
ethMessages.subscribe(EthPV63.GET_RECEIPTS, this::handleGetReceipts);
|
||||
ethMessages.subscribe(EthPV63.GET_NODE_DATA, this::handleGetNodeData);
|
||||
ethMessages.subscribe(EthPV65.GET_POOLED_TRANSACTIONS, this::handleGetPooledTransactions);
|
||||
}
|
||||
|
||||
private void handleGetBlockHeaders(final EthMessage message) {
|
||||
@@ -143,6 +152,26 @@ class EthServer {
|
||||
}
|
||||
}
|
||||
|
||||
private void handleGetPooledTransactions(final EthMessage message) {
|
||||
LOG.trace("Responding to GET_POOLED_TRANSACTIONS request");
|
||||
try {
|
||||
final MessageData response =
|
||||
constructGetPooledTransactionsResponse(
|
||||
transactionPool,
|
||||
message.getData(),
|
||||
ethereumWireProtocolConfiguration.getMaxGetPooledTransactions());
|
||||
message.getPeer().send(response);
|
||||
} catch (final RLPException e) {
|
||||
LOG.debug(
|
||||
"Received malformed GET_POOLED_TRANSACTIONS message, disconnecting: {}",
|
||||
message.getPeer(),
|
||||
e);
|
||||
message.getPeer().disconnect(DisconnectReason.BREACH_OF_PROTOCOL);
|
||||
} catch (final PeerNotConnected peerNotConnected) {
|
||||
// Peer disconnected before we could respond - nothing to do
|
||||
}
|
||||
}
|
||||
|
||||
static MessageData constructGetHeadersResponse(
|
||||
final Blockchain blockchain, final MessageData message, final int requestLimit) {
|
||||
final GetBlockHeadersMessage getHeaders = GetBlockHeadersMessage.readFrom(message);
|
||||
@@ -222,6 +251,28 @@ class EthServer {
|
||||
return ReceiptsMessage.create(receipts);
|
||||
}
|
||||
|
||||
static MessageData constructGetPooledTransactionsResponse(
|
||||
final TransactionPool transactionPool, final MessageData message, final int requestLimit) {
|
||||
final GetPooledTransactionsMessage getPooledTransactions =
|
||||
GetPooledTransactionsMessage.readFrom(message);
|
||||
final Iterable<Hash> hashes = getPooledTransactions.pooledTransactions();
|
||||
|
||||
final List<Transaction> tx = new ArrayList<>();
|
||||
int count = 0;
|
||||
for (final Hash hash : hashes) {
|
||||
if (count >= requestLimit) {
|
||||
break;
|
||||
}
|
||||
count++;
|
||||
final Optional<Transaction> maybeTx = transactionPool.getTransactionByHash(hash);
|
||||
if (maybeTx.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
tx.add(maybeTx.get());
|
||||
}
|
||||
return PooledTransactionsMessage.create(tx);
|
||||
}
|
||||
|
||||
static MessageData constructGetNodeDataResponse(
|
||||
final WorldStateArchive worldStateArchive,
|
||||
final MessageData message,
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* 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.eth.manager.task;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
|
||||
import org.hyperledger.besu.ethereum.core.Hash;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthPeer;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.PendingPeerRequest;
|
||||
import org.hyperledger.besu.ethereum.eth.messages.EthPV65;
|
||||
import org.hyperledger.besu.ethereum.eth.messages.PooledTransactionsMessage;
|
||||
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData;
|
||||
import org.hyperledger.besu.plugin.services.MetricsSystem;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
public class GetPooledTransactionsFromPeerTask extends AbstractPeerRequestTask<List<Transaction>> {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger();
|
||||
|
||||
private final List<Hash> hashes;
|
||||
|
||||
private GetPooledTransactionsFromPeerTask(
|
||||
final EthContext ethContext, final List<Hash> hashes, final MetricsSystem metricsSystem) {
|
||||
super(ethContext, EthPV65.GET_POOLED_TRANSACTIONS, metricsSystem);
|
||||
this.hashes = new ArrayList<>(hashes);
|
||||
}
|
||||
|
||||
public static GetPooledTransactionsFromPeerTask forHashes(
|
||||
final EthContext ethContext, final List<Hash> hashes, final MetricsSystem metricsSystem) {
|
||||
return new GetPooledTransactionsFromPeerTask(ethContext, hashes, metricsSystem);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected PendingPeerRequest sendRequest() {
|
||||
return sendRequestToPeer(
|
||||
peer -> {
|
||||
LOG.debug("Requesting {} transaction pool entries from peer {}.", hashes.size(), peer);
|
||||
return peer.getPooledTransactions(hashes);
|
||||
},
|
||||
0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Optional<List<Transaction>> processResponse(
|
||||
final boolean streamClosed, final MessageData message, final EthPeer peer) {
|
||||
if (streamClosed) {
|
||||
// We don't record this as a useless response because it's impossible to know if a peer has
|
||||
// the data we're requesting.
|
||||
return Optional.of(emptyList());
|
||||
}
|
||||
final PooledTransactionsMessage pooledTransactionsMessage =
|
||||
PooledTransactionsMessage.readFrom(message);
|
||||
final List<Transaction> tx = pooledTransactionsMessage.transactions();
|
||||
if (tx.size() > hashes.size()) {
|
||||
// Can't be the response to our request
|
||||
return Optional.empty();
|
||||
}
|
||||
return mapNodeDataByHash(tx);
|
||||
}
|
||||
|
||||
private Optional<List<Transaction>> mapNodeDataByHash(final List<Transaction> transactions) {
|
||||
final List<Transaction> result = new ArrayList<>();
|
||||
for (final Transaction tx : transactions) {
|
||||
final Hash hash = tx.getHash();
|
||||
if (!hashes.contains(hash)) {
|
||||
return Optional.empty();
|
||||
}
|
||||
result.add(tx);
|
||||
}
|
||||
return Optional.of(result);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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.eth.messages;
|
||||
|
||||
public final class EthPV65 {
|
||||
|
||||
public static final int NEW_POOLED_TRANSACTION_HASHES = 0x08;
|
||||
|
||||
public static final int GET_POOLED_TRANSACTIONS = 0x09;
|
||||
|
||||
public static final int POOLED_TRANSACTIONS = 0x0A;
|
||||
|
||||
private EthPV65() {
|
||||
// Holder for constants only
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright ConsenSys AG.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package org.hyperledger.besu.ethereum.eth.messages;
|
||||
|
||||
import org.hyperledger.besu.ethereum.core.Hash;
|
||||
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.AbstractMessageData;
|
||||
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData;
|
||||
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput;
|
||||
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
|
||||
public final class GetPooledTransactionsMessage extends AbstractMessageData {
|
||||
|
||||
private static final int MESSAGE_CODE = EthPV65.GET_POOLED_TRANSACTIONS;
|
||||
private List<Hash> pooledTransactions;
|
||||
|
||||
private GetPooledTransactionsMessage(final Bytes rlp) {
|
||||
super(rlp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCode() {
|
||||
return MESSAGE_CODE;
|
||||
}
|
||||
|
||||
public static GetPooledTransactionsMessage create(final List<Hash> pooledTransactions) {
|
||||
List<Hash> tx = pooledTransactions;
|
||||
final BytesValueRLPOutput out = new BytesValueRLPOutput();
|
||||
out.writeList(tx, (h, w) -> w.writeBytes(h));
|
||||
return new GetPooledTransactionsMessage(out.encoded());
|
||||
}
|
||||
|
||||
public static GetPooledTransactionsMessage readFrom(final MessageData message) {
|
||||
if (message instanceof GetPooledTransactionsMessage) {
|
||||
return (GetPooledTransactionsMessage) message;
|
||||
}
|
||||
final int code = message.getCode();
|
||||
if (code != MESSAGE_CODE) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format(
|
||||
"Message has code %d and thus is not a GetPooledTransactionsMessage.", code));
|
||||
}
|
||||
|
||||
return new GetPooledTransactionsMessage(message.getData());
|
||||
}
|
||||
|
||||
public List<Hash> pooledTransactions() {
|
||||
if (pooledTransactions == null) {
|
||||
final BytesValueRLPInput in = new BytesValueRLPInput(getData(), false);
|
||||
pooledTransactions = in.readList(rlp -> Hash.wrap(rlp.readBytes32()));
|
||||
}
|
||||
return pooledTransactions;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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.eth.messages;
|
||||
|
||||
import org.hyperledger.besu.ethereum.core.Hash;
|
||||
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
|
||||
public final class LimitedNewPooledTransactionHashesMessages {
|
||||
|
||||
static final int MAX_COUNT = 4096;
|
||||
|
||||
private final NewPooledTransactionHashesMessage transactionsMessage;
|
||||
private final List<Hash> includedTransactions;
|
||||
|
||||
public LimitedNewPooledTransactionHashesMessages(
|
||||
final NewPooledTransactionHashesMessage transactionsMessage,
|
||||
final List<Hash> includedTransactions) {
|
||||
this.transactionsMessage = transactionsMessage;
|
||||
this.includedTransactions = includedTransactions;
|
||||
}
|
||||
|
||||
public static LimitedNewPooledTransactionHashesMessages createLimited(
|
||||
final Iterable<Hash> hashes) {
|
||||
final List<Hash> includedTransactions = new ArrayList<>();
|
||||
final BytesValueRLPOutput message = new BytesValueRLPOutput();
|
||||
int count = 0;
|
||||
message.startList();
|
||||
for (final Hash txHash : hashes) {
|
||||
final BytesValueRLPOutput encodedHashes = new BytesValueRLPOutput();
|
||||
encodedHashes.writeBytes(txHash);
|
||||
Bytes encodedBytes = encodedHashes.encoded();
|
||||
|
||||
message.writeRLPUnsafe(encodedBytes);
|
||||
includedTransactions.add(txHash);
|
||||
// Check if last transaction to add to the message
|
||||
count++;
|
||||
if (count >= MAX_COUNT) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
message.endList();
|
||||
return new LimitedNewPooledTransactionHashesMessages(
|
||||
new NewPooledTransactionHashesMessage(message.encoded()), includedTransactions);
|
||||
}
|
||||
|
||||
public final NewPooledTransactionHashesMessage getTransactionsMessage() {
|
||||
return transactionsMessage;
|
||||
}
|
||||
|
||||
public final List<Hash> getIncludedTransactions() {
|
||||
return includedTransactions;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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.eth.messages;
|
||||
|
||||
import org.hyperledger.besu.ethereum.core.Hash;
|
||||
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.AbstractMessageData;
|
||||
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData;
|
||||
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput;
|
||||
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
|
||||
public final class NewPooledTransactionHashesMessage extends AbstractMessageData {
|
||||
|
||||
private static final int MESSAGE_CODE = EthPV65.NEW_POOLED_TRANSACTION_HASHES;
|
||||
private List<Hash> pendingTransactions;
|
||||
|
||||
NewPooledTransactionHashesMessage(final Bytes rlp) {
|
||||
super(rlp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCode() {
|
||||
return MESSAGE_CODE;
|
||||
}
|
||||
|
||||
public static NewPooledTransactionHashesMessage create(final List<Hash> pendingTransactions) {
|
||||
final BytesValueRLPOutput out = new BytesValueRLPOutput();
|
||||
out.writeList(pendingTransactions, (h, w) -> w.writeBytes(h));
|
||||
return new NewPooledTransactionHashesMessage(out.encoded());
|
||||
}
|
||||
|
||||
public static NewPooledTransactionHashesMessage readFrom(final MessageData message) {
|
||||
if (message instanceof NewPooledTransactionHashesMessage) {
|
||||
return (NewPooledTransactionHashesMessage) message;
|
||||
}
|
||||
final int code = message.getCode();
|
||||
if (code != MESSAGE_CODE) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format(
|
||||
"Message has code %d and thus is not a NewPooledTransactionHashesMessage.", code));
|
||||
}
|
||||
|
||||
return new NewPooledTransactionHashesMessage(message.getData());
|
||||
}
|
||||
|
||||
public List<Hash> pendingTransactions() {
|
||||
if (pendingTransactions == null) {
|
||||
final BytesValueRLPInput in = new BytesValueRLPInput(getData(), false);
|
||||
pendingTransactions = in.readList(rlp -> Hash.wrap(rlp.readBytes32()));
|
||||
}
|
||||
return pendingTransactions;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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.eth.messages;
|
||||
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.AbstractMessageData;
|
||||
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData;
|
||||
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput;
|
||||
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
|
||||
public final class PooledTransactionsMessage extends AbstractMessageData {
|
||||
|
||||
private static final int MESSAGE_CODE = EthPV65.POOLED_TRANSACTIONS;
|
||||
private List<Transaction> pooledTransactions;
|
||||
|
||||
private PooledTransactionsMessage(final Bytes rlp) {
|
||||
super(rlp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCode() {
|
||||
return MESSAGE_CODE;
|
||||
}
|
||||
|
||||
public static PooledTransactionsMessage create(final List<Transaction> transactions) {
|
||||
List<Transaction> tx = transactions;
|
||||
final BytesValueRLPOutput out = new BytesValueRLPOutput();
|
||||
out.writeList(tx, Transaction::writeTo);
|
||||
return new PooledTransactionsMessage(out.encoded());
|
||||
}
|
||||
|
||||
public static PooledTransactionsMessage readFrom(final MessageData message) {
|
||||
if (message instanceof PooledTransactionsMessage) {
|
||||
return (PooledTransactionsMessage) message;
|
||||
}
|
||||
final int code = message.getCode();
|
||||
if (code != MESSAGE_CODE) {
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Message has code %d and thus is not a PooledTransactionsMessage.", code));
|
||||
}
|
||||
|
||||
return new PooledTransactionsMessage(message.getData());
|
||||
}
|
||||
|
||||
public List<Transaction> transactions() {
|
||||
if (pooledTransactions == null) {
|
||||
final BytesValueRLPInput in = new BytesValueRLPInput(getData(), false);
|
||||
pooledTransactions = in.readList(Transaction::readFrom);
|
||||
}
|
||||
return pooledTransactions;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* 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.eth.transactions;
|
||||
|
||||
import static java.util.Collections.emptySet;
|
||||
|
||||
import org.hyperledger.besu.ethereum.core.Hash;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthPeer;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class PeerPendingTransactionTracker implements EthPeer.DisconnectCallback {
|
||||
private static final int MAX_TRACKED_SEEN_TRANSACTIONS = 10_000;
|
||||
private final Map<EthPeer, Set<Hash>> seenTransactions = new ConcurrentHashMap<>();
|
||||
private final Map<EthPeer, Set<Hash>> transactionsToSend = new ConcurrentHashMap<>();
|
||||
private final PendingTransactions pendingTransactions;
|
||||
|
||||
public PeerPendingTransactionTracker(final PendingTransactions pendingTransactions) {
|
||||
this.pendingTransactions = pendingTransactions;
|
||||
}
|
||||
|
||||
public synchronized void markTransactionsHashesAsSeen(
|
||||
final EthPeer peer, final Collection<Hash> transactions) {
|
||||
final Set<Hash> seenTransactionsForPeer = getOrCreateSeenTransactionsForPeer(peer);
|
||||
transactions.stream().forEach(seenTransactionsForPeer::add);
|
||||
}
|
||||
|
||||
public synchronized void addToPeerSendQueue(final EthPeer peer, final Hash hash) {
|
||||
if (!hasPeerSeenTransaction(peer, hash)) {
|
||||
transactionsToSend.computeIfAbsent(peer, key -> createTransactionsSet()).add(hash);
|
||||
}
|
||||
}
|
||||
|
||||
public Iterable<EthPeer> getEthPeersWithUnsentTransactions() {
|
||||
return transactionsToSend.keySet();
|
||||
}
|
||||
|
||||
public synchronized Set<Hash> claimTransactionsToSendToPeer(final EthPeer peer) {
|
||||
final Set<Hash> transactionsToSend = this.transactionsToSend.remove(peer);
|
||||
if (transactionsToSend != null) {
|
||||
markTransactionsHashesAsSeen(
|
||||
peer,
|
||||
transactionsToSend.stream()
|
||||
.filter(h -> pendingTransactions.getTransactionByHash(h).isPresent())
|
||||
.collect(Collectors.toSet()));
|
||||
return transactionsToSend;
|
||||
} else {
|
||||
return emptySet();
|
||||
}
|
||||
}
|
||||
|
||||
private Set<Hash> getOrCreateSeenTransactionsForPeer(final EthPeer peer) {
|
||||
return seenTransactions.computeIfAbsent(peer, key -> createTransactionsSet());
|
||||
}
|
||||
|
||||
private boolean hasPeerSeenTransaction(final EthPeer peer, final Hash hash) {
|
||||
final Set<Hash> seenTransactionsForPeer = seenTransactions.get(peer);
|
||||
return seenTransactionsForPeer != null && seenTransactionsForPeer.contains(hash);
|
||||
}
|
||||
|
||||
private <T> Set<T> createTransactionsSet() {
|
||||
return Collections.newSetFromMap(
|
||||
new LinkedHashMap<T, Boolean>(1 << 4, 0.75f, true) {
|
||||
@Override
|
||||
protected boolean removeEldestEntry(final Map.Entry<T, Boolean> eldest) {
|
||||
return size() > MAX_TRACKED_SEEN_TRANSACTIONS;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisconnect(final EthPeer peer) {
|
||||
seenTransactions.remove(peer);
|
||||
transactionsToSend.remove(peer);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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.eth.transactions;
|
||||
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
|
||||
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool.TransactionBatchAddedListener;
|
||||
|
||||
class PendingTransactionSender implements TransactionBatchAddedListener {
|
||||
|
||||
private final PeerPendingTransactionTracker transactionTracker;
|
||||
private final PendingTransactionsMessageSender transactionsMessageSender;
|
||||
private final EthContext ethContext;
|
||||
|
||||
public PendingTransactionSender(
|
||||
final PeerPendingTransactionTracker transactionTracker,
|
||||
final PendingTransactionsMessageSender transactionsMessageSender,
|
||||
final EthContext ethContext) {
|
||||
this.transactionTracker = transactionTracker;
|
||||
this.transactionsMessageSender = transactionsMessageSender;
|
||||
this.ethContext = ethContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTransactionsAdded(final Iterable<Transaction> transactions) {
|
||||
ethContext
|
||||
.getEthPeers()
|
||||
.streamAvailablePeers()
|
||||
.forEach(
|
||||
peer ->
|
||||
transactions.forEach(
|
||||
transaction ->
|
||||
transactionTracker.addToPeerSendQueue(peer, transaction.getHash())));
|
||||
ethContext
|
||||
.getScheduler()
|
||||
.scheduleSyncWorkerTask(transactionsMessageSender::sendTransactionsToPeers);
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,7 @@ import java.time.Clock;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@@ -37,6 +38,7 @@ import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.OptionalLong;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
@@ -44,6 +46,9 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.collect.EvictingQueue;
|
||||
|
||||
/**
|
||||
* Holds the current set of pending transactions with the ability to iterate them based on priority
|
||||
* for mining or look-up by hash.
|
||||
@@ -55,6 +60,7 @@ public class PendingTransactions {
|
||||
private final int maxTransactionRetentionHours;
|
||||
private final Clock clock;
|
||||
|
||||
private final Queue<Hash> newPooledHashes;
|
||||
private final Map<Hash, TransactionInfo> pendingTransactions = new ConcurrentHashMap<>();
|
||||
private final SortedSet<TransactionInfo> prioritizedTransactions =
|
||||
new TreeSet<>(
|
||||
@@ -72,17 +78,20 @@ public class PendingTransactions {
|
||||
private final LabelledMetric<Counter> transactionRemovedCounter;
|
||||
private final Counter localTransactionAddedCounter;
|
||||
private final Counter remoteTransactionAddedCounter;
|
||||
private final Counter localTransactionHashesAddedCounter;
|
||||
|
||||
private final long maxPendingTransactions;
|
||||
|
||||
public PendingTransactions(
|
||||
final int maxTransactionRetentionHours,
|
||||
final int maxPendingTransactions,
|
||||
final int maxPooledTransactionHashes,
|
||||
final Clock clock,
|
||||
final MetricsSystem metricsSystem) {
|
||||
this.maxTransactionRetentionHours = maxTransactionRetentionHours;
|
||||
this.maxPendingTransactions = maxPendingTransactions;
|
||||
this.clock = clock;
|
||||
this.newPooledHashes = EvictingQueue.create(maxPooledTransactionHashes);
|
||||
final LabelledMetric<Counter> transactionAddedCounter =
|
||||
metricsSystem.createLabelledCounter(
|
||||
BesuMetricCategory.TRANSACTION_POOL,
|
||||
@@ -91,6 +100,7 @@ public class PendingTransactions {
|
||||
"source");
|
||||
localTransactionAddedCounter = transactionAddedCounter.labels("local");
|
||||
remoteTransactionAddedCounter = transactionAddedCounter.labels("remote");
|
||||
localTransactionHashesAddedCounter = transactionAddedCounter.labels("pool");
|
||||
|
||||
transactionRemovedCounter =
|
||||
metricsSystem.createLabelledCounter(
|
||||
@@ -127,7 +137,19 @@ public class PendingTransactions {
|
||||
return transactionAdded;
|
||||
}
|
||||
|
||||
boolean addLocalTransaction(final Transaction transaction) {
|
||||
boolean addTransactionHash(final Hash transactionHash) {
|
||||
boolean hashAdded;
|
||||
synchronized (newPooledHashes) {
|
||||
hashAdded = newPooledHashes.add(transactionHash);
|
||||
}
|
||||
if (hashAdded) {
|
||||
localTransactionHashesAddedCounter.inc();
|
||||
}
|
||||
return hashAdded;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public boolean addLocalTransaction(final Transaction transaction) {
|
||||
final boolean transactionAdded =
|
||||
addTransaction(new TransactionInfo(transaction, true, clock.instant()));
|
||||
if (transactionAdded) {
|
||||
@@ -220,6 +242,7 @@ public class PendingTransactions {
|
||||
}
|
||||
prioritizedTransactions.add(transactionInfo);
|
||||
pendingTransactions.put(transactionInfo.getHash(), transactionInfo);
|
||||
tryEvictTransactionHash(transactionInfo.getHash());
|
||||
|
||||
if (pendingTransactions.size() > maxPendingTransactions) {
|
||||
final TransactionInfo toRemove = prioritizedTransactions.last();
|
||||
@@ -340,6 +363,16 @@ public class PendingTransactions {
|
||||
}
|
||||
}
|
||||
|
||||
public void tryEvictTransactionHash(final Hash hash) {
|
||||
synchronized (newPooledHashes) {
|
||||
newPooledHashes.remove(hash);
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<Hash> getNewPooledHashes() {
|
||||
return newPooledHashes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tracks the additional metadata associated with transactions to enable prioritization for mining
|
||||
* and deciding which transactions to drop when the transaction pool reaches its size limit.
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.eth.transactions;
|
||||
|
||||
import static java.time.Instant.now;
|
||||
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthMessage;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthMessages;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
|
||||
import org.hyperledger.besu.ethereum.eth.messages.NewPooledTransactionHashesMessage;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
|
||||
class PendingTransactionsMessageHandler implements EthMessages.MessageCallback {
|
||||
|
||||
private final PendingTransactionsMessageProcessor transactionsMessageProcessor;
|
||||
private final EthScheduler scheduler;
|
||||
private final Duration txMsgKeepAlive;
|
||||
|
||||
public PendingTransactionsMessageHandler(
|
||||
final EthScheduler scheduler,
|
||||
final PendingTransactionsMessageProcessor transactionsMessageProcessor,
|
||||
final int txMsgKeepAliveSeconds) {
|
||||
this.scheduler = scheduler;
|
||||
this.transactionsMessageProcessor = transactionsMessageProcessor;
|
||||
this.txMsgKeepAlive = Duration.ofSeconds(txMsgKeepAliveSeconds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exec(final EthMessage message) {
|
||||
final NewPooledTransactionHashesMessage transactionsMessage =
|
||||
NewPooledTransactionHashesMessage.readFrom(message.getData());
|
||||
final Instant startedAt = now();
|
||||
scheduler.scheduleTxWorkerTask(
|
||||
() ->
|
||||
transactionsMessageProcessor.processNewPooledTransactionHashesMessage(
|
||||
message.getPeer(), transactionsMessage, startedAt, txMsgKeepAlive));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* 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.eth.transactions;
|
||||
|
||||
import static java.time.Instant.now;
|
||||
import static org.apache.logging.log4j.LogManager.getLogger;
|
||||
|
||||
import org.hyperledger.besu.ethereum.core.Hash;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthPeer;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.task.GetPooledTransactionsFromPeerTask;
|
||||
import org.hyperledger.besu.ethereum.eth.messages.NewPooledTransactionHashesMessage;
|
||||
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.messages.DisconnectMessage.DisconnectReason;
|
||||
import org.hyperledger.besu.ethereum.rlp.RLPException;
|
||||
import org.hyperledger.besu.metrics.RunnableCounter;
|
||||
import org.hyperledger.besu.plugin.services.MetricsSystem;
|
||||
import org.hyperledger.besu.plugin.services.metrics.Counter;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
class PendingTransactionsMessageProcessor {
|
||||
|
||||
private static final int MAX_HASHES = 256;
|
||||
private static final int SKIPPED_MESSAGES_LOGGING_THRESHOLD = 1000;
|
||||
private static final Logger LOG = getLogger();
|
||||
private final PeerPendingTransactionTracker transactionTracker;
|
||||
private final Counter totalSkippedTransactionsMessageCounter;
|
||||
private final TransactionPool transactionPool;
|
||||
private final EthContext ethContext;
|
||||
private final MetricsSystem metricsSystem;
|
||||
|
||||
PendingTransactionsMessageProcessor(
|
||||
final PeerPendingTransactionTracker transactionTracker,
|
||||
final TransactionPool transactionPool,
|
||||
final Counter metricsCounter,
|
||||
final EthContext ethContext,
|
||||
final MetricsSystem metricsSystem) {
|
||||
this.transactionTracker = transactionTracker;
|
||||
this.transactionPool = transactionPool;
|
||||
this.ethContext = ethContext;
|
||||
this.metricsSystem = metricsSystem;
|
||||
this.totalSkippedTransactionsMessageCounter =
|
||||
new RunnableCounter(
|
||||
metricsCounter,
|
||||
() ->
|
||||
LOG.warn(
|
||||
"{} expired transaction messages have been skipped.",
|
||||
SKIPPED_MESSAGES_LOGGING_THRESHOLD),
|
||||
SKIPPED_MESSAGES_LOGGING_THRESHOLD);
|
||||
}
|
||||
|
||||
void processNewPooledTransactionHashesMessage(
|
||||
final EthPeer peer,
|
||||
final NewPooledTransactionHashesMessage transactionsMessage,
|
||||
final Instant startedAt,
|
||||
final Duration keepAlive) {
|
||||
// Check if message not expired.
|
||||
if (startedAt.plus(keepAlive).isAfter(now())) {
|
||||
this.processNewPooledTransactionHashesMessage(peer, transactionsMessage);
|
||||
} else {
|
||||
totalSkippedTransactionsMessageCounter.inc();
|
||||
}
|
||||
}
|
||||
|
||||
private void processNewPooledTransactionHashesMessage(
|
||||
final EthPeer peer, final NewPooledTransactionHashesMessage transactionsMessage) {
|
||||
try {
|
||||
LOG.trace("Received pooled transaction hashes message from {}", peer);
|
||||
|
||||
final List<Hash> pendingHashes = transactionsMessage.pendingTransactions();
|
||||
transactionTracker.markTransactionsHashesAsSeen(peer, pendingHashes);
|
||||
List<Hash> toRequest = new ArrayList<>();
|
||||
for (Hash hash : pendingHashes) {
|
||||
if (transactionPool.addTransactionHashes(hash)) {
|
||||
toRequest.add(hash);
|
||||
}
|
||||
}
|
||||
while (!toRequest.isEmpty()) {
|
||||
List<Hash> messageHashes = toRequest.subList(0, Math.min(toRequest.size(), MAX_HASHES));
|
||||
GetPooledTransactionsFromPeerTask task =
|
||||
GetPooledTransactionsFromPeerTask.forHashes(ethContext, messageHashes, metricsSystem);
|
||||
task.assignPeer(peer);
|
||||
ethContext
|
||||
.getScheduler()
|
||||
.scheduleSyncWorkerTask(task)
|
||||
.thenAccept(
|
||||
result -> {
|
||||
List<Transaction> txs = result.getResult();
|
||||
transactionPool.addRemoteTransactions(txs);
|
||||
});
|
||||
|
||||
toRequest.removeAll(messageHashes);
|
||||
}
|
||||
} catch (final RLPException ex) {
|
||||
if (peer != null) {
|
||||
LOG.debug(
|
||||
"Malformed pooled transaction hashes message received, disconnecting: {}", peer, ex);
|
||||
peer.disconnect(DisconnectReason.BREACH_OF_PROTOCOL);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.eth.transactions;
|
||||
|
||||
import org.hyperledger.besu.ethereum.core.Hash;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthPeer;
|
||||
import org.hyperledger.besu.ethereum.eth.messages.LimitedNewPooledTransactionHashesMessages;
|
||||
import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection.PeerNotConnected;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
class PendingTransactionsMessageSender {
|
||||
|
||||
private final PeerPendingTransactionTracker transactionTracker;
|
||||
|
||||
public PendingTransactionsMessageSender(final PeerPendingTransactionTracker transactionTracker) {
|
||||
this.transactionTracker = transactionTracker;
|
||||
}
|
||||
|
||||
public void sendTransactionsToPeers() {
|
||||
StreamSupport.stream(transactionTracker.getEthPeersWithUnsentTransactions().spliterator(), true)
|
||||
.parallel()
|
||||
.forEach(this::sendTransactionsToPeer);
|
||||
}
|
||||
|
||||
private void sendTransactionsToPeer(final EthPeer peer) {
|
||||
final Set<Hash> allTxToSend = transactionTracker.claimTransactionsToSendToPeer(peer);
|
||||
while (!allTxToSend.isEmpty()) {
|
||||
final LimitedNewPooledTransactionHashesMessages limitedTransactionsMessages =
|
||||
LimitedNewPooledTransactionHashesMessages.createLimited(allTxToSend);
|
||||
allTxToSend.removeAll(limitedTransactionsMessages.getIncludedTransactions());
|
||||
try {
|
||||
peer.send(limitedTransactionsMessages.getTransactionsMessage());
|
||||
} catch (final PeerNotConnected e) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,7 @@ import org.hyperledger.besu.ethereum.chain.Blockchain;
|
||||
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
|
||||
import org.hyperledger.besu.ethereum.core.Account;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||
import org.hyperledger.besu.ethereum.core.Hash;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.core.Wei;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
|
||||
@@ -43,6 +44,7 @@ import org.hyperledger.besu.plugin.services.metrics.LabelledMetric;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.logging.log4j.Logger;
|
||||
@@ -65,27 +67,33 @@ public class TransactionPool implements BlockAddedObserver {
|
||||
private final ProtocolSchedule<?> protocolSchedule;
|
||||
private final ProtocolContext<?> protocolContext;
|
||||
private final TransactionBatchAddedListener transactionBatchAddedListener;
|
||||
private final TransactionBatchAddedListener pendingTransactionBatchAddedListener;
|
||||
private final SyncState syncState;
|
||||
private final Wei minTransactionGasPrice;
|
||||
private final LabelledMetric<Counter> duplicateTransactionCounter;
|
||||
private final PeerTransactionTracker peerTransactionTracker;
|
||||
private final PeerPendingTransactionTracker peerPendingTransactionTracker;
|
||||
|
||||
public TransactionPool(
|
||||
final PendingTransactions pendingTransactions,
|
||||
final ProtocolSchedule<?> protocolSchedule,
|
||||
final ProtocolContext<?> protocolContext,
|
||||
final TransactionBatchAddedListener transactionBatchAddedListener,
|
||||
final TransactionBatchAddedListener pendingTransactionBatchAddedListener,
|
||||
final SyncState syncState,
|
||||
final EthContext ethContext,
|
||||
final PeerTransactionTracker peerTransactionTracker,
|
||||
final PeerPendingTransactionTracker peerPendingTransactionTracker,
|
||||
final Wei minTransactionGasPrice,
|
||||
final MetricsSystem metricsSystem) {
|
||||
this.pendingTransactions = pendingTransactions;
|
||||
this.protocolSchedule = protocolSchedule;
|
||||
this.protocolContext = protocolContext;
|
||||
this.transactionBatchAddedListener = transactionBatchAddedListener;
|
||||
this.pendingTransactionBatchAddedListener = pendingTransactionBatchAddedListener;
|
||||
this.syncState = syncState;
|
||||
this.peerTransactionTracker = peerTransactionTracker;
|
||||
this.peerPendingTransactionTracker = peerPendingTransactionTracker;
|
||||
this.minTransactionGasPrice = minTransactionGasPrice;
|
||||
|
||||
duplicateTransactionCounter =
|
||||
@@ -103,12 +111,24 @@ public class TransactionPool implements BlockAddedObserver {
|
||||
for (final Transaction transaction : localTransactions) {
|
||||
peerTransactionTracker.addToPeerSendQueue(peer, transaction);
|
||||
}
|
||||
final Collection<Hash> hashes = getNewPooledHashes();
|
||||
for (final Hash hash : hashes) {
|
||||
peerPendingTransactionTracker.addToPeerSendQueue(peer, hash);
|
||||
}
|
||||
}
|
||||
|
||||
public List<Transaction> getLocalTransactions() {
|
||||
return pendingTransactions.getLocalTransactions();
|
||||
}
|
||||
|
||||
public Collection<Hash> getNewPooledHashes() {
|
||||
return pendingTransactions.getNewPooledHashes();
|
||||
}
|
||||
|
||||
public boolean addTransactionHashes(final Hash transactionHash) {
|
||||
return pendingTransactions.addTransactionHash(transactionHash);
|
||||
}
|
||||
|
||||
public ValidationResult<TransactionInvalidReason> addLocalTransaction(
|
||||
final Transaction transaction) {
|
||||
if (transaction.getGasPrice().compareTo(minTransactionGasPrice) < 0) {
|
||||
@@ -121,7 +141,9 @@ public class TransactionPool implements BlockAddedObserver {
|
||||
() -> {
|
||||
final boolean added = pendingTransactions.addLocalTransaction(transaction);
|
||||
if (added) {
|
||||
transactionBatchAddedListener.onTransactionsAdded(singletonList(transaction));
|
||||
Collection<Transaction> txs = singletonList(transaction);
|
||||
transactionBatchAddedListener.onTransactionsAdded(txs);
|
||||
pendingTransactionBatchAddedListener.onTransactionsAdded(txs);
|
||||
} else {
|
||||
duplicateTransactionCounter.labels(LOCAL).inc();
|
||||
}
|
||||
@@ -135,6 +157,7 @@ public class TransactionPool implements BlockAddedObserver {
|
||||
}
|
||||
final Set<Transaction> addedTransactions = new HashSet<>();
|
||||
for (final Transaction transaction : transactions) {
|
||||
pendingTransactions.tryEvictTransactionHash(transaction.getHash());
|
||||
if (pendingTransactions.containsTransaction(transaction.getHash())) {
|
||||
// We already have this transaction, don't even validate it.
|
||||
duplicateTransactionCounter.labels(REMOTE).inc();
|
||||
@@ -226,6 +249,10 @@ public class TransactionPool implements BlockAddedObserver {
|
||||
.orElseGet(() -> ValidationResult.invalid(CHAIN_HEAD_WORLD_STATE_NOT_AVAILABLE));
|
||||
}
|
||||
|
||||
public Optional<Transaction> getTransactionByHash(final Hash hash) {
|
||||
return pendingTransactions.getTransactionByHash(hash);
|
||||
}
|
||||
|
||||
private BlockHeader getChainHeadBlockHeader() {
|
||||
final MutableBlockchain blockchain = protocolContext.getBlockchain();
|
||||
return blockchain.getBlockHeader(blockchain.getChainHeadHash()).get();
|
||||
|
||||
@@ -19,17 +19,21 @@ import java.util.Objects;
|
||||
public class TransactionPoolConfiguration {
|
||||
public static final int DEFAULT_TX_MSG_KEEP_ALIVE = 60;
|
||||
public static final int MAX_PENDING_TRANSACTIONS = 4096;
|
||||
public static final int MAX_PENDING_TRANSACTIONS_HASHES = 4096;
|
||||
public static final int DEFAULT_TX_RETENTION_HOURS = 13;
|
||||
|
||||
private final int txPoolMaxSize;
|
||||
private final int pooledTransactionHashesSize;
|
||||
private final int pendingTxRetentionPeriod;
|
||||
private final int txMessageKeepAliveSeconds;
|
||||
|
||||
public TransactionPoolConfiguration(
|
||||
final int txPoolMaxSize,
|
||||
final int pooledTransactionHashesSize,
|
||||
final int pendingTxRetentionPeriod,
|
||||
final int txMessageKeepAliveSeconds) {
|
||||
this.txPoolMaxSize = txPoolMaxSize;
|
||||
this.pooledTransactionHashesSize = pooledTransactionHashesSize;
|
||||
this.pendingTxRetentionPeriod = pendingTxRetentionPeriod;
|
||||
this.txMessageKeepAliveSeconds = txMessageKeepAliveSeconds;
|
||||
}
|
||||
@@ -38,6 +42,10 @@ public class TransactionPoolConfiguration {
|
||||
return txPoolMaxSize;
|
||||
}
|
||||
|
||||
public int getPooledTransactionHashesSize() {
|
||||
return pooledTransactionHashesSize;
|
||||
}
|
||||
|
||||
public int getPendingTxRetentionPeriod() {
|
||||
return pendingTxRetentionPeriod;
|
||||
}
|
||||
@@ -85,12 +93,18 @@ public class TransactionPoolConfiguration {
|
||||
private int txPoolMaxSize = MAX_PENDING_TRANSACTIONS;
|
||||
private int pendingTxRetentionPeriod = DEFAULT_TX_RETENTION_HOURS;
|
||||
private Integer txMessageKeepAliveSeconds = DEFAULT_TX_MSG_KEEP_ALIVE;
|
||||
private int pooledTransactionHashesSize = MAX_PENDING_TRANSACTIONS_HASHES;
|
||||
|
||||
public Builder txPoolMaxSize(final int txPoolMaxSize) {
|
||||
this.txPoolMaxSize = txPoolMaxSize;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder pooledTransactionHashesSize(final int pooledTransactionHashesSize) {
|
||||
this.pooledTransactionHashesSize = pooledTransactionHashesSize;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder pendingTxRetentionPeriod(final int pendingTxRetentionPeriod) {
|
||||
this.pendingTxRetentionPeriod = pendingTxRetentionPeriod;
|
||||
return this;
|
||||
@@ -103,7 +117,10 @@ public class TransactionPoolConfiguration {
|
||||
|
||||
public TransactionPoolConfiguration build() {
|
||||
return new TransactionPoolConfiguration(
|
||||
txPoolMaxSize, pendingTxRetentionPeriod, txMessageKeepAliveSeconds);
|
||||
txPoolMaxSize,
|
||||
pooledTransactionHashesSize,
|
||||
pendingTxRetentionPeriod,
|
||||
txMessageKeepAliveSeconds);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import org.hyperledger.besu.ethereum.ProtocolContext;
|
||||
import org.hyperledger.besu.ethereum.core.Wei;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
|
||||
import org.hyperledger.besu.ethereum.eth.messages.EthPV62;
|
||||
import org.hyperledger.besu.ethereum.eth.messages.EthPV65;
|
||||
import org.hyperledger.besu.ethereum.eth.sync.state.SyncState;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
|
||||
import org.hyperledger.besu.metrics.BesuMetricCategory;
|
||||
@@ -41,22 +42,30 @@ public class TransactionPoolFactory {
|
||||
new PendingTransactions(
|
||||
transactionPoolConfiguration.getPendingTxRetentionPeriod(),
|
||||
transactionPoolConfiguration.getTxPoolMaxSize(),
|
||||
transactionPoolConfiguration.getPooledTransactionHashesSize(),
|
||||
clock,
|
||||
metricsSystem);
|
||||
|
||||
final PeerTransactionTracker transactionTracker = new PeerTransactionTracker();
|
||||
final TransactionsMessageSender transactionsMessageSender =
|
||||
new TransactionsMessageSender(transactionTracker);
|
||||
final PeerPendingTransactionTracker pendingTransactionTracker =
|
||||
new PeerPendingTransactionTracker(pendingTransactions);
|
||||
|
||||
final PendingTransactionsMessageSender pendingTransactionsMessageSender =
|
||||
new PendingTransactionsMessageSender(pendingTransactionTracker);
|
||||
final TransactionPool transactionPool =
|
||||
new TransactionPool(
|
||||
pendingTransactions,
|
||||
protocolSchedule,
|
||||
protocolContext,
|
||||
new TransactionSender(transactionTracker, transactionsMessageSender, ethContext),
|
||||
new PendingTransactionSender(
|
||||
pendingTransactionTracker, pendingTransactionsMessageSender, ethContext),
|
||||
syncState,
|
||||
ethContext,
|
||||
transactionTracker,
|
||||
pendingTransactionTracker,
|
||||
minTransactionGasPrice,
|
||||
metricsSystem);
|
||||
|
||||
@@ -71,8 +80,23 @@ public class TransactionPoolFactory {
|
||||
"transactions_messages_skipped_total",
|
||||
"Total number of transactions messages skipped by the processor.")),
|
||||
transactionPoolConfiguration.getTxMessageKeepAliveSeconds());
|
||||
|
||||
final PendingTransactionsMessageHandler pooledTransactionsMessageHandler =
|
||||
new PendingTransactionsMessageHandler(
|
||||
ethContext.getScheduler(),
|
||||
new PendingTransactionsMessageProcessor(
|
||||
pendingTransactionTracker,
|
||||
transactionPool,
|
||||
metricsSystem.createCounter(
|
||||
BesuMetricCategory.TRANSACTION_POOL,
|
||||
"pending_transactions_messages_skipped_total",
|
||||
"Total number of pending transactions messages skipped by the processor."),
|
||||
ethContext,
|
||||
metricsSystem),
|
||||
transactionPoolConfiguration.getTxMessageKeepAliveSeconds());
|
||||
ethContext.getEthMessages().subscribe(EthPV62.TRANSACTIONS, transactionsMessageHandler);
|
||||
ethContext
|
||||
.getEthMessages()
|
||||
.subscribe(EthPV65.NEW_POOLED_TRANSACTION_HASHES, pooledTransactionsMessageHandler);
|
||||
protocolContext.getBlockchain().observeBlockAdded(transactionPool);
|
||||
ethContext.getEthPeers().subscribeDisconnect(transactionTracker);
|
||||
return transactionPool;
|
||||
|
||||
@@ -53,6 +53,7 @@ import org.hyperledger.besu.ethereum.eth.messages.ReceiptsMessage;
|
||||
import org.hyperledger.besu.ethereum.eth.messages.StatusMessage;
|
||||
import org.hyperledger.besu.ethereum.eth.messages.TransactionsMessage;
|
||||
import org.hyperledger.besu.ethereum.eth.sync.state.SyncState;
|
||||
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
|
||||
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration;
|
||||
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolFactory;
|
||||
import org.hyperledger.besu.ethereum.mainnet.MainnetProtocolSchedule;
|
||||
@@ -94,6 +95,7 @@ import org.mockito.ArgumentCaptor;
|
||||
public final class EthProtocolManagerTest {
|
||||
|
||||
private static Blockchain blockchain;
|
||||
private static TransactionPool transactionPool;
|
||||
private static ProtocolSchedule<Void> protocolSchedule;
|
||||
private static BlockDataGenerator gen;
|
||||
private static ProtocolContext<Void> protocolContext;
|
||||
@@ -105,6 +107,7 @@ public final class EthProtocolManagerTest {
|
||||
final BlockchainSetupUtil<Void> blockchainSetupUtil = BlockchainSetupUtil.forTesting();
|
||||
blockchainSetupUtil.importAllBlocks();
|
||||
blockchain = blockchainSetupUtil.getBlockchain();
|
||||
transactionPool = blockchainSetupUtil.getTransactionPool();
|
||||
protocolSchedule = blockchainSetupUtil.getProtocolSchedule();
|
||||
protocolContext = blockchainSetupUtil.getProtocolContext();
|
||||
assert (blockchainSetupUtil.getMaxBlockNumber() >= 20L);
|
||||
@@ -113,17 +116,11 @@ public final class EthProtocolManagerTest {
|
||||
@Test
|
||||
public void disconnectOnUnsolicitedMessage() {
|
||||
try (final EthProtocolManager ethManager =
|
||||
new EthProtocolManager(
|
||||
EthProtocolManagerTestUtil.create(
|
||||
blockchain,
|
||||
() -> false,
|
||||
protocolContext.getWorldStateArchive(),
|
||||
BigInteger.ONE,
|
||||
Collections.emptyList(),
|
||||
true,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
TestClock.fixed(),
|
||||
new NoOpMetricsSystem(),
|
||||
transactionPool,
|
||||
EthProtocolConfiguration.defaultConfig())) {
|
||||
final MessageData messageData =
|
||||
BlockHeadersMessage.create(Collections.singletonList(blockchain.getBlockHeader(1).get()));
|
||||
@@ -136,17 +133,11 @@ public final class EthProtocolManagerTest {
|
||||
@Test
|
||||
public void disconnectOnFailureToSendStatusMessage() {
|
||||
try (final EthProtocolManager ethManager =
|
||||
new EthProtocolManager(
|
||||
EthProtocolManagerTestUtil.create(
|
||||
blockchain,
|
||||
() -> false,
|
||||
protocolContext.getWorldStateArchive(),
|
||||
BigInteger.ONE,
|
||||
Collections.emptyList(),
|
||||
true,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
TestClock.fixed(),
|
||||
new NoOpMetricsSystem(),
|
||||
transactionPool,
|
||||
EthProtocolConfiguration.defaultConfig())) {
|
||||
final MessageData messageData =
|
||||
BlockHeadersMessage.create(Collections.singletonList(blockchain.getBlockHeader(1).get()));
|
||||
@@ -160,17 +151,11 @@ public final class EthProtocolManagerTest {
|
||||
@Test
|
||||
public void disconnectOnWrongChainId() {
|
||||
try (final EthProtocolManager ethManager =
|
||||
new EthProtocolManager(
|
||||
EthProtocolManagerTestUtil.create(
|
||||
blockchain,
|
||||
() -> false,
|
||||
protocolContext.getWorldStateArchive(),
|
||||
BigInteger.ONE,
|
||||
Collections.emptyList(),
|
||||
true,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
TestClock.fixed(),
|
||||
new NoOpMetricsSystem(),
|
||||
transactionPool,
|
||||
EthProtocolConfiguration.defaultConfig())) {
|
||||
final MessageData messageData =
|
||||
BlockHeadersMessage.create(Collections.singletonList(blockchain.getBlockHeader(1).get()));
|
||||
@@ -195,17 +180,11 @@ public final class EthProtocolManagerTest {
|
||||
@Test
|
||||
public void disconnectOnWrongGenesisHash() {
|
||||
try (final EthProtocolManager ethManager =
|
||||
new EthProtocolManager(
|
||||
EthProtocolManagerTestUtil.create(
|
||||
blockchain,
|
||||
() -> false,
|
||||
protocolContext.getWorldStateArchive(),
|
||||
BigInteger.ONE,
|
||||
Collections.emptyList(),
|
||||
true,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
TestClock.fixed(),
|
||||
new NoOpMetricsSystem(),
|
||||
transactionPool,
|
||||
EthProtocolConfiguration.defaultConfig())) {
|
||||
final MessageData messageData =
|
||||
BlockHeadersMessage.create(Collections.singletonList(blockchain.getBlockHeader(1).get()));
|
||||
@@ -230,17 +209,11 @@ public final class EthProtocolManagerTest {
|
||||
@Test(expected = ConditionTimeoutException.class)
|
||||
public void doNotDisconnectOnValidMessage() {
|
||||
try (final EthProtocolManager ethManager =
|
||||
new EthProtocolManager(
|
||||
EthProtocolManagerTestUtil.create(
|
||||
blockchain,
|
||||
() -> false,
|
||||
protocolContext.getWorldStateArchive(),
|
||||
BigInteger.ONE,
|
||||
Collections.emptyList(),
|
||||
true,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
TestClock.fixed(),
|
||||
new NoOpMetricsSystem(),
|
||||
transactionPool,
|
||||
EthProtocolConfiguration.defaultConfig())) {
|
||||
final MessageData messageData =
|
||||
GetBlockBodiesMessage.create(Collections.singletonList(gen.hash()));
|
||||
@@ -257,17 +230,11 @@ public final class EthProtocolManagerTest {
|
||||
public void respondToGetHeaders() throws ExecutionException, InterruptedException {
|
||||
final CompletableFuture<Void> done = new CompletableFuture<>();
|
||||
try (final EthProtocolManager ethManager =
|
||||
new EthProtocolManager(
|
||||
EthProtocolManagerTestUtil.create(
|
||||
blockchain,
|
||||
() -> false,
|
||||
protocolContext.getWorldStateArchive(),
|
||||
BigInteger.ONE,
|
||||
Collections.emptyList(),
|
||||
true,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
TestClock.fixed(),
|
||||
new NoOpMetricsSystem(),
|
||||
transactionPool,
|
||||
EthProtocolConfiguration.defaultConfig())) {
|
||||
final long startBlock = 5L;
|
||||
final int blockCount = 5;
|
||||
@@ -300,18 +267,12 @@ public final class EthProtocolManagerTest {
|
||||
final CompletableFuture<Void> done = new CompletableFuture<>();
|
||||
final int limit = 5;
|
||||
try (final EthProtocolManager ethManager =
|
||||
new EthProtocolManager(
|
||||
EthProtocolManagerTestUtil.create(
|
||||
blockchain,
|
||||
() -> false,
|
||||
protocolContext.getWorldStateArchive(),
|
||||
BigInteger.ONE,
|
||||
Collections.emptyList(),
|
||||
true,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
TestClock.fixed(),
|
||||
new NoOpMetricsSystem(),
|
||||
new EthProtocolConfiguration(limit, limit, limit, limit))) {
|
||||
transactionPool,
|
||||
new EthProtocolConfiguration(limit, limit, limit, limit, limit))) {
|
||||
final long startBlock = 5L;
|
||||
final int blockCount = 10;
|
||||
final MessageData messageData =
|
||||
@@ -342,18 +303,13 @@ public final class EthProtocolManagerTest {
|
||||
public void respondToGetHeadersReversed() throws ExecutionException, InterruptedException {
|
||||
final CompletableFuture<Void> done = new CompletableFuture<>();
|
||||
try (final EthProtocolManager ethManager =
|
||||
new EthProtocolManager(
|
||||
EthProtocolManagerTestUtil.create(
|
||||
blockchain,
|
||||
() -> false,
|
||||
protocolContext.getWorldStateArchive(),
|
||||
BigInteger.ONE,
|
||||
Collections.emptyList(),
|
||||
true,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
TestClock.fixed(),
|
||||
new NoOpMetricsSystem(),
|
||||
transactionPool,
|
||||
EthProtocolConfiguration.defaultConfig())) {
|
||||
|
||||
final long endBlock = 10L;
|
||||
final int blockCount = 5;
|
||||
final MessageData messageData = GetBlockHeadersMessage.create(endBlock, blockCount, 0, true);
|
||||
@@ -383,18 +339,13 @@ public final class EthProtocolManagerTest {
|
||||
public void respondToGetHeadersWithSkip() throws ExecutionException, InterruptedException {
|
||||
final CompletableFuture<Void> done = new CompletableFuture<>();
|
||||
try (final EthProtocolManager ethManager =
|
||||
new EthProtocolManager(
|
||||
EthProtocolManagerTestUtil.create(
|
||||
blockchain,
|
||||
() -> false,
|
||||
protocolContext.getWorldStateArchive(),
|
||||
BigInteger.ONE,
|
||||
Collections.emptyList(),
|
||||
true,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
TestClock.fixed(),
|
||||
new NoOpMetricsSystem(),
|
||||
transactionPool,
|
||||
EthProtocolConfiguration.defaultConfig())) {
|
||||
|
||||
final long startBlock = 5L;
|
||||
final int blockCount = 5;
|
||||
final int skip = 1;
|
||||
@@ -427,18 +378,13 @@ public final class EthProtocolManagerTest {
|
||||
throws ExecutionException, InterruptedException {
|
||||
final CompletableFuture<Void> done = new CompletableFuture<>();
|
||||
try (final EthProtocolManager ethManager =
|
||||
new EthProtocolManager(
|
||||
EthProtocolManagerTestUtil.create(
|
||||
blockchain,
|
||||
() -> false,
|
||||
protocolContext.getWorldStateArchive(),
|
||||
BigInteger.ONE,
|
||||
Collections.emptyList(),
|
||||
true,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
TestClock.fixed(),
|
||||
new NoOpMetricsSystem(),
|
||||
transactionPool,
|
||||
EthProtocolConfiguration.defaultConfig())) {
|
||||
|
||||
final long endBlock = 10L;
|
||||
final int blockCount = 5;
|
||||
final int skip = 1;
|
||||
@@ -492,18 +438,13 @@ public final class EthProtocolManagerTest {
|
||||
public void respondToGetHeadersPartial() throws ExecutionException, InterruptedException {
|
||||
final CompletableFuture<Void> done = new CompletableFuture<>();
|
||||
try (final EthProtocolManager ethManager =
|
||||
new EthProtocolManager(
|
||||
EthProtocolManagerTestUtil.create(
|
||||
blockchain,
|
||||
() -> false,
|
||||
protocolContext.getWorldStateArchive(),
|
||||
BigInteger.ONE,
|
||||
Collections.emptyList(),
|
||||
true,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
TestClock.fixed(),
|
||||
new NoOpMetricsSystem(),
|
||||
transactionPool,
|
||||
EthProtocolConfiguration.defaultConfig())) {
|
||||
|
||||
final long startBlock = blockchain.getChainHeadBlockNumber() - 1L;
|
||||
final int blockCount = 5;
|
||||
final MessageData messageData =
|
||||
@@ -534,18 +475,13 @@ public final class EthProtocolManagerTest {
|
||||
public void respondToGetHeadersEmpty() throws ExecutionException, InterruptedException {
|
||||
final CompletableFuture<Void> done = new CompletableFuture<>();
|
||||
try (final EthProtocolManager ethManager =
|
||||
new EthProtocolManager(
|
||||
EthProtocolManagerTestUtil.create(
|
||||
blockchain,
|
||||
() -> false,
|
||||
protocolContext.getWorldStateArchive(),
|
||||
BigInteger.ONE,
|
||||
Collections.emptyList(),
|
||||
true,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
TestClock.fixed(),
|
||||
new NoOpMetricsSystem(),
|
||||
transactionPool,
|
||||
EthProtocolConfiguration.defaultConfig())) {
|
||||
|
||||
final long startBlock = blockchain.getChainHeadBlockNumber() + 1;
|
||||
final int blockCount = 5;
|
||||
final MessageData messageData =
|
||||
@@ -573,18 +509,13 @@ public final class EthProtocolManagerTest {
|
||||
public void respondToGetBodies() throws ExecutionException, InterruptedException {
|
||||
final CompletableFuture<Void> done = new CompletableFuture<>();
|
||||
try (final EthProtocolManager ethManager =
|
||||
new EthProtocolManager(
|
||||
EthProtocolManagerTestUtil.create(
|
||||
blockchain,
|
||||
() -> false,
|
||||
protocolContext.getWorldStateArchive(),
|
||||
BigInteger.ONE,
|
||||
Collections.emptyList(),
|
||||
true,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
TestClock.fixed(),
|
||||
new NoOpMetricsSystem(),
|
||||
transactionPool,
|
||||
EthProtocolConfiguration.defaultConfig())) {
|
||||
|
||||
// Setup blocks query
|
||||
final long startBlock = blockchain.getChainHeadBlockNumber() - 5;
|
||||
final int blockCount = 2;
|
||||
@@ -628,18 +559,12 @@ public final class EthProtocolManagerTest {
|
||||
final CompletableFuture<Void> done = new CompletableFuture<>();
|
||||
final int limit = 5;
|
||||
try (final EthProtocolManager ethManager =
|
||||
new EthProtocolManager(
|
||||
EthProtocolManagerTestUtil.create(
|
||||
blockchain,
|
||||
() -> false,
|
||||
protocolContext.getWorldStateArchive(),
|
||||
BigInteger.ONE,
|
||||
Collections.emptyList(),
|
||||
true,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
TestClock.fixed(),
|
||||
new NoOpMetricsSystem(),
|
||||
new EthProtocolConfiguration(limit, limit, limit, limit))) {
|
||||
transactionPool,
|
||||
new EthProtocolConfiguration(limit, limit, limit, limit, limit))) {
|
||||
// Setup blocks query
|
||||
final int blockCount = 10;
|
||||
final long startBlock = blockchain.getChainHeadBlockNumber() - blockCount;
|
||||
@@ -682,17 +607,11 @@ public final class EthProtocolManagerTest {
|
||||
public void respondToGetBodiesPartial() throws ExecutionException, InterruptedException {
|
||||
final CompletableFuture<Void> done = new CompletableFuture<>();
|
||||
try (final EthProtocolManager ethManager =
|
||||
new EthProtocolManager(
|
||||
EthProtocolManagerTestUtil.create(
|
||||
blockchain,
|
||||
() -> false,
|
||||
protocolContext.getWorldStateArchive(),
|
||||
BigInteger.ONE,
|
||||
Collections.emptyList(),
|
||||
true,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
TestClock.fixed(),
|
||||
new NoOpMetricsSystem(),
|
||||
transactionPool,
|
||||
EthProtocolConfiguration.defaultConfig())) {
|
||||
// Setup blocks query
|
||||
final long expectedBlockNumber = blockchain.getChainHeadBlockNumber() - 1;
|
||||
@@ -730,17 +649,11 @@ public final class EthProtocolManagerTest {
|
||||
public void respondToGetReceipts() throws ExecutionException, InterruptedException {
|
||||
final CompletableFuture<Void> done = new CompletableFuture<>();
|
||||
try (final EthProtocolManager ethManager =
|
||||
new EthProtocolManager(
|
||||
EthProtocolManagerTestUtil.create(
|
||||
blockchain,
|
||||
() -> false,
|
||||
protocolContext.getWorldStateArchive(),
|
||||
BigInteger.ONE,
|
||||
Collections.emptyList(),
|
||||
true,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
TestClock.fixed(),
|
||||
new NoOpMetricsSystem(),
|
||||
transactionPool,
|
||||
EthProtocolConfiguration.defaultConfig())) {
|
||||
// Setup blocks query
|
||||
final long startBlock = blockchain.getChainHeadBlockNumber() - 5;
|
||||
@@ -784,18 +697,12 @@ public final class EthProtocolManagerTest {
|
||||
final CompletableFuture<Void> done = new CompletableFuture<>();
|
||||
final int limit = 5;
|
||||
try (final EthProtocolManager ethManager =
|
||||
new EthProtocolManager(
|
||||
EthProtocolManagerTestUtil.create(
|
||||
blockchain,
|
||||
() -> false,
|
||||
protocolContext.getWorldStateArchive(),
|
||||
BigInteger.ONE,
|
||||
Collections.emptyList(),
|
||||
true,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
TestClock.fixed(),
|
||||
new NoOpMetricsSystem(),
|
||||
new EthProtocolConfiguration(limit, limit, limit, limit))) {
|
||||
transactionPool,
|
||||
new EthProtocolConfiguration(limit, limit, limit, limit, limit))) {
|
||||
// Setup blocks query
|
||||
final int blockCount = 10;
|
||||
final long startBlock = blockchain.getChainHeadBlockNumber() - blockCount;
|
||||
@@ -837,17 +744,11 @@ public final class EthProtocolManagerTest {
|
||||
public void respondToGetReceiptsPartial() throws ExecutionException, InterruptedException {
|
||||
final CompletableFuture<Void> done = new CompletableFuture<>();
|
||||
try (final EthProtocolManager ethManager =
|
||||
new EthProtocolManager(
|
||||
EthProtocolManagerTestUtil.create(
|
||||
blockchain,
|
||||
() -> false,
|
||||
protocolContext.getWorldStateArchive(),
|
||||
BigInteger.ONE,
|
||||
Collections.emptyList(),
|
||||
true,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
TestClock.fixed(),
|
||||
new NoOpMetricsSystem(),
|
||||
transactionPool,
|
||||
EthProtocolConfiguration.defaultConfig())) {
|
||||
// Setup blocks query
|
||||
final long blockNumber = blockchain.getChainHeadBlockNumber() - 5;
|
||||
@@ -887,17 +788,11 @@ public final class EthProtocolManagerTest {
|
||||
final WorldStateArchive worldStateArchive = protocolContext.getWorldStateArchive();
|
||||
|
||||
try (final EthProtocolManager ethManager =
|
||||
new EthProtocolManager(
|
||||
EthProtocolManagerTestUtil.create(
|
||||
blockchain,
|
||||
worldStateArchive,
|
||||
BigInteger.ONE,
|
||||
Collections.emptyList(),
|
||||
true,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
TestClock.fixed(),
|
||||
new NoOpMetricsSystem(),
|
||||
() -> false,
|
||||
protocolContext.getWorldStateArchive(),
|
||||
transactionPool,
|
||||
EthProtocolConfiguration.defaultConfig())) {
|
||||
// Setup node data query
|
||||
|
||||
@@ -939,63 +834,59 @@ public final class EthProtocolManagerTest {
|
||||
|
||||
@Test
|
||||
public void newBlockMinedSendsNewBlockMessageToAllPeers() {
|
||||
final EthProtocolManager ethManager =
|
||||
new EthProtocolManager(
|
||||
try (final EthProtocolManager ethManager =
|
||||
EthProtocolManagerTestUtil.create(
|
||||
blockchain,
|
||||
() -> false,
|
||||
protocolContext.getWorldStateArchive(),
|
||||
BigInteger.ONE,
|
||||
Collections.emptyList(),
|
||||
true,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
TestClock.fixed(),
|
||||
new NoOpMetricsSystem(),
|
||||
EthProtocolConfiguration.defaultConfig());
|
||||
transactionPool,
|
||||
EthProtocolConfiguration.defaultConfig())) {
|
||||
// Define handler to validate response
|
||||
final PeerSendHandler onSend = mock(PeerSendHandler.class);
|
||||
final List<PeerConnection> peers = Lists.newArrayList();
|
||||
|
||||
// Define handler to validate response
|
||||
final PeerSendHandler onSend = mock(PeerSendHandler.class);
|
||||
final List<PeerConnection> peers = Lists.newArrayList();
|
||||
final int PEER_COUNT = 5;
|
||||
for (int i = 0; i < PEER_COUNT; i++) {
|
||||
peers.add(setupPeer(ethManager, onSend));
|
||||
}
|
||||
|
||||
final int PEER_COUNT = 5;
|
||||
for (int i = 0; i < PEER_COUNT; i++) {
|
||||
peers.add(setupPeer(ethManager, onSend));
|
||||
final Hash chainHeadHash = blockchain.getChainHeadHash();
|
||||
final Block minedBlock =
|
||||
new Block(
|
||||
blockchain.getBlockHeader(chainHeadHash).get(),
|
||||
blockchain.getBlockBody(chainHeadHash).get());
|
||||
|
||||
final Difficulty expectedTotalDifficulty = blockchain.getChainHead().getTotalDifficulty();
|
||||
|
||||
reset(onSend);
|
||||
|
||||
ethManager.blockMined(minedBlock);
|
||||
|
||||
final ArgumentCaptor<NewBlockMessage> messageSentCaptor =
|
||||
ArgumentCaptor.forClass(NewBlockMessage.class);
|
||||
final ArgumentCaptor<PeerConnection> receivingPeerCaptor =
|
||||
ArgumentCaptor.forClass(PeerConnection.class);
|
||||
final ArgumentCaptor<Capability> capabilityCaptor = ArgumentCaptor.forClass(Capability.class);
|
||||
|
||||
verify(onSend, times(PEER_COUNT))
|
||||
.exec(
|
||||
capabilityCaptor.capture(),
|
||||
messageSentCaptor.capture(),
|
||||
receivingPeerCaptor.capture());
|
||||
|
||||
// assert that all entries in capability param were Eth63
|
||||
assertThat(capabilityCaptor.getAllValues().stream().distinct().collect(Collectors.toList()))
|
||||
.isEqualTo(Collections.singletonList(EthProtocol.ETH63));
|
||||
|
||||
// assert that all messages transmitted contain the expected block & total difficulty.
|
||||
final ProtocolSchedule<Void> protocolSchdeule = MainnetProtocolSchedule.create();
|
||||
for (final NewBlockMessage msg : messageSentCaptor.getAllValues()) {
|
||||
assertThat(msg.block(protocolSchdeule)).isEqualTo(minedBlock);
|
||||
assertThat(msg.totalDifficulty(protocolSchdeule)).isEqualTo(expectedTotalDifficulty);
|
||||
}
|
||||
|
||||
assertThat(receivingPeerCaptor.getAllValues().containsAll(peers)).isTrue();
|
||||
}
|
||||
|
||||
final Hash chainHeadHash = blockchain.getChainHeadHash();
|
||||
final Block minedBlock =
|
||||
new Block(
|
||||
blockchain.getBlockHeader(chainHeadHash).get(),
|
||||
blockchain.getBlockBody(chainHeadHash).get());
|
||||
|
||||
final Difficulty expectedTotalDifficulty = blockchain.getChainHead().getTotalDifficulty();
|
||||
|
||||
reset(onSend);
|
||||
|
||||
ethManager.blockMined(minedBlock);
|
||||
|
||||
final ArgumentCaptor<NewBlockMessage> messageSentCaptor =
|
||||
ArgumentCaptor.forClass(NewBlockMessage.class);
|
||||
final ArgumentCaptor<PeerConnection> receivingPeerCaptor =
|
||||
ArgumentCaptor.forClass(PeerConnection.class);
|
||||
final ArgumentCaptor<Capability> capabilityCaptor = ArgumentCaptor.forClass(Capability.class);
|
||||
|
||||
verify(onSend, times(PEER_COUNT))
|
||||
.exec(
|
||||
capabilityCaptor.capture(), messageSentCaptor.capture(), receivingPeerCaptor.capture());
|
||||
|
||||
// assert that all entries in capability param were Eth63
|
||||
assertThat(capabilityCaptor.getAllValues().stream().distinct().collect(Collectors.toList()))
|
||||
.isEqualTo(Collections.singletonList(EthProtocol.ETH63));
|
||||
|
||||
// assert that all messages transmitted contain the expected block & total difficulty.
|
||||
final ProtocolSchedule<Void> protocolSchdeule = MainnetProtocolSchedule.create();
|
||||
for (final NewBlockMessage msg : messageSentCaptor.getAllValues()) {
|
||||
assertThat(msg.block(protocolSchdeule)).isEqualTo(minedBlock);
|
||||
assertThat(msg.totalDifficulty(protocolSchdeule)).isEqualTo(expectedTotalDifficulty);
|
||||
}
|
||||
|
||||
assertThat(receivingPeerCaptor.getAllValues().containsAll(peers)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -1014,18 +905,13 @@ public final class EthProtocolManagerTest {
|
||||
|
||||
final CompletableFuture<Void> done = new CompletableFuture<>();
|
||||
try (final EthProtocolManager ethManager =
|
||||
new EthProtocolManager(
|
||||
EthProtocolManagerTestUtil.create(
|
||||
blockchain,
|
||||
() -> false,
|
||||
protocolContext.getWorldStateArchive(),
|
||||
BigInteger.ONE,
|
||||
Collections.emptyList(),
|
||||
true,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
TestClock.fixed(),
|
||||
new NoOpMetricsSystem(),
|
||||
transactionPool,
|
||||
EthProtocolConfiguration.defaultConfig())) {
|
||||
|
||||
final long startBlock = 1L;
|
||||
final int requestedBlockCount = 13;
|
||||
final int receivedBlockCount = 2;
|
||||
@@ -1084,18 +970,12 @@ public final class EthProtocolManagerTest {
|
||||
final TransactionsMessage transactionMessage = TransactionsMessage.readFrom(raw);
|
||||
|
||||
try (final EthProtocolManager ethManager =
|
||||
new EthProtocolManager(
|
||||
EthProtocolManagerTestUtil.create(
|
||||
blockchain,
|
||||
protocolContext.getWorldStateArchive(),
|
||||
BigInteger.ONE,
|
||||
Collections.emptyList(),
|
||||
true,
|
||||
ethScheduler,
|
||||
EthProtocolConfiguration.defaultConfig(),
|
||||
TestClock.fixed(),
|
||||
metricsSystem,
|
||||
new ForkIdManager(blockchain, Collections.emptyList()))) {
|
||||
|
||||
protocolContext.getWorldStateArchive(),
|
||||
transactionPool,
|
||||
EthProtocolConfiguration.defaultConfig())) {
|
||||
// Create a transaction pool. This has a side effect of registering a listener for the
|
||||
// transactions message.
|
||||
TransactionPoolFactory.createTransactionPool(
|
||||
|
||||
@@ -16,17 +16,19 @@ package org.hyperledger.besu.ethereum.eth.manager;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static org.hyperledger.besu.ethereum.core.InMemoryStorageProvider.createInMemoryBlockchain;
|
||||
import static org.hyperledger.besu.ethereum.core.InMemoryStorageProvider.createInMemoryWorldStateArchive;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import org.hyperledger.besu.config.GenesisConfigFile;
|
||||
import org.hyperledger.besu.ethereum.chain.Blockchain;
|
||||
import org.hyperledger.besu.ethereum.chain.ChainHead;
|
||||
import org.hyperledger.besu.ethereum.chain.GenesisState;
|
||||
import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil;
|
||||
import org.hyperledger.besu.ethereum.core.Difficulty;
|
||||
import org.hyperledger.besu.ethereum.eth.EthProtocol;
|
||||
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.DeterministicEthScheduler.TimeoutPolicy;
|
||||
import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator;
|
||||
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
|
||||
import org.hyperledger.besu.ethereum.mainnet.MainnetProtocolSchedule;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
|
||||
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.DefaultMessage;
|
||||
@@ -43,32 +45,58 @@ public class EthProtocolManagerTestUtil {
|
||||
|
||||
public static EthProtocolManager create(
|
||||
final Blockchain blockchain,
|
||||
final TimeoutPolicy timeoutPolicy,
|
||||
final WorldStateArchive worldStateArchive,
|
||||
final TimeoutPolicy timeoutPolicy) {
|
||||
return create(blockchain, worldStateArchive, new DeterministicEthScheduler(timeoutPolicy));
|
||||
final TransactionPool transactionPool,
|
||||
final EthProtocolConfiguration ethereumWireProtocolConfiguration) {
|
||||
return create(
|
||||
blockchain,
|
||||
new DeterministicEthScheduler(timeoutPolicy),
|
||||
worldStateArchive,
|
||||
transactionPool,
|
||||
ethereumWireProtocolConfiguration);
|
||||
}
|
||||
|
||||
public static EthProtocolManager create(
|
||||
final Blockchain blockchain,
|
||||
final EthScheduler ethScheduler,
|
||||
final WorldStateArchive worldStateArchive,
|
||||
final TransactionPool transactionPool,
|
||||
final EthProtocolConfiguration ethereumWireProtocolConfiguration,
|
||||
final EthPeers ethPeers,
|
||||
final EthMessages ethMessages,
|
||||
final EthContext ethContext) {
|
||||
final BigInteger networkId = BigInteger.ONE;
|
||||
return new EthProtocolManager(
|
||||
blockchain,
|
||||
networkId,
|
||||
worldStateArchive,
|
||||
transactionPool,
|
||||
ethereumWireProtocolConfiguration,
|
||||
ethPeers,
|
||||
ethMessages,
|
||||
ethContext,
|
||||
Collections.emptyList(),
|
||||
false,
|
||||
ethScheduler,
|
||||
new ForkIdManager(blockchain, Collections.emptyList()));
|
||||
}
|
||||
|
||||
public static EthProtocolManager create(final Blockchain blockchain) {
|
||||
return create(blockchain, new DeterministicEthScheduler(TimeoutPolicy.NEVER_TIMEOUT));
|
||||
}
|
||||
|
||||
public static EthProtocolManager create(
|
||||
final Blockchain blockchain,
|
||||
final WorldStateArchive worldStateArchive,
|
||||
final EthScheduler ethScheduler) {
|
||||
final BigInteger networkId = BigInteger.ONE;
|
||||
return new EthProtocolManager(
|
||||
final TransactionPool transactionPool,
|
||||
final EthProtocolConfiguration ethProtocolConfiguration) {
|
||||
return create(
|
||||
blockchain,
|
||||
new DeterministicEthScheduler(TimeoutPolicy.NEVER_TIMEOUT),
|
||||
worldStateArchive,
|
||||
networkId,
|
||||
Collections.emptyList(),
|
||||
false,
|
||||
ethScheduler,
|
||||
EthProtocolConfiguration.defaultConfig(),
|
||||
TestClock.fixed(),
|
||||
new NoOpMetricsSystem(),
|
||||
new ForkIdManager(blockchain, Collections.emptyList()));
|
||||
}
|
||||
|
||||
public static EthProtocolManager create(
|
||||
final Blockchain blockchain, final WorldStateArchive worldStateArchive) {
|
||||
return create(blockchain, worldStateArchive, TimeoutPolicy.NEVER_TIMEOUT);
|
||||
transactionPool,
|
||||
ethProtocolConfiguration);
|
||||
}
|
||||
|
||||
public static EthProtocolManager create(final EthScheduler ethScheduler) {
|
||||
@@ -76,8 +104,43 @@ public class EthProtocolManagerTestUtil {
|
||||
final GenesisConfigFile config = GenesisConfigFile.mainnet();
|
||||
final GenesisState genesisState = GenesisState.fromConfig(config, protocolSchedule);
|
||||
final Blockchain blockchain = createInMemoryBlockchain(genesisState.getBlock());
|
||||
final WorldStateArchive worldStateArchive = createInMemoryWorldStateArchive();
|
||||
return create(blockchain, worldStateArchive, ethScheduler);
|
||||
return create(blockchain, ethScheduler);
|
||||
}
|
||||
|
||||
public static EthProtocolManager create(
|
||||
final Blockchain blockchain,
|
||||
final EthScheduler ethScheduler,
|
||||
final WorldStateArchive worldStateArchive,
|
||||
final TransactionPool transactionPool,
|
||||
final EthProtocolConfiguration configuration) {
|
||||
EthPeers peers = new EthPeers(EthProtocol.NAME, TestClock.fixed(), new NoOpMetricsSystem());
|
||||
EthMessages messages = new EthMessages();
|
||||
|
||||
return create(
|
||||
blockchain,
|
||||
ethScheduler,
|
||||
worldStateArchive,
|
||||
transactionPool,
|
||||
configuration,
|
||||
peers,
|
||||
messages,
|
||||
new EthContext(peers, messages, ethScheduler));
|
||||
}
|
||||
|
||||
public static EthProtocolManager create(
|
||||
final Blockchain blockchain, final EthScheduler ethScheduler) {
|
||||
EthPeers peers = new EthPeers(EthProtocol.NAME, TestClock.fixed(), new NoOpMetricsSystem());
|
||||
EthMessages messages = new EthMessages();
|
||||
|
||||
return create(
|
||||
blockchain,
|
||||
ethScheduler,
|
||||
BlockchainSetupUtil.forTesting().getWorldArchive(),
|
||||
mock(TransactionPool.class),
|
||||
EthProtocolConfiguration.defaultConfig(),
|
||||
peers,
|
||||
messages,
|
||||
new EthContext(peers, messages, ethScheduler));
|
||||
}
|
||||
|
||||
public static EthProtocolManager create() {
|
||||
|
||||
@@ -25,6 +25,7 @@ import org.hyperledger.besu.ethereum.core.Hash;
|
||||
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
|
||||
import org.hyperledger.besu.ethereum.eth.messages.GetNodeDataMessage;
|
||||
import org.hyperledger.besu.ethereum.eth.messages.NodeDataMessage;
|
||||
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
|
||||
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
|
||||
|
||||
import java.util.Optional;
|
||||
@@ -43,13 +44,18 @@ public class EthServerTest {
|
||||
private static final Hash HASH3 = Hash.hash(VALUE3);
|
||||
private final Blockchain blockchain = mock(Blockchain.class);
|
||||
private final WorldStateArchive worldStateArchive = mock(WorldStateArchive.class);
|
||||
private final TransactionPool transactionPool = mock(TransactionPool.class);
|
||||
private final EthPeer ethPeer = mock(EthPeer.class);
|
||||
private final EthMessages ethMessages = new EthMessages();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
new EthServer(
|
||||
blockchain, worldStateArchive, ethMessages, new EthProtocolConfiguration(2, 2, 2, 2));
|
||||
blockchain,
|
||||
worldStateArchive,
|
||||
transactionPool,
|
||||
ethMessages,
|
||||
new EthProtocolConfiguration(2, 2, 2, 2, 2));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -17,6 +17,7 @@ package org.hyperledger.besu.ethereum.eth.manager;
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
import static org.hyperledger.besu.ethereum.core.InMemoryStorageProvider.createInMemoryWorldStateArchive;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import org.hyperledger.besu.ethereum.chain.Blockchain;
|
||||
import org.hyperledger.besu.ethereum.core.BlockBody;
|
||||
@@ -24,15 +25,19 @@ import org.hyperledger.besu.ethereum.core.BlockDataGenerator;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||
import org.hyperledger.besu.ethereum.core.Difficulty;
|
||||
import org.hyperledger.besu.ethereum.core.Hash;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
|
||||
import org.hyperledger.besu.ethereum.eth.EthProtocol;
|
||||
import org.hyperledger.besu.ethereum.eth.messages.BlockBodiesMessage;
|
||||
import org.hyperledger.besu.ethereum.eth.messages.BlockHeadersMessage;
|
||||
import org.hyperledger.besu.ethereum.eth.messages.EthPV62;
|
||||
import org.hyperledger.besu.ethereum.eth.messages.EthPV63;
|
||||
import org.hyperledger.besu.ethereum.eth.messages.EthPV65;
|
||||
import org.hyperledger.besu.ethereum.eth.messages.NodeDataMessage;
|
||||
import org.hyperledger.besu.ethereum.eth.messages.PooledTransactionsMessage;
|
||||
import org.hyperledger.besu.ethereum.eth.messages.ReceiptsMessage;
|
||||
import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator;
|
||||
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
|
||||
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Capability;
|
||||
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.DefaultMessage;
|
||||
@@ -223,12 +228,24 @@ public class RespondingEthPeer {
|
||||
};
|
||||
}
|
||||
|
||||
public static Responder blockchainResponder(final Blockchain blockchain) {
|
||||
return blockchainResponder(blockchain, createInMemoryWorldStateArchive());
|
||||
private static TransactionPool createTransactionPool() {
|
||||
return mock(TransactionPool.class);
|
||||
}
|
||||
|
||||
public static Responder blockchainResponder(
|
||||
final Blockchain blockchain, final WorldStateArchive worldStateArchive) {
|
||||
return blockchainResponder(blockchain, worldStateArchive, createTransactionPool());
|
||||
}
|
||||
|
||||
public static Responder blockchainResponder(final Blockchain blockchain) {
|
||||
return blockchainResponder(
|
||||
blockchain, createInMemoryWorldStateArchive(), createTransactionPool());
|
||||
}
|
||||
|
||||
public static Responder blockchainResponder(
|
||||
final Blockchain blockchain,
|
||||
final WorldStateArchive worldStateArchive,
|
||||
final TransactionPool transactionPool) {
|
||||
return (cap, msg) -> {
|
||||
MessageData response = null;
|
||||
switch (msg.getCode()) {
|
||||
@@ -244,6 +261,8 @@ public class RespondingEthPeer {
|
||||
case EthPV63.GET_NODE_DATA:
|
||||
response = EthServer.constructGetNodeDataResponse(worldStateArchive, msg, 200);
|
||||
break;
|
||||
case EthPV65.GET_POOLED_TRANSACTIONS:
|
||||
response = EthServer.constructGetPooledTransactionsResponse(transactionPool, msg, 200);
|
||||
}
|
||||
return Optional.ofNullable(response);
|
||||
};
|
||||
@@ -265,11 +284,13 @@ public class RespondingEthPeer {
|
||||
public static <C> Responder partialResponder(
|
||||
final Blockchain blockchain,
|
||||
final WorldStateArchive worldStateArchive,
|
||||
final TransactionPool transactionPool,
|
||||
final ProtocolSchedule<C> protocolSchedule,
|
||||
final float portion) {
|
||||
checkArgument(portion >= 0.0 && portion <= 1.0, "Portion is in the range [0.0..1.0]");
|
||||
|
||||
final Responder fullResponder = blockchainResponder(blockchain, worldStateArchive);
|
||||
final Responder fullResponder =
|
||||
blockchainResponder(blockchain, worldStateArchive, transactionPool);
|
||||
return (cap, msg) -> {
|
||||
final Optional<MessageData> maybeResponse = fullResponder.respond(cap, msg);
|
||||
if (!maybeResponse.isPresent()) {
|
||||
@@ -310,6 +331,15 @@ public class RespondingEthPeer {
|
||||
originalNodeData.subList(0, (int) (originalNodeData.size() * portion));
|
||||
partialResponse = NodeDataMessage.create(partialNodeData);
|
||||
break;
|
||||
case EthPV65.GET_POOLED_TRANSACTIONS:
|
||||
final PooledTransactionsMessage pooledTransactionsMessage =
|
||||
PooledTransactionsMessage.readFrom(originalResponse);
|
||||
final List<Transaction> originalPooledTx =
|
||||
Lists.newArrayList(pooledTransactionsMessage.transactions());
|
||||
final List<Transaction> partialPooledTx =
|
||||
originalPooledTx.subList(0, (int) (originalPooledTx.size() * portion));
|
||||
partialResponse = PooledTransactionsMessage.create(partialPooledTx);
|
||||
break;
|
||||
}
|
||||
return Optional.of(partialResponse);
|
||||
};
|
||||
@@ -331,6 +361,9 @@ public class RespondingEthPeer {
|
||||
case EthPV63.GET_NODE_DATA:
|
||||
response = NodeDataMessage.create(Collections.emptyList());
|
||||
break;
|
||||
case EthPV65.GET_POOLED_TRANSACTIONS:
|
||||
response = PooledTransactionsMessage.create(Collections.emptyList());
|
||||
break;
|
||||
}
|
||||
return Optional.ofNullable(response);
|
||||
};
|
||||
|
||||
@@ -19,15 +19,27 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
import org.hyperledger.besu.ethereum.ProtocolContext;
|
||||
import org.hyperledger.besu.ethereum.chain.Blockchain;
|
||||
import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil;
|
||||
import org.hyperledger.besu.ethereum.core.Wei;
|
||||
import org.hyperledger.besu.ethereum.eth.EthProtocol;
|
||||
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.DeterministicEthScheduler;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthMessages;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthPeer;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthPeers;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.task.EthTask;
|
||||
import org.hyperledger.besu.ethereum.eth.sync.state.SyncState;
|
||||
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
|
||||
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration;
|
||||
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolFactory;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
|
||||
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
|
||||
import org.hyperledger.besu.plugin.services.MetricsSystem;
|
||||
import org.hyperledger.besu.testutil.TestClock;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
@@ -49,6 +61,7 @@ public abstract class AbstractMessageTaskTest<T, R> {
|
||||
protected static MetricsSystem metricsSystem = new NoOpMetricsSystem();
|
||||
protected EthProtocolManager ethProtocolManager;
|
||||
protected EthContext ethContext;
|
||||
protected TransactionPool transactionPool;
|
||||
protected AtomicBoolean peersDoTimeout;
|
||||
protected AtomicInteger peerCountToTimeout;
|
||||
|
||||
@@ -59,6 +72,7 @@ public abstract class AbstractMessageTaskTest<T, R> {
|
||||
blockchain = blockchainSetupUtil.getBlockchain();
|
||||
protocolSchedule = blockchainSetupUtil.getProtocolSchedule();
|
||||
protocolContext = blockchainSetupUtil.getProtocolContext();
|
||||
|
||||
assert (blockchainSetupUtil.getMaxBlockNumber() >= 20L);
|
||||
}
|
||||
|
||||
@@ -66,12 +80,33 @@ public abstract class AbstractMessageTaskTest<T, R> {
|
||||
public void setupTest() {
|
||||
peersDoTimeout = new AtomicBoolean(false);
|
||||
peerCountToTimeout = new AtomicInteger(0);
|
||||
final EthPeers ethPeers = new EthPeers(EthProtocol.NAME, TestClock.fixed(), metricsSystem);
|
||||
final EthMessages ethMessages = new EthMessages();
|
||||
final EthScheduler ethScheduler =
|
||||
new DeterministicEthScheduler(
|
||||
() -> peerCountToTimeout.getAndDecrement() > 0 || peersDoTimeout.get());
|
||||
ethContext = new EthContext(ethPeers, ethMessages, ethScheduler);
|
||||
final SyncState syncState = new SyncState(blockchain, ethContext.getEthPeers());
|
||||
transactionPool =
|
||||
TransactionPoolFactory.createTransactionPool(
|
||||
protocolSchedule,
|
||||
protocolContext,
|
||||
ethContext,
|
||||
TestClock.fixed(),
|
||||
metricsSystem,
|
||||
syncState,
|
||||
Wei.of(1),
|
||||
TransactionPoolConfiguration.builder().build());
|
||||
ethProtocolManager =
|
||||
EthProtocolManagerTestUtil.create(
|
||||
blockchain,
|
||||
ethScheduler,
|
||||
protocolContext.getWorldStateArchive(),
|
||||
() -> peerCountToTimeout.getAndDecrement() > 0 || peersDoTimeout.get());
|
||||
ethContext = ethProtocolManager.ethContext();
|
||||
transactionPool,
|
||||
EthProtocolConfiguration.defaultConfig(),
|
||||
ethPeers,
|
||||
ethMessages,
|
||||
ethContext);
|
||||
}
|
||||
|
||||
protected abstract T generateDataToBeRequested();
|
||||
@@ -85,7 +120,8 @@ public abstract class AbstractMessageTaskTest<T, R> {
|
||||
public void completesWhenPeersAreResponsive() {
|
||||
// Setup a responsive peer
|
||||
final RespondingEthPeer.Responder responder =
|
||||
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive());
|
||||
RespondingEthPeer.blockchainResponder(
|
||||
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
|
||||
final RespondingEthPeer respondingPeer =
|
||||
EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000);
|
||||
|
||||
|
||||
@@ -43,7 +43,11 @@ public abstract class PeerMessageTaskTest<T>
|
||||
// Setup a partially responsive peer
|
||||
final RespondingEthPeer.Responder responder =
|
||||
RespondingEthPeer.partialResponder(
|
||||
blockchain, protocolContext.getWorldStateArchive(), protocolSchedule, 0.5f);
|
||||
blockchain,
|
||||
protocolContext.getWorldStateArchive(),
|
||||
transactionPool,
|
||||
protocolSchedule,
|
||||
0.5f);
|
||||
final RespondingEthPeer respondingEthPeer =
|
||||
EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000);
|
||||
|
||||
|
||||
@@ -54,7 +54,11 @@ public abstract class RetryingMessageTaskTest<T> extends AbstractMessageTaskTest
|
||||
// Setup a partially responsive peer and a non-responsive peer
|
||||
final RespondingEthPeer.Responder partialResponder =
|
||||
RespondingEthPeer.partialResponder(
|
||||
blockchain, protocolContext.getWorldStateArchive(), protocolSchedule, 0.5f);
|
||||
blockchain,
|
||||
protocolContext.getWorldStateArchive(),
|
||||
transactionPool,
|
||||
protocolSchedule,
|
||||
0.5f);
|
||||
final RespondingEthPeer.Responder emptyResponder = RespondingEthPeer.emptyResponder();
|
||||
final RespondingEthPeer respondingPeer =
|
||||
EthProtocolManagerTestUtil.createPeer(ethProtocolManager);
|
||||
@@ -100,16 +104,32 @@ public abstract class RetryingMessageTaskTest<T> extends AbstractMessageTaskTest
|
||||
// Respond with partial data up until complete.
|
||||
respondingPeer.respond(
|
||||
RespondingEthPeer.partialResponder(
|
||||
blockchain, protocolContext.getWorldStateArchive(), protocolSchedule, 0.25f));
|
||||
blockchain,
|
||||
protocolContext.getWorldStateArchive(),
|
||||
transactionPool,
|
||||
protocolSchedule,
|
||||
0.25f));
|
||||
respondingPeer.respond(
|
||||
RespondingEthPeer.partialResponder(
|
||||
blockchain, protocolContext.getWorldStateArchive(), protocolSchedule, 0.50f));
|
||||
blockchain,
|
||||
protocolContext.getWorldStateArchive(),
|
||||
transactionPool,
|
||||
protocolSchedule,
|
||||
0.50f));
|
||||
respondingPeer.respond(
|
||||
RespondingEthPeer.partialResponder(
|
||||
blockchain, protocolContext.getWorldStateArchive(), protocolSchedule, 0.75f));
|
||||
blockchain,
|
||||
protocolContext.getWorldStateArchive(),
|
||||
transactionPool,
|
||||
protocolSchedule,
|
||||
0.75f));
|
||||
respondingPeer.respond(
|
||||
RespondingEthPeer.partialResponder(
|
||||
blockchain, protocolContext.getWorldStateArchive(), protocolSchedule, 1.0f));
|
||||
blockchain,
|
||||
protocolContext.getWorldStateArchive(),
|
||||
transactionPool,
|
||||
protocolSchedule,
|
||||
1.0f));
|
||||
|
||||
assertThat(future.isDone()).isTrue();
|
||||
assertResultMatchesExpectation(requestedData, future.get(), respondingPeer.getEthPeer());
|
||||
@@ -140,7 +160,8 @@ public abstract class RetryingMessageTaskTest<T> extends AbstractMessageTaskTest
|
||||
|
||||
// Setup a peer
|
||||
final RespondingEthPeer.Responder responder =
|
||||
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive());
|
||||
RespondingEthPeer.blockchainResponder(
|
||||
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
|
||||
final RespondingEthPeer respondingPeer =
|
||||
EthProtocolManagerTestUtil.createPeer(ethProtocolManager);
|
||||
respondingPeer.respondWhile(responder, () -> !future.isDone());
|
||||
@@ -153,7 +174,8 @@ public abstract class RetryingMessageTaskTest<T> extends AbstractMessageTaskTest
|
||||
throws ExecutionException, InterruptedException {
|
||||
peerCountToTimeout.set(1);
|
||||
final RespondingEthPeer.Responder responder =
|
||||
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive());
|
||||
RespondingEthPeer.blockchainResponder(
|
||||
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
|
||||
final RespondingEthPeer respondingPeer =
|
||||
EthProtocolManagerTestUtil.createPeer(ethProtocolManager);
|
||||
final T requestedData = generateDataToBeRequested();
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright ConsenSys AG.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
|
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
package org.hyperledger.besu.ethereum.eth.manager.task;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import org.hyperledger.besu.crypto.SECP256K1;
|
||||
import org.hyperledger.besu.ethereum.core.Hash;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.core.TransactionTestFixture;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthPeer;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.ethtaskutils.PeerMessageTaskTest;
|
||||
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
|
||||
import org.hyperledger.besu.plugin.services.MetricsSystem;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
public class GetPooledTransactionsFromPeerTaskTest extends PeerMessageTaskTest<List<Transaction>> {
|
||||
|
||||
private final MetricsSystem metricsSystem = new NoOpMetricsSystem();
|
||||
|
||||
@Override
|
||||
protected List<Transaction> generateDataToBeRequested() {
|
||||
|
||||
final List<Transaction> requestedData = new ArrayList<>();
|
||||
SECP256K1.KeyPair keyPair = SECP256K1.KeyPair.generate();
|
||||
for (int i = 0; i < 3; i++) {
|
||||
Transaction tx =
|
||||
new TransactionTestFixture()
|
||||
.nonce(i)
|
||||
.gasLimit(100000)
|
||||
.chainId(Optional.empty())
|
||||
.createTransaction(keyPair);
|
||||
assertThat(transactionPool.getPendingTransactions().addLocalTransaction(tx)).isTrue();
|
||||
requestedData.add(tx);
|
||||
}
|
||||
return requestedData;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected EthTask<AbstractPeerTask.PeerTaskResult<List<Transaction>>> createTask(
|
||||
final List<Transaction> requestedData) {
|
||||
final List<Hash> hashes =
|
||||
Lists.newArrayList(requestedData).stream()
|
||||
.map(Transaction::getHash)
|
||||
.collect(Collectors.toList());
|
||||
return GetPooledTransactionsFromPeerTask.forHashes(ethContext, hashes, metricsSystem);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void assertPartialResultMatchesExpectation(
|
||||
final List<Transaction> requestedData, final List<Transaction> partialResponse) {
|
||||
assertThat(partialResponse.size()).isLessThanOrEqualTo(requestedData.size());
|
||||
assertThat(partialResponse.size()).isGreaterThan(0);
|
||||
for (Transaction data : partialResponse) {
|
||||
assertThat(requestedData).contains(data);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void assertResultMatchesExpectation(
|
||||
final List<Transaction> requestedData,
|
||||
final AbstractPeerTask.PeerTaskResult<List<Transaction>> response,
|
||||
final EthPeer respondingPeer) {
|
||||
assertThat(response.getResult().size()).isEqualTo(requestedData.size());
|
||||
for (Transaction data : response.getResult()) {
|
||||
assertThat(requestedData).contains(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -74,7 +74,11 @@ public class RetryingGetNodeDataFromPeerTaskTest extends RetryingMessageTaskTest
|
||||
// Respond with partial data.
|
||||
respondingPeer.respond(
|
||||
RespondingEthPeer.partialResponder(
|
||||
blockchain, protocolContext.getWorldStateArchive(), protocolSchedule, 0.50f));
|
||||
blockchain,
|
||||
protocolContext.getWorldStateArchive(),
|
||||
transactionPool,
|
||||
protocolSchedule,
|
||||
0.50f));
|
||||
|
||||
assertThat(future.isDone()).isTrue();
|
||||
// Check that it immediately returns the data we got in the response.
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.eth.messages;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
import org.hyperledger.besu.ethereum.core.Hash;
|
||||
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.RawMessage;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.apache.tuweni.bytes.Bytes32;
|
||||
import org.junit.Test;
|
||||
|
||||
public class GetPooledTransactionsMessageTest {
|
||||
|
||||
@Test
|
||||
public void roundTripGetPooledTransactionsMessage() {
|
||||
List<Hash> hashes = Arrays.asList(Hash.wrap(Bytes32.random()));
|
||||
final GetPooledTransactionsMessage msg = GetPooledTransactionsMessage.create(hashes);
|
||||
assertThat(msg.getCode()).isEqualTo(EthPV65.GET_POOLED_TRANSACTIONS);
|
||||
assertThat(msg.pooledTransactions()).isEqualTo(hashes);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readFromMessageWithWrongCodeThrows() {
|
||||
final RawMessage rawMsg = new RawMessage(EthPV62.BLOCK_HEADERS, Bytes.of(0));
|
||||
|
||||
assertThatExceptionOfType(IllegalArgumentException.class)
|
||||
.isThrownBy(() -> GetPooledTransactionsMessage.readFrom(rawMsg));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* 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.eth.messages;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import org.hyperledger.besu.ethereum.core.BlockDataGenerator;
|
||||
import org.hyperledger.besu.ethereum.core.Hash;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class LimitedNewPooledTransactionHashesMessagesTest {
|
||||
|
||||
private final BlockDataGenerator generator = new BlockDataGenerator();
|
||||
private final List<Hash> sampleTxs =
|
||||
generator.transactions(1).stream().map(Transaction::getHash).collect(Collectors.toList());
|
||||
private final NewPooledTransactionHashesMessage sampleTransactionMessages =
|
||||
NewPooledTransactionHashesMessage.create(sampleTxs);
|
||||
private final LimitedNewPooledTransactionHashesMessages sampleLimitedTransactionsMessages =
|
||||
new LimitedNewPooledTransactionHashesMessages(sampleTransactionMessages, sampleTxs);
|
||||
|
||||
@Test
|
||||
public void createLimited() {
|
||||
final List<Hash> txs =
|
||||
generator.transactions(6000).stream()
|
||||
.map(Transaction::getHash)
|
||||
.collect(Collectors.toList());
|
||||
final LimitedNewPooledTransactionHashesMessages firstMessage =
|
||||
LimitedNewPooledTransactionHashesMessages.createLimited(txs);
|
||||
assertThat(firstMessage.getIncludedTransactions().size()).isEqualTo(4096);
|
||||
|
||||
txs.removeAll(firstMessage.getIncludedTransactions());
|
||||
assertThat(txs.size()).isEqualTo(6000 - 4096);
|
||||
final LimitedNewPooledTransactionHashesMessages secondMessage =
|
||||
LimitedNewPooledTransactionHashesMessages.createLimited(txs);
|
||||
assertThat(secondMessage.getIncludedTransactions().size()).isEqualTo(6000 - 4096);
|
||||
txs.removeAll(secondMessage.getIncludedTransactions());
|
||||
assertThat(txs.size()).isEqualTo(0);
|
||||
assertThat(
|
||||
firstMessage.getTransactionsMessage().getSize()
|
||||
+ secondMessage.getTransactionsMessage().getSize())
|
||||
.isLessThan(2 * LimitedTransactionsMessages.LIMIT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getTransactionsMessage() {
|
||||
assertThat(sampleLimitedTransactionsMessages.getTransactionsMessage())
|
||||
.isEqualTo(sampleTransactionMessages);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getIncludedTransactions() {
|
||||
assertThat(sampleLimitedTransactionsMessages.getIncludedTransactions()).isEqualTo(sampleTxs);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* 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.eth.messages;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
import org.hyperledger.besu.ethereum.core.Hash;
|
||||
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.RawMessage;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.apache.tuweni.bytes.Bytes32;
|
||||
import org.junit.Test;
|
||||
|
||||
public class NewPooledTransactionHashesMessageTest {
|
||||
|
||||
@Test
|
||||
public void roundTripNewPooledTransactionHashesMessage() {
|
||||
List<Hash> hashes = Arrays.asList(Hash.wrap(Bytes32.random()));
|
||||
final NewPooledTransactionHashesMessage msg = NewPooledTransactionHashesMessage.create(hashes);
|
||||
assertThat(msg.getCode()).isEqualTo(EthPV65.NEW_POOLED_TRANSACTION_HASHES);
|
||||
assertThat(msg.pendingTransactions()).isEqualTo(hashes);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readFromMessageWithWrongCodeThrows() {
|
||||
final RawMessage rawMsg = new RawMessage(EthPV62.BLOCK_HEADERS, Bytes.of(0));
|
||||
|
||||
assertThatExceptionOfType(IllegalArgumentException.class)
|
||||
.isThrownBy(() -> NewPooledTransactionHashesMessage.readFrom(rawMsg));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* 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.eth.messages;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
|
||||
import org.hyperledger.besu.crypto.SECP256K1;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.core.Wei;
|
||||
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.RawMessage;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.tuweni.bytes.Bytes;
|
||||
import org.junit.Test;
|
||||
|
||||
public class PooledTransactionsMessageTest {
|
||||
|
||||
@Test
|
||||
public void roundTripPooledTransactionsMessage() {
|
||||
List<Transaction> tx =
|
||||
Arrays.asList(
|
||||
Transaction.builder()
|
||||
.nonce(42)
|
||||
.gasLimit(654321)
|
||||
.gasPrice(Wei.of(2))
|
||||
.value(Wei.of(1337))
|
||||
.payload(Bytes.EMPTY)
|
||||
.signAndBuild(SECP256K1.KeyPair.generate()));
|
||||
final PooledTransactionsMessage msg = PooledTransactionsMessage.create(tx);
|
||||
assertThat(msg.getCode()).isEqualTo(EthPV65.POOLED_TRANSACTIONS);
|
||||
assertThat(msg.transactions()).isEqualTo(tx);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readFromMessageWithWrongCodeThrows() {
|
||||
final RawMessage rawMsg = new RawMessage(EthPV62.BLOCK_HEADERS, Bytes.of(0));
|
||||
|
||||
assertThatExceptionOfType(IllegalArgumentException.class)
|
||||
.isThrownBy(() -> PooledTransactionsMessage.readFrom(rawMsg));
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,7 @@ import org.hyperledger.besu.ethereum.core.BlockDataGenerator.BlockOptions;
|
||||
import org.hyperledger.besu.ethereum.core.BlockImporter;
|
||||
import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil;
|
||||
import org.hyperledger.besu.ethereum.core.Difficulty;
|
||||
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthMessages;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthPeers;
|
||||
@@ -93,7 +94,11 @@ public class BlockPropagationManagerTest {
|
||||
tempProtocolContext.getWorldStateArchive(),
|
||||
tempProtocolContext.getConsensusState());
|
||||
ethProtocolManager =
|
||||
EthProtocolManagerTestUtil.create(blockchain, blockchainUtil.getWorldArchive());
|
||||
EthProtocolManagerTestUtil.create(
|
||||
blockchain,
|
||||
blockchainUtil.getWorldArchive(),
|
||||
blockchainUtil.getTransactionPool(),
|
||||
EthProtocolConfiguration.defaultConfig());
|
||||
syncConfig = SynchronizerConfiguration.builder().blockPropagationRange(-3, 5).build();
|
||||
syncState = new SyncState(blockchain, ethProtocolManager.ethContext().getEthPeers());
|
||||
blockBroadcaster = mock(BlockBroadcaster.class);
|
||||
|
||||
@@ -38,7 +38,7 @@ public class ChainHeadTrackerTest {
|
||||
private final BlockchainSetupUtil<Void> blockchainSetupUtil = BlockchainSetupUtil.forTesting();
|
||||
private final MutableBlockchain blockchain = blockchainSetupUtil.getBlockchain();
|
||||
private final EthProtocolManager ethProtocolManager =
|
||||
EthProtocolManagerTestUtil.create(blockchain, blockchainSetupUtil.getWorldArchive());
|
||||
EthProtocolManagerTestUtil.create(blockchain);
|
||||
private final RespondingEthPeer respondingPeer =
|
||||
RespondingEthPeer.builder()
|
||||
.ethProtocolManager(ethProtocolManager)
|
||||
@@ -62,7 +62,9 @@ public class ChainHeadTrackerTest {
|
||||
public void shouldRequestHeaderChainHeadWhenNewPeerConnects() {
|
||||
final Responder responder =
|
||||
RespondingEthPeer.blockchainResponder(
|
||||
blockchainSetupUtil.getBlockchain(), blockchainSetupUtil.getWorldArchive());
|
||||
blockchainSetupUtil.getBlockchain(),
|
||||
blockchainSetupUtil.getWorldArchive(),
|
||||
blockchainSetupUtil.getTransactionPool());
|
||||
chainHeadTracker.onPeerConnected(respondingPeer.getEthPeer());
|
||||
|
||||
Assertions.assertThat(chainHeadState().getEstimatedHeight()).isZero();
|
||||
@@ -77,7 +79,9 @@ public class ChainHeadTrackerTest {
|
||||
public void shouldIgnoreHeadersIfChainHeadHasAlreadyBeenUpdatedWhileWaiting() {
|
||||
final Responder responder =
|
||||
RespondingEthPeer.blockchainResponder(
|
||||
blockchainSetupUtil.getBlockchain(), blockchainSetupUtil.getWorldArchive());
|
||||
blockchainSetupUtil.getBlockchain(),
|
||||
blockchainSetupUtil.getWorldArchive(),
|
||||
blockchainSetupUtil.getTransactionPool());
|
||||
chainHeadTracker.onPeerConnected(respondingPeer.getEthPeer());
|
||||
|
||||
// Change the hash of the current known head
|
||||
@@ -92,7 +96,9 @@ public class ChainHeadTrackerTest {
|
||||
public void shouldCheckTrialingPeerLimits() {
|
||||
final Responder responder =
|
||||
RespondingEthPeer.blockchainResponder(
|
||||
blockchainSetupUtil.getBlockchain(), blockchainSetupUtil.getWorldArchive());
|
||||
blockchainSetupUtil.getBlockchain(),
|
||||
blockchainSetupUtil.getWorldArchive(),
|
||||
blockchainSetupUtil.getTransactionPool());
|
||||
chainHeadTracker.onPeerConnected(respondingPeer.getEthPeer());
|
||||
|
||||
Assertions.assertThat(chainHeadState().getEstimatedHeight()).isZero();
|
||||
|
||||
@@ -23,11 +23,13 @@ import org.hyperledger.besu.ethereum.ProtocolContext;
|
||||
import org.hyperledger.besu.ethereum.chain.Blockchain;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||
import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil;
|
||||
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer.Responder;
|
||||
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
|
||||
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
|
||||
import org.hyperledger.besu.plugin.services.MetricsSystem;
|
||||
@@ -50,6 +52,7 @@ public class CheckpointHeaderFetcherTest {
|
||||
private static ProtocolSchedule<Void> protocolSchedule;
|
||||
private static ProtocolContext<Void> protocolContext;
|
||||
private static final MetricsSystem metricsSystem = new NoOpMetricsSystem();
|
||||
private static TransactionPool transactionPool;
|
||||
private EthProtocolManager ethProtocolManager;
|
||||
private Responder responder;
|
||||
private RespondingEthPeer respondingPeer;
|
||||
@@ -59,6 +62,7 @@ public class CheckpointHeaderFetcherTest {
|
||||
final BlockchainSetupUtil<Void> blockchainSetupUtil = BlockchainSetupUtil.forTesting();
|
||||
blockchainSetupUtil.importAllBlocks();
|
||||
blockchain = blockchainSetupUtil.getBlockchain();
|
||||
transactionPool = blockchainSetupUtil.getTransactionPool();
|
||||
protocolSchedule = blockchainSetupUtil.getProtocolSchedule();
|
||||
protocolContext = blockchainSetupUtil.getProtocolContext();
|
||||
}
|
||||
@@ -67,9 +71,14 @@ public class CheckpointHeaderFetcherTest {
|
||||
public void setUpTest() {
|
||||
ethProtocolManager =
|
||||
EthProtocolManagerTestUtil.create(
|
||||
blockchain, protocolContext.getWorldStateArchive(), () -> false);
|
||||
blockchain,
|
||||
() -> false,
|
||||
protocolContext.getWorldStateArchive(),
|
||||
transactionPool,
|
||||
EthProtocolConfiguration.defaultConfig());
|
||||
responder =
|
||||
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive());
|
||||
RespondingEthPeer.blockchainResponder(
|
||||
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
|
||||
respondingPeer =
|
||||
EthProtocolManagerTestUtil.createPeer(
|
||||
ethProtocolManager, blockchain.getChainHeadBlockNumber());
|
||||
|
||||
@@ -62,8 +62,7 @@ public class DownloadHeadersStepTest {
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
ethProtocolManager =
|
||||
EthProtocolManagerTestUtil.create(blockchain, protocolContext.getWorldStateArchive());
|
||||
ethProtocolManager = EthProtocolManagerTestUtil.create(blockchain);
|
||||
downloader =
|
||||
new DownloadHeadersStep<>(
|
||||
protocolSchedule,
|
||||
|
||||
@@ -16,6 +16,7 @@ package org.hyperledger.besu.ethereum.eth.sync.fastsync;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import org.hyperledger.besu.ethereum.ProtocolContext;
|
||||
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
|
||||
@@ -24,9 +25,11 @@ import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||
import org.hyperledger.besu.ethereum.core.BlockWithReceipts;
|
||||
import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil;
|
||||
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
|
||||
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer;
|
||||
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
|
||||
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
|
||||
|
||||
import java.util.List;
|
||||
@@ -54,8 +57,14 @@ public class DownloadReceiptsStepTest {
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
TransactionPool transactionPool = mock(TransactionPool.class);
|
||||
ethProtocolManager =
|
||||
EthProtocolManagerTestUtil.create(blockchain, protocolContext.getWorldStateArchive());
|
||||
EthProtocolManagerTestUtil.create(
|
||||
blockchain,
|
||||
() -> false,
|
||||
protocolContext.getWorldStateArchive(),
|
||||
transactionPool,
|
||||
EthProtocolConfiguration.defaultConfig());
|
||||
downloadReceiptsStep =
|
||||
new DownloadReceiptsStep(ethProtocolManager.ethContext(), new NoOpMetricsSystem());
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import org.hyperledger.besu.ethereum.core.BlockHeaderFunctions;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
|
||||
import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil;
|
||||
import org.hyperledger.besu.ethereum.core.Difficulty;
|
||||
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthPeer;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
|
||||
@@ -67,8 +68,10 @@ public class FastSyncActionsTest {
|
||||
ethProtocolManager =
|
||||
EthProtocolManagerTestUtil.create(
|
||||
blockchain,
|
||||
() -> timeoutCount.getAndDecrement() > 0,
|
||||
blockchainSetupUtil.getWorldArchive(),
|
||||
() -> timeoutCount.getAndDecrement() > 0);
|
||||
blockchainSetupUtil.getTransactionPool(),
|
||||
EthProtocolConfiguration.defaultConfig());
|
||||
fastSyncActions = createFastSyncActions(syncConfig);
|
||||
}
|
||||
|
||||
|
||||
@@ -70,9 +70,8 @@ public class FastSyncChainDownloaderTest {
|
||||
protocolContext = localBlockchainSetup.getProtocolContext();
|
||||
ethProtocolManager =
|
||||
EthProtocolManagerTestUtil.create(
|
||||
localBlockchain,
|
||||
localBlockchainSetup.getWorldArchive(),
|
||||
new EthScheduler(1, 1, 1, new NoOpMetricsSystem()));
|
||||
localBlockchain, new EthScheduler(1, 1, 1, 1, new NoOpMetricsSystem()));
|
||||
|
||||
ethContext = ethProtocolManager.ethContext();
|
||||
syncState = new SyncState(protocolContext.getBlockchain(), ethContext.getEthPeers());
|
||||
}
|
||||
|
||||
@@ -24,11 +24,13 @@ import org.hyperledger.besu.ethereum.chain.Blockchain;
|
||||
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
|
||||
import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil;
|
||||
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer.Responder;
|
||||
import org.hyperledger.besu.ethereum.eth.sync.fastsync.PivotBlockConfirmer.ContestedPivotBlockException;
|
||||
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
|
||||
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
|
||||
import org.hyperledger.besu.plugin.services.MetricsSystem;
|
||||
@@ -51,6 +53,7 @@ public class PivotBlockConfirmerTest {
|
||||
private final AtomicBoolean timeout = new AtomicBoolean(false);
|
||||
private EthProtocolManager ethProtocolManager;
|
||||
private MutableBlockchain blockchain;
|
||||
private TransactionPool transactionPool;
|
||||
private PivotBlockConfirmer<Void> pivotBlockConfirmer;
|
||||
private ProtocolSchedule<Void> protocolSchedule;
|
||||
|
||||
@@ -59,11 +62,16 @@ public class PivotBlockConfirmerTest {
|
||||
final BlockchainSetupUtil<Void> blockchainSetupUtil = BlockchainSetupUtil.forTesting();
|
||||
blockchainSetupUtil.importAllBlocks();
|
||||
blockchain = blockchainSetupUtil.getBlockchain();
|
||||
transactionPool = blockchainSetupUtil.getTransactionPool();
|
||||
protocolSchedule = blockchainSetupUtil.getProtocolSchedule();
|
||||
protocolContext = blockchainSetupUtil.getProtocolContext();
|
||||
ethProtocolManager =
|
||||
EthProtocolManagerTestUtil.create(
|
||||
blockchain, blockchainSetupUtil.getWorldArchive(), timeout::get);
|
||||
blockchain,
|
||||
timeout::get,
|
||||
blockchainSetupUtil.getWorldArchive(),
|
||||
transactionPool,
|
||||
EthProtocolConfiguration.defaultConfig());
|
||||
pivotBlockConfirmer = createPivotBlockConfirmer(3, 1);
|
||||
}
|
||||
|
||||
@@ -85,7 +93,8 @@ public class PivotBlockConfirmerTest {
|
||||
pivotBlockConfirmer = createPivotBlockConfirmer(2, 1);
|
||||
|
||||
final Responder responder =
|
||||
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive());
|
||||
RespondingEthPeer.blockchainResponder(
|
||||
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
|
||||
final RespondingEthPeer respondingPeerA =
|
||||
EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000);
|
||||
|
||||
@@ -113,7 +122,8 @@ public class PivotBlockConfirmerTest {
|
||||
pivotBlockConfirmer = createPivotBlockConfirmer(2, 1);
|
||||
|
||||
final Responder responder =
|
||||
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive());
|
||||
RespondingEthPeer.blockchainResponder(
|
||||
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
|
||||
final RespondingEthPeer respondingPeerA =
|
||||
EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000);
|
||||
|
||||
@@ -145,7 +155,8 @@ public class PivotBlockConfirmerTest {
|
||||
pivotBlockConfirmer = createPivotBlockConfirmer(2, 1);
|
||||
|
||||
final Responder responder =
|
||||
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive());
|
||||
RespondingEthPeer.blockchainResponder(
|
||||
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
|
||||
|
||||
final RespondingEthPeer respondingPeerA =
|
||||
EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000);
|
||||
@@ -184,7 +195,8 @@ public class PivotBlockConfirmerTest {
|
||||
pivotBlockConfirmer = createPivotBlockConfirmer(2, 1);
|
||||
|
||||
final Responder responder =
|
||||
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive());
|
||||
RespondingEthPeer.blockchainResponder(
|
||||
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
|
||||
|
||||
final RespondingEthPeer respondingPeerA =
|
||||
EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000);
|
||||
@@ -223,7 +235,8 @@ public class PivotBlockConfirmerTest {
|
||||
pivotBlockConfirmer = createPivotBlockConfirmer(2, 1);
|
||||
|
||||
final Responder responder =
|
||||
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive());
|
||||
RespondingEthPeer.blockchainResponder(
|
||||
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
|
||||
|
||||
final RespondingEthPeer respondingPeerA =
|
||||
EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000);
|
||||
@@ -264,7 +277,8 @@ public class PivotBlockConfirmerTest {
|
||||
pivotBlockConfirmer = createPivotBlockConfirmer(3, 1);
|
||||
|
||||
final Responder responderA =
|
||||
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive());
|
||||
RespondingEthPeer.blockchainResponder(
|
||||
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
|
||||
final RespondingEthPeer respondingPeerA =
|
||||
EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000);
|
||||
|
||||
|
||||
@@ -26,11 +26,13 @@ import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
|
||||
import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil;
|
||||
import org.hyperledger.besu.ethereum.core.Difficulty;
|
||||
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer.Responder;
|
||||
import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator;
|
||||
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
|
||||
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
|
||||
import org.hyperledger.besu.plugin.services.MetricsSystem;
|
||||
@@ -54,6 +56,7 @@ public class PivotBlockRetrieverTest {
|
||||
private final AtomicBoolean timeout = new AtomicBoolean(false);
|
||||
private EthProtocolManager ethProtocolManager;
|
||||
private MutableBlockchain blockchain;
|
||||
private TransactionPool transactionPool;
|
||||
private PivotBlockRetriever<Void> pivotBlockRetriever;
|
||||
private ProtocolSchedule<Void> protocolSchedule;
|
||||
|
||||
@@ -64,9 +67,15 @@ public class PivotBlockRetrieverTest {
|
||||
blockchain = blockchainSetupUtil.getBlockchain();
|
||||
protocolSchedule = blockchainSetupUtil.getProtocolSchedule();
|
||||
protocolContext = blockchainSetupUtil.getProtocolContext();
|
||||
transactionPool = blockchainSetupUtil.getTransactionPool();
|
||||
ethProtocolManager =
|
||||
EthProtocolManagerTestUtil.create(
|
||||
blockchain, blockchainSetupUtil.getWorldArchive(), timeout::get);
|
||||
blockchain,
|
||||
timeout::get,
|
||||
blockchainSetupUtil.getWorldArchive(),
|
||||
transactionPool,
|
||||
EthProtocolConfiguration.defaultConfig());
|
||||
|
||||
pivotBlockRetriever = createPivotBlockRetriever(3, 1, 1);
|
||||
}
|
||||
|
||||
@@ -87,7 +96,8 @@ public class PivotBlockRetrieverTest {
|
||||
@Test
|
||||
public void shouldSucceedWhenAllPeersAgree() {
|
||||
final RespondingEthPeer.Responder responder =
|
||||
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive());
|
||||
RespondingEthPeer.blockchainResponder(
|
||||
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
|
||||
final RespondingEthPeer respondingPeerA =
|
||||
EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000);
|
||||
final RespondingEthPeer respondingPeerB =
|
||||
@@ -111,7 +121,8 @@ public class PivotBlockRetrieverTest {
|
||||
EthProtocolManagerTestUtil.disableEthSchedulerAutoRun(ethProtocolManager);
|
||||
|
||||
final Responder responder =
|
||||
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive());
|
||||
RespondingEthPeer.blockchainResponder(
|
||||
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
|
||||
final RespondingEthPeer respondingPeerA =
|
||||
EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000);
|
||||
final RespondingEthPeer badPeerA = EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1);
|
||||
@@ -153,7 +164,8 @@ public class PivotBlockRetrieverTest {
|
||||
public void shouldIgnorePeersThatAreNotFullyValidated() {
|
||||
final PeerValidator peerValidator = mock(PeerValidator.class);
|
||||
final RespondingEthPeer.Responder responder =
|
||||
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive());
|
||||
RespondingEthPeer.blockchainResponder(
|
||||
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
|
||||
final RespondingEthPeer respondingPeerA =
|
||||
EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000, peerValidator);
|
||||
final RespondingEthPeer badPeerA =
|
||||
@@ -212,7 +224,8 @@ public class PivotBlockRetrieverTest {
|
||||
EthProtocolManagerTestUtil.disableEthSchedulerAutoRun(ethProtocolManager);
|
||||
|
||||
final Responder responder =
|
||||
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive());
|
||||
RespondingEthPeer.blockchainResponder(
|
||||
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
|
||||
|
||||
final RespondingEthPeer peerA =
|
||||
EthProtocolManagerTestUtil.createPeer(ethProtocolManager, Difficulty.of(1000), 1000);
|
||||
@@ -239,7 +252,8 @@ public class PivotBlockRetrieverTest {
|
||||
EthProtocolManagerTestUtil.disableEthSchedulerAutoRun(ethProtocolManager);
|
||||
|
||||
final Responder responder =
|
||||
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive());
|
||||
RespondingEthPeer.blockchainResponder(
|
||||
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
|
||||
final Responder emptyResponder = RespondingEthPeer.emptyResponder();
|
||||
|
||||
final RespondingEthPeer peerA =
|
||||
@@ -273,7 +287,8 @@ public class PivotBlockRetrieverTest {
|
||||
pivotBlockRetriever = createPivotBlockRetriever(2, pivotBlockDelta, 1);
|
||||
|
||||
final Responder responderA =
|
||||
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive());
|
||||
RespondingEthPeer.blockchainResponder(
|
||||
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
|
||||
final RespondingEthPeer respondingPeerA =
|
||||
EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000);
|
||||
|
||||
@@ -306,7 +321,8 @@ public class PivotBlockRetrieverTest {
|
||||
pivotBlockRetriever = createPivotBlockRetriever(2, pivotBlockDelta, 1);
|
||||
|
||||
final Responder responderA =
|
||||
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive());
|
||||
RespondingEthPeer.blockchainResponder(
|
||||
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
|
||||
final RespondingEthPeer respondingPeerA =
|
||||
EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000);
|
||||
|
||||
@@ -342,7 +358,8 @@ public class PivotBlockRetrieverTest {
|
||||
pivotBlockRetriever = createPivotBlockRetriever(2, pivotBlockDelta, 1);
|
||||
|
||||
final Responder responderA =
|
||||
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive());
|
||||
RespondingEthPeer.blockchainResponder(
|
||||
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
|
||||
final RespondingEthPeer respondingPeerA =
|
||||
EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000);
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@ import org.hyperledger.besu.ethereum.chain.Blockchain;
|
||||
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
|
||||
import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil;
|
||||
import org.hyperledger.besu.ethereum.core.Difficulty;
|
||||
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil;
|
||||
@@ -64,8 +65,10 @@ public class FullSyncChainDownloaderForkTest {
|
||||
ethProtocolManager =
|
||||
EthProtocolManagerTestUtil.create(
|
||||
localBlockchain,
|
||||
new EthScheduler(1, 1, 1, 1, new NoOpMetricsSystem()),
|
||||
localBlockchainSetup.getWorldArchive(),
|
||||
new EthScheduler(1, 1, 1, new NoOpMetricsSystem()));
|
||||
localBlockchainSetup.getTransactionPool(),
|
||||
EthProtocolConfiguration.defaultConfig());
|
||||
ethContext = ethProtocolManager.ethContext();
|
||||
syncState = new SyncState(protocolContext.getBlockchain(), ethContext.getEthPeers());
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||
import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil;
|
||||
import org.hyperledger.besu.ethereum.core.Difficulty;
|
||||
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
|
||||
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil;
|
||||
@@ -83,8 +84,10 @@ public class FullSyncChainDownloaderTest {
|
||||
ethProtocolManager =
|
||||
EthProtocolManagerTestUtil.create(
|
||||
localBlockchain,
|
||||
new EthScheduler(1, 1, 1, 1, new NoOpMetricsSystem()),
|
||||
localBlockchainSetup.getWorldArchive(),
|
||||
new EthScheduler(1, 1, 1, new NoOpMetricsSystem()));
|
||||
localBlockchainSetup.getTransactionPool(),
|
||||
EthProtocolConfiguration.defaultConfig());
|
||||
ethContext = ethProtocolManager.ethContext();
|
||||
syncState = new SyncState(protocolContext.getBlockchain(), ethContext.getEthPeers());
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
||||
import org.hyperledger.besu.ethereum.ProtocolContext;
|
||||
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
|
||||
import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil;
|
||||
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil;
|
||||
@@ -57,8 +58,10 @@ public class FullSyncDownloaderTest {
|
||||
ethProtocolManager =
|
||||
EthProtocolManagerTestUtil.create(
|
||||
localBlockchain,
|
||||
new EthScheduler(1, 1, 1, 1, new NoOpMetricsSystem()),
|
||||
localBlockchainSetup.getWorldArchive(),
|
||||
new EthScheduler(1, 1, 1, new NoOpMetricsSystem()));
|
||||
localBlockchainSetup.getTransactionPool(),
|
||||
EthProtocolConfiguration.defaultConfig());
|
||||
ethContext = ethProtocolManager.ethContext();
|
||||
syncState = new SyncState(protocolContext.getBlockchain(), ethContext.getEthPeers());
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.hyperledger.besu.ethereum.chain.Blockchain;
|
||||
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
|
||||
import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil;
|
||||
import org.hyperledger.besu.ethereum.core.Difficulty;
|
||||
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil;
|
||||
@@ -65,7 +66,11 @@ public class FullSyncTargetManagerTest {
|
||||
new ProtocolContext<>(localBlockchain, localWorldState, null);
|
||||
ethProtocolManager =
|
||||
EthProtocolManagerTestUtil.create(
|
||||
localBlockchain, localWorldState, new EthScheduler(1, 1, 1, new NoOpMetricsSystem()));
|
||||
localBlockchain,
|
||||
new EthScheduler(1, 1, 1, 1, new NoOpMetricsSystem()),
|
||||
localWorldState,
|
||||
localBlockchainSetup.getTransactionPool(),
|
||||
EthProtocolConfiguration.defaultConfig());
|
||||
final EthContext ethContext = ethProtocolManager.ethContext();
|
||||
localBlockchainSetup.importFirstBlocks(5);
|
||||
otherBlockchainSetup.importFirstBlocks(20);
|
||||
|
||||
@@ -90,9 +90,7 @@ public class SyncStateTest {
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
ethProtocolManager =
|
||||
EthProtocolManagerTestUtil.create(
|
||||
blockchain, InMemoryStorageProvider.createInMemoryWorldStateArchive());
|
||||
ethProtocolManager = EthProtocolManagerTestUtil.create(blockchain);
|
||||
ethPeers = spy(ethProtocolManager.ethContext().getEthPeers());
|
||||
syncTargetPeer = createPeer(TARGET_DIFFICULTY, TARGET_CHAIN_HEIGHT);
|
||||
otherPeer = createPeer(Difficulty.ZERO, 0);
|
||||
|
||||
@@ -17,6 +17,7 @@ package org.hyperledger.besu.ethereum.eth.sync.tasks;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.hyperledger.besu.ethereum.core.InMemoryStorageProvider.createInMemoryBlockchain;
|
||||
import static org.hyperledger.besu.ethereum.core.InMemoryStorageProvider.createInMemoryWorldStateArchive;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import org.hyperledger.besu.ethereum.ProtocolContext;
|
||||
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
|
||||
@@ -26,11 +27,13 @@ import org.hyperledger.besu.ethereum.core.BlockDataGenerator;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||
import org.hyperledger.besu.ethereum.core.Difficulty;
|
||||
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
|
||||
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.task.EthTask;
|
||||
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
|
||||
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions;
|
||||
import org.hyperledger.besu.ethereum.mainnet.MainnetProtocolSchedule;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
|
||||
@@ -140,8 +143,11 @@ public class DetermineCommonAncestorTaskParameterizedTest {
|
||||
|
||||
final WorldStateArchive worldStateArchive = createInMemoryWorldStateArchive();
|
||||
final EthProtocolManager ethProtocolManager =
|
||||
EthProtocolManagerTestUtil.create(localBlockchain, worldStateArchive);
|
||||
|
||||
EthProtocolManagerTestUtil.create(
|
||||
localBlockchain,
|
||||
worldStateArchive,
|
||||
mock(TransactionPool.class),
|
||||
EthProtocolConfiguration.defaultConfig());
|
||||
final RespondingEthPeer.Responder responder =
|
||||
RespondingEthPeer.blockchainResponder(remoteBlockchain);
|
||||
final RespondingEthPeer respondingEthPeer =
|
||||
|
||||
@@ -22,6 +22,7 @@ import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
@@ -33,6 +34,7 @@ import org.hyperledger.besu.ethereum.core.Block;
|
||||
import org.hyperledger.besu.ethereum.core.BlockDataGenerator;
|
||||
import org.hyperledger.besu.ethereum.core.BlockHeader;
|
||||
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
|
||||
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthPeer;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
|
||||
@@ -40,6 +42,7 @@ import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.exceptions.EthTaskException;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.task.EthTask;
|
||||
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
|
||||
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions;
|
||||
import org.hyperledger.besu.ethereum.mainnet.MainnetProtocolSchedule;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
|
||||
@@ -74,7 +77,12 @@ public class DetermineCommonAncestorTaskTest {
|
||||
localGenesisBlock = blockDataGenerator.genesisBlock();
|
||||
localBlockchain = createInMemoryBlockchain(localGenesisBlock);
|
||||
final WorldStateArchive worldStateArchive = createInMemoryWorldStateArchive();
|
||||
ethProtocolManager = EthProtocolManagerTestUtil.create(localBlockchain, worldStateArchive);
|
||||
ethProtocolManager =
|
||||
EthProtocolManagerTestUtil.create(
|
||||
localBlockchain,
|
||||
worldStateArchive,
|
||||
mock(TransactionPool.class),
|
||||
EthProtocolConfiguration.defaultConfig());
|
||||
ethContext = ethProtocolManager.ethContext();
|
||||
protocolContext = new ProtocolContext<>(localBlockchain, worldStateArchive, null);
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@ import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer;
|
||||
import org.hyperledger.besu.ethereum.eth.messages.EthPV63;
|
||||
import org.hyperledger.besu.ethereum.eth.messages.GetNodeDataMessage;
|
||||
import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration;
|
||||
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
|
||||
import org.hyperledger.besu.ethereum.mainnet.MainnetProtocolSchedule;
|
||||
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData;
|
||||
import org.hyperledger.besu.ethereum.rlp.RLP;
|
||||
@@ -111,7 +112,7 @@ public class WorldStateDownloaderTest {
|
||||
.build());
|
||||
|
||||
final EthProtocolManager ethProtocolManager =
|
||||
EthProtocolManagerTestUtil.create(new EthScheduler(1, 1, 1, new NoOpMetricsSystem()));
|
||||
EthProtocolManagerTestUtil.create(new EthScheduler(1, 1, 1, 1, new NoOpMetricsSystem()));
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
@@ -576,7 +577,8 @@ public class WorldStateDownloaderTest {
|
||||
// Respond to node data requests
|
||||
final List<MessageData> sentMessages = new ArrayList<>();
|
||||
final RespondingEthPeer.Responder blockChainResponder =
|
||||
RespondingEthPeer.blockchainResponder(mock(Blockchain.class), remoteWorldStateArchive);
|
||||
RespondingEthPeer.blockchainResponder(
|
||||
mock(Blockchain.class), remoteWorldStateArchive, mock(TransactionPool.class));
|
||||
final RespondingEthPeer.Responder responder =
|
||||
RespondingEthPeer.wrapResponderWithCollector(blockChainResponder, sentMessages);
|
||||
|
||||
@@ -606,7 +608,7 @@ public class WorldStateDownloaderTest {
|
||||
@Test
|
||||
public void stalledDownloader() {
|
||||
final EthProtocolManager ethProtocolManager =
|
||||
EthProtocolManagerTestUtil.create(new EthScheduler(1, 1, 1, new NoOpMetricsSystem()));
|
||||
EthProtocolManagerTestUtil.create(new EthScheduler(1, 1, 1, 1, new NoOpMetricsSystem()));
|
||||
|
||||
// Setup "remote" state
|
||||
final WorldStateStorage remoteStorage =
|
||||
@@ -898,10 +900,15 @@ public class WorldStateDownloaderTest {
|
||||
final WorldStateArchive remoteWorldStateArchive,
|
||||
final CompletableFuture<?> downloaderFuture) {
|
||||
final RespondingEthPeer.Responder fullResponder =
|
||||
RespondingEthPeer.blockchainResponder(mock(Blockchain.class), remoteWorldStateArchive);
|
||||
RespondingEthPeer.blockchainResponder(
|
||||
mock(Blockchain.class), remoteWorldStateArchive, mock(TransactionPool.class));
|
||||
final RespondingEthPeer.Responder partialResponder =
|
||||
RespondingEthPeer.partialResponder(
|
||||
mock(Blockchain.class), remoteWorldStateArchive, MainnetProtocolSchedule.create(), .5f);
|
||||
mock(Blockchain.class),
|
||||
remoteWorldStateArchive,
|
||||
mock(TransactionPool.class),
|
||||
MainnetProtocolSchedule.create(),
|
||||
.5f);
|
||||
final RespondingEthPeer.Responder emptyResponder = RespondingEthPeer.emptyResponder();
|
||||
|
||||
// Send a few partial responses
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* 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.eth.transactions;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import org.hyperledger.besu.ethereum.core.BlockDataGenerator;
|
||||
import org.hyperledger.besu.ethereum.core.Hash;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthPeer;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class PeerPendingTransactionTrackerTest {
|
||||
|
||||
private final EthPeer ethPeer1 = mock(EthPeer.class);
|
||||
private final EthPeer ethPeer2 = mock(EthPeer.class);
|
||||
private final BlockDataGenerator generator = new BlockDataGenerator();
|
||||
private final PendingTransactions pendingTransactions = mock(PendingTransactions.class);
|
||||
private final PeerPendingTransactionTracker tracker =
|
||||
new PeerPendingTransactionTracker(pendingTransactions);
|
||||
private final Hash hash1 = generator.transaction().getHash();
|
||||
private final Hash hash2 = generator.transaction().getHash();
|
||||
private final Hash hash3 = generator.transaction().getHash();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
Transaction tx = mock(Transaction.class);
|
||||
when(pendingTransactions.getTransactionByHash(any())).thenReturn(Optional.of(tx));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldTrackTransactionsToSendToPeer() {
|
||||
tracker.addToPeerSendQueue(ethPeer1, hash1);
|
||||
tracker.addToPeerSendQueue(ethPeer1, hash2);
|
||||
tracker.addToPeerSendQueue(ethPeer2, hash3);
|
||||
|
||||
assertThat(tracker.getEthPeersWithUnsentTransactions()).containsOnly(ethPeer1, ethPeer2);
|
||||
assertThat(tracker.claimTransactionsToSendToPeer(ethPeer1)).containsOnly(hash1, hash2);
|
||||
assertThat(tracker.claimTransactionsToSendToPeer(ethPeer2)).containsOnly(hash3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldExcludeAlreadySeenTransactionsFromTransactionsToSend() {
|
||||
tracker.markTransactionsHashesAsSeen(ethPeer1, ImmutableSet.of(hash2));
|
||||
|
||||
tracker.addToPeerSendQueue(ethPeer1, hash1);
|
||||
tracker.addToPeerSendQueue(ethPeer1, hash2);
|
||||
tracker.addToPeerSendQueue(ethPeer2, hash3);
|
||||
|
||||
assertThat(tracker.getEthPeersWithUnsentTransactions()).containsOnly(ethPeer1, ethPeer2);
|
||||
assertThat(tracker.claimTransactionsToSendToPeer(ethPeer1)).containsOnly(hash1);
|
||||
assertThat(tracker.claimTransactionsToSendToPeer(ethPeer2)).containsOnly(hash3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldExcludeAlreadySeenTransactionsAsACollectionFromTransactionsToSend() {
|
||||
tracker.markTransactionsHashesAsSeen(ethPeer1, ImmutableSet.of(hash1, hash2));
|
||||
|
||||
tracker.addToPeerSendQueue(ethPeer1, hash1);
|
||||
tracker.addToPeerSendQueue(ethPeer1, hash2);
|
||||
tracker.addToPeerSendQueue(ethPeer2, hash3);
|
||||
|
||||
assertThat(tracker.getEthPeersWithUnsentTransactions()).containsOnly(ethPeer2);
|
||||
assertThat(tracker.claimTransactionsToSendToPeer(ethPeer1)).isEmpty();
|
||||
assertThat(tracker.claimTransactionsToSendToPeer(ethPeer2)).containsOnly(hash3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldClearDataWhenPeerDisconnects() {
|
||||
tracker.markTransactionsHashesAsSeen(ethPeer1, ImmutableSet.of(hash3));
|
||||
|
||||
tracker.addToPeerSendQueue(ethPeer1, hash2);
|
||||
tracker.addToPeerSendQueue(ethPeer2, hash3);
|
||||
|
||||
tracker.onDisconnect(ethPeer1);
|
||||
|
||||
assertThat(tracker.getEthPeersWithUnsentTransactions()).containsOnly(ethPeer2);
|
||||
|
||||
// Should have cleared data that ethPeer1 has already seen transaction1
|
||||
tracker.addToPeerSendQueue(ethPeer1, hash1);
|
||||
|
||||
assertThat(tracker.getEthPeersWithUnsentTransactions()).containsOnly(ethPeer1, ethPeer2);
|
||||
assertThat(tracker.claimTransactionsToSendToPeer(ethPeer1)).containsOnly(hash1);
|
||||
assertThat(tracker.claimTransactionsToSendToPeer(ethPeer2)).containsOnly(hash3);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* 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.eth.transactions;
|
||||
|
||||
import static java.time.Duration.ofMillis;
|
||||
import static java.time.Duration.ofMinutes;
|
||||
import static java.time.Instant.now;
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||
|
||||
import org.hyperledger.besu.ethereum.core.BlockDataGenerator;
|
||||
import org.hyperledger.besu.ethereum.core.Hash;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthPeer;
|
||||
import org.hyperledger.besu.ethereum.eth.messages.NewPooledTransactionHashesMessage;
|
||||
import org.hyperledger.besu.plugin.services.metrics.Counter;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class PendingTransactionsMessageProcessorTest {
|
||||
|
||||
@Mock private TransactionPool transactionPool;
|
||||
@Mock private PeerPendingTransactionTracker transactionTracker;
|
||||
@Mock private Counter totalSkippedTransactionsMessageCounter;
|
||||
@Mock private EthPeer peer1;
|
||||
@InjectMocks private PendingTransactionsMessageProcessor messageHandler;
|
||||
|
||||
private final BlockDataGenerator generator = new BlockDataGenerator();
|
||||
private final Hash hash1 = generator.transaction().getHash();
|
||||
private final Hash hash2 = generator.transaction().getHash();
|
||||
private final Hash hash3 = generator.transaction().getHash();
|
||||
|
||||
@Test
|
||||
public void shouldMarkAllReceivedTransactionsAsSeen() {
|
||||
messageHandler.processNewPooledTransactionHashesMessage(
|
||||
peer1,
|
||||
NewPooledTransactionHashesMessage.create(asList(hash1, hash2, hash3)),
|
||||
now(),
|
||||
ofMinutes(1));
|
||||
|
||||
verify(transactionTracker)
|
||||
.markTransactionsHashesAsSeen(peer1, Arrays.asList(hash1, hash2, hash3));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldAddInitiatedRequestingTransactions() {
|
||||
messageHandler.processNewPooledTransactionHashesMessage(
|
||||
peer1,
|
||||
NewPooledTransactionHashesMessage.create(asList(hash1, hash2, hash3)),
|
||||
now(),
|
||||
ofMinutes(1));
|
||||
verify(transactionPool).addTransactionHashes(hash1);
|
||||
verify(transactionPool).addTransactionHashes(hash2);
|
||||
verify(transactionPool).addTransactionHashes(hash3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotMarkReceivedExpiredTransactionsAsSeen() {
|
||||
messageHandler.processNewPooledTransactionHashesMessage(
|
||||
peer1,
|
||||
NewPooledTransactionHashesMessage.create(asList(hash1, hash2, hash3)),
|
||||
now().minus(ofMinutes(1)),
|
||||
ofMillis(1));
|
||||
verifyZeroInteractions(transactionTracker);
|
||||
verify(totalSkippedTransactionsMessageCounter).inc(1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldNotAddReceivedTransactionsToTransactionPoolIfExpired() {
|
||||
messageHandler.processNewPooledTransactionHashesMessage(
|
||||
peer1,
|
||||
NewPooledTransactionHashesMessage.create(asList(hash1, hash2, hash3)),
|
||||
now().minus(ofMinutes(1)),
|
||||
ofMillis(1));
|
||||
verifyZeroInteractions(transactionPool);
|
||||
verify(totalSkippedTransactionsMessageCounter).inc(1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* 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.eth.transactions;
|
||||
|
||||
import static com.google.common.collect.Sets.newHashSet;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.argThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import org.hyperledger.besu.ethereum.core.BlockDataGenerator;
|
||||
import org.hyperledger.besu.ethereum.core.Hash;
|
||||
import org.hyperledger.besu.ethereum.core.Transaction;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthPeer;
|
||||
import org.hyperledger.besu.ethereum.eth.messages.EthPV65;
|
||||
import org.hyperledger.besu.ethereum.eth.messages.NewPooledTransactionHashesMessage;
|
||||
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
public class PendingTransactionsMessageSenderTest {
|
||||
|
||||
private final EthPeer peer1 = mock(EthPeer.class);
|
||||
private final EthPeer peer2 = mock(EthPeer.class);
|
||||
|
||||
private final BlockDataGenerator generator = new BlockDataGenerator();
|
||||
private final Hash transaction1 = generator.transaction().getHash();
|
||||
private final Hash transaction2 = generator.transaction().getHash();
|
||||
private final Hash transaction3 = generator.transaction().getHash();
|
||||
|
||||
private final PendingTransactions pendingTransactions = mock(PendingTransactions.class);
|
||||
private final PeerPendingTransactionTracker transactionTracker =
|
||||
new PeerPendingTransactionTracker(pendingTransactions);
|
||||
private final PendingTransactionsMessageSender messageSender =
|
||||
new PendingTransactionsMessageSender(transactionTracker);
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
Transaction tx = mock(Transaction.class);
|
||||
when(pendingTransactions.getTransactionByHash(any())).thenReturn(Optional.of(tx));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldSendPendingTransactionsToEachPeer() throws Exception {
|
||||
|
||||
transactionTracker.addToPeerSendQueue(peer1, transaction1);
|
||||
transactionTracker.addToPeerSendQueue(peer1, transaction2);
|
||||
transactionTracker.addToPeerSendQueue(peer2, transaction3);
|
||||
|
||||
messageSender.sendTransactionsToPeers();
|
||||
|
||||
verify(peer1).send(transactionsMessageContaining(transaction1, transaction2));
|
||||
verify(peer2).send(transactionsMessageContaining(transaction3));
|
||||
verifyNoMoreInteractions(peer1, peer2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldSendTransactionsInBatchesWithLimit() throws Exception {
|
||||
final Set<Hash> transactions =
|
||||
generator.transactions(6000).stream().map(Transaction::getHash).collect(Collectors.toSet());
|
||||
|
||||
transactions.forEach(transaction -> transactionTracker.addToPeerSendQueue(peer1, transaction));
|
||||
|
||||
messageSender.sendTransactionsToPeers();
|
||||
final ArgumentCaptor<MessageData> messageDataArgumentCaptor =
|
||||
ArgumentCaptor.forClass(MessageData.class);
|
||||
verify(peer1, times(2)).send(messageDataArgumentCaptor.capture());
|
||||
|
||||
final List<MessageData> sentMessages = messageDataArgumentCaptor.getAllValues();
|
||||
|
||||
assertThat(sentMessages).hasSize(2);
|
||||
assertThat(sentMessages)
|
||||
.allMatch(message -> message.getCode() == EthPV65.NEW_POOLED_TRANSACTION_HASHES);
|
||||
final Set<Hash> firstBatch = getTransactionsFromMessage(sentMessages.get(0));
|
||||
final Set<Hash> secondBatch = getTransactionsFromMessage(sentMessages.get(1));
|
||||
|
||||
final int expectedFirstBatchSize = 4096, expectedSecondBatchSize = 1904, toleranceDelta = 0;
|
||||
assertThat(firstBatch)
|
||||
.hasSizeBetween(
|
||||
expectedFirstBatchSize - toleranceDelta, expectedFirstBatchSize + toleranceDelta);
|
||||
assertThat(secondBatch)
|
||||
.hasSizeBetween(
|
||||
expectedSecondBatchSize - toleranceDelta, expectedSecondBatchSize + toleranceDelta);
|
||||
|
||||
assertThat(Sets.union(firstBatch, secondBatch)).isEqualTo(transactions);
|
||||
}
|
||||
|
||||
private MessageData transactionsMessageContaining(final Hash... transactions) {
|
||||
return argThat(
|
||||
message -> {
|
||||
final Set<Hash> actualSentTransactions = getTransactionsFromMessage(message);
|
||||
final Set<Hash> expectedTransactions = newHashSet(transactions);
|
||||
return message.getCode() == EthPV65.NEW_POOLED_TRANSACTION_HASHES
|
||||
&& actualSentTransactions.equals(expectedTransactions);
|
||||
});
|
||||
}
|
||||
|
||||
private Set<Hash> getTransactionsFromMessage(final MessageData message) {
|
||||
final NewPooledTransactionHashesMessage transactionsMessage =
|
||||
NewPooledTransactionHashesMessage.readFrom(message);
|
||||
return newHashSet(transactionsMessage.pendingTransactions());
|
||||
}
|
||||
}
|
||||
@@ -41,6 +41,7 @@ import org.junit.Test;
|
||||
public class PendingTransactionsTest {
|
||||
|
||||
private static final int MAX_TRANSACTIONS = 5;
|
||||
private static final int MAX_TRANSACTION_HASHES = 5;
|
||||
private static final KeyPair KEYS1 = KeyPair.generate();
|
||||
private static final KeyPair KEYS2 = KeyPair.generate();
|
||||
private static final String ADDED_COUNTER = "transactions_added_total";
|
||||
@@ -55,6 +56,7 @@ public class PendingTransactionsTest {
|
||||
new PendingTransactions(
|
||||
TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS,
|
||||
MAX_TRANSACTIONS,
|
||||
MAX_TRANSACTION_HASHES,
|
||||
TestClock.fixed(),
|
||||
metricsSystem);
|
||||
private final Transaction transaction1 = createTransaction(2);
|
||||
@@ -551,7 +553,11 @@ public class PendingTransactionsTest {
|
||||
final int maxTransactionRetentionHours = 1;
|
||||
final PendingTransactions transactions =
|
||||
new PendingTransactions(
|
||||
maxTransactionRetentionHours, MAX_TRANSACTIONS, clock, metricsSystem);
|
||||
maxTransactionRetentionHours,
|
||||
MAX_TRANSACTIONS,
|
||||
MAX_TRANSACTION_HASHES,
|
||||
clock,
|
||||
metricsSystem);
|
||||
|
||||
transactions.addRemoteTransaction(transaction1);
|
||||
assertThat(transactions.size()).isEqualTo(1);
|
||||
@@ -569,7 +575,11 @@ public class PendingTransactionsTest {
|
||||
final int maxTransactionRetentionHours = 1;
|
||||
final PendingTransactions transactions =
|
||||
new PendingTransactions(
|
||||
maxTransactionRetentionHours, MAX_TRANSACTIONS, clock, metricsSystem);
|
||||
maxTransactionRetentionHours,
|
||||
MAX_TRANSACTIONS,
|
||||
MAX_TRANSACTION_HASHES,
|
||||
clock,
|
||||
metricsSystem);
|
||||
transactions.addRemoteTransaction(transaction1);
|
||||
assertThat(transactions.size()).isEqualTo(1);
|
||||
clock.step(2L, ChronoUnit.HOURS);
|
||||
@@ -583,7 +593,11 @@ public class PendingTransactionsTest {
|
||||
final int maxTransactionRetentionHours = 2;
|
||||
final PendingTransactions transactions =
|
||||
new PendingTransactions(
|
||||
maxTransactionRetentionHours, MAX_TRANSACTIONS, clock, metricsSystem);
|
||||
maxTransactionRetentionHours,
|
||||
MAX_TRANSACTIONS,
|
||||
MAX_TRANSACTION_HASHES,
|
||||
clock,
|
||||
metricsSystem);
|
||||
transactions.addRemoteTransaction(transaction1);
|
||||
assertThat(transactions.size()).isEqualTo(1);
|
||||
clock.step(3L, ChronoUnit.HOURS);
|
||||
|
||||
@@ -34,7 +34,10 @@ import org.hyperledger.besu.ethereum.difficulty.fixed.FixedDifficultyProtocolSch
|
||||
import org.hyperledger.besu.ethereum.eth.EthProtocol;
|
||||
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthContext;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthMessages;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthPeers;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
|
||||
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
|
||||
import org.hyperledger.besu.ethereum.eth.sync.state.SyncState;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
|
||||
import org.hyperledger.besu.ethereum.mainnet.ScheduleBasedBlockHeaderFunctions;
|
||||
@@ -110,19 +113,41 @@ public class TestNode implements Closeable {
|
||||
genesisState.writeStateTo(worldStateArchive.getMutable());
|
||||
final ProtocolContext<Void> protocolContext =
|
||||
new ProtocolContext<>(blockchain, worldStateArchive, null);
|
||||
|
||||
final SyncState syncState = mock(SyncState.class);
|
||||
when(syncState.isInSync(anyLong())).thenReturn(true);
|
||||
|
||||
final EthMessages ethMessages = new EthMessages();
|
||||
|
||||
final EthPeers ethPeers = new EthPeers(EthProtocol.NAME, TestClock.fixed(), metricsSystem);
|
||||
|
||||
final EthScheduler scheduler = new EthScheduler(1, 1, 1, metricsSystem);
|
||||
final EthContext ethContext = new EthContext(ethPeers, ethMessages, scheduler);
|
||||
|
||||
transactionPool =
|
||||
TransactionPoolFactory.createTransactionPool(
|
||||
protocolSchedule,
|
||||
protocolContext,
|
||||
ethContext,
|
||||
TestClock.fixed(),
|
||||
metricsSystem,
|
||||
syncState,
|
||||
Wei.ZERO,
|
||||
TransactionPoolConfiguration.builder().build());
|
||||
|
||||
final EthProtocolManager ethProtocolManager =
|
||||
new EthProtocolManager(
|
||||
blockchain,
|
||||
worldStateArchive,
|
||||
BigInteger.ONE,
|
||||
worldStateArchive,
|
||||
transactionPool,
|
||||
EthProtocolConfiguration.defaultConfig(),
|
||||
ethPeers,
|
||||
ethMessages,
|
||||
ethContext,
|
||||
Collections.emptyList(),
|
||||
false,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
TestClock.fixed(),
|
||||
new NoOpMetricsSystem(),
|
||||
EthProtocolConfiguration.defaultConfig());
|
||||
scheduler);
|
||||
|
||||
final NetworkRunner networkRunner =
|
||||
NetworkRunner.builder()
|
||||
@@ -143,22 +168,6 @@ public class TestNode implements Closeable {
|
||||
network.subscribeDisconnect(
|
||||
(connection, reason, initiatedByPeer) -> disconnections.put(connection, reason));
|
||||
|
||||
final EthContext ethContext = ethProtocolManager.ethContext();
|
||||
|
||||
final SyncState syncState = mock(SyncState.class);
|
||||
when(syncState.isInSync(anyLong())).thenReturn(true);
|
||||
|
||||
transactionPool =
|
||||
TransactionPoolFactory.createTransactionPool(
|
||||
protocolSchedule,
|
||||
protocolContext,
|
||||
ethContext,
|
||||
TestClock.fixed(),
|
||||
metricsSystem,
|
||||
syncState,
|
||||
Wei.ZERO,
|
||||
TransactionPoolConfiguration.builder().build());
|
||||
|
||||
networkRunner.start();
|
||||
selfPeer = DefaultPeer.fromEnodeURL(network.getLocalEnode().get());
|
||||
}
|
||||
|
||||
@@ -77,11 +77,14 @@ import org.mockito.ArgumentCaptor;
|
||||
public class TransactionPoolTest {
|
||||
|
||||
private static final int MAX_TRANSACTIONS = 5;
|
||||
private static final int MAX_TRANSACTION_HASHES = 5;
|
||||
private static final KeyPair KEY_PAIR1 = KeyPair.generate();
|
||||
|
||||
private final PendingTransactionListener listener = mock(PendingTransactionListener.class);
|
||||
private final TransactionPool.TransactionBatchAddedListener batchAddedListener =
|
||||
mock(TransactionPool.TransactionBatchAddedListener.class);
|
||||
private final TransactionPool.TransactionBatchAddedListener pendingBatchAddedListener =
|
||||
mock(TransactionPool.TransactionBatchAddedListener.class);
|
||||
private final MetricsSystem metricsSystem = new NoOpMetricsSystem();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -97,6 +100,7 @@ public class TransactionPoolTest {
|
||||
new PendingTransactions(
|
||||
TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS,
|
||||
MAX_TRANSACTIONS,
|
||||
MAX_TRANSACTION_HASHES,
|
||||
TestClock.fixed(),
|
||||
metricsSystem);
|
||||
private final Transaction transaction1 = createTransaction(1);
|
||||
@@ -108,6 +112,7 @@ public class TransactionPoolTest {
|
||||
private SyncState syncState;
|
||||
private EthContext ethContext;
|
||||
private PeerTransactionTracker peerTransactionTracker;
|
||||
private PeerPendingTransactionTracker peerPendingTransactionTracker;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
@@ -121,15 +126,18 @@ public class TransactionPoolTest {
|
||||
EthPeers ethPeers = mock(EthPeers.class);
|
||||
when(ethContext.getEthPeers()).thenReturn(ethPeers);
|
||||
peerTransactionTracker = mock(PeerTransactionTracker.class);
|
||||
peerPendingTransactionTracker = mock(PeerPendingTransactionTracker.class);
|
||||
transactionPool =
|
||||
new TransactionPool(
|
||||
transactions,
|
||||
protocolSchedule,
|
||||
protocolContext,
|
||||
batchAddedListener,
|
||||
pendingBatchAddedListener,
|
||||
syncState,
|
||||
ethContext,
|
||||
peerTransactionTracker,
|
||||
peerPendingTransactionTracker,
|
||||
Wei.of(2),
|
||||
metricsSystem);
|
||||
blockchain.observeBlockAdded(transactionPool);
|
||||
@@ -380,9 +388,11 @@ public class TransactionPoolTest {
|
||||
protocolSchedule,
|
||||
protocolContext,
|
||||
batchAddedListener,
|
||||
pendingBatchAddedListener,
|
||||
syncState,
|
||||
ethContext,
|
||||
peerTransactionTracker,
|
||||
peerPendingTransactionTracker,
|
||||
Wei.ZERO,
|
||||
metricsSystem);
|
||||
|
||||
@@ -391,6 +401,7 @@ public class TransactionPoolTest {
|
||||
transactionPool.addRemoteTransactions(singletonList(transaction1));
|
||||
|
||||
verify(pendingTransactions).containsTransaction(transaction1.getHash());
|
||||
verify(pendingTransactions).tryEvictTransactionHash(transaction1.getHash());
|
||||
verifyZeroInteractions(transactionValidator);
|
||||
verifyNoMoreInteractions(pendingTransactions);
|
||||
}
|
||||
@@ -497,9 +508,11 @@ public class TransactionPoolTest {
|
||||
protocolSchedule,
|
||||
protocolContext,
|
||||
batchAddedListener,
|
||||
pendingBatchAddedListener,
|
||||
syncState,
|
||||
ethContext,
|
||||
peerTransactionTracker,
|
||||
peerPendingTransactionTracker,
|
||||
Wei.ZERO,
|
||||
metricsSystem);
|
||||
|
||||
@@ -563,9 +576,11 @@ public class TransactionPoolTest {
|
||||
protocolSchedule,
|
||||
protocolContext,
|
||||
batchAddedListener,
|
||||
pendingBatchAddedListener,
|
||||
syncState,
|
||||
ethContext,
|
||||
peerTransactionTracker,
|
||||
peerPendingTransactionTracker,
|
||||
Wei.ZERO,
|
||||
metricsSystem);
|
||||
|
||||
|
||||
@@ -163,7 +163,7 @@ public class RetestethContext {
|
||||
final EthPeers ethPeers = new EthPeers("reteseth", retestethClock, metricsSystem);
|
||||
final SyncState syncState = new SyncState(blockchain, ethPeers);
|
||||
|
||||
ethScheduler = new EthScheduler(1, 1, 1, metricsSystem);
|
||||
ethScheduler = new EthScheduler(1, 1, 1, 1, metricsSystem);
|
||||
final EthContext ethContext = new EthContext(ethPeers, new EthMessages(), ethScheduler);
|
||||
|
||||
final TransactionPoolConfiguration transactionPoolConfiguration =
|
||||
|
||||
@@ -67,4 +67,9 @@ public class PositiveNumber {
|
||||
public int hashCode() {
|
||||
return Objects.hash(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "+" + value;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user