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>
pull/634/head
Antoine Toulme 5 years ago committed by GitHub
parent a93d06f182
commit b9c6c4b3cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/AcceptanceTestBase.java
  2. 51
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/condition/txpool/TxPoolConditions.java
  3. 2
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/BesuNode.java
  4. 5
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeConfigurationBuilder.java
  5. 7
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeFactory.java
  6. 8
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/NodeRequests.java
  7. 34
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/txpool/TxPoolBesuTransaction.java
  8. 38
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/txpool/TxPoolRequestFactory.java
  9. 22
      acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/transaction/txpool/TxPoolTransactions.java
  10. 48
      acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/GossipTransactionAcceptanceTest.java
  11. 10
      besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java
  12. 16
      besu/src/main/java/org/hyperledger/besu/cli/options/EthProtocolOptions.java
  13. 74
      besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java
  14. 30
      besu/src/main/java/org/hyperledger/besu/controller/IbftLegacyBesuControllerBuilder.java
  15. 3
      besu/src/test/java/org/hyperledger/besu/cli/options/EthProtocolOptionsTest.java
  16. 12
      besu/src/test/java/org/hyperledger/besu/services/BesuEventsImplTest.java
  17. 1
      besu/src/test/resources/everything_config.toml
  18. 4
      consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockCreatorTest.java
  19. 3
      consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueMinerExecutorTest.java
  20. 2
      consensus/ibft/src/integration-test/java/org/hyperledger/besu/consensus/ibft/support/TestContextBuilder.java
  21. 1
      consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/blockcreation/IbftBlockCreatorTest.java
  22. 10
      consensus/ibftlegacy/src/main/java/org/hyperledger/besu/consensus/ibftlegacy/protocol/Istanbul64Protocol.java
  23. 35
      consensus/ibftlegacy/src/main/java/org/hyperledger/besu/consensus/ibftlegacy/protocol/Istanbul64ProtocolManager.java
  24. 1
      consensus/ibftlegacy/src/test/java/org/hyperledger/besu/consensus/ibftlegacy/blockcreation/IbftBlockCreatorTest.java
  25. 8
      ethereum/api/src/integration-test/java/org/hyperledger/besu/ethereum/api/jsonrpc/methods/EthGetFilterChangesIntegrationTest.java
  26. 1
      ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/BlockTransactionSelectorTest.java
  27. 4
      ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/EthHashBlockCreatorTest.java
  28. 2
      ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/EthHashMinerExecutorTest.java
  29. 11
      ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/BlockchainSetupUtil.java
  30. 25
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/EthProtocol.java
  31. 28
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/EthProtocolConfiguration.java
  32. 25
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthPeer.java
  33. 82
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthProtocolManager.java
  34. 19
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthScheduler.java
  35. 51
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthServer.java
  36. 92
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/task/GetPooledTransactionsFromPeerTask.java
  37. 28
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/messages/EthPV65.java
  38. 69
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/messages/GetPooledTransactionsMessage.java
  39. 70
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/messages/LimitedNewPooledTransactionHashesMessages.java
  40. 68
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/messages/NewPooledTransactionHashesMessage.java
  41. 68
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/messages/PooledTransactionsMessage.java
  42. 94
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/PeerPendingTransactionTracker.java
  43. 50
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/PendingTransactionSender.java
  44. 35
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/PendingTransactions.java
  45. 52
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/PendingTransactionsMessageHandler.java
  46. 120
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/PendingTransactionsMessageProcessor.java
  47. 52
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/PendingTransactionsMessageSender.java
  48. 29
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPool.java
  49. 19
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolConfiguration.java
  50. 26
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolFactory.java
  51. 360
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/EthProtocolManagerTest.java
  52. 87
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/EthProtocolManagerTestUtil.java
  53. 8
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/EthServerTest.java
  54. 39
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/RespondingEthPeer.java
  55. 42
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/ethtaskutils/AbstractMessageTaskTest.java
  56. 6
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/ethtaskutils/PeerMessageTaskTest.java
  57. 36
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/ethtaskutils/RetryingMessageTaskTest.java
  58. 87
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/task/GetPooledTransactionsFromPeerTaskTest.java
  59. 6
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/task/RetryingGetNodeDataFromPeerTaskTest.java
  60. 47
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/messages/GetPooledTransactionsMessageTest.java
  61. 71
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/messages/LimitedNewPooledTransactionHashesMessagesTest.java
  62. 47
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/messages/NewPooledTransactionHashesMessageTest.java
  63. 56
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/messages/PooledTransactionsMessageTest.java
  64. 7
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/BlockPropagationManagerTest.java
  65. 14
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/ChainHeadTrackerTest.java
  66. 13
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/CheckpointHeaderFetcherTest.java
  67. 3
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/DownloadHeadersStepTest.java
  68. 11
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/DownloadReceiptsStepTest.java
  69. 5
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/FastSyncActionsTest.java
  70. 5
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/FastSyncChainDownloaderTest.java
  71. 28
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/PivotBlockConfirmerTest.java
  72. 35
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/PivotBlockRetrieverTest.java
  73. 5
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncChainDownloaderForkTest.java
  74. 5
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncChainDownloaderTest.java
  75. 5
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncDownloaderTest.java
  76. 7
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncTargetManagerTest.java
  77. 4
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/state/SyncStateTest.java
  78. 10
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/tasks/DetermineCommonAncestorTaskParameterizedTest.java
  79. 10
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/tasks/DetermineCommonAncestorTaskTest.java
  80. 17
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/worldstate/WorldStateDownloaderTest.java
  81. 106
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/PeerPendingTransactionTrackerTest.java
  82. 97
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/PendingTransactionsMessageProcessorTest.java
  83. 127
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/PendingTransactionsMessageSenderTest.java
  84. 20
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/PendingTransactionsTest.java
  85. 55
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TestNode.java
  86. 15
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolTest.java
  87. 2
      ethereum/retesteth/src/main/java/org/hyperledger/besu/ethereum/retesteth/RetestethContext.java
  88. 5
      util/src/main/java/org/hyperledger/besu/util/number/PositiveNumber.java

@ -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.net.NetConditions;
import org.hyperledger.besu.tests.acceptance.dsl.condition.perm.PermissioningConditions; 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.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.condition.web3.Web3Conditions;
import org.hyperledger.besu.tests.acceptance.dsl.contract.ContractVerifier; import org.hyperledger.besu.tests.acceptance.dsl.contract.ContractVerifier;
import org.hyperledger.besu.tests.acceptance.dsl.node.cluster.Cluster; 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.net.NetTransactions;
import org.hyperledger.besu.tests.acceptance.dsl.transaction.perm.PermissioningTransactions; 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.privacy.PrivacyTransactions;
import org.hyperledger.besu.tests.acceptance.dsl.transaction.txpool.TxPoolTransactions;
import org.hyperledger.besu.tests.acceptance.dsl.transaction.web3.Web3Transactions; import org.hyperledger.besu.tests.acceptance.dsl.transaction.web3.Web3Transactions;
import java.io.File; import java.io.File;
@ -83,6 +85,8 @@ public class AcceptanceTestBase {
protected final Web3Conditions web3; protected final Web3Conditions web3;
protected final PrivConditions priv; protected final PrivConditions priv;
protected final PrivacyTransactions privacyTransactions; protected final PrivacyTransactions privacyTransactions;
protected final TxPoolConditions txPoolConditions;
protected final TxPoolTransactions txPoolTransactions;
protected AcceptanceTestBase() { protected AcceptanceTestBase() {
ethTransactions = new EthTransactions(); ethTransactions = new EthTransactions();
@ -108,6 +112,8 @@ public class AcceptanceTestBase {
admin = new AdminConditions(adminTransactions); admin = new AdminConditions(adminTransactions);
web3 = new Web3Conditions(new Web3Transactions()); web3 = new Web3Conditions(new Web3Transactions());
besu = new BesuNodeFactory(); besu = new BesuNodeFactory();
txPoolTransactions = new TxPoolTransactions();
txPoolConditions = new TxPoolConditions(txPoolTransactions);
contractVerifier = new ContractVerifier(accounts.getPrimaryBenefactor()); contractVerifier = new ContractVerifier(accounts.getPrimaryBenefactor());
permissionedNodeBuilder = new PermissionedNodeBuilder(); 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.net.CustomRequestFactory;
import org.hyperledger.besu.tests.acceptance.dsl.transaction.perm.PermissioningJsonRpcRequestFactory; 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.privacy.PrivacyRequestFactory;
import org.hyperledger.besu.tests.acceptance.dsl.transaction.txpool.TxPoolRequestFactory;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
@ -330,6 +331,7 @@ public class BesuNode implements NodeConfiguration, RunnableNode, AutoCloseable
new PrivacyRequestFactory(web3jService), new PrivacyRequestFactory(web3jService),
new CustomRequestFactory(web3jService), new CustomRequestFactory(web3jService),
new MinerRequestFactory(web3jService), new MinerRequestFactory(web3jService),
new TxPoolRequestFactory(web3jService),
websocketService, websocketService,
loginRequestFactory()); loginRequestFactory());
} }

@ -120,6 +120,11 @@ public class BesuNodeConfigurationBuilder {
return this; return this;
} }
public BesuNodeConfigurationBuilder jsonRpcTxPool() {
this.jsonRpcConfiguration.addRpcApi(RpcApis.TX_POOL);
return this;
}
public BesuNodeConfigurationBuilder jsonRpcAuthenticationConfiguration(final String authFile) public BesuNodeConfigurationBuilder jsonRpcAuthenticationConfiguration(final String authFile)
throws URISyntaxException { throws URISyntaxException {
final String authTomlPath = final String authTomlPath =

@ -92,7 +92,12 @@ public class BesuNodeFactory {
public BesuNode createArchiveNode(final String name) throws IOException { public BesuNode createArchiveNode(final String name) throws IOException {
return create( return create(
new BesuNodeConfigurationBuilder().name(name).jsonRpcEnabled().webSocketEnabled().build()); new BesuNodeConfigurationBuilder()
.name(name)
.jsonRpcEnabled()
.jsonRpcTxPool()
.webSocketEnabled()
.build());
} }
public BesuNode createNode( 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.net.CustomRequestFactory;
import org.hyperledger.besu.tests.acceptance.dsl.transaction.perm.PermissioningJsonRpcRequestFactory; 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.privacy.PrivacyRequestFactory;
import org.hyperledger.besu.tests.acceptance.dsl.transaction.txpool.TxPoolRequestFactory;
import java.util.Optional; import java.util.Optional;
@ -40,6 +41,7 @@ public class NodeRequests {
private final Optional<WebSocketService> websocketService; private final Optional<WebSocketService> websocketService;
private final LoginRequestFactory login; private final LoginRequestFactory login;
private final MinerRequestFactory miner; private final MinerRequestFactory miner;
private final TxPoolRequestFactory txPool;
public NodeRequests( public NodeRequests(
final Web3j netEth, final Web3j netEth,
@ -50,6 +52,7 @@ public class NodeRequests {
final PrivacyRequestFactory privacy, final PrivacyRequestFactory privacy,
final CustomRequestFactory custom, final CustomRequestFactory custom,
final MinerRequestFactory miner, final MinerRequestFactory miner,
final TxPoolRequestFactory txPool,
final Optional<WebSocketService> websocketService, final Optional<WebSocketService> websocketService,
final LoginRequestFactory login) { final LoginRequestFactory login) {
this.netEth = netEth; this.netEth = netEth;
@ -60,6 +63,7 @@ public class NodeRequests {
this.privacy = privacy; this.privacy = privacy;
this.custom = custom; this.custom = custom;
this.miner = miner; this.miner = miner;
this.txPool = txPool;
this.websocketService = websocketService; this.websocketService = websocketService;
this.login = login; this.login = login;
} }
@ -104,6 +108,10 @@ public class NodeRequests {
return miner; return miner;
} }
public TxPoolRequestFactory txPool() {
return txPool;
}
public void shutdown() { public void shutdown() {
netEth.shutdown(); netEth.shutdown();
websocketService.ifPresent(WebSocketService::close); 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") arity = "1")
private final Integer txPoolMaxSize = TransactionPoolConfiguration.MAX_PENDING_TRANSACTIONS; 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( @Option(
names = {"--tx-pool-retention-hours"}, names = {"--tx-pool-retention-hours"},
paramLabel = MANDATORY_INTEGER_FORMAT_HELP, paramLabel = MANDATORY_INTEGER_FORMAT_HELP,
@ -1690,6 +1699,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable {
return transactionPoolOptions return transactionPoolOptions
.toDomainObject() .toDomainObject()
.txPoolMaxSize(txPoolMaxSize) .txPoolMaxSize(txPoolMaxSize)
.pooledTransactionHashesSize(pooledTransactionHashesSize)
.pendingTxRetentionPeriod(pendingTxRetentionPeriod) .pendingTxRetentionPeriod(pendingTxRetentionPeriod)
.build(); .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_BODIES_FLAG = "--Xewp-max-get-bodies";
private static final String MAX_GET_RECEIPTS_FLAG = "--Xewp-max-get-receipts"; 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_NODE_DATA_FLAG = "--Xewp-max-get-node-data";
private static final String MAX_GET_POOLED_TRANSACTIONS = "--Xewp-max-get-pooled-transactions";
@CommandLine.Option( @CommandLine.Option(
hidden = true, hidden = true,
@ -64,6 +65,15 @@ public class EthProtocolOptions implements CLIOptions<EthProtocolConfiguration>
private PositiveNumber maxGetNodeData = private PositiveNumber maxGetNodeData =
PositiveNumber.fromInt(EthProtocolConfiguration.DEFAULT_MAX_GET_NODE_DATA); 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() {} private EthProtocolOptions() {}
public static EthProtocolOptions create() { public static EthProtocolOptions create() {
@ -76,6 +86,7 @@ public class EthProtocolOptions implements CLIOptions<EthProtocolConfiguration>
options.maxGetBlockBodies = PositiveNumber.fromInt(config.getMaxGetBlockBodies()); options.maxGetBlockBodies = PositiveNumber.fromInt(config.getMaxGetBlockBodies());
options.maxGetReceipts = PositiveNumber.fromInt(config.getMaxGetReceipts()); options.maxGetReceipts = PositiveNumber.fromInt(config.getMaxGetReceipts());
options.maxGetNodeData = PositiveNumber.fromInt(config.getMaxGetNodeData()); options.maxGetNodeData = PositiveNumber.fromInt(config.getMaxGetNodeData());
options.maxGetPooledTransactions = PositiveNumber.fromInt(config.getMaxGetPooledTransactions());
return options; return options;
} }
@ -86,6 +97,7 @@ public class EthProtocolOptions implements CLIOptions<EthProtocolConfiguration>
.maxGetBlockBodies(maxGetBlockBodies) .maxGetBlockBodies(maxGetBlockBodies)
.maxGetReceipts(maxGetReceipts) .maxGetReceipts(maxGetReceipts)
.maxGetNodeData(maxGetNodeData) .maxGetNodeData(maxGetNodeData)
.maxGetPooledTransactions(maxGetPooledTransactions)
.build(); .build();
} }
@ -99,6 +111,8 @@ public class EthProtocolOptions implements CLIOptions<EthProtocolConfiguration>
MAX_GET_RECEIPTS_FLAG, MAX_GET_RECEIPTS_FLAG,
OptionParser.format(maxGetReceipts.getValue()), OptionParser.format(maxGetReceipts.getValue()),
MAX_GET_NODE_DATA_FLAG, 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.core.Synchronizer;
import org.hyperledger.besu.ethereum.eth.EthProtocol; import org.hyperledger.besu.ethereum.eth.EthProtocol;
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration; 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.EthProtocolManager;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.peervalidation.ClassicForkPeerValidator; import org.hyperledger.besu.ethereum.eth.peervalidation.ClassicForkPeerValidator;
import org.hyperledger.besu.ethereum.eth.peervalidation.DaoForkPeerValidator; import org.hyperledger.besu.ethereum.eth.peervalidation.DaoForkPeerValidator;
import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator; import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator;
@ -242,13 +246,40 @@ public abstract class BesuControllerBuilder<C> {
prunerConfiguration)); 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 boolean fastSyncEnabled = syncConfig.getSyncMode().equals(SyncMode.FAST);
final TransactionPool transactionPool =
TransactionPoolFactory.createTransactionPool(
protocolSchedule,
protocolContext,
ethContext,
clock,
metricsSystem,
syncState,
miningParameters.getMinTransactionGasPrice(),
transactionPoolConfiguration);
final EthProtocolManager ethProtocolManager = final EthProtocolManager ethProtocolManager =
createEthProtocolManager( createEthProtocolManager(
protocolContext, fastSyncEnabled, createPeerValidators(protocolSchedule)); protocolContext,
final SyncState syncState = fastSyncEnabled,
new SyncState(blockchain, ethProtocolManager.ethContext().getEthPeers()); transactionPool,
ethereumWireProtocolConfiguration,
ethPeers,
ethContext,
ethMessages,
scheduler,
createPeerValidators(protocolSchedule));
final Synchronizer synchronizer = final Synchronizer synchronizer =
new DefaultSynchronizer<>( new DefaultSynchronizer<>(
syncConfig, syncConfig,
@ -263,17 +294,6 @@ public abstract class BesuControllerBuilder<C> {
clock, clock,
metricsSystem); metricsSystem);
final TransactionPool transactionPool =
TransactionPoolFactory.createTransactionPool(
protocolSchedule,
protocolContext,
ethProtocolManager.ethContext(),
clock,
metricsSystem,
syncState,
miningParameters.getMinTransactionGasPrice(),
transactionPoolConfiguration);
final MiningCoordinator miningCoordinator = final MiningCoordinator miningCoordinator =
createMiningCoordinator( createMiningCoordinator(
protocolSchedule, protocolSchedule,
@ -343,22 +363,32 @@ public abstract class BesuControllerBuilder<C> {
protected abstract C createConsensusContext( protected abstract C createConsensusContext(
Blockchain blockchain, WorldStateArchive worldStateArchive); Blockchain blockchain, WorldStateArchive worldStateArchive);
protected String getSupportedProtocol() {
return EthProtocol.NAME;
}
protected EthProtocolManager createEthProtocolManager( protected EthProtocolManager createEthProtocolManager(
final ProtocolContext<C> protocolContext, final ProtocolContext<C> protocolContext,
final boolean fastSyncEnabled, 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) { final List<PeerValidator> peerValidators) {
return new EthProtocolManager( return new EthProtocolManager(
protocolContext.getBlockchain(), protocolContext.getBlockchain(),
protocolContext.getWorldStateArchive(),
networkId, networkId,
protocolContext.getWorldStateArchive(),
transactionPool,
ethereumWireProtocolConfiguration,
ethPeers,
ethMessages,
ethContext,
peerValidators, peerValidators,
fastSyncEnabled, fastSyncEnabled,
syncConfig.getDownloaderParallelism(), scheduler,
syncConfig.getTransactionsParallelism(),
syncConfig.getComputationParallelism(),
clock,
metricsSystem,
ethereumWireProtocolConfiguration,
genesisConfig.getForks()); 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.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.MiningParameters; 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.EthProtocolManager;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator; import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator;
import org.hyperledger.besu.ethereum.eth.sync.state.SyncState; import org.hyperledger.besu.ethereum.eth.sync.state.SyncState;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; 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 @Override
protected EthProtocolManager createEthProtocolManager( protected EthProtocolManager createEthProtocolManager(
final ProtocolContext<IbftContext> protocolContext, final ProtocolContext<IbftContext> protocolContext,
final boolean fastSyncEnabled, 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) { final List<PeerValidator> peerValidators) {
LOG.info("Operating on IBFT-1.0 network."); LOG.info("Operating on IBFT-1.0 network.");
return new Istanbul64ProtocolManager( return new Istanbul64ProtocolManager(
protocolContext.getBlockchain(), protocolContext.getBlockchain(),
protocolContext.getWorldStateArchive(),
networkId, networkId,
protocolContext.getWorldStateArchive(),
transactionPool,
ethereumWireProtocolConfiguration,
ethPeers,
ethMessages,
ethContext,
peerValidators, peerValidators,
fastSyncEnabled, fastSyncEnabled,
syncConfig.getDownloaderParallelism(), scheduler);
syncConfig.getTransactionsParallelism(),
syncConfig.getComputationParallelism(),
clock,
metricsSystem,
ethereumWireProtocolConfiguration);
} }
} }

@ -130,6 +130,9 @@ public class EthProtocolOptionsTest
PositiveNumber.fromInt(EthProtocolConfiguration.DEFAULT_MAX_GET_RECEIPTS + 2)) PositiveNumber.fromInt(EthProtocolConfiguration.DEFAULT_MAX_GET_RECEIPTS + 2))
.maxGetNodeData( .maxGetNodeData(
PositiveNumber.fromInt(EthProtocolConfiguration.DEFAULT_MAX_GET_NODE_DATA + 2)) PositiveNumber.fromInt(EthProtocolConfiguration.DEFAULT_MAX_GET_NODE_DATA + 2))
.maxGetPooledTransactions(
PositiveNumber.fromInt(
EthProtocolConfiguration.DEFAULT_MAX_GET_POOLED_TRANSACTIONS + 2))
.build(); .build();
} }

@ -103,10 +103,15 @@ public class BesuEventsImplTest {
new KeyValueStoragePrefixedKeyBlockchainStorage( new KeyValueStoragePrefixedKeyBlockchainStorage(
new InMemoryKeyValueStorage(), new MainnetBlockHeaderFunctions()), new InMemoryKeyValueStorage(), new MainnetBlockHeaderFunctions()),
new NoOpMetricsSystem()); new NoOpMetricsSystem());
when(mockEthContext.getEthMessages()).thenReturn(mockEthMessages); when(mockEthContext.getEthMessages()).thenReturn(mockEthMessages);
when(mockEthContext.getEthPeers()).thenReturn(mockEthPeers); when(mockEthContext.getEthPeers()).thenReturn(mockEthPeers);
when(mockEthContext.getScheduler()).thenReturn(mockEthScheduler); 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.getBlockchain()).thenReturn(blockchain);
when(mockProtocolContext.getWorldStateArchive()).thenReturn(mockWorldStateArchive); when(mockProtocolContext.getWorldStateArchive()).thenReturn(mockWorldStateArchive);
when(mockProtocolSchedule.getByBlockNumber(anyLong())).thenReturn(mockProtocolSpec); when(mockProtocolSchedule.getByBlockNumber(anyLong())).thenReturn(mockProtocolSpec);
@ -118,6 +123,9 @@ public class BesuEventsImplTest {
blockBroadcaster = new BlockBroadcaster(mockEthContext); blockBroadcaster = new BlockBroadcaster(mockEthContext);
syncState = new SyncState(blockchain, mockEthPeers); syncState = new SyncState(blockchain, mockEthPeers);
TransactionPoolConfiguration txPoolConfig =
TransactionPoolConfiguration.builder().txPoolMaxSize(1).build();
transactionPool = transactionPool =
TransactionPoolFactory.createTransactionPool( TransactionPoolFactory.createTransactionPool(
mockProtocolSchedule, mockProtocolSchedule,
@ -127,7 +135,7 @@ public class BesuEventsImplTest {
new NoOpMetricsSystem(), new NoOpMetricsSystem(),
syncState, syncState,
Wei.ZERO, Wei.ZERO,
TransactionPoolConfiguration.builder().txPoolMaxSize(1).build()); txPoolConfig);
serviceImpl = new BesuEventsImpl(blockchain, blockBroadcaster, transactionPool, syncState); serviceImpl = new BesuEventsImpl(blockchain, blockBroadcaster, transactionPool, syncState);
} }

@ -129,6 +129,7 @@ privacy-onchain-groups-enabled=false
tx-pool-retention-hours=999 tx-pool-retention-hours=999
tx-pool-max-size=1234 tx-pool-max-size=1234
tx-pool-hashes-max-size=10000
Xincoming-tx-messages-keep-alive-seconds=60 Xincoming-tx-messages-keep-alive-seconds=60
# Revert Reason # Revert Reason

@ -122,6 +122,7 @@ public class CliqueBlockCreatorTest {
new PendingTransactions( new PendingTransactions(
TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS, TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS,
5, 5,
5,
TestClock.fixed(), TestClock.fixed(),
metricsSystem), metricsSystem),
protocolContext, protocolContext,
@ -153,6 +154,7 @@ public class CliqueBlockCreatorTest {
new PendingTransactions( new PendingTransactions(
TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS, TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS,
5, 5,
5,
TestClock.fixed(), TestClock.fixed(),
metricsSystem), metricsSystem),
protocolContext, protocolContext,
@ -183,6 +185,7 @@ public class CliqueBlockCreatorTest {
new PendingTransactions( new PendingTransactions(
TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS, TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS,
5, 5,
5,
TestClock.fixed(), TestClock.fixed(),
metricsSystem), metricsSystem),
protocolContext, protocolContext,
@ -216,6 +219,7 @@ public class CliqueBlockCreatorTest {
new PendingTransactions( new PendingTransactions(
TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS, TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS,
5, 5,
5,
TestClock.fixed(), TestClock.fixed(),
metricsSystem), metricsSystem),
protocolContext, protocolContext,

@ -97,6 +97,7 @@ public class CliqueMinerExecutorTest {
new PendingTransactions( new PendingTransactions(
TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS, TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS,
1, 1,
5,
TestClock.fixed(), TestClock.fixed(),
metricsSystem), metricsSystem),
proposerKeyPair, proposerKeyPair,
@ -134,6 +135,7 @@ public class CliqueMinerExecutorTest {
new PendingTransactions( new PendingTransactions(
TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS, TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS,
1, 1,
5,
TestClock.fixed(), TestClock.fixed(),
metricsSystem), metricsSystem),
proposerKeyPair, proposerKeyPair,
@ -171,6 +173,7 @@ public class CliqueMinerExecutorTest {
new PendingTransactions( new PendingTransactions(
TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS, TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS,
1, 1,
5,
TestClock.fixed(), TestClock.fixed(),
metricsSystem), metricsSystem),
proposerKeyPair, proposerKeyPair,

@ -300,7 +300,7 @@ public class TestContextBuilder {
final PendingTransactions pendingTransactions = final PendingTransactions pendingTransactions =
new PendingTransactions( new PendingTransactions(
TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS, 1, clock, metricsSystem); TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS, 1, 1, clock, metricsSystem);
final IbftBlockCreatorFactory blockCreatorFactory = final IbftBlockCreatorFactory blockCreatorFactory =
new IbftBlockCreatorFactory( new IbftBlockCreatorFactory(

@ -87,6 +87,7 @@ public class IbftBlockCreatorTest {
new PendingTransactions( new PendingTransactions(
TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS, TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS,
1, 1,
5,
TestClock.fixed(), TestClock.fixed(),
metricsSystem); 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.EthPV62;
import org.hyperledger.besu.ethereum.eth.messages.EthPV63; 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.Capability;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.SubProtocol; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.SubProtocol;
@ -47,6 +48,9 @@ public class Istanbul64Protocol implements SubProtocol {
EthPV62.GET_BLOCK_BODIES, EthPV62.GET_BLOCK_BODIES,
EthPV62.BLOCK_BODIES, EthPV62.BLOCK_BODIES,
EthPV62.NEW_BLOCK, EthPV62.NEW_BLOCK,
EthPV65.NEW_POOLED_TRANSACTION_HASHES,
EthPV65.GET_POOLED_TRANSACTIONS,
EthPV65.POOLED_TRANSACTIONS,
EthPV63.GET_NODE_DATA, EthPV63.GET_NODE_DATA,
EthPV63.NODE_DATA, EthPV63.NODE_DATA,
EthPV63.GET_RECEIPTS, EthPV63.GET_RECEIPTS,
@ -90,6 +94,12 @@ public class Istanbul64Protocol implements SubProtocol {
return "BlockBodies"; return "BlockBodies";
case EthPV62.NEW_BLOCK: case EthPV62.NEW_BLOCK:
return "NewBlock"; 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: case EthPV63.GET_NODE_DATA:
return "GetNodeData"; return "GetNodeData";
case EthPV63.NODE_DATA: 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.chain.Blockchain;
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration; 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.EthProtocolManager;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator; 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.p2p.rlpx.wire.Capability;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.plugin.services.MetricsSystem;
import java.math.BigInteger; import java.math.BigInteger;
import java.time.Clock;
import java.util.List; import java.util.List;
/** This allows for interoperability with Quorum, but shouldn't be used otherwise. */ /** This allows for interoperability with Quorum, but shouldn't be used otherwise. */
@ -33,28 +36,28 @@ public class Istanbul64ProtocolManager extends EthProtocolManager {
public Istanbul64ProtocolManager( public Istanbul64ProtocolManager(
final Blockchain blockchain, final Blockchain blockchain,
final WorldStateArchive worldStateArchive,
final BigInteger networkId, 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 List<PeerValidator> peerValidators,
final boolean fastSyncEnabled, final boolean fastSyncEnabled,
final int syncWorkers, final EthScheduler scheduler) {
final int txWorkers,
final int computationWorkers,
final Clock clock,
final MetricsSystem metricsSystem,
final EthProtocolConfiguration ethereumWireProtocolConfiguration) {
super( super(
blockchain, blockchain,
worldStateArchive,
networkId, networkId,
worldStateArchive,
transactionPool,
ethereumWireProtocolConfiguration,
ethPeers,
ethMessages,
ethContext,
peerValidators, peerValidators,
fastSyncEnabled, fastSyncEnabled,
syncWorkers, scheduler);
txWorkers,
computationWorkers,
clock,
metricsSystem,
ethereumWireProtocolConfiguration);
} }
@Override @Override

@ -101,6 +101,7 @@ public class IbftBlockCreatorTest {
new PendingTransactions( new PendingTransactions(
TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS, TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS,
1, 1,
5,
TestClock.fixed(), TestClock.fixed(),
metricsSystem), metricsSystem),
protContext, 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.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthPeers; import org.hyperledger.besu.ethereum.eth.manager.EthPeers;
import org.hyperledger.besu.ethereum.eth.sync.state.SyncState; 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.PeerTransactionTracker;
import org.hyperledger.besu.ethereum.eth.transactions.PendingTransactions; import org.hyperledger.besu.ethereum.eth.transactions.PendingTransactions;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
@ -73,6 +74,7 @@ import org.mockito.junit.MockitoJUnitRunner;
public class EthGetFilterChangesIntegrationTest { public class EthGetFilterChangesIntegrationTest {
@Mock private TransactionBatchAddedListener batchAddedListener; @Mock private TransactionBatchAddedListener batchAddedListener;
@Mock private TransactionBatchAddedListener pendingBatchAddedListener;
private MutableBlockchain blockchain; private MutableBlockchain blockchain;
private final String ETH_METHOD = "eth_getFilterChanges"; private final String ETH_METHOD = "eth_getFilterChanges";
private final String JSON_RPC_VERSION = "2.0"; private final String JSON_RPC_VERSION = "2.0";
@ -83,10 +85,12 @@ public class EthGetFilterChangesIntegrationTest {
new PendingTransactions( new PendingTransactions(
TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS, TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS,
MAX_TRANSACTIONS, MAX_TRANSACTIONS,
MAX_HASHES,
TestClock.fixed(), TestClock.fixed(),
metricsSystem); metricsSystem);
private static final int MAX_TRANSACTIONS = 5; private static final int MAX_TRANSACTIONS = 5;
private static final int MAX_HASHES = 5;
private static final KeyPair keyPair = KeyPair.generate(); private static final KeyPair keyPair = KeyPair.generate();
private final Transaction transaction = createTransaction(1); private final Transaction transaction = createTransaction(1);
private FilterManager filterManager; private FilterManager filterManager;
@ -100,6 +104,8 @@ public class EthGetFilterChangesIntegrationTest {
final ProtocolContext<Void> protocolContext = executionContext.getProtocolContext(); final ProtocolContext<Void> protocolContext = executionContext.getProtocolContext();
PeerTransactionTracker peerTransactionTracker = mock(PeerTransactionTracker.class); PeerTransactionTracker peerTransactionTracker = mock(PeerTransactionTracker.class);
PeerPendingTransactionTracker peerPendingTransactionTracker =
mock(PeerPendingTransactionTracker.class);
EthContext ethContext = mock(EthContext.class); EthContext ethContext = mock(EthContext.class);
EthPeers ethPeers = mock(EthPeers.class); EthPeers ethPeers = mock(EthPeers.class);
when(ethContext.getEthPeers()).thenReturn(ethPeers); when(ethContext.getEthPeers()).thenReturn(ethPeers);
@ -110,9 +116,11 @@ public class EthGetFilterChangesIntegrationTest {
executionContext.getProtocolSchedule(), executionContext.getProtocolSchedule(),
protocolContext, protocolContext,
batchAddedListener, batchAddedListener,
pendingBatchAddedListener,
syncState, syncState,
ethContext, ethContext,
peerTransactionTracker, peerTransactionTracker,
peerPendingTransactionTracker,
Wei.ZERO, Wei.ZERO,
metricsSystem); metricsSystem);
final BlockchainQueries blockchainQueries = final BlockchainQueries blockchainQueries =

@ -72,6 +72,7 @@ public class BlockTransactionSelectorTest {
new PendingTransactions( new PendingTransactions(
TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS, TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS,
5, 5,
5,
TestClock.fixed(), TestClock.fixed(),
metricsSystem); metricsSystem);
private final Blockchain blockchain = new TestBlockchain(); private final Blockchain blockchain = new TestBlockchain();

@ -82,6 +82,7 @@ public class EthHashBlockCreatorTest {
new PendingTransactions( new PendingTransactions(
TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS, TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS,
1, 1,
5,
TestClock.fixed(), TestClock.fixed(),
metricsSystem); metricsSystem);
@ -131,6 +132,7 @@ public class EthHashBlockCreatorTest {
new PendingTransactions( new PendingTransactions(
TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS, TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS,
1, 1,
5,
TestClock.fixed(), TestClock.fixed(),
metricsSystem); metricsSystem);
@ -175,6 +177,7 @@ public class EthHashBlockCreatorTest {
new PendingTransactions( new PendingTransactions(
TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS, TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS,
1, 1,
5,
TestClock.fixed(), TestClock.fixed(),
metricsSystem); metricsSystem);
@ -235,6 +238,7 @@ public class EthHashBlockCreatorTest {
new PendingTransactions( new PendingTransactions(
TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS, TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS,
1, 1,
5,
TestClock.fixed(), TestClock.fixed(),
metricsSystem); metricsSystem);

@ -41,6 +41,7 @@ public class EthHashMinerExecutorTest {
new PendingTransactions( new PendingTransactions(
TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS, TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS,
1, 1,
5,
TestClock.fixed(), TestClock.fixed(),
metricsSystem); metricsSystem);
@ -66,6 +67,7 @@ public class EthHashMinerExecutorTest {
new PendingTransactions( new PendingTransactions(
TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS, TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS,
1, 1,
5,
TestClock.fixed(), TestClock.fixed(),
metricsSystem); metricsSystem);

@ -17,6 +17,7 @@ package org.hyperledger.besu.ethereum.core;
import static org.assertj.core.util.Preconditions.checkArgument; 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.createInMemoryBlockchain;
import static org.hyperledger.besu.ethereum.core.InMemoryStorageProvider.createInMemoryWorldStateArchive; 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.config.GenesisConfigFile;
import org.hyperledger.besu.ethereum.ProtocolContext; 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.GenesisState;
import org.hyperledger.besu.ethereum.chain.MutableBlockchain; import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler; 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.HeaderValidationMode;
import org.hyperledger.besu.ethereum.mainnet.MainnetProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.MainnetProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
@ -52,6 +54,7 @@ public class BlockchainSetupUtil<C> {
private final ProtocolContext<C> protocolContext; private final ProtocolContext<C> protocolContext;
private final ProtocolSchedule<C> protocolSchedule; private final ProtocolSchedule<C> protocolSchedule;
private final WorldStateArchive worldArchive; private final WorldStateArchive worldArchive;
private final TransactionPool transactionPool;
private final List<Block> blocks; private final List<Block> blocks;
private final EthScheduler scheduler; private final EthScheduler scheduler;
private long maxBlockNumber; private long maxBlockNumber;
@ -62,6 +65,7 @@ public class BlockchainSetupUtil<C> {
final ProtocolContext<C> protocolContext, final ProtocolContext<C> protocolContext,
final ProtocolSchedule<C> protocolSchedule, final ProtocolSchedule<C> protocolSchedule,
final WorldStateArchive worldArchive, final WorldStateArchive worldArchive,
final TransactionPool transactionPool,
final List<Block> blocks, final List<Block> blocks,
final EthScheduler scheduler) { final EthScheduler scheduler) {
this.genesisState = genesisState; this.genesisState = genesisState;
@ -69,6 +73,7 @@ public class BlockchainSetupUtil<C> {
this.protocolContext = protocolContext; this.protocolContext = protocolContext;
this.protocolSchedule = protocolSchedule; this.protocolSchedule = protocolSchedule;
this.worldArchive = worldArchive; this.worldArchive = worldArchive;
this.transactionPool = transactionPool;
this.blocks = blocks; this.blocks = blocks;
this.scheduler = scheduler; this.scheduler = scheduler;
} }
@ -150,6 +155,7 @@ public class BlockchainSetupUtil<C> {
final GenesisState genesisState = GenesisState.fromJson(genesisJson, protocolSchedule); final GenesisState genesisState = GenesisState.fromJson(genesisJson, protocolSchedule);
final MutableBlockchain blockchain = createInMemoryBlockchain(genesisState.getBlock()); final MutableBlockchain blockchain = createInMemoryBlockchain(genesisState.getBlock());
final WorldStateArchive worldArchive = createInMemoryWorldStateArchive(); final WorldStateArchive worldArchive = createInMemoryWorldStateArchive();
final TransactionPool transactionPool = mock(TransactionPool.class);
genesisState.writeStateTo(worldArchive.getMutable()); genesisState.writeStateTo(worldArchive.getMutable());
final ProtocolContext<T> protocolContext = final ProtocolContext<T> protocolContext =
@ -172,6 +178,7 @@ public class BlockchainSetupUtil<C> {
protocolContext, protocolContext,
protocolSchedule, protocolSchedule,
worldArchive, worldArchive,
transactionPool,
blocks, blocks,
scheduler); scheduler);
} catch (final IOException | URISyntaxException ex) { } catch (final IOException | URISyntaxException ex) {
@ -209,6 +216,10 @@ public class BlockchainSetupUtil<C> {
return scheduler; return scheduler;
} }
public TransactionPool getTransactionPool() {
return transactionPool;
}
private void importBlocks(final List<Block> blocks) { private void importBlocks(final List<Block> blocks) {
for (final Block block : blocks) { for (final Block block : blocks) {
if (block.getHeader().getNumber() == BlockHeader.GENESIS_BLOCK_NUMBER) { 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.EthPV62;
import org.hyperledger.besu.ethereum.eth.messages.EthPV63; 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.Capability;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.SubProtocol; 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 ETH62 = Capability.create(NAME, EthVersion.V62);
public static final Capability ETH63 = Capability.create(NAME, EthVersion.V63); public static final Capability ETH63 = Capability.create(NAME, EthVersion.V63);
public static final Capability ETH64 = Capability.create(NAME, EthVersion.V64); 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 EthProtocol INSTANCE = new EthProtocol();
private static final List<Integer> eth62Messages = 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)); 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 @Override
public String getName() { public String getName() {
return NAME; return NAME;
@ -65,6 +78,9 @@ public class EthProtocol implements SubProtocol {
return 8; return 8;
case EthVersion.V63: case EthVersion.V63:
case EthVersion.V64: 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; return 17;
default: default:
return 0; return 0;
@ -79,6 +95,8 @@ public class EthProtocol implements SubProtocol {
case EthVersion.V63: case EthVersion.V63:
case EthVersion.V64: case EthVersion.V64:
return eth63Messages.contains(code); return eth63Messages.contains(code);
case EthVersion.V65:
return eth65Messages.contains(code);
default: default:
return false; return false;
} }
@ -103,6 +121,12 @@ public class EthProtocol implements SubProtocol {
return "BlockBodies"; return "BlockBodies";
case EthPV62.NEW_BLOCK: case EthPV62.NEW_BLOCK:
return "NewBlock"; 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: case EthPV63.GET_NODE_DATA:
return "GetNodeData"; return "GetNodeData";
case EthPV63.NODE_DATA: case EthPV63.NODE_DATA:
@ -124,5 +148,6 @@ public class EthProtocol implements SubProtocol {
public static final int V62 = 62; public static final int V62 = 62;
public static final int V63 = 63; public static final int V63 = 63;
public static final int V64 = 64; 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_BLOCK_BODIES = 128;
public static final int DEFAULT_MAX_GET_RECEIPTS = 256; 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_NODE_DATA = 384;
public static final int DEFAULT_MAX_GET_POOLED_TRANSACTIONS = 256;
private final int maxGetBlockHeaders; private final int maxGetBlockHeaders;
private final int maxGetBlockBodies; private final int maxGetBlockBodies;
private final int maxGetReceipts; private final int maxGetReceipts;
private final int maxGetNodeData; private final int maxGetNodeData;
private final int maxGetPooledTransactions;
public EthProtocolConfiguration( public EthProtocolConfiguration(
final int maxGetBlockHeaders, final int maxGetBlockHeaders,
final int maxGetBlockBodies, final int maxGetBlockBodies,
final int maxGetReceipts, final int maxGetReceipts,
final int maxGetNodeData) { final int maxGetNodeData,
final int maxGetPooledTransactions) {
this.maxGetBlockHeaders = maxGetBlockHeaders; this.maxGetBlockHeaders = maxGetBlockHeaders;
this.maxGetBlockBodies = maxGetBlockBodies; this.maxGetBlockBodies = maxGetBlockBodies;
this.maxGetReceipts = maxGetReceipts; this.maxGetReceipts = maxGetReceipts;
this.maxGetNodeData = maxGetNodeData; this.maxGetNodeData = maxGetNodeData;
this.maxGetPooledTransactions = maxGetPooledTransactions;
} }
public static EthProtocolConfiguration defaultConfig() { public static EthProtocolConfiguration defaultConfig() {
@ -48,7 +52,8 @@ public class EthProtocolConfiguration {
DEFAULT_MAX_GET_BLOCK_HEADERS, DEFAULT_MAX_GET_BLOCK_HEADERS,
DEFAULT_MAX_GET_BLOCK_BODIES, DEFAULT_MAX_GET_BLOCK_BODIES,
DEFAULT_MAX_GET_RECEIPTS, DEFAULT_MAX_GET_RECEIPTS,
DEFAULT_MAX_GET_NODE_DATA); DEFAULT_MAX_GET_NODE_DATA,
DEFAULT_MAX_GET_POOLED_TRANSACTIONS);
} }
public static Builder builder() { public static Builder builder() {
@ -71,6 +76,10 @@ public class EthProtocolConfiguration {
return maxGetNodeData; return maxGetNodeData;
} }
public int getMaxGetPooledTransactions() {
return maxGetPooledTransactions;
}
@Override @Override
public boolean equals(final Object o) { public boolean equals(final Object o) {
if (this == o) { if (this == o) {
@ -83,7 +92,8 @@ public class EthProtocolConfiguration {
return maxGetBlockHeaders == that.maxGetBlockHeaders return maxGetBlockHeaders == that.maxGetBlockHeaders
&& maxGetBlockBodies == that.maxGetBlockBodies && maxGetBlockBodies == that.maxGetBlockBodies
&& maxGetReceipts == that.maxGetReceipts && maxGetReceipts == that.maxGetReceipts
&& maxGetNodeData == that.maxGetNodeData; && maxGetNodeData == that.maxGetNodeData
&& maxGetPooledTransactions == that.maxGetPooledTransactions;
} }
@Override @Override
@ -98,6 +108,7 @@ public class EthProtocolConfiguration {
.add("maxGetBlockBodies", maxGetBlockBodies) .add("maxGetBlockBodies", maxGetBlockBodies)
.add("maxGetReceipts", maxGetReceipts) .add("maxGetReceipts", maxGetReceipts)
.add("maxGetNodeData", maxGetNodeData) .add("maxGetNodeData", maxGetNodeData)
.add("maxGetPooledTransactions", maxGetPooledTransactions)
.toString(); .toString();
} }
@ -114,6 +125,9 @@ public class EthProtocolConfiguration {
private PositiveNumber maxGetNodeData = private PositiveNumber maxGetNodeData =
PositiveNumber.fromInt(EthProtocolConfiguration.DEFAULT_MAX_GET_NODE_DATA); 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) { public Builder maxGetBlockHeaders(final PositiveNumber maxGetBlockHeaders) {
this.maxGetBlockHeaders = maxGetBlockHeaders; this.maxGetBlockHeaders = maxGetBlockHeaders;
return this; return this;
@ -134,12 +148,18 @@ public class EthProtocolConfiguration {
return this; return this;
} }
public Builder maxGetPooledTransactions(final PositiveNumber maxGetPooledTransactions) {
this.maxGetPooledTransactions = maxGetPooledTransactions;
return this;
}
public EthProtocolConfiguration build() { public EthProtocolConfiguration build() {
return new EthProtocolConfiguration( return new EthProtocolConfiguration(
maxGetBlockHeaders.getValue(), maxGetBlockHeaders.getValue(),
maxGetBlockBodies.getValue(), maxGetBlockBodies.getValue(),
maxGetReceipts.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.core.Hash;
import org.hyperledger.besu.ethereum.eth.messages.EthPV62; import org.hyperledger.besu.ethereum.eth.messages.EthPV62;
import org.hyperledger.besu.ethereum.eth.messages.EthPV63; 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.GetBlockBodiesMessage;
import org.hyperledger.besu.ethereum.eth.messages.GetBlockHeadersMessage; import org.hyperledger.besu.ethereum.eth.messages.GetBlockHeadersMessage;
import org.hyperledger.besu.ethereum.eth.messages.GetNodeDataMessage; 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.GetReceiptsMessage;
import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator; import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator;
import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection; 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 bodiesRequestManager = new RequestManager(this);
private final RequestManager receiptsRequestManager = new RequestManager(this); private final RequestManager receiptsRequestManager = new RequestManager(this);
private final RequestManager nodeDataRequestManager = 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 AtomicReference<Consumer<EthPeer>> onStatusesExchanged = new AtomicReference<>();
private final PeerReputation reputation = new PeerReputation(); private final PeerReputation reputation = new PeerReputation();
@ -154,6 +157,8 @@ public class EthPeer {
return sendRequest(receiptsRequestManager, messageData); return sendRequest(receiptsRequestManager, messageData);
case EthPV63.GET_NODE_DATA: case EthPV63.GET_NODE_DATA:
return sendRequest(nodeDataRequestManager, messageData); return sendRequest(nodeDataRequestManager, messageData);
case EthPV65.GET_POOLED_TRANSACTIONS:
return sendRequest(pooledTransactionsRequestManager, messageData);
default: default:
connection.sendForProtocol(protocolName, messageData); connection.sendForProtocol(protocolName, messageData);
return null; return null;
@ -201,6 +206,12 @@ public class EthPeer {
return sendRequest(nodeDataRequestManager, message); 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) { boolean validateReceivedMessage(final EthMessage message) {
checkArgument(message.getPeer().equals(this), "Mismatched message sent to peer for dispatch"); checkArgument(message.getPeer().equals(this), "Mismatched message sent to peer for dispatch");
switch (message.getData().getCode()) { switch (message.getData().getCode()) {
@ -228,6 +239,12 @@ public class EthPeer {
return false; return false;
} }
break; break;
case EthPV65.POOLED_TRANSACTIONS:
if (pooledTransactionsRequestManager.outstandingRequests() == 0) {
LOG.warn("Unsolicited pooling transactions received.");
return false;
}
break;
default: default:
// Nothing to do // Nothing to do
} }
@ -258,6 +275,10 @@ public class EthPeer {
reputation.resetTimeoutCount(EthPV63.GET_NODE_DATA); reputation.resetTimeoutCount(EthPV63.GET_NODE_DATA);
nodeDataRequestManager.dispatchResponse(message); nodeDataRequestManager.dispatchResponse(message);
break; break;
case EthPV65.POOLED_TRANSACTIONS:
reputation.resetTimeoutCount(EthPV65.GET_POOLED_TRANSACTIONS);
pooledTransactionsRequestManager.dispatchResponse(message);
break;
default: default:
// Nothing to do // Nothing to do
} }
@ -272,6 +293,7 @@ public class EthPeer {
bodiesRequestManager.close(); bodiesRequestManager.close();
receiptsRequestManager.close(); receiptsRequestManager.close();
nodeDataRequestManager.close(); nodeDataRequestManager.close();
pooledTransactionsRequestManager.close();
} }
public void registerKnownBlock(final Hash hash) { public void registerKnownBlock(final Hash hash) {
@ -344,7 +366,8 @@ public class EthPeer {
return headersRequestManager.outstandingRequests() return headersRequestManager.outstandingRequests()
+ bodiesRequestManager.outstandingRequests() + bodiesRequestManager.outstandingRequests()
+ receiptsRequestManager.outstandingRequests() + receiptsRequestManager.outstandingRequests()
+ nodeDataRequestManager.outstandingRequests(); + nodeDataRequestManager.outstandingRequests()
+ pooledTransactionsRequestManager.outstandingRequests();
} }
public long getLastRequestTimestamp() { 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.PeerValidator;
import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidatorRunner; import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidatorRunner;
import org.hyperledger.besu.ethereum.eth.sync.BlockBroadcaster; 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.network.ProtocolManager;
import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection; import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection;
import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection.PeerNotConnected; 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.p2p.rlpx.wire.messages.DisconnectMessage.DisconnectReason;
import org.hyperledger.besu.ethereum.rlp.RLPException; import org.hyperledger.besu.ethereum.rlp.RLPException;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.plugin.services.MetricsSystem;
import java.math.BigInteger; import java.math.BigInteger;
import java.time.Clock;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
@ -54,9 +53,9 @@ import org.apache.logging.log4j.Logger;
public class EthProtocolManager implements ProtocolManager, MinedBlockObserver { public class EthProtocolManager implements ProtocolManager, MinedBlockObserver {
private static final Logger LOG = LogManager.getLogger(); private static final Logger LOG = LogManager.getLogger();
private static final List<Capability> FAST_SYNC_CAPS = 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 = 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 EthScheduler scheduler;
private final CountDownLatch shutdown; private final CountDownLatch shutdown;
@ -76,14 +75,16 @@ public class EthProtocolManager implements ProtocolManager, MinedBlockObserver {
public EthProtocolManager( public EthProtocolManager(
final Blockchain blockchain, final Blockchain blockchain,
final WorldStateArchive worldStateArchive,
final BigInteger networkId, 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 List<PeerValidator> peerValidators,
final boolean fastSyncEnabled, final boolean fastSyncEnabled,
final EthScheduler scheduler, final EthScheduler scheduler,
final EthProtocolConfiguration ethereumWireProtocolConfiguration,
final Clock clock,
final MetricsSystem metricsSystem,
final ForkIdManager forkIdManager) { final ForkIdManager forkIdManager) {
this.networkId = networkId; this.networkId = networkId;
this.peerValidators = peerValidators; this.peerValidators = peerValidators;
@ -96,9 +97,9 @@ public class EthProtocolManager implements ProtocolManager, MinedBlockObserver {
this.forkIdManager = forkIdManager; this.forkIdManager = forkIdManager;
ethPeers = new EthPeers(getSupportedProtocol(), clock, metricsSystem); this.ethPeers = ethPeers;
ethMessages = new EthMessages(); this.ethMessages = ethMessages;
ethContext = new EthContext(ethPeers, ethMessages, scheduler); this.ethContext = ethContext;
this.blockBroadcaster = new BlockBroadcaster(ethContext); this.blockBroadcaster = new BlockBroadcaster(ethContext);
@ -108,58 +109,67 @@ public class EthProtocolManager implements ProtocolManager, MinedBlockObserver {
} }
// Set up request handlers // Set up request handlers
new EthServer(blockchain, worldStateArchive, ethMessages, ethereumWireProtocolConfiguration); new EthServer(
blockchain,
worldStateArchive,
transactionPool,
ethMessages,
ethereumWireProtocolConfiguration);
} }
@VisibleForTesting @VisibleForTesting
public EthProtocolManager( public EthProtocolManager(
final Blockchain blockchain, final Blockchain blockchain,
final WorldStateArchive worldStateArchive,
final BigInteger networkId, 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 List<PeerValidator> peerValidators,
final boolean fastSyncEnabled, final boolean fastSyncEnabled,
final int syncWorkers, final EthScheduler scheduler) {
final int txWorkers,
final int computationWorkers,
final Clock clock,
final MetricsSystem metricsSystem,
final EthProtocolConfiguration ethereumWireProtocolConfiguration) {
this( this(
blockchain, blockchain,
worldStateArchive,
networkId, networkId,
worldStateArchive,
transactionPool,
ethereumWireProtocolConfiguration,
ethPeers,
ethMessages,
ethContext,
peerValidators, peerValidators,
fastSyncEnabled, fastSyncEnabled,
new EthScheduler(syncWorkers, txWorkers, computationWorkers, metricsSystem), scheduler,
ethereumWireProtocolConfiguration,
clock,
metricsSystem,
new ForkIdManager(blockchain, Collections.emptyList())); new ForkIdManager(blockchain, Collections.emptyList()));
} }
public EthProtocolManager( public EthProtocolManager(
final Blockchain blockchain, final Blockchain blockchain,
final WorldStateArchive worldStateArchive,
final BigInteger networkId, 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 List<PeerValidator> peerValidators,
final boolean fastSyncEnabled, final boolean fastSyncEnabled,
final int syncWorkers, final EthScheduler scheduler,
final int txWorkers,
final int computationWorkers,
final Clock clock,
final MetricsSystem metricsSystem,
final EthProtocolConfiguration ethereumWireProtocolConfiguration,
final List<Long> forks) { final List<Long> forks) {
this( this(
blockchain, blockchain,
worldStateArchive,
networkId, networkId,
worldStateArchive,
transactionPool,
ethereumWireProtocolConfiguration,
ethPeers,
ethMessages,
ethContext,
peerValidators, peerValidators,
fastSyncEnabled, fastSyncEnabled,
new EthScheduler(syncWorkers, txWorkers, computationWorkers, metricsSystem), scheduler,
ethereumWireProtocolConfiguration,
clock,
metricsSystem,
new ForkIdManager(blockchain, forks)); new ForkIdManager(blockchain, forks));
} }

@ -54,7 +54,7 @@ public class EthScheduler {
protected final ExecutorService servicesExecutor; protected final ExecutorService servicesExecutor;
protected final ExecutorService computationExecutor; protected final ExecutorService computationExecutor;
private final Collection<CompletableFuture<?>> serviceFutures = new ConcurrentLinkedDeque<>(); private final Collection<CompletableFuture<?>> pendingFutures = new ConcurrentLinkedDeque<>();
public EthScheduler( public EthScheduler(
final int syncWorkerCount, final int syncWorkerCount,
@ -120,21 +120,28 @@ public class EthScheduler {
syncWorkerExecutor.execute(command); 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) { public void scheduleTxWorkerTask(final Runnable command) {
txWorkerExecutor.execute(command); txWorkerExecutor.execute(command);
} }
public <T> CompletableFuture<T> scheduleServiceTask(final EthTask<T> task) { public <T> CompletableFuture<T> scheduleServiceTask(final EthTask<T> task) {
final CompletableFuture<T> serviceFuture = task.runAsync(servicesExecutor); final CompletableFuture<T> serviceFuture = task.runAsync(servicesExecutor);
serviceFutures.add(serviceFuture); pendingFutures.add(serviceFuture);
serviceFuture.whenComplete((r, t) -> serviceFutures.remove(serviceFuture)); serviceFuture.whenComplete((r, t) -> pendingFutures.remove(serviceFuture));
return serviceFuture; return serviceFuture;
} }
public CompletableFuture<Void> startPipeline(final Pipeline<?> pipeline) { public CompletableFuture<Void> startPipeline(final Pipeline<?> pipeline) {
final CompletableFuture<Void> pipelineFuture = pipeline.start(servicesExecutor); final CompletableFuture<Void> pipelineFuture = pipeline.start(servicesExecutor);
serviceFutures.add(pipelineFuture); pendingFutures.add(pipelineFuture);
pipelineFuture.whenComplete((r, t) -> serviceFutures.remove(pipelineFuture)); pipelineFuture.whenComplete((r, t) -> pendingFutures.remove(pipelineFuture));
return pipelineFuture; return pipelineFuture;
} }
@ -226,7 +233,7 @@ public class EthScheduler {
public void awaitStop() throws InterruptedException { public void awaitStop() throws InterruptedException {
shutdown.await(); shutdown.await();
serviceFutures.forEach(future -> future.cancel(true)); pendingFutures.forEach(future -> future.cancel(true));
if (!syncWorkerExecutor.awaitTermination(30, TimeUnit.SECONDS)) { if (!syncWorkerExecutor.awaitTermination(30, TimeUnit.SECONDS)) {
LOG.error("{} worker executor did not shutdown cleanly.", this.getClass().getSimpleName()); 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.BlockBody;
import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.Hash; 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.core.TransactionReceipt;
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration; import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
import org.hyperledger.besu.ethereum.eth.messages.BlockBodiesMessage; import org.hyperledger.besu.ethereum.eth.messages.BlockBodiesMessage;
import org.hyperledger.besu.ethereum.eth.messages.BlockHeadersMessage; import org.hyperledger.besu.ethereum.eth.messages.BlockHeadersMessage;
import org.hyperledger.besu.ethereum.eth.messages.EthPV62; import org.hyperledger.besu.ethereum.eth.messages.EthPV62;
import org.hyperledger.besu.ethereum.eth.messages.EthPV63; 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.GetBlockBodiesMessage;
import org.hyperledger.besu.ethereum.eth.messages.GetBlockHeadersMessage; import org.hyperledger.besu.ethereum.eth.messages.GetBlockHeadersMessage;
import org.hyperledger.besu.ethereum.eth.messages.GetNodeDataMessage; 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.GetReceiptsMessage;
import org.hyperledger.besu.ethereum.eth.messages.NodeDataMessage; 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.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.connections.PeerConnection.PeerNotConnected;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData; 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.p2p.rlpx.wire.messages.DisconnectMessage.DisconnectReason;
@ -52,16 +57,19 @@ class EthServer {
private final Blockchain blockchain; private final Blockchain blockchain;
private final WorldStateArchive worldStateArchive; private final WorldStateArchive worldStateArchive;
private final TransactionPool transactionPool;
private final EthMessages ethMessages; private final EthMessages ethMessages;
private final EthProtocolConfiguration ethereumWireProtocolConfiguration; private final EthProtocolConfiguration ethereumWireProtocolConfiguration;
EthServer( EthServer(
final Blockchain blockchain, final Blockchain blockchain,
final WorldStateArchive worldStateArchive, final WorldStateArchive worldStateArchive,
final TransactionPool transactionPool,
final EthMessages ethMessages, final EthMessages ethMessages,
final EthProtocolConfiguration ethereumWireProtocolConfiguration) { final EthProtocolConfiguration ethereumWireProtocolConfiguration) {
this.blockchain = blockchain; this.blockchain = blockchain;
this.worldStateArchive = worldStateArchive; this.worldStateArchive = worldStateArchive;
this.transactionPool = transactionPool;
this.ethMessages = ethMessages; this.ethMessages = ethMessages;
this.ethereumWireProtocolConfiguration = ethereumWireProtocolConfiguration; this.ethereumWireProtocolConfiguration = ethereumWireProtocolConfiguration;
this.setupListeners(); this.setupListeners();
@ -72,6 +80,7 @@ class EthServer {
ethMessages.subscribe(EthPV62.GET_BLOCK_BODIES, this::handleGetBlockBodies); ethMessages.subscribe(EthPV62.GET_BLOCK_BODIES, this::handleGetBlockBodies);
ethMessages.subscribe(EthPV63.GET_RECEIPTS, this::handleGetReceipts); ethMessages.subscribe(EthPV63.GET_RECEIPTS, this::handleGetReceipts);
ethMessages.subscribe(EthPV63.GET_NODE_DATA, this::handleGetNodeData); ethMessages.subscribe(EthPV63.GET_NODE_DATA, this::handleGetNodeData);
ethMessages.subscribe(EthPV65.GET_POOLED_TRANSACTIONS, this::handleGetPooledTransactions);
} }
private void handleGetBlockHeaders(final EthMessage message) { 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( static MessageData constructGetHeadersResponse(
final Blockchain blockchain, final MessageData message, final int requestLimit) { final Blockchain blockchain, final MessageData message, final int requestLimit) {
final GetBlockHeadersMessage getHeaders = GetBlockHeadersMessage.readFrom(message); final GetBlockHeadersMessage getHeaders = GetBlockHeadersMessage.readFrom(message);
@ -222,6 +251,28 @@ class EthServer {
return ReceiptsMessage.create(receipts); 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( static MessageData constructGetNodeDataResponse(
final WorldStateArchive worldStateArchive, final WorldStateArchive worldStateArchive,
final MessageData message, 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.Instant;
import java.time.temporal.ChronoUnit; import java.time.temporal.ChronoUnit;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@ -37,6 +38,7 @@ import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.OptionalLong; import java.util.OptionalLong;
import java.util.Queue;
import java.util.Set; import java.util.Set;
import java.util.SortedSet; import java.util.SortedSet;
import java.util.TreeSet; import java.util.TreeSet;
@ -44,6 +46,9 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors; 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 * Holds the current set of pending transactions with the ability to iterate them based on priority
* for mining or look-up by hash. * for mining or look-up by hash.
@ -55,6 +60,7 @@ public class PendingTransactions {
private final int maxTransactionRetentionHours; private final int maxTransactionRetentionHours;
private final Clock clock; private final Clock clock;
private final Queue<Hash> newPooledHashes;
private final Map<Hash, TransactionInfo> pendingTransactions = new ConcurrentHashMap<>(); private final Map<Hash, TransactionInfo> pendingTransactions = new ConcurrentHashMap<>();
private final SortedSet<TransactionInfo> prioritizedTransactions = private final SortedSet<TransactionInfo> prioritizedTransactions =
new TreeSet<>( new TreeSet<>(
@ -72,17 +78,20 @@ public class PendingTransactions {
private final LabelledMetric<Counter> transactionRemovedCounter; private final LabelledMetric<Counter> transactionRemovedCounter;
private final Counter localTransactionAddedCounter; private final Counter localTransactionAddedCounter;
private final Counter remoteTransactionAddedCounter; private final Counter remoteTransactionAddedCounter;
private final Counter localTransactionHashesAddedCounter;
private final long maxPendingTransactions; private final long maxPendingTransactions;
public PendingTransactions( public PendingTransactions(
final int maxTransactionRetentionHours, final int maxTransactionRetentionHours,
final int maxPendingTransactions, final int maxPendingTransactions,
final int maxPooledTransactionHashes,
final Clock clock, final Clock clock,
final MetricsSystem metricsSystem) { final MetricsSystem metricsSystem) {
this.maxTransactionRetentionHours = maxTransactionRetentionHours; this.maxTransactionRetentionHours = maxTransactionRetentionHours;
this.maxPendingTransactions = maxPendingTransactions; this.maxPendingTransactions = maxPendingTransactions;
this.clock = clock; this.clock = clock;
this.newPooledHashes = EvictingQueue.create(maxPooledTransactionHashes);
final LabelledMetric<Counter> transactionAddedCounter = final LabelledMetric<Counter> transactionAddedCounter =
metricsSystem.createLabelledCounter( metricsSystem.createLabelledCounter(
BesuMetricCategory.TRANSACTION_POOL, BesuMetricCategory.TRANSACTION_POOL,
@ -91,6 +100,7 @@ public class PendingTransactions {
"source"); "source");
localTransactionAddedCounter = transactionAddedCounter.labels("local"); localTransactionAddedCounter = transactionAddedCounter.labels("local");
remoteTransactionAddedCounter = transactionAddedCounter.labels("remote"); remoteTransactionAddedCounter = transactionAddedCounter.labels("remote");
localTransactionHashesAddedCounter = transactionAddedCounter.labels("pool");
transactionRemovedCounter = transactionRemovedCounter =
metricsSystem.createLabelledCounter( metricsSystem.createLabelledCounter(
@ -127,7 +137,19 @@ public class PendingTransactions {
return transactionAdded; 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 = final boolean transactionAdded =
addTransaction(new TransactionInfo(transaction, true, clock.instant())); addTransaction(new TransactionInfo(transaction, true, clock.instant()));
if (transactionAdded) { if (transactionAdded) {
@ -220,6 +242,7 @@ public class PendingTransactions {
} }
prioritizedTransactions.add(transactionInfo); prioritizedTransactions.add(transactionInfo);
pendingTransactions.put(transactionInfo.getHash(), transactionInfo); pendingTransactions.put(transactionInfo.getHash(), transactionInfo);
tryEvictTransactionHash(transactionInfo.getHash());
if (pendingTransactions.size() > maxPendingTransactions) { if (pendingTransactions.size() > maxPendingTransactions) {
final TransactionInfo toRemove = prioritizedTransactions.last(); 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 * 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. * 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.chain.MutableBlockchain;
import org.hyperledger.besu.ethereum.core.Account; import org.hyperledger.besu.ethereum.core.Account;
import org.hyperledger.besu.ethereum.core.BlockHeader; 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.Transaction;
import org.hyperledger.besu.ethereum.core.Wei; import org.hyperledger.besu.ethereum.core.Wei;
import org.hyperledger.besu.ethereum.eth.manager.EthContext; 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.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.Set; import java.util.Set;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
@ -65,27 +67,33 @@ public class TransactionPool implements BlockAddedObserver {
private final ProtocolSchedule<?> protocolSchedule; private final ProtocolSchedule<?> protocolSchedule;
private final ProtocolContext<?> protocolContext; private final ProtocolContext<?> protocolContext;
private final TransactionBatchAddedListener transactionBatchAddedListener; private final TransactionBatchAddedListener transactionBatchAddedListener;
private final TransactionBatchAddedListener pendingTransactionBatchAddedListener;
private final SyncState syncState; private final SyncState syncState;
private final Wei minTransactionGasPrice; private final Wei minTransactionGasPrice;
private final LabelledMetric<Counter> duplicateTransactionCounter; private final LabelledMetric<Counter> duplicateTransactionCounter;
private final PeerTransactionTracker peerTransactionTracker; private final PeerTransactionTracker peerTransactionTracker;
private final PeerPendingTransactionTracker peerPendingTransactionTracker;
public TransactionPool( public TransactionPool(
final PendingTransactions pendingTransactions, final PendingTransactions pendingTransactions,
final ProtocolSchedule<?> protocolSchedule, final ProtocolSchedule<?> protocolSchedule,
final ProtocolContext<?> protocolContext, final ProtocolContext<?> protocolContext,
final TransactionBatchAddedListener transactionBatchAddedListener, final TransactionBatchAddedListener transactionBatchAddedListener,
final TransactionBatchAddedListener pendingTransactionBatchAddedListener,
final SyncState syncState, final SyncState syncState,
final EthContext ethContext, final EthContext ethContext,
final PeerTransactionTracker peerTransactionTracker, final PeerTransactionTracker peerTransactionTracker,
final PeerPendingTransactionTracker peerPendingTransactionTracker,
final Wei minTransactionGasPrice, final Wei minTransactionGasPrice,
final MetricsSystem metricsSystem) { final MetricsSystem metricsSystem) {
this.pendingTransactions = pendingTransactions; this.pendingTransactions = pendingTransactions;
this.protocolSchedule = protocolSchedule; this.protocolSchedule = protocolSchedule;
this.protocolContext = protocolContext; this.protocolContext = protocolContext;
this.transactionBatchAddedListener = transactionBatchAddedListener; this.transactionBatchAddedListener = transactionBatchAddedListener;
this.pendingTransactionBatchAddedListener = pendingTransactionBatchAddedListener;
this.syncState = syncState; this.syncState = syncState;
this.peerTransactionTracker = peerTransactionTracker; this.peerTransactionTracker = peerTransactionTracker;
this.peerPendingTransactionTracker = peerPendingTransactionTracker;
this.minTransactionGasPrice = minTransactionGasPrice; this.minTransactionGasPrice = minTransactionGasPrice;
duplicateTransactionCounter = duplicateTransactionCounter =
@ -103,12 +111,24 @@ public class TransactionPool implements BlockAddedObserver {
for (final Transaction transaction : localTransactions) { for (final Transaction transaction : localTransactions) {
peerTransactionTracker.addToPeerSendQueue(peer, transaction); peerTransactionTracker.addToPeerSendQueue(peer, transaction);
} }
final Collection<Hash> hashes = getNewPooledHashes();
for (final Hash hash : hashes) {
peerPendingTransactionTracker.addToPeerSendQueue(peer, hash);
}
} }
public List<Transaction> getLocalTransactions() { public List<Transaction> getLocalTransactions() {
return pendingTransactions.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( public ValidationResult<TransactionInvalidReason> addLocalTransaction(
final Transaction transaction) { final Transaction transaction) {
if (transaction.getGasPrice().compareTo(minTransactionGasPrice) < 0) { if (transaction.getGasPrice().compareTo(minTransactionGasPrice) < 0) {
@ -121,7 +141,9 @@ public class TransactionPool implements BlockAddedObserver {
() -> { () -> {
final boolean added = pendingTransactions.addLocalTransaction(transaction); final boolean added = pendingTransactions.addLocalTransaction(transaction);
if (added) { if (added) {
transactionBatchAddedListener.onTransactionsAdded(singletonList(transaction)); Collection<Transaction> txs = singletonList(transaction);
transactionBatchAddedListener.onTransactionsAdded(txs);
pendingTransactionBatchAddedListener.onTransactionsAdded(txs);
} else { } else {
duplicateTransactionCounter.labels(LOCAL).inc(); duplicateTransactionCounter.labels(LOCAL).inc();
} }
@ -135,6 +157,7 @@ public class TransactionPool implements BlockAddedObserver {
} }
final Set<Transaction> addedTransactions = new HashSet<>(); final Set<Transaction> addedTransactions = new HashSet<>();
for (final Transaction transaction : transactions) { for (final Transaction transaction : transactions) {
pendingTransactions.tryEvictTransactionHash(transaction.getHash());
if (pendingTransactions.containsTransaction(transaction.getHash())) { if (pendingTransactions.containsTransaction(transaction.getHash())) {
// We already have this transaction, don't even validate it. // We already have this transaction, don't even validate it.
duplicateTransactionCounter.labels(REMOTE).inc(); duplicateTransactionCounter.labels(REMOTE).inc();
@ -226,6 +249,10 @@ public class TransactionPool implements BlockAddedObserver {
.orElseGet(() -> ValidationResult.invalid(CHAIN_HEAD_WORLD_STATE_NOT_AVAILABLE)); .orElseGet(() -> ValidationResult.invalid(CHAIN_HEAD_WORLD_STATE_NOT_AVAILABLE));
} }
public Optional<Transaction> getTransactionByHash(final Hash hash) {
return pendingTransactions.getTransactionByHash(hash);
}
private BlockHeader getChainHeadBlockHeader() { private BlockHeader getChainHeadBlockHeader() {
final MutableBlockchain blockchain = protocolContext.getBlockchain(); final MutableBlockchain blockchain = protocolContext.getBlockchain();
return blockchain.getBlockHeader(blockchain.getChainHeadHash()).get(); return blockchain.getBlockHeader(blockchain.getChainHeadHash()).get();

@ -19,17 +19,21 @@ import java.util.Objects;
public class TransactionPoolConfiguration { public class TransactionPoolConfiguration {
public static final int DEFAULT_TX_MSG_KEEP_ALIVE = 60; 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 = 4096;
public static final int MAX_PENDING_TRANSACTIONS_HASHES = 4096;
public static final int DEFAULT_TX_RETENTION_HOURS = 13; public static final int DEFAULT_TX_RETENTION_HOURS = 13;
private final int txPoolMaxSize; private final int txPoolMaxSize;
private final int pooledTransactionHashesSize;
private final int pendingTxRetentionPeriod; private final int pendingTxRetentionPeriod;
private final int txMessageKeepAliveSeconds; private final int txMessageKeepAliveSeconds;
public TransactionPoolConfiguration( public TransactionPoolConfiguration(
final int txPoolMaxSize, final int txPoolMaxSize,
final int pooledTransactionHashesSize,
final int pendingTxRetentionPeriod, final int pendingTxRetentionPeriod,
final int txMessageKeepAliveSeconds) { final int txMessageKeepAliveSeconds) {
this.txPoolMaxSize = txPoolMaxSize; this.txPoolMaxSize = txPoolMaxSize;
this.pooledTransactionHashesSize = pooledTransactionHashesSize;
this.pendingTxRetentionPeriod = pendingTxRetentionPeriod; this.pendingTxRetentionPeriod = pendingTxRetentionPeriod;
this.txMessageKeepAliveSeconds = txMessageKeepAliveSeconds; this.txMessageKeepAliveSeconds = txMessageKeepAliveSeconds;
} }
@ -38,6 +42,10 @@ public class TransactionPoolConfiguration {
return txPoolMaxSize; return txPoolMaxSize;
} }
public int getPooledTransactionHashesSize() {
return pooledTransactionHashesSize;
}
public int getPendingTxRetentionPeriod() { public int getPendingTxRetentionPeriod() {
return pendingTxRetentionPeriod; return pendingTxRetentionPeriod;
} }
@ -85,12 +93,18 @@ public class TransactionPoolConfiguration {
private int txPoolMaxSize = MAX_PENDING_TRANSACTIONS; private int txPoolMaxSize = MAX_PENDING_TRANSACTIONS;
private int pendingTxRetentionPeriod = DEFAULT_TX_RETENTION_HOURS; private int pendingTxRetentionPeriod = DEFAULT_TX_RETENTION_HOURS;
private Integer txMessageKeepAliveSeconds = DEFAULT_TX_MSG_KEEP_ALIVE; private Integer txMessageKeepAliveSeconds = DEFAULT_TX_MSG_KEEP_ALIVE;
private int pooledTransactionHashesSize = MAX_PENDING_TRANSACTIONS_HASHES;
public Builder txPoolMaxSize(final int txPoolMaxSize) { public Builder txPoolMaxSize(final int txPoolMaxSize) {
this.txPoolMaxSize = txPoolMaxSize; this.txPoolMaxSize = txPoolMaxSize;
return this; return this;
} }
public Builder pooledTransactionHashesSize(final int pooledTransactionHashesSize) {
this.pooledTransactionHashesSize = pooledTransactionHashesSize;
return this;
}
public Builder pendingTxRetentionPeriod(final int pendingTxRetentionPeriod) { public Builder pendingTxRetentionPeriod(final int pendingTxRetentionPeriod) {
this.pendingTxRetentionPeriod = pendingTxRetentionPeriod; this.pendingTxRetentionPeriod = pendingTxRetentionPeriod;
return this; return this;
@ -103,7 +117,10 @@ public class TransactionPoolConfiguration {
public TransactionPoolConfiguration build() { public TransactionPoolConfiguration build() {
return new TransactionPoolConfiguration( 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.core.Wei;
import org.hyperledger.besu.ethereum.eth.manager.EthContext; import org.hyperledger.besu.ethereum.eth.manager.EthContext;
import org.hyperledger.besu.ethereum.eth.messages.EthPV62; 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.eth.sync.state.SyncState;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.metrics.BesuMetricCategory; import org.hyperledger.besu.metrics.BesuMetricCategory;
@ -41,22 +42,30 @@ public class TransactionPoolFactory {
new PendingTransactions( new PendingTransactions(
transactionPoolConfiguration.getPendingTxRetentionPeriod(), transactionPoolConfiguration.getPendingTxRetentionPeriod(),
transactionPoolConfiguration.getTxPoolMaxSize(), transactionPoolConfiguration.getTxPoolMaxSize(),
transactionPoolConfiguration.getPooledTransactionHashesSize(),
clock, clock,
metricsSystem); metricsSystem);
final PeerTransactionTracker transactionTracker = new PeerTransactionTracker(); final PeerTransactionTracker transactionTracker = new PeerTransactionTracker();
final TransactionsMessageSender transactionsMessageSender = final TransactionsMessageSender transactionsMessageSender =
new TransactionsMessageSender(transactionTracker); new TransactionsMessageSender(transactionTracker);
final PeerPendingTransactionTracker pendingTransactionTracker =
new PeerPendingTransactionTracker(pendingTransactions);
final PendingTransactionsMessageSender pendingTransactionsMessageSender =
new PendingTransactionsMessageSender(pendingTransactionTracker);
final TransactionPool transactionPool = final TransactionPool transactionPool =
new TransactionPool( new TransactionPool(
pendingTransactions, pendingTransactions,
protocolSchedule, protocolSchedule,
protocolContext, protocolContext,
new TransactionSender(transactionTracker, transactionsMessageSender, ethContext), new TransactionSender(transactionTracker, transactionsMessageSender, ethContext),
new PendingTransactionSender(
pendingTransactionTracker, pendingTransactionsMessageSender, ethContext),
syncState, syncState,
ethContext, ethContext,
transactionTracker, transactionTracker,
pendingTransactionTracker,
minTransactionGasPrice, minTransactionGasPrice,
metricsSystem); metricsSystem);
@ -71,8 +80,23 @@ public class TransactionPoolFactory {
"transactions_messages_skipped_total", "transactions_messages_skipped_total",
"Total number of transactions messages skipped by the processor.")), "Total number of transactions messages skipped by the processor.")),
transactionPoolConfiguration.getTxMessageKeepAliveSeconds()); 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(EthPV62.TRANSACTIONS, transactionsMessageHandler);
ethContext
.getEthMessages()
.subscribe(EthPV65.NEW_POOLED_TRANSACTION_HASHES, pooledTransactionsMessageHandler);
protocolContext.getBlockchain().observeBlockAdded(transactionPool); protocolContext.getBlockchain().observeBlockAdded(transactionPool);
ethContext.getEthPeers().subscribeDisconnect(transactionTracker); ethContext.getEthPeers().subscribeDisconnect(transactionTracker);
return transactionPool; 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.StatusMessage;
import org.hyperledger.besu.ethereum.eth.messages.TransactionsMessage; import org.hyperledger.besu.ethereum.eth.messages.TransactionsMessage;
import org.hyperledger.besu.ethereum.eth.sync.state.SyncState; 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.TransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolFactory; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolFactory;
import org.hyperledger.besu.ethereum.mainnet.MainnetProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.MainnetProtocolSchedule;
@ -94,6 +95,7 @@ import org.mockito.ArgumentCaptor;
public final class EthProtocolManagerTest { public final class EthProtocolManagerTest {
private static Blockchain blockchain; private static Blockchain blockchain;
private static TransactionPool transactionPool;
private static ProtocolSchedule<Void> protocolSchedule; private static ProtocolSchedule<Void> protocolSchedule;
private static BlockDataGenerator gen; private static BlockDataGenerator gen;
private static ProtocolContext<Void> protocolContext; private static ProtocolContext<Void> protocolContext;
@ -105,6 +107,7 @@ public final class EthProtocolManagerTest {
final BlockchainSetupUtil<Void> blockchainSetupUtil = BlockchainSetupUtil.forTesting(); final BlockchainSetupUtil<Void> blockchainSetupUtil = BlockchainSetupUtil.forTesting();
blockchainSetupUtil.importAllBlocks(); blockchainSetupUtil.importAllBlocks();
blockchain = blockchainSetupUtil.getBlockchain(); blockchain = blockchainSetupUtil.getBlockchain();
transactionPool = blockchainSetupUtil.getTransactionPool();
protocolSchedule = blockchainSetupUtil.getProtocolSchedule(); protocolSchedule = blockchainSetupUtil.getProtocolSchedule();
protocolContext = blockchainSetupUtil.getProtocolContext(); protocolContext = blockchainSetupUtil.getProtocolContext();
assert (blockchainSetupUtil.getMaxBlockNumber() >= 20L); assert (blockchainSetupUtil.getMaxBlockNumber() >= 20L);
@ -113,17 +116,11 @@ public final class EthProtocolManagerTest {
@Test @Test
public void disconnectOnUnsolicitedMessage() { public void disconnectOnUnsolicitedMessage() {
try (final EthProtocolManager ethManager = try (final EthProtocolManager ethManager =
new EthProtocolManager( EthProtocolManagerTestUtil.create(
blockchain, blockchain,
() -> false,
protocolContext.getWorldStateArchive(), protocolContext.getWorldStateArchive(),
BigInteger.ONE, transactionPool,
Collections.emptyList(),
true,
1,
1,
1,
TestClock.fixed(),
new NoOpMetricsSystem(),
EthProtocolConfiguration.defaultConfig())) { EthProtocolConfiguration.defaultConfig())) {
final MessageData messageData = final MessageData messageData =
BlockHeadersMessage.create(Collections.singletonList(blockchain.getBlockHeader(1).get())); BlockHeadersMessage.create(Collections.singletonList(blockchain.getBlockHeader(1).get()));
@ -136,17 +133,11 @@ public final class EthProtocolManagerTest {
@Test @Test
public void disconnectOnFailureToSendStatusMessage() { public void disconnectOnFailureToSendStatusMessage() {
try (final EthProtocolManager ethManager = try (final EthProtocolManager ethManager =
new EthProtocolManager( EthProtocolManagerTestUtil.create(
blockchain, blockchain,
() -> false,
protocolContext.getWorldStateArchive(), protocolContext.getWorldStateArchive(),
BigInteger.ONE, transactionPool,
Collections.emptyList(),
true,
1,
1,
1,
TestClock.fixed(),
new NoOpMetricsSystem(),
EthProtocolConfiguration.defaultConfig())) { EthProtocolConfiguration.defaultConfig())) {
final MessageData messageData = final MessageData messageData =
BlockHeadersMessage.create(Collections.singletonList(blockchain.getBlockHeader(1).get())); BlockHeadersMessage.create(Collections.singletonList(blockchain.getBlockHeader(1).get()));
@ -160,17 +151,11 @@ public final class EthProtocolManagerTest {
@Test @Test
public void disconnectOnWrongChainId() { public void disconnectOnWrongChainId() {
try (final EthProtocolManager ethManager = try (final EthProtocolManager ethManager =
new EthProtocolManager( EthProtocolManagerTestUtil.create(
blockchain, blockchain,
() -> false,
protocolContext.getWorldStateArchive(), protocolContext.getWorldStateArchive(),
BigInteger.ONE, transactionPool,
Collections.emptyList(),
true,
1,
1,
1,
TestClock.fixed(),
new NoOpMetricsSystem(),
EthProtocolConfiguration.defaultConfig())) { EthProtocolConfiguration.defaultConfig())) {
final MessageData messageData = final MessageData messageData =
BlockHeadersMessage.create(Collections.singletonList(blockchain.getBlockHeader(1).get())); BlockHeadersMessage.create(Collections.singletonList(blockchain.getBlockHeader(1).get()));
@ -195,17 +180,11 @@ public final class EthProtocolManagerTest {
@Test @Test
public void disconnectOnWrongGenesisHash() { public void disconnectOnWrongGenesisHash() {
try (final EthProtocolManager ethManager = try (final EthProtocolManager ethManager =
new EthProtocolManager( EthProtocolManagerTestUtil.create(
blockchain, blockchain,
() -> false,
protocolContext.getWorldStateArchive(), protocolContext.getWorldStateArchive(),
BigInteger.ONE, transactionPool,
Collections.emptyList(),
true,
1,
1,
1,
TestClock.fixed(),
new NoOpMetricsSystem(),
EthProtocolConfiguration.defaultConfig())) { EthProtocolConfiguration.defaultConfig())) {
final MessageData messageData = final MessageData messageData =
BlockHeadersMessage.create(Collections.singletonList(blockchain.getBlockHeader(1).get())); BlockHeadersMessage.create(Collections.singletonList(blockchain.getBlockHeader(1).get()));
@ -230,17 +209,11 @@ public final class EthProtocolManagerTest {
@Test(expected = ConditionTimeoutException.class) @Test(expected = ConditionTimeoutException.class)
public void doNotDisconnectOnValidMessage() { public void doNotDisconnectOnValidMessage() {
try (final EthProtocolManager ethManager = try (final EthProtocolManager ethManager =
new EthProtocolManager( EthProtocolManagerTestUtil.create(
blockchain, blockchain,
() -> false,
protocolContext.getWorldStateArchive(), protocolContext.getWorldStateArchive(),
BigInteger.ONE, transactionPool,
Collections.emptyList(),
true,
1,
1,
1,
TestClock.fixed(),
new NoOpMetricsSystem(),
EthProtocolConfiguration.defaultConfig())) { EthProtocolConfiguration.defaultConfig())) {
final MessageData messageData = final MessageData messageData =
GetBlockBodiesMessage.create(Collections.singletonList(gen.hash())); GetBlockBodiesMessage.create(Collections.singletonList(gen.hash()));
@ -257,17 +230,11 @@ public final class EthProtocolManagerTest {
public void respondToGetHeaders() throws ExecutionException, InterruptedException { public void respondToGetHeaders() throws ExecutionException, InterruptedException {
final CompletableFuture<Void> done = new CompletableFuture<>(); final CompletableFuture<Void> done = new CompletableFuture<>();
try (final EthProtocolManager ethManager = try (final EthProtocolManager ethManager =
new EthProtocolManager( EthProtocolManagerTestUtil.create(
blockchain, blockchain,
() -> false,
protocolContext.getWorldStateArchive(), protocolContext.getWorldStateArchive(),
BigInteger.ONE, transactionPool,
Collections.emptyList(),
true,
1,
1,
1,
TestClock.fixed(),
new NoOpMetricsSystem(),
EthProtocolConfiguration.defaultConfig())) { EthProtocolConfiguration.defaultConfig())) {
final long startBlock = 5L; final long startBlock = 5L;
final int blockCount = 5; final int blockCount = 5;
@ -300,18 +267,12 @@ public final class EthProtocolManagerTest {
final CompletableFuture<Void> done = new CompletableFuture<>(); final CompletableFuture<Void> done = new CompletableFuture<>();
final int limit = 5; final int limit = 5;
try (final EthProtocolManager ethManager = try (final EthProtocolManager ethManager =
new EthProtocolManager( EthProtocolManagerTestUtil.create(
blockchain, blockchain,
() -> false,
protocolContext.getWorldStateArchive(), protocolContext.getWorldStateArchive(),
BigInteger.ONE, transactionPool,
Collections.emptyList(), new EthProtocolConfiguration(limit, limit, limit, limit, limit))) {
true,
1,
1,
1,
TestClock.fixed(),
new NoOpMetricsSystem(),
new EthProtocolConfiguration(limit, limit, limit, limit))) {
final long startBlock = 5L; final long startBlock = 5L;
final int blockCount = 10; final int blockCount = 10;
final MessageData messageData = final MessageData messageData =
@ -342,18 +303,13 @@ public final class EthProtocolManagerTest {
public void respondToGetHeadersReversed() throws ExecutionException, InterruptedException { public void respondToGetHeadersReversed() throws ExecutionException, InterruptedException {
final CompletableFuture<Void> done = new CompletableFuture<>(); final CompletableFuture<Void> done = new CompletableFuture<>();
try (final EthProtocolManager ethManager = try (final EthProtocolManager ethManager =
new EthProtocolManager( EthProtocolManagerTestUtil.create(
blockchain, blockchain,
() -> false,
protocolContext.getWorldStateArchive(), protocolContext.getWorldStateArchive(),
BigInteger.ONE, transactionPool,
Collections.emptyList(),
true,
1,
1,
1,
TestClock.fixed(),
new NoOpMetricsSystem(),
EthProtocolConfiguration.defaultConfig())) { EthProtocolConfiguration.defaultConfig())) {
final long endBlock = 10L; final long endBlock = 10L;
final int blockCount = 5; final int blockCount = 5;
final MessageData messageData = GetBlockHeadersMessage.create(endBlock, blockCount, 0, true); final MessageData messageData = GetBlockHeadersMessage.create(endBlock, blockCount, 0, true);
@ -383,18 +339,13 @@ public final class EthProtocolManagerTest {
public void respondToGetHeadersWithSkip() throws ExecutionException, InterruptedException { public void respondToGetHeadersWithSkip() throws ExecutionException, InterruptedException {
final CompletableFuture<Void> done = new CompletableFuture<>(); final CompletableFuture<Void> done = new CompletableFuture<>();
try (final EthProtocolManager ethManager = try (final EthProtocolManager ethManager =
new EthProtocolManager( EthProtocolManagerTestUtil.create(
blockchain, blockchain,
() -> false,
protocolContext.getWorldStateArchive(), protocolContext.getWorldStateArchive(),
BigInteger.ONE, transactionPool,
Collections.emptyList(),
true,
1,
1,
1,
TestClock.fixed(),
new NoOpMetricsSystem(),
EthProtocolConfiguration.defaultConfig())) { EthProtocolConfiguration.defaultConfig())) {
final long startBlock = 5L; final long startBlock = 5L;
final int blockCount = 5; final int blockCount = 5;
final int skip = 1; final int skip = 1;
@ -427,18 +378,13 @@ public final class EthProtocolManagerTest {
throws ExecutionException, InterruptedException { throws ExecutionException, InterruptedException {
final CompletableFuture<Void> done = new CompletableFuture<>(); final CompletableFuture<Void> done = new CompletableFuture<>();
try (final EthProtocolManager ethManager = try (final EthProtocolManager ethManager =
new EthProtocolManager( EthProtocolManagerTestUtil.create(
blockchain, blockchain,
() -> false,
protocolContext.getWorldStateArchive(), protocolContext.getWorldStateArchive(),
BigInteger.ONE, transactionPool,
Collections.emptyList(),
true,
1,
1,
1,
TestClock.fixed(),
new NoOpMetricsSystem(),
EthProtocolConfiguration.defaultConfig())) { EthProtocolConfiguration.defaultConfig())) {
final long endBlock = 10L; final long endBlock = 10L;
final int blockCount = 5; final int blockCount = 5;
final int skip = 1; final int skip = 1;
@ -492,18 +438,13 @@ public final class EthProtocolManagerTest {
public void respondToGetHeadersPartial() throws ExecutionException, InterruptedException { public void respondToGetHeadersPartial() throws ExecutionException, InterruptedException {
final CompletableFuture<Void> done = new CompletableFuture<>(); final CompletableFuture<Void> done = new CompletableFuture<>();
try (final EthProtocolManager ethManager = try (final EthProtocolManager ethManager =
new EthProtocolManager( EthProtocolManagerTestUtil.create(
blockchain, blockchain,
() -> false,
protocolContext.getWorldStateArchive(), protocolContext.getWorldStateArchive(),
BigInteger.ONE, transactionPool,
Collections.emptyList(),
true,
1,
1,
1,
TestClock.fixed(),
new NoOpMetricsSystem(),
EthProtocolConfiguration.defaultConfig())) { EthProtocolConfiguration.defaultConfig())) {
final long startBlock = blockchain.getChainHeadBlockNumber() - 1L; final long startBlock = blockchain.getChainHeadBlockNumber() - 1L;
final int blockCount = 5; final int blockCount = 5;
final MessageData messageData = final MessageData messageData =
@ -534,18 +475,13 @@ public final class EthProtocolManagerTest {
public void respondToGetHeadersEmpty() throws ExecutionException, InterruptedException { public void respondToGetHeadersEmpty() throws ExecutionException, InterruptedException {
final CompletableFuture<Void> done = new CompletableFuture<>(); final CompletableFuture<Void> done = new CompletableFuture<>();
try (final EthProtocolManager ethManager = try (final EthProtocolManager ethManager =
new EthProtocolManager( EthProtocolManagerTestUtil.create(
blockchain, blockchain,
() -> false,
protocolContext.getWorldStateArchive(), protocolContext.getWorldStateArchive(),
BigInteger.ONE, transactionPool,
Collections.emptyList(),
true,
1,
1,
1,
TestClock.fixed(),
new NoOpMetricsSystem(),
EthProtocolConfiguration.defaultConfig())) { EthProtocolConfiguration.defaultConfig())) {
final long startBlock = blockchain.getChainHeadBlockNumber() + 1; final long startBlock = blockchain.getChainHeadBlockNumber() + 1;
final int blockCount = 5; final int blockCount = 5;
final MessageData messageData = final MessageData messageData =
@ -573,18 +509,13 @@ public final class EthProtocolManagerTest {
public void respondToGetBodies() throws ExecutionException, InterruptedException { public void respondToGetBodies() throws ExecutionException, InterruptedException {
final CompletableFuture<Void> done = new CompletableFuture<>(); final CompletableFuture<Void> done = new CompletableFuture<>();
try (final EthProtocolManager ethManager = try (final EthProtocolManager ethManager =
new EthProtocolManager( EthProtocolManagerTestUtil.create(
blockchain, blockchain,
() -> false,
protocolContext.getWorldStateArchive(), protocolContext.getWorldStateArchive(),
BigInteger.ONE, transactionPool,
Collections.emptyList(),
true,
1,
1,
1,
TestClock.fixed(),
new NoOpMetricsSystem(),
EthProtocolConfiguration.defaultConfig())) { EthProtocolConfiguration.defaultConfig())) {
// Setup blocks query // Setup blocks query
final long startBlock = blockchain.getChainHeadBlockNumber() - 5; final long startBlock = blockchain.getChainHeadBlockNumber() - 5;
final int blockCount = 2; final int blockCount = 2;
@ -628,18 +559,12 @@ public final class EthProtocolManagerTest {
final CompletableFuture<Void> done = new CompletableFuture<>(); final CompletableFuture<Void> done = new CompletableFuture<>();
final int limit = 5; final int limit = 5;
try (final EthProtocolManager ethManager = try (final EthProtocolManager ethManager =
new EthProtocolManager( EthProtocolManagerTestUtil.create(
blockchain, blockchain,
() -> false,
protocolContext.getWorldStateArchive(), protocolContext.getWorldStateArchive(),
BigInteger.ONE, transactionPool,
Collections.emptyList(), new EthProtocolConfiguration(limit, limit, limit, limit, limit))) {
true,
1,
1,
1,
TestClock.fixed(),
new NoOpMetricsSystem(),
new EthProtocolConfiguration(limit, limit, limit, limit))) {
// Setup blocks query // Setup blocks query
final int blockCount = 10; final int blockCount = 10;
final long startBlock = blockchain.getChainHeadBlockNumber() - blockCount; final long startBlock = blockchain.getChainHeadBlockNumber() - blockCount;
@ -682,17 +607,11 @@ public final class EthProtocolManagerTest {
public void respondToGetBodiesPartial() throws ExecutionException, InterruptedException { public void respondToGetBodiesPartial() throws ExecutionException, InterruptedException {
final CompletableFuture<Void> done = new CompletableFuture<>(); final CompletableFuture<Void> done = new CompletableFuture<>();
try (final EthProtocolManager ethManager = try (final EthProtocolManager ethManager =
new EthProtocolManager( EthProtocolManagerTestUtil.create(
blockchain, blockchain,
() -> false,
protocolContext.getWorldStateArchive(), protocolContext.getWorldStateArchive(),
BigInteger.ONE, transactionPool,
Collections.emptyList(),
true,
1,
1,
1,
TestClock.fixed(),
new NoOpMetricsSystem(),
EthProtocolConfiguration.defaultConfig())) { EthProtocolConfiguration.defaultConfig())) {
// Setup blocks query // Setup blocks query
final long expectedBlockNumber = blockchain.getChainHeadBlockNumber() - 1; final long expectedBlockNumber = blockchain.getChainHeadBlockNumber() - 1;
@ -730,17 +649,11 @@ public final class EthProtocolManagerTest {
public void respondToGetReceipts() throws ExecutionException, InterruptedException { public void respondToGetReceipts() throws ExecutionException, InterruptedException {
final CompletableFuture<Void> done = new CompletableFuture<>(); final CompletableFuture<Void> done = new CompletableFuture<>();
try (final EthProtocolManager ethManager = try (final EthProtocolManager ethManager =
new EthProtocolManager( EthProtocolManagerTestUtil.create(
blockchain, blockchain,
() -> false,
protocolContext.getWorldStateArchive(), protocolContext.getWorldStateArchive(),
BigInteger.ONE, transactionPool,
Collections.emptyList(),
true,
1,
1,
1,
TestClock.fixed(),
new NoOpMetricsSystem(),
EthProtocolConfiguration.defaultConfig())) { EthProtocolConfiguration.defaultConfig())) {
// Setup blocks query // Setup blocks query
final long startBlock = blockchain.getChainHeadBlockNumber() - 5; final long startBlock = blockchain.getChainHeadBlockNumber() - 5;
@ -784,18 +697,12 @@ public final class EthProtocolManagerTest {
final CompletableFuture<Void> done = new CompletableFuture<>(); final CompletableFuture<Void> done = new CompletableFuture<>();
final int limit = 5; final int limit = 5;
try (final EthProtocolManager ethManager = try (final EthProtocolManager ethManager =
new EthProtocolManager( EthProtocolManagerTestUtil.create(
blockchain, blockchain,
() -> false,
protocolContext.getWorldStateArchive(), protocolContext.getWorldStateArchive(),
BigInteger.ONE, transactionPool,
Collections.emptyList(), new EthProtocolConfiguration(limit, limit, limit, limit, limit))) {
true,
1,
1,
1,
TestClock.fixed(),
new NoOpMetricsSystem(),
new EthProtocolConfiguration(limit, limit, limit, limit))) {
// Setup blocks query // Setup blocks query
final int blockCount = 10; final int blockCount = 10;
final long startBlock = blockchain.getChainHeadBlockNumber() - blockCount; final long startBlock = blockchain.getChainHeadBlockNumber() - blockCount;
@ -837,17 +744,11 @@ public final class EthProtocolManagerTest {
public void respondToGetReceiptsPartial() throws ExecutionException, InterruptedException { public void respondToGetReceiptsPartial() throws ExecutionException, InterruptedException {
final CompletableFuture<Void> done = new CompletableFuture<>(); final CompletableFuture<Void> done = new CompletableFuture<>();
try (final EthProtocolManager ethManager = try (final EthProtocolManager ethManager =
new EthProtocolManager( EthProtocolManagerTestUtil.create(
blockchain, blockchain,
() -> false,
protocolContext.getWorldStateArchive(), protocolContext.getWorldStateArchive(),
BigInteger.ONE, transactionPool,
Collections.emptyList(),
true,
1,
1,
1,
TestClock.fixed(),
new NoOpMetricsSystem(),
EthProtocolConfiguration.defaultConfig())) { EthProtocolConfiguration.defaultConfig())) {
// Setup blocks query // Setup blocks query
final long blockNumber = blockchain.getChainHeadBlockNumber() - 5; final long blockNumber = blockchain.getChainHeadBlockNumber() - 5;
@ -887,17 +788,11 @@ public final class EthProtocolManagerTest {
final WorldStateArchive worldStateArchive = protocolContext.getWorldStateArchive(); final WorldStateArchive worldStateArchive = protocolContext.getWorldStateArchive();
try (final EthProtocolManager ethManager = try (final EthProtocolManager ethManager =
new EthProtocolManager( EthProtocolManagerTestUtil.create(
blockchain, blockchain,
worldStateArchive, () -> false,
BigInteger.ONE, protocolContext.getWorldStateArchive(),
Collections.emptyList(), transactionPool,
true,
1,
1,
1,
TestClock.fixed(),
new NoOpMetricsSystem(),
EthProtocolConfiguration.defaultConfig())) { EthProtocolConfiguration.defaultConfig())) {
// Setup node data query // Setup node data query
@ -939,63 +834,59 @@ public final class EthProtocolManagerTest {
@Test @Test
public void newBlockMinedSendsNewBlockMessageToAllPeers() { public void newBlockMinedSendsNewBlockMessageToAllPeers() {
final EthProtocolManager ethManager = try (final EthProtocolManager ethManager =
new EthProtocolManager( EthProtocolManagerTestUtil.create(
blockchain, blockchain,
() -> false,
protocolContext.getWorldStateArchive(), protocolContext.getWorldStateArchive(),
BigInteger.ONE, transactionPool,
Collections.emptyList(), EthProtocolConfiguration.defaultConfig())) {
true, // Define handler to validate response
1, final PeerSendHandler onSend = mock(PeerSendHandler.class);
1, final List<PeerConnection> peers = Lists.newArrayList();
1,
TestClock.fixed(),
new NoOpMetricsSystem(),
EthProtocolConfiguration.defaultConfig());
// 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 Hash chainHeadHash = blockchain.getChainHeadHash(); final int PEER_COUNT = 5;
final Block minedBlock = for (int i = 0; i < PEER_COUNT; i++) {
new Block( peers.add(setupPeer(ethManager, onSend));
blockchain.getBlockHeader(chainHeadHash).get(), }
blockchain.getBlockBody(chainHeadHash).get());
final Difficulty expectedTotalDifficulty = blockchain.getChainHead().getTotalDifficulty(); final Hash chainHeadHash = blockchain.getChainHeadHash();
final Block minedBlock =
new Block(
blockchain.getBlockHeader(chainHeadHash).get(),
blockchain.getBlockBody(chainHeadHash).get());
reset(onSend); final Difficulty expectedTotalDifficulty = blockchain.getChainHead().getTotalDifficulty();
ethManager.blockMined(minedBlock); reset(onSend);
final ArgumentCaptor<NewBlockMessage> messageSentCaptor = ethManager.blockMined(minedBlock);
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)) final ArgumentCaptor<NewBlockMessage> messageSentCaptor =
.exec( ArgumentCaptor.forClass(NewBlockMessage.class);
capabilityCaptor.capture(), messageSentCaptor.capture(), receivingPeerCaptor.capture()); final ArgumentCaptor<PeerConnection> receivingPeerCaptor =
ArgumentCaptor.forClass(PeerConnection.class);
final ArgumentCaptor<Capability> capabilityCaptor = ArgumentCaptor.forClass(Capability.class);
// assert that all entries in capability param were Eth63 verify(onSend, times(PEER_COUNT))
assertThat(capabilityCaptor.getAllValues().stream().distinct().collect(Collectors.toList())) .exec(
.isEqualTo(Collections.singletonList(EthProtocol.ETH63)); capabilityCaptor.capture(),
messageSentCaptor.capture(),
receivingPeerCaptor.capture());
// assert that all messages transmitted contain the expected block & total difficulty. // assert that all entries in capability param were Eth63
final ProtocolSchedule<Void> protocolSchdeule = MainnetProtocolSchedule.create(); assertThat(capabilityCaptor.getAllValues().stream().distinct().collect(Collectors.toList()))
for (final NewBlockMessage msg : messageSentCaptor.getAllValues()) { .isEqualTo(Collections.singletonList(EthProtocol.ETH63));
assertThat(msg.block(protocolSchdeule)).isEqualTo(minedBlock);
assertThat(msg.totalDifficulty(protocolSchdeule)).isEqualTo(expectedTotalDifficulty);
}
assertThat(receivingPeerCaptor.getAllValues().containsAll(peers)).isTrue(); // 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 @Test
@ -1014,18 +905,13 @@ public final class EthProtocolManagerTest {
final CompletableFuture<Void> done = new CompletableFuture<>(); final CompletableFuture<Void> done = new CompletableFuture<>();
try (final EthProtocolManager ethManager = try (final EthProtocolManager ethManager =
new EthProtocolManager( EthProtocolManagerTestUtil.create(
blockchain, blockchain,
() -> false,
protocolContext.getWorldStateArchive(), protocolContext.getWorldStateArchive(),
BigInteger.ONE, transactionPool,
Collections.emptyList(),
true,
1,
1,
1,
TestClock.fixed(),
new NoOpMetricsSystem(),
EthProtocolConfiguration.defaultConfig())) { EthProtocolConfiguration.defaultConfig())) {
final long startBlock = 1L; final long startBlock = 1L;
final int requestedBlockCount = 13; final int requestedBlockCount = 13;
final int receivedBlockCount = 2; final int receivedBlockCount = 2;
@ -1084,18 +970,12 @@ public final class EthProtocolManagerTest {
final TransactionsMessage transactionMessage = TransactionsMessage.readFrom(raw); final TransactionsMessage transactionMessage = TransactionsMessage.readFrom(raw);
try (final EthProtocolManager ethManager = try (final EthProtocolManager ethManager =
new EthProtocolManager( EthProtocolManagerTestUtil.create(
blockchain, blockchain,
protocolContext.getWorldStateArchive(),
BigInteger.ONE,
Collections.emptyList(),
true,
ethScheduler, ethScheduler,
EthProtocolConfiguration.defaultConfig(), protocolContext.getWorldStateArchive(),
TestClock.fixed(), transactionPool,
metricsSystem, EthProtocolConfiguration.defaultConfig())) {
new ForkIdManager(blockchain, Collections.emptyList()))) {
// Create a transaction pool. This has a side effect of registering a listener for the // Create a transaction pool. This has a side effect of registering a listener for the
// transactions message. // transactions message.
TransactionPoolFactory.createTransactionPool( TransactionPoolFactory.createTransactionPool(

@ -16,17 +16,19 @@ package org.hyperledger.besu.ethereum.eth.manager;
import static com.google.common.base.Preconditions.checkArgument; 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.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.config.GenesisConfigFile;
import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.chain.ChainHead; import org.hyperledger.besu.ethereum.chain.ChainHead;
import org.hyperledger.besu.ethereum.chain.GenesisState; 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.core.Difficulty;
import org.hyperledger.besu.ethereum.eth.EthProtocol; import org.hyperledger.besu.ethereum.eth.EthProtocol;
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration; import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
import org.hyperledger.besu.ethereum.eth.manager.DeterministicEthScheduler.TimeoutPolicy; import org.hyperledger.besu.ethereum.eth.manager.DeterministicEthScheduler.TimeoutPolicy;
import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator; 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.MainnetProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.DefaultMessage; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.DefaultMessage;
@ -43,32 +45,58 @@ public class EthProtocolManagerTestUtil {
public static EthProtocolManager create( public static EthProtocolManager create(
final Blockchain blockchain, final Blockchain blockchain,
final TimeoutPolicy timeoutPolicy,
final WorldStateArchive worldStateArchive, final WorldStateArchive worldStateArchive,
final TimeoutPolicy timeoutPolicy) { final TransactionPool transactionPool,
return create(blockchain, worldStateArchive, new DeterministicEthScheduler(timeoutPolicy)); final EthProtocolConfiguration ethereumWireProtocolConfiguration) {
return create(
blockchain,
new DeterministicEthScheduler(timeoutPolicy),
worldStateArchive,
transactionPool,
ethereumWireProtocolConfiguration);
} }
public static EthProtocolManager create( public static EthProtocolManager create(
final Blockchain blockchain, final Blockchain blockchain,
final EthScheduler ethScheduler,
final WorldStateArchive worldStateArchive, final WorldStateArchive worldStateArchive,
final EthScheduler ethScheduler) { final TransactionPool transactionPool,
final EthProtocolConfiguration ethereumWireProtocolConfiguration,
final EthPeers ethPeers,
final EthMessages ethMessages,
final EthContext ethContext) {
final BigInteger networkId = BigInteger.ONE; final BigInteger networkId = BigInteger.ONE;
return new EthProtocolManager( return new EthProtocolManager(
blockchain, blockchain,
worldStateArchive,
networkId, networkId,
worldStateArchive,
transactionPool,
ethereumWireProtocolConfiguration,
ethPeers,
ethMessages,
ethContext,
Collections.emptyList(), Collections.emptyList(),
false, false,
ethScheduler, ethScheduler,
EthProtocolConfiguration.defaultConfig(),
TestClock.fixed(),
new NoOpMetricsSystem(),
new ForkIdManager(blockchain, Collections.emptyList())); new ForkIdManager(blockchain, Collections.emptyList()));
} }
public static EthProtocolManager create(final Blockchain blockchain) {
return create(blockchain, new DeterministicEthScheduler(TimeoutPolicy.NEVER_TIMEOUT));
}
public static EthProtocolManager create( public static EthProtocolManager create(
final Blockchain blockchain, final WorldStateArchive worldStateArchive) { final Blockchain blockchain,
return create(blockchain, worldStateArchive, TimeoutPolicy.NEVER_TIMEOUT); final WorldStateArchive worldStateArchive,
final TransactionPool transactionPool,
final EthProtocolConfiguration ethProtocolConfiguration) {
return create(
blockchain,
new DeterministicEthScheduler(TimeoutPolicy.NEVER_TIMEOUT),
worldStateArchive,
transactionPool,
ethProtocolConfiguration);
} }
public static EthProtocolManager create(final EthScheduler ethScheduler) { public static EthProtocolManager create(final EthScheduler ethScheduler) {
@ -76,8 +104,43 @@ public class EthProtocolManagerTestUtil {
final GenesisConfigFile config = GenesisConfigFile.mainnet(); final GenesisConfigFile config = GenesisConfigFile.mainnet();
final GenesisState genesisState = GenesisState.fromConfig(config, protocolSchedule); final GenesisState genesisState = GenesisState.fromConfig(config, protocolSchedule);
final Blockchain blockchain = createInMemoryBlockchain(genesisState.getBlock()); final Blockchain blockchain = createInMemoryBlockchain(genesisState.getBlock());
final WorldStateArchive worldStateArchive = createInMemoryWorldStateArchive(); return create(blockchain, ethScheduler);
return create(blockchain, worldStateArchive, 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() { 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.EthProtocolConfiguration;
import org.hyperledger.besu.ethereum.eth.messages.GetNodeDataMessage; import org.hyperledger.besu.ethereum.eth.messages.GetNodeDataMessage;
import org.hyperledger.besu.ethereum.eth.messages.NodeDataMessage; import org.hyperledger.besu.ethereum.eth.messages.NodeDataMessage;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import java.util.Optional; import java.util.Optional;
@ -43,13 +44,18 @@ public class EthServerTest {
private static final Hash HASH3 = Hash.hash(VALUE3); private static final Hash HASH3 = Hash.hash(VALUE3);
private final Blockchain blockchain = mock(Blockchain.class); private final Blockchain blockchain = mock(Blockchain.class);
private final WorldStateArchive worldStateArchive = mock(WorldStateArchive.class); private final WorldStateArchive worldStateArchive = mock(WorldStateArchive.class);
private final TransactionPool transactionPool = mock(TransactionPool.class);
private final EthPeer ethPeer = mock(EthPeer.class); private final EthPeer ethPeer = mock(EthPeer.class);
private final EthMessages ethMessages = new EthMessages(); private final EthMessages ethMessages = new EthMessages();
@Before @Before
public void setUp() { public void setUp() {
new EthServer( new EthServer(
blockchain, worldStateArchive, ethMessages, new EthProtocolConfiguration(2, 2, 2, 2)); blockchain,
worldStateArchive,
transactionPool,
ethMessages,
new EthProtocolConfiguration(2, 2, 2, 2, 2));
} }
@Test @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.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
import static org.hyperledger.besu.ethereum.core.InMemoryStorageProvider.createInMemoryWorldStateArchive; 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.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.BlockBody; 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.BlockHeader;
import org.hyperledger.besu.ethereum.core.Difficulty; import org.hyperledger.besu.ethereum.core.Difficulty;
import org.hyperledger.besu.ethereum.core.Hash; 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.core.TransactionReceipt;
import org.hyperledger.besu.ethereum.eth.EthProtocol; import org.hyperledger.besu.ethereum.eth.EthProtocol;
import org.hyperledger.besu.ethereum.eth.messages.BlockBodiesMessage; import org.hyperledger.besu.ethereum.eth.messages.BlockBodiesMessage;
import org.hyperledger.besu.ethereum.eth.messages.BlockHeadersMessage; import org.hyperledger.besu.ethereum.eth.messages.BlockHeadersMessage;
import org.hyperledger.besu.ethereum.eth.messages.EthPV62; import org.hyperledger.besu.ethereum.eth.messages.EthPV62;
import org.hyperledger.besu.ethereum.eth.messages.EthPV63; 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.NodeDataMessage;
import org.hyperledger.besu.ethereum.eth.messages.PooledTransactionsMessage;
import org.hyperledger.besu.ethereum.eth.messages.ReceiptsMessage; import org.hyperledger.besu.ethereum.eth.messages.ReceiptsMessage;
import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator; 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.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Capability; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.Capability;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.DefaultMessage; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.DefaultMessage;
@ -223,12 +228,24 @@ public class RespondingEthPeer {
}; };
} }
public static Responder blockchainResponder(final Blockchain blockchain) { private static TransactionPool createTransactionPool() {
return blockchainResponder(blockchain, createInMemoryWorldStateArchive()); return mock(TransactionPool.class);
} }
public static Responder blockchainResponder( public static Responder blockchainResponder(
final Blockchain blockchain, final WorldStateArchive worldStateArchive) { 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) -> { return (cap, msg) -> {
MessageData response = null; MessageData response = null;
switch (msg.getCode()) { switch (msg.getCode()) {
@ -244,6 +261,8 @@ public class RespondingEthPeer {
case EthPV63.GET_NODE_DATA: case EthPV63.GET_NODE_DATA:
response = EthServer.constructGetNodeDataResponse(worldStateArchive, msg, 200); response = EthServer.constructGetNodeDataResponse(worldStateArchive, msg, 200);
break; break;
case EthPV65.GET_POOLED_TRANSACTIONS:
response = EthServer.constructGetPooledTransactionsResponse(transactionPool, msg, 200);
} }
return Optional.ofNullable(response); return Optional.ofNullable(response);
}; };
@ -265,11 +284,13 @@ public class RespondingEthPeer {
public static <C> Responder partialResponder( public static <C> Responder partialResponder(
final Blockchain blockchain, final Blockchain blockchain,
final WorldStateArchive worldStateArchive, final WorldStateArchive worldStateArchive,
final TransactionPool transactionPool,
final ProtocolSchedule<C> protocolSchedule, final ProtocolSchedule<C> protocolSchedule,
final float portion) { final float portion) {
checkArgument(portion >= 0.0 && portion <= 1.0, "Portion is in the range [0.0..1.0]"); 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) -> { return (cap, msg) -> {
final Optional<MessageData> maybeResponse = fullResponder.respond(cap, msg); final Optional<MessageData> maybeResponse = fullResponder.respond(cap, msg);
if (!maybeResponse.isPresent()) { if (!maybeResponse.isPresent()) {
@ -310,6 +331,15 @@ public class RespondingEthPeer {
originalNodeData.subList(0, (int) (originalNodeData.size() * portion)); originalNodeData.subList(0, (int) (originalNodeData.size() * portion));
partialResponse = NodeDataMessage.create(partialNodeData); partialResponse = NodeDataMessage.create(partialNodeData);
break; 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); return Optional.of(partialResponse);
}; };
@ -331,6 +361,9 @@ public class RespondingEthPeer {
case EthPV63.GET_NODE_DATA: case EthPV63.GET_NODE_DATA:
response = NodeDataMessage.create(Collections.emptyList()); response = NodeDataMessage.create(Collections.emptyList());
break; break;
case EthPV65.GET_POOLED_TRANSACTIONS:
response = PooledTransactionsMessage.create(Collections.emptyList());
break;
} }
return Optional.ofNullable(response); 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.ProtocolContext;
import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil; 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.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthMessages;
import org.hyperledger.besu.ethereum.eth.manager.EthPeer; 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.EthProtocolManager;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil; 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.RespondingEthPeer;
import org.hyperledger.besu.ethereum.eth.manager.task.EthTask; 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.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.plugin.services.MetricsSystem; import org.hyperledger.besu.plugin.services.MetricsSystem;
import org.hyperledger.besu.testutil.TestClock;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
@ -49,6 +61,7 @@ public abstract class AbstractMessageTaskTest<T, R> {
protected static MetricsSystem metricsSystem = new NoOpMetricsSystem(); protected static MetricsSystem metricsSystem = new NoOpMetricsSystem();
protected EthProtocolManager ethProtocolManager; protected EthProtocolManager ethProtocolManager;
protected EthContext ethContext; protected EthContext ethContext;
protected TransactionPool transactionPool;
protected AtomicBoolean peersDoTimeout; protected AtomicBoolean peersDoTimeout;
protected AtomicInteger peerCountToTimeout; protected AtomicInteger peerCountToTimeout;
@ -59,6 +72,7 @@ public abstract class AbstractMessageTaskTest<T, R> {
blockchain = blockchainSetupUtil.getBlockchain(); blockchain = blockchainSetupUtil.getBlockchain();
protocolSchedule = blockchainSetupUtil.getProtocolSchedule(); protocolSchedule = blockchainSetupUtil.getProtocolSchedule();
protocolContext = blockchainSetupUtil.getProtocolContext(); protocolContext = blockchainSetupUtil.getProtocolContext();
assert (blockchainSetupUtil.getMaxBlockNumber() >= 20L); assert (blockchainSetupUtil.getMaxBlockNumber() >= 20L);
} }
@ -66,12 +80,33 @@ public abstract class AbstractMessageTaskTest<T, R> {
public void setupTest() { public void setupTest() {
peersDoTimeout = new AtomicBoolean(false); peersDoTimeout = new AtomicBoolean(false);
peerCountToTimeout = new AtomicInteger(0); 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 = ethProtocolManager =
EthProtocolManagerTestUtil.create( EthProtocolManagerTestUtil.create(
blockchain, blockchain,
ethScheduler,
protocolContext.getWorldStateArchive(), protocolContext.getWorldStateArchive(),
() -> peerCountToTimeout.getAndDecrement() > 0 || peersDoTimeout.get()); transactionPool,
ethContext = ethProtocolManager.ethContext(); EthProtocolConfiguration.defaultConfig(),
ethPeers,
ethMessages,
ethContext);
} }
protected abstract T generateDataToBeRequested(); protected abstract T generateDataToBeRequested();
@ -85,7 +120,8 @@ public abstract class AbstractMessageTaskTest<T, R> {
public void completesWhenPeersAreResponsive() { public void completesWhenPeersAreResponsive() {
// Setup a responsive peer // Setup a responsive peer
final RespondingEthPeer.Responder responder = final RespondingEthPeer.Responder responder =
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive()); RespondingEthPeer.blockchainResponder(
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
final RespondingEthPeer respondingPeer = final RespondingEthPeer respondingPeer =
EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000); EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000);

@ -43,7 +43,11 @@ public abstract class PeerMessageTaskTest<T>
// Setup a partially responsive peer // Setup a partially responsive peer
final RespondingEthPeer.Responder responder = final RespondingEthPeer.Responder responder =
RespondingEthPeer.partialResponder( RespondingEthPeer.partialResponder(
blockchain, protocolContext.getWorldStateArchive(), protocolSchedule, 0.5f); blockchain,
protocolContext.getWorldStateArchive(),
transactionPool,
protocolSchedule,
0.5f);
final RespondingEthPeer respondingEthPeer = final RespondingEthPeer respondingEthPeer =
EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000); 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 // Setup a partially responsive peer and a non-responsive peer
final RespondingEthPeer.Responder partialResponder = final RespondingEthPeer.Responder partialResponder =
RespondingEthPeer.partialResponder( RespondingEthPeer.partialResponder(
blockchain, protocolContext.getWorldStateArchive(), protocolSchedule, 0.5f); blockchain,
protocolContext.getWorldStateArchive(),
transactionPool,
protocolSchedule,
0.5f);
final RespondingEthPeer.Responder emptyResponder = RespondingEthPeer.emptyResponder(); final RespondingEthPeer.Responder emptyResponder = RespondingEthPeer.emptyResponder();
final RespondingEthPeer respondingPeer = final RespondingEthPeer respondingPeer =
EthProtocolManagerTestUtil.createPeer(ethProtocolManager); EthProtocolManagerTestUtil.createPeer(ethProtocolManager);
@ -100,16 +104,32 @@ public abstract class RetryingMessageTaskTest<T> extends AbstractMessageTaskTest
// Respond with partial data up until complete. // Respond with partial data up until complete.
respondingPeer.respond( respondingPeer.respond(
RespondingEthPeer.partialResponder( RespondingEthPeer.partialResponder(
blockchain, protocolContext.getWorldStateArchive(), protocolSchedule, 0.25f)); blockchain,
protocolContext.getWorldStateArchive(),
transactionPool,
protocolSchedule,
0.25f));
respondingPeer.respond( respondingPeer.respond(
RespondingEthPeer.partialResponder( RespondingEthPeer.partialResponder(
blockchain, protocolContext.getWorldStateArchive(), protocolSchedule, 0.50f)); blockchain,
protocolContext.getWorldStateArchive(),
transactionPool,
protocolSchedule,
0.50f));
respondingPeer.respond( respondingPeer.respond(
RespondingEthPeer.partialResponder( RespondingEthPeer.partialResponder(
blockchain, protocolContext.getWorldStateArchive(), protocolSchedule, 0.75f)); blockchain,
protocolContext.getWorldStateArchive(),
transactionPool,
protocolSchedule,
0.75f));
respondingPeer.respond( respondingPeer.respond(
RespondingEthPeer.partialResponder( RespondingEthPeer.partialResponder(
blockchain, protocolContext.getWorldStateArchive(), protocolSchedule, 1.0f)); blockchain,
protocolContext.getWorldStateArchive(),
transactionPool,
protocolSchedule,
1.0f));
assertThat(future.isDone()).isTrue(); assertThat(future.isDone()).isTrue();
assertResultMatchesExpectation(requestedData, future.get(), respondingPeer.getEthPeer()); assertResultMatchesExpectation(requestedData, future.get(), respondingPeer.getEthPeer());
@ -140,7 +160,8 @@ public abstract class RetryingMessageTaskTest<T> extends AbstractMessageTaskTest
// Setup a peer // Setup a peer
final RespondingEthPeer.Responder responder = final RespondingEthPeer.Responder responder =
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive()); RespondingEthPeer.blockchainResponder(
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
final RespondingEthPeer respondingPeer = final RespondingEthPeer respondingPeer =
EthProtocolManagerTestUtil.createPeer(ethProtocolManager); EthProtocolManagerTestUtil.createPeer(ethProtocolManager);
respondingPeer.respondWhile(responder, () -> !future.isDone()); respondingPeer.respondWhile(responder, () -> !future.isDone());
@ -153,7 +174,8 @@ public abstract class RetryingMessageTaskTest<T> extends AbstractMessageTaskTest
throws ExecutionException, InterruptedException { throws ExecutionException, InterruptedException {
peerCountToTimeout.set(1); peerCountToTimeout.set(1);
final RespondingEthPeer.Responder responder = final RespondingEthPeer.Responder responder =
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive()); RespondingEthPeer.blockchainResponder(
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
final RespondingEthPeer respondingPeer = final RespondingEthPeer respondingPeer =
EthProtocolManagerTestUtil.createPeer(ethProtocolManager); EthProtocolManagerTestUtil.createPeer(ethProtocolManager);
final T requestedData = generateDataToBeRequested(); 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. // Respond with partial data.
respondingPeer.respond( respondingPeer.respond(
RespondingEthPeer.partialResponder( RespondingEthPeer.partialResponder(
blockchain, protocolContext.getWorldStateArchive(), protocolSchedule, 0.50f)); blockchain,
protocolContext.getWorldStateArchive(),
transactionPool,
protocolSchedule,
0.50f));
assertThat(future.isDone()).isTrue(); assertThat(future.isDone()).isTrue();
// Check that it immediately returns the data we got in the response. // 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.BlockImporter;
import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil; import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil;
import org.hyperledger.besu.ethereum.core.Difficulty; 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.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthMessages; import org.hyperledger.besu.ethereum.eth.manager.EthMessages;
import org.hyperledger.besu.ethereum.eth.manager.EthPeers; import org.hyperledger.besu.ethereum.eth.manager.EthPeers;
@ -93,7 +94,11 @@ public class BlockPropagationManagerTest {
tempProtocolContext.getWorldStateArchive(), tempProtocolContext.getWorldStateArchive(),
tempProtocolContext.getConsensusState()); tempProtocolContext.getConsensusState());
ethProtocolManager = ethProtocolManager =
EthProtocolManagerTestUtil.create(blockchain, blockchainUtil.getWorldArchive()); EthProtocolManagerTestUtil.create(
blockchain,
blockchainUtil.getWorldArchive(),
blockchainUtil.getTransactionPool(),
EthProtocolConfiguration.defaultConfig());
syncConfig = SynchronizerConfiguration.builder().blockPropagationRange(-3, 5).build(); syncConfig = SynchronizerConfiguration.builder().blockPropagationRange(-3, 5).build();
syncState = new SyncState(blockchain, ethProtocolManager.ethContext().getEthPeers()); syncState = new SyncState(blockchain, ethProtocolManager.ethContext().getEthPeers());
blockBroadcaster = mock(BlockBroadcaster.class); blockBroadcaster = mock(BlockBroadcaster.class);

@ -38,7 +38,7 @@ public class ChainHeadTrackerTest {
private final BlockchainSetupUtil<Void> blockchainSetupUtil = BlockchainSetupUtil.forTesting(); private final BlockchainSetupUtil<Void> blockchainSetupUtil = BlockchainSetupUtil.forTesting();
private final MutableBlockchain blockchain = blockchainSetupUtil.getBlockchain(); private final MutableBlockchain blockchain = blockchainSetupUtil.getBlockchain();
private final EthProtocolManager ethProtocolManager = private final EthProtocolManager ethProtocolManager =
EthProtocolManagerTestUtil.create(blockchain, blockchainSetupUtil.getWorldArchive()); EthProtocolManagerTestUtil.create(blockchain);
private final RespondingEthPeer respondingPeer = private final RespondingEthPeer respondingPeer =
RespondingEthPeer.builder() RespondingEthPeer.builder()
.ethProtocolManager(ethProtocolManager) .ethProtocolManager(ethProtocolManager)
@ -62,7 +62,9 @@ public class ChainHeadTrackerTest {
public void shouldRequestHeaderChainHeadWhenNewPeerConnects() { public void shouldRequestHeaderChainHeadWhenNewPeerConnects() {
final Responder responder = final Responder responder =
RespondingEthPeer.blockchainResponder( RespondingEthPeer.blockchainResponder(
blockchainSetupUtil.getBlockchain(), blockchainSetupUtil.getWorldArchive()); blockchainSetupUtil.getBlockchain(),
blockchainSetupUtil.getWorldArchive(),
blockchainSetupUtil.getTransactionPool());
chainHeadTracker.onPeerConnected(respondingPeer.getEthPeer()); chainHeadTracker.onPeerConnected(respondingPeer.getEthPeer());
Assertions.assertThat(chainHeadState().getEstimatedHeight()).isZero(); Assertions.assertThat(chainHeadState().getEstimatedHeight()).isZero();
@ -77,7 +79,9 @@ public class ChainHeadTrackerTest {
public void shouldIgnoreHeadersIfChainHeadHasAlreadyBeenUpdatedWhileWaiting() { public void shouldIgnoreHeadersIfChainHeadHasAlreadyBeenUpdatedWhileWaiting() {
final Responder responder = final Responder responder =
RespondingEthPeer.blockchainResponder( RespondingEthPeer.blockchainResponder(
blockchainSetupUtil.getBlockchain(), blockchainSetupUtil.getWorldArchive()); blockchainSetupUtil.getBlockchain(),
blockchainSetupUtil.getWorldArchive(),
blockchainSetupUtil.getTransactionPool());
chainHeadTracker.onPeerConnected(respondingPeer.getEthPeer()); chainHeadTracker.onPeerConnected(respondingPeer.getEthPeer());
// Change the hash of the current known head // Change the hash of the current known head
@ -92,7 +96,9 @@ public class ChainHeadTrackerTest {
public void shouldCheckTrialingPeerLimits() { public void shouldCheckTrialingPeerLimits() {
final Responder responder = final Responder responder =
RespondingEthPeer.blockchainResponder( RespondingEthPeer.blockchainResponder(
blockchainSetupUtil.getBlockchain(), blockchainSetupUtil.getWorldArchive()); blockchainSetupUtil.getBlockchain(),
blockchainSetupUtil.getWorldArchive(),
blockchainSetupUtil.getTransactionPool());
chainHeadTracker.onPeerConnected(respondingPeer.getEthPeer()); chainHeadTracker.onPeerConnected(respondingPeer.getEthPeer());
Assertions.assertThat(chainHeadState().getEstimatedHeight()).isZero(); 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.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil; 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.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager; import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil; import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil;
import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer; import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer;
import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer.Responder; 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.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.plugin.services.MetricsSystem; import org.hyperledger.besu.plugin.services.MetricsSystem;
@ -50,6 +52,7 @@ public class CheckpointHeaderFetcherTest {
private static ProtocolSchedule<Void> protocolSchedule; private static ProtocolSchedule<Void> protocolSchedule;
private static ProtocolContext<Void> protocolContext; private static ProtocolContext<Void> protocolContext;
private static final MetricsSystem metricsSystem = new NoOpMetricsSystem(); private static final MetricsSystem metricsSystem = new NoOpMetricsSystem();
private static TransactionPool transactionPool;
private EthProtocolManager ethProtocolManager; private EthProtocolManager ethProtocolManager;
private Responder responder; private Responder responder;
private RespondingEthPeer respondingPeer; private RespondingEthPeer respondingPeer;
@ -59,6 +62,7 @@ public class CheckpointHeaderFetcherTest {
final BlockchainSetupUtil<Void> blockchainSetupUtil = BlockchainSetupUtil.forTesting(); final BlockchainSetupUtil<Void> blockchainSetupUtil = BlockchainSetupUtil.forTesting();
blockchainSetupUtil.importAllBlocks(); blockchainSetupUtil.importAllBlocks();
blockchain = blockchainSetupUtil.getBlockchain(); blockchain = blockchainSetupUtil.getBlockchain();
transactionPool = blockchainSetupUtil.getTransactionPool();
protocolSchedule = blockchainSetupUtil.getProtocolSchedule(); protocolSchedule = blockchainSetupUtil.getProtocolSchedule();
protocolContext = blockchainSetupUtil.getProtocolContext(); protocolContext = blockchainSetupUtil.getProtocolContext();
} }
@ -67,9 +71,14 @@ public class CheckpointHeaderFetcherTest {
public void setUpTest() { public void setUpTest() {
ethProtocolManager = ethProtocolManager =
EthProtocolManagerTestUtil.create( EthProtocolManagerTestUtil.create(
blockchain, protocolContext.getWorldStateArchive(), () -> false); blockchain,
() -> false,
protocolContext.getWorldStateArchive(),
transactionPool,
EthProtocolConfiguration.defaultConfig());
responder = responder =
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive()); RespondingEthPeer.blockchainResponder(
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
respondingPeer = respondingPeer =
EthProtocolManagerTestUtil.createPeer( EthProtocolManagerTestUtil.createPeer(
ethProtocolManager, blockchain.getChainHeadBlockNumber()); ethProtocolManager, blockchain.getChainHeadBlockNumber());

@ -62,8 +62,7 @@ public class DownloadHeadersStepTest {
@Before @Before
public void setUp() { public void setUp() {
ethProtocolManager = ethProtocolManager = EthProtocolManagerTestUtil.create(blockchain);
EthProtocolManagerTestUtil.create(blockchain, protocolContext.getWorldStateArchive());
downloader = downloader =
new DownloadHeadersStep<>( new DownloadHeadersStep<>(
protocolSchedule, protocolSchedule,

@ -16,6 +16,7 @@ package org.hyperledger.besu.ethereum.eth.sync.fastsync;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat; 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.ProtocolContext;
import org.hyperledger.besu.ethereum.chain.MutableBlockchain; 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.BlockWithReceipts;
import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil; import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil;
import org.hyperledger.besu.ethereum.core.TransactionReceipt; 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.EthProtocolManager;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil; import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil;
import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer; import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import java.util.List; import java.util.List;
@ -54,8 +57,14 @@ public class DownloadReceiptsStepTest {
@Before @Before
public void setUp() { public void setUp() {
TransactionPool transactionPool = mock(TransactionPool.class);
ethProtocolManager = ethProtocolManager =
EthProtocolManagerTestUtil.create(blockchain, protocolContext.getWorldStateArchive()); EthProtocolManagerTestUtil.create(
blockchain,
() -> false,
protocolContext.getWorldStateArchive(),
transactionPool,
EthProtocolConfiguration.defaultConfig());
downloadReceiptsStep = downloadReceiptsStep =
new DownloadReceiptsStep(ethProtocolManager.ethContext(), new NoOpMetricsSystem()); 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.BlockHeaderTestFixture;
import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil; import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil;
import org.hyperledger.besu.ethereum.core.Difficulty; 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.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthPeer; import org.hyperledger.besu.ethereum.eth.manager.EthPeer;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager; import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
@ -67,8 +68,10 @@ public class FastSyncActionsTest {
ethProtocolManager = ethProtocolManager =
EthProtocolManagerTestUtil.create( EthProtocolManagerTestUtil.create(
blockchain, blockchain,
() -> timeoutCount.getAndDecrement() > 0,
blockchainSetupUtil.getWorldArchive(), blockchainSetupUtil.getWorldArchive(),
() -> timeoutCount.getAndDecrement() > 0); blockchainSetupUtil.getTransactionPool(),
EthProtocolConfiguration.defaultConfig());
fastSyncActions = createFastSyncActions(syncConfig); fastSyncActions = createFastSyncActions(syncConfig);
} }

@ -70,9 +70,8 @@ public class FastSyncChainDownloaderTest {
protocolContext = localBlockchainSetup.getProtocolContext(); protocolContext = localBlockchainSetup.getProtocolContext();
ethProtocolManager = ethProtocolManager =
EthProtocolManagerTestUtil.create( EthProtocolManagerTestUtil.create(
localBlockchain, localBlockchain, new EthScheduler(1, 1, 1, 1, new NoOpMetricsSystem()));
localBlockchainSetup.getWorldArchive(),
new EthScheduler(1, 1, 1, new NoOpMetricsSystem()));
ethContext = ethProtocolManager.ethContext(); ethContext = ethProtocolManager.ethContext();
syncState = new SyncState(protocolContext.getBlockchain(), ethContext.getEthPeers()); 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.chain.MutableBlockchain;
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture; import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil; 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.EthProtocolManager;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil; import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil;
import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer; import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer;
import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer.Responder; 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.sync.fastsync.PivotBlockConfirmer.ContestedPivotBlockException;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.plugin.services.MetricsSystem; import org.hyperledger.besu.plugin.services.MetricsSystem;
@ -51,6 +53,7 @@ public class PivotBlockConfirmerTest {
private final AtomicBoolean timeout = new AtomicBoolean(false); private final AtomicBoolean timeout = new AtomicBoolean(false);
private EthProtocolManager ethProtocolManager; private EthProtocolManager ethProtocolManager;
private MutableBlockchain blockchain; private MutableBlockchain blockchain;
private TransactionPool transactionPool;
private PivotBlockConfirmer<Void> pivotBlockConfirmer; private PivotBlockConfirmer<Void> pivotBlockConfirmer;
private ProtocolSchedule<Void> protocolSchedule; private ProtocolSchedule<Void> protocolSchedule;
@ -59,11 +62,16 @@ public class PivotBlockConfirmerTest {
final BlockchainSetupUtil<Void> blockchainSetupUtil = BlockchainSetupUtil.forTesting(); final BlockchainSetupUtil<Void> blockchainSetupUtil = BlockchainSetupUtil.forTesting();
blockchainSetupUtil.importAllBlocks(); blockchainSetupUtil.importAllBlocks();
blockchain = blockchainSetupUtil.getBlockchain(); blockchain = blockchainSetupUtil.getBlockchain();
transactionPool = blockchainSetupUtil.getTransactionPool();
protocolSchedule = blockchainSetupUtil.getProtocolSchedule(); protocolSchedule = blockchainSetupUtil.getProtocolSchedule();
protocolContext = blockchainSetupUtil.getProtocolContext(); protocolContext = blockchainSetupUtil.getProtocolContext();
ethProtocolManager = ethProtocolManager =
EthProtocolManagerTestUtil.create( EthProtocolManagerTestUtil.create(
blockchain, blockchainSetupUtil.getWorldArchive(), timeout::get); blockchain,
timeout::get,
blockchainSetupUtil.getWorldArchive(),
transactionPool,
EthProtocolConfiguration.defaultConfig());
pivotBlockConfirmer = createPivotBlockConfirmer(3, 1); pivotBlockConfirmer = createPivotBlockConfirmer(3, 1);
} }
@ -85,7 +93,8 @@ public class PivotBlockConfirmerTest {
pivotBlockConfirmer = createPivotBlockConfirmer(2, 1); pivotBlockConfirmer = createPivotBlockConfirmer(2, 1);
final Responder responder = final Responder responder =
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive()); RespondingEthPeer.blockchainResponder(
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
final RespondingEthPeer respondingPeerA = final RespondingEthPeer respondingPeerA =
EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000); EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000);
@ -113,7 +122,8 @@ public class PivotBlockConfirmerTest {
pivotBlockConfirmer = createPivotBlockConfirmer(2, 1); pivotBlockConfirmer = createPivotBlockConfirmer(2, 1);
final Responder responder = final Responder responder =
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive()); RespondingEthPeer.blockchainResponder(
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
final RespondingEthPeer respondingPeerA = final RespondingEthPeer respondingPeerA =
EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000); EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000);
@ -145,7 +155,8 @@ public class PivotBlockConfirmerTest {
pivotBlockConfirmer = createPivotBlockConfirmer(2, 1); pivotBlockConfirmer = createPivotBlockConfirmer(2, 1);
final Responder responder = final Responder responder =
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive()); RespondingEthPeer.blockchainResponder(
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
final RespondingEthPeer respondingPeerA = final RespondingEthPeer respondingPeerA =
EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000); EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000);
@ -184,7 +195,8 @@ public class PivotBlockConfirmerTest {
pivotBlockConfirmer = createPivotBlockConfirmer(2, 1); pivotBlockConfirmer = createPivotBlockConfirmer(2, 1);
final Responder responder = final Responder responder =
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive()); RespondingEthPeer.blockchainResponder(
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
final RespondingEthPeer respondingPeerA = final RespondingEthPeer respondingPeerA =
EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000); EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000);
@ -223,7 +235,8 @@ public class PivotBlockConfirmerTest {
pivotBlockConfirmer = createPivotBlockConfirmer(2, 1); pivotBlockConfirmer = createPivotBlockConfirmer(2, 1);
final Responder responder = final Responder responder =
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive()); RespondingEthPeer.blockchainResponder(
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
final RespondingEthPeer respondingPeerA = final RespondingEthPeer respondingPeerA =
EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000); EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000);
@ -264,7 +277,8 @@ public class PivotBlockConfirmerTest {
pivotBlockConfirmer = createPivotBlockConfirmer(3, 1); pivotBlockConfirmer = createPivotBlockConfirmer(3, 1);
final Responder responderA = final Responder responderA =
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive()); RespondingEthPeer.blockchainResponder(
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
final RespondingEthPeer respondingPeerA = final RespondingEthPeer respondingPeerA =
EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000); 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.BlockHeaderTestFixture;
import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil; import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil;
import org.hyperledger.besu.ethereum.core.Difficulty; 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.EthProtocolManager;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil; import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil;
import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer; import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer;
import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer.Responder; import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer.Responder;
import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator; 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.mainnet.ProtocolSchedule;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.plugin.services.MetricsSystem; import org.hyperledger.besu.plugin.services.MetricsSystem;
@ -54,6 +56,7 @@ public class PivotBlockRetrieverTest {
private final AtomicBoolean timeout = new AtomicBoolean(false); private final AtomicBoolean timeout = new AtomicBoolean(false);
private EthProtocolManager ethProtocolManager; private EthProtocolManager ethProtocolManager;
private MutableBlockchain blockchain; private MutableBlockchain blockchain;
private TransactionPool transactionPool;
private PivotBlockRetriever<Void> pivotBlockRetriever; private PivotBlockRetriever<Void> pivotBlockRetriever;
private ProtocolSchedule<Void> protocolSchedule; private ProtocolSchedule<Void> protocolSchedule;
@ -64,9 +67,15 @@ public class PivotBlockRetrieverTest {
blockchain = blockchainSetupUtil.getBlockchain(); blockchain = blockchainSetupUtil.getBlockchain();
protocolSchedule = blockchainSetupUtil.getProtocolSchedule(); protocolSchedule = blockchainSetupUtil.getProtocolSchedule();
protocolContext = blockchainSetupUtil.getProtocolContext(); protocolContext = blockchainSetupUtil.getProtocolContext();
transactionPool = blockchainSetupUtil.getTransactionPool();
ethProtocolManager = ethProtocolManager =
EthProtocolManagerTestUtil.create( EthProtocolManagerTestUtil.create(
blockchain, blockchainSetupUtil.getWorldArchive(), timeout::get); blockchain,
timeout::get,
blockchainSetupUtil.getWorldArchive(),
transactionPool,
EthProtocolConfiguration.defaultConfig());
pivotBlockRetriever = createPivotBlockRetriever(3, 1, 1); pivotBlockRetriever = createPivotBlockRetriever(3, 1, 1);
} }
@ -87,7 +96,8 @@ public class PivotBlockRetrieverTest {
@Test @Test
public void shouldSucceedWhenAllPeersAgree() { public void shouldSucceedWhenAllPeersAgree() {
final RespondingEthPeer.Responder responder = final RespondingEthPeer.Responder responder =
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive()); RespondingEthPeer.blockchainResponder(
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
final RespondingEthPeer respondingPeerA = final RespondingEthPeer respondingPeerA =
EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000); EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000);
final RespondingEthPeer respondingPeerB = final RespondingEthPeer respondingPeerB =
@ -111,7 +121,8 @@ public class PivotBlockRetrieverTest {
EthProtocolManagerTestUtil.disableEthSchedulerAutoRun(ethProtocolManager); EthProtocolManagerTestUtil.disableEthSchedulerAutoRun(ethProtocolManager);
final Responder responder = final Responder responder =
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive()); RespondingEthPeer.blockchainResponder(
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
final RespondingEthPeer respondingPeerA = final RespondingEthPeer respondingPeerA =
EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000); EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000);
final RespondingEthPeer badPeerA = EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1); final RespondingEthPeer badPeerA = EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1);
@ -153,7 +164,8 @@ public class PivotBlockRetrieverTest {
public void shouldIgnorePeersThatAreNotFullyValidated() { public void shouldIgnorePeersThatAreNotFullyValidated() {
final PeerValidator peerValidator = mock(PeerValidator.class); final PeerValidator peerValidator = mock(PeerValidator.class);
final RespondingEthPeer.Responder responder = final RespondingEthPeer.Responder responder =
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive()); RespondingEthPeer.blockchainResponder(
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
final RespondingEthPeer respondingPeerA = final RespondingEthPeer respondingPeerA =
EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000, peerValidator); EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000, peerValidator);
final RespondingEthPeer badPeerA = final RespondingEthPeer badPeerA =
@ -212,7 +224,8 @@ public class PivotBlockRetrieverTest {
EthProtocolManagerTestUtil.disableEthSchedulerAutoRun(ethProtocolManager); EthProtocolManagerTestUtil.disableEthSchedulerAutoRun(ethProtocolManager);
final Responder responder = final Responder responder =
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive()); RespondingEthPeer.blockchainResponder(
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
final RespondingEthPeer peerA = final RespondingEthPeer peerA =
EthProtocolManagerTestUtil.createPeer(ethProtocolManager, Difficulty.of(1000), 1000); EthProtocolManagerTestUtil.createPeer(ethProtocolManager, Difficulty.of(1000), 1000);
@ -239,7 +252,8 @@ public class PivotBlockRetrieverTest {
EthProtocolManagerTestUtil.disableEthSchedulerAutoRun(ethProtocolManager); EthProtocolManagerTestUtil.disableEthSchedulerAutoRun(ethProtocolManager);
final Responder responder = final Responder responder =
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive()); RespondingEthPeer.blockchainResponder(
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
final Responder emptyResponder = RespondingEthPeer.emptyResponder(); final Responder emptyResponder = RespondingEthPeer.emptyResponder();
final RespondingEthPeer peerA = final RespondingEthPeer peerA =
@ -273,7 +287,8 @@ public class PivotBlockRetrieverTest {
pivotBlockRetriever = createPivotBlockRetriever(2, pivotBlockDelta, 1); pivotBlockRetriever = createPivotBlockRetriever(2, pivotBlockDelta, 1);
final Responder responderA = final Responder responderA =
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive()); RespondingEthPeer.blockchainResponder(
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
final RespondingEthPeer respondingPeerA = final RespondingEthPeer respondingPeerA =
EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000); EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000);
@ -306,7 +321,8 @@ public class PivotBlockRetrieverTest {
pivotBlockRetriever = createPivotBlockRetriever(2, pivotBlockDelta, 1); pivotBlockRetriever = createPivotBlockRetriever(2, pivotBlockDelta, 1);
final Responder responderA = final Responder responderA =
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive()); RespondingEthPeer.blockchainResponder(
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
final RespondingEthPeer respondingPeerA = final RespondingEthPeer respondingPeerA =
EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000); EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000);
@ -342,7 +358,8 @@ public class PivotBlockRetrieverTest {
pivotBlockRetriever = createPivotBlockRetriever(2, pivotBlockDelta, 1); pivotBlockRetriever = createPivotBlockRetriever(2, pivotBlockDelta, 1);
final Responder responderA = final Responder responderA =
RespondingEthPeer.blockchainResponder(blockchain, protocolContext.getWorldStateArchive()); RespondingEthPeer.blockchainResponder(
blockchain, protocolContext.getWorldStateArchive(), transactionPool);
final RespondingEthPeer respondingPeerA = final RespondingEthPeer respondingPeerA =
EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000); 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.chain.MutableBlockchain;
import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil; import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil;
import org.hyperledger.besu.ethereum.core.Difficulty; 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.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager; import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil; import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil;
@ -64,8 +65,10 @@ public class FullSyncChainDownloaderForkTest {
ethProtocolManager = ethProtocolManager =
EthProtocolManagerTestUtil.create( EthProtocolManagerTestUtil.create(
localBlockchain, localBlockchain,
new EthScheduler(1, 1, 1, 1, new NoOpMetricsSystem()),
localBlockchainSetup.getWorldArchive(), localBlockchainSetup.getWorldArchive(),
new EthScheduler(1, 1, 1, new NoOpMetricsSystem())); localBlockchainSetup.getTransactionPool(),
EthProtocolConfiguration.defaultConfig());
ethContext = ethProtocolManager.ethContext(); ethContext = ethProtocolManager.ethContext();
syncState = new SyncState(protocolContext.getBlockchain(), ethContext.getEthPeers()); 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.BlockchainSetupUtil;
import org.hyperledger.besu.ethereum.core.Difficulty; import org.hyperledger.besu.ethereum.core.Difficulty;
import org.hyperledger.besu.ethereum.core.TransactionReceipt; 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.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager; import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil; import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil;
@ -83,8 +84,10 @@ public class FullSyncChainDownloaderTest {
ethProtocolManager = ethProtocolManager =
EthProtocolManagerTestUtil.create( EthProtocolManagerTestUtil.create(
localBlockchain, localBlockchain,
new EthScheduler(1, 1, 1, 1, new NoOpMetricsSystem()),
localBlockchainSetup.getWorldArchive(), localBlockchainSetup.getWorldArchive(),
new EthScheduler(1, 1, 1, new NoOpMetricsSystem())); localBlockchainSetup.getTransactionPool(),
EthProtocolConfiguration.defaultConfig());
ethContext = ethProtocolManager.ethContext(); ethContext = ethProtocolManager.ethContext();
syncState = new SyncState(protocolContext.getBlockchain(), ethContext.getEthPeers()); 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.ProtocolContext;
import org.hyperledger.besu.ethereum.chain.MutableBlockchain; import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil; 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.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager; import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil; import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil;
@ -57,8 +58,10 @@ public class FullSyncDownloaderTest {
ethProtocolManager = ethProtocolManager =
EthProtocolManagerTestUtil.create( EthProtocolManagerTestUtil.create(
localBlockchain, localBlockchain,
new EthScheduler(1, 1, 1, 1, new NoOpMetricsSystem()),
localBlockchainSetup.getWorldArchive(), localBlockchainSetup.getWorldArchive(),
new EthScheduler(1, 1, 1, new NoOpMetricsSystem())); localBlockchainSetup.getTransactionPool(),
EthProtocolConfiguration.defaultConfig());
ethContext = ethProtocolManager.ethContext(); ethContext = ethProtocolManager.ethContext();
syncState = new SyncState(protocolContext.getBlockchain(), ethContext.getEthPeers()); 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.chain.MutableBlockchain;
import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil; import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil;
import org.hyperledger.besu.ethereum.core.Difficulty; 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.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager; import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil; import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil;
@ -65,7 +66,11 @@ public class FullSyncTargetManagerTest {
new ProtocolContext<>(localBlockchain, localWorldState, null); new ProtocolContext<>(localBlockchain, localWorldState, null);
ethProtocolManager = ethProtocolManager =
EthProtocolManagerTestUtil.create( 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(); final EthContext ethContext = ethProtocolManager.ethContext();
localBlockchainSetup.importFirstBlocks(5); localBlockchainSetup.importFirstBlocks(5);
otherBlockchainSetup.importFirstBlocks(20); otherBlockchainSetup.importFirstBlocks(20);

@ -90,9 +90,7 @@ public class SyncStateTest {
@Before @Before
public void setUp() { public void setUp() {
ethProtocolManager = ethProtocolManager = EthProtocolManagerTestUtil.create(blockchain);
EthProtocolManagerTestUtil.create(
blockchain, InMemoryStorageProvider.createInMemoryWorldStateArchive());
ethPeers = spy(ethProtocolManager.ethContext().getEthPeers()); ethPeers = spy(ethProtocolManager.ethContext().getEthPeers());
syncTargetPeer = createPeer(TARGET_DIFFICULTY, TARGET_CHAIN_HEIGHT); syncTargetPeer = createPeer(TARGET_DIFFICULTY, TARGET_CHAIN_HEIGHT);
otherPeer = createPeer(Difficulty.ZERO, 0); 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.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.ethereum.core.InMemoryStorageProvider.createInMemoryBlockchain; import static org.hyperledger.besu.ethereum.core.InMemoryStorageProvider.createInMemoryBlockchain;
import static org.hyperledger.besu.ethereum.core.InMemoryStorageProvider.createInMemoryWorldStateArchive; 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.ProtocolContext;
import org.hyperledger.besu.ethereum.chain.MutableBlockchain; 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.BlockHeader;
import org.hyperledger.besu.ethereum.core.Difficulty; import org.hyperledger.besu.ethereum.core.Difficulty;
import org.hyperledger.besu.ethereum.core.TransactionReceipt; 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.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager; import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil; import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil;
import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer; import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer;
import org.hyperledger.besu.ethereum.eth.manager.task.EthTask; 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.MainnetBlockHeaderFunctions;
import org.hyperledger.besu.ethereum.mainnet.MainnetProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.MainnetProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
@ -140,8 +143,11 @@ public class DetermineCommonAncestorTaskParameterizedTest {
final WorldStateArchive worldStateArchive = createInMemoryWorldStateArchive(); final WorldStateArchive worldStateArchive = createInMemoryWorldStateArchive();
final EthProtocolManager ethProtocolManager = final EthProtocolManager ethProtocolManager =
EthProtocolManagerTestUtil.create(localBlockchain, worldStateArchive); EthProtocolManagerTestUtil.create(
localBlockchain,
worldStateArchive,
mock(TransactionPool.class),
EthProtocolConfiguration.defaultConfig());
final RespondingEthPeer.Responder responder = final RespondingEthPeer.Responder responder =
RespondingEthPeer.blockchainResponder(remoteBlockchain); RespondingEthPeer.blockchainResponder(remoteBlockchain);
final RespondingEthPeer respondingEthPeer = final RespondingEthPeer respondingEthPeer =

@ -22,6 +22,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; 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.BlockDataGenerator;
import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.TransactionReceipt; 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.EthContext;
import org.hyperledger.besu.ethereum.eth.manager.EthPeer; import org.hyperledger.besu.ethereum.eth.manager.EthPeer;
import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager; 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.RespondingEthPeer;
import org.hyperledger.besu.ethereum.eth.manager.exceptions.EthTaskException; import org.hyperledger.besu.ethereum.eth.manager.exceptions.EthTaskException;
import org.hyperledger.besu.ethereum.eth.manager.task.EthTask; 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.MainnetBlockHeaderFunctions;
import org.hyperledger.besu.ethereum.mainnet.MainnetProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.MainnetProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
@ -74,7 +77,12 @@ public class DetermineCommonAncestorTaskTest {
localGenesisBlock = blockDataGenerator.genesisBlock(); localGenesisBlock = blockDataGenerator.genesisBlock();
localBlockchain = createInMemoryBlockchain(localGenesisBlock); localBlockchain = createInMemoryBlockchain(localGenesisBlock);
final WorldStateArchive worldStateArchive = createInMemoryWorldStateArchive(); final WorldStateArchive worldStateArchive = createInMemoryWorldStateArchive();
ethProtocolManager = EthProtocolManagerTestUtil.create(localBlockchain, worldStateArchive); ethProtocolManager =
EthProtocolManagerTestUtil.create(
localBlockchain,
worldStateArchive,
mock(TransactionPool.class),
EthProtocolConfiguration.defaultConfig());
ethContext = ethProtocolManager.ethContext(); ethContext = ethProtocolManager.ethContext();
protocolContext = new ProtocolContext<>(localBlockchain, worldStateArchive, null); 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.EthPV63;
import org.hyperledger.besu.ethereum.eth.messages.GetNodeDataMessage; import org.hyperledger.besu.ethereum.eth.messages.GetNodeDataMessage;
import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration; 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.mainnet.MainnetProtocolSchedule;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData; import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData;
import org.hyperledger.besu.ethereum.rlp.RLP; import org.hyperledger.besu.ethereum.rlp.RLP;
@ -111,7 +112,7 @@ public class WorldStateDownloaderTest {
.build()); .build());
final EthProtocolManager ethProtocolManager = final EthProtocolManager ethProtocolManager =
EthProtocolManagerTestUtil.create(new EthScheduler(1, 1, 1, new NoOpMetricsSystem())); EthProtocolManagerTestUtil.create(new EthScheduler(1, 1, 1, 1, new NoOpMetricsSystem()));
@After @After
public void tearDown() throws Exception { public void tearDown() throws Exception {
@ -576,7 +577,8 @@ public class WorldStateDownloaderTest {
// Respond to node data requests // Respond to node data requests
final List<MessageData> sentMessages = new ArrayList<>(); final List<MessageData> sentMessages = new ArrayList<>();
final RespondingEthPeer.Responder blockChainResponder = final RespondingEthPeer.Responder blockChainResponder =
RespondingEthPeer.blockchainResponder(mock(Blockchain.class), remoteWorldStateArchive); RespondingEthPeer.blockchainResponder(
mock(Blockchain.class), remoteWorldStateArchive, mock(TransactionPool.class));
final RespondingEthPeer.Responder responder = final RespondingEthPeer.Responder responder =
RespondingEthPeer.wrapResponderWithCollector(blockChainResponder, sentMessages); RespondingEthPeer.wrapResponderWithCollector(blockChainResponder, sentMessages);
@ -606,7 +608,7 @@ public class WorldStateDownloaderTest {
@Test @Test
public void stalledDownloader() { public void stalledDownloader() {
final EthProtocolManager ethProtocolManager = 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 // Setup "remote" state
final WorldStateStorage remoteStorage = final WorldStateStorage remoteStorage =
@ -898,10 +900,15 @@ public class WorldStateDownloaderTest {
final WorldStateArchive remoteWorldStateArchive, final WorldStateArchive remoteWorldStateArchive,
final CompletableFuture<?> downloaderFuture) { final CompletableFuture<?> downloaderFuture) {
final RespondingEthPeer.Responder fullResponder = final RespondingEthPeer.Responder fullResponder =
RespondingEthPeer.blockchainResponder(mock(Blockchain.class), remoteWorldStateArchive); RespondingEthPeer.blockchainResponder(
mock(Blockchain.class), remoteWorldStateArchive, mock(TransactionPool.class));
final RespondingEthPeer.Responder partialResponder = final RespondingEthPeer.Responder partialResponder =
RespondingEthPeer.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(); final RespondingEthPeer.Responder emptyResponder = RespondingEthPeer.emptyResponder();
// Send a few partial responses // 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 { public class PendingTransactionsTest {
private static final int MAX_TRANSACTIONS = 5; 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 KEYS1 = KeyPair.generate();
private static final KeyPair KEYS2 = KeyPair.generate(); private static final KeyPair KEYS2 = KeyPair.generate();
private static final String ADDED_COUNTER = "transactions_added_total"; private static final String ADDED_COUNTER = "transactions_added_total";
@ -55,6 +56,7 @@ public class PendingTransactionsTest {
new PendingTransactions( new PendingTransactions(
TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS, TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS,
MAX_TRANSACTIONS, MAX_TRANSACTIONS,
MAX_TRANSACTION_HASHES,
TestClock.fixed(), TestClock.fixed(),
metricsSystem); metricsSystem);
private final Transaction transaction1 = createTransaction(2); private final Transaction transaction1 = createTransaction(2);
@ -551,7 +553,11 @@ public class PendingTransactionsTest {
final int maxTransactionRetentionHours = 1; final int maxTransactionRetentionHours = 1;
final PendingTransactions transactions = final PendingTransactions transactions =
new PendingTransactions( new PendingTransactions(
maxTransactionRetentionHours, MAX_TRANSACTIONS, clock, metricsSystem); maxTransactionRetentionHours,
MAX_TRANSACTIONS,
MAX_TRANSACTION_HASHES,
clock,
metricsSystem);
transactions.addRemoteTransaction(transaction1); transactions.addRemoteTransaction(transaction1);
assertThat(transactions.size()).isEqualTo(1); assertThat(transactions.size()).isEqualTo(1);
@ -569,7 +575,11 @@ public class PendingTransactionsTest {
final int maxTransactionRetentionHours = 1; final int maxTransactionRetentionHours = 1;
final PendingTransactions transactions = final PendingTransactions transactions =
new PendingTransactions( new PendingTransactions(
maxTransactionRetentionHours, MAX_TRANSACTIONS, clock, metricsSystem); maxTransactionRetentionHours,
MAX_TRANSACTIONS,
MAX_TRANSACTION_HASHES,
clock,
metricsSystem);
transactions.addRemoteTransaction(transaction1); transactions.addRemoteTransaction(transaction1);
assertThat(transactions.size()).isEqualTo(1); assertThat(transactions.size()).isEqualTo(1);
clock.step(2L, ChronoUnit.HOURS); clock.step(2L, ChronoUnit.HOURS);
@ -583,7 +593,11 @@ public class PendingTransactionsTest {
final int maxTransactionRetentionHours = 2; final int maxTransactionRetentionHours = 2;
final PendingTransactions transactions = final PendingTransactions transactions =
new PendingTransactions( new PendingTransactions(
maxTransactionRetentionHours, MAX_TRANSACTIONS, clock, metricsSystem); maxTransactionRetentionHours,
MAX_TRANSACTIONS,
MAX_TRANSACTION_HASHES,
clock,
metricsSystem);
transactions.addRemoteTransaction(transaction1); transactions.addRemoteTransaction(transaction1);
assertThat(transactions.size()).isEqualTo(1); assertThat(transactions.size()).isEqualTo(1);
clock.step(3L, ChronoUnit.HOURS); 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.EthProtocol;
import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration; import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration;
import org.hyperledger.besu.ethereum.eth.manager.EthContext; 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.EthProtocolManager;
import org.hyperledger.besu.ethereum.eth.manager.EthScheduler;
import org.hyperledger.besu.ethereum.eth.sync.state.SyncState; import org.hyperledger.besu.ethereum.eth.sync.state.SyncState;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ScheduleBasedBlockHeaderFunctions; import org.hyperledger.besu.ethereum.mainnet.ScheduleBasedBlockHeaderFunctions;
@ -110,19 +113,41 @@ public class TestNode implements Closeable {
genesisState.writeStateTo(worldStateArchive.getMutable()); genesisState.writeStateTo(worldStateArchive.getMutable());
final ProtocolContext<Void> protocolContext = final ProtocolContext<Void> protocolContext =
new ProtocolContext<>(blockchain, worldStateArchive, null); 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 = final EthProtocolManager ethProtocolManager =
new EthProtocolManager( new EthProtocolManager(
blockchain, blockchain,
worldStateArchive,
BigInteger.ONE, BigInteger.ONE,
worldStateArchive,
transactionPool,
EthProtocolConfiguration.defaultConfig(),
ethPeers,
ethMessages,
ethContext,
Collections.emptyList(), Collections.emptyList(),
false, false,
1, scheduler);
1,
1,
TestClock.fixed(),
new NoOpMetricsSystem(),
EthProtocolConfiguration.defaultConfig());
final NetworkRunner networkRunner = final NetworkRunner networkRunner =
NetworkRunner.builder() NetworkRunner.builder()
@ -143,22 +168,6 @@ public class TestNode implements Closeable {
network.subscribeDisconnect( network.subscribeDisconnect(
(connection, reason, initiatedByPeer) -> disconnections.put(connection, reason)); (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(); networkRunner.start();
selfPeer = DefaultPeer.fromEnodeURL(network.getLocalEnode().get()); selfPeer = DefaultPeer.fromEnodeURL(network.getLocalEnode().get());
} }

@ -77,11 +77,14 @@ import org.mockito.ArgumentCaptor;
public class TransactionPoolTest { public class TransactionPoolTest {
private static final int MAX_TRANSACTIONS = 5; private static final int MAX_TRANSACTIONS = 5;
private static final int MAX_TRANSACTION_HASHES = 5;
private static final KeyPair KEY_PAIR1 = KeyPair.generate(); private static final KeyPair KEY_PAIR1 = KeyPair.generate();
private final PendingTransactionListener listener = mock(PendingTransactionListener.class); private final PendingTransactionListener listener = mock(PendingTransactionListener.class);
private final TransactionPool.TransactionBatchAddedListener batchAddedListener = private final TransactionPool.TransactionBatchAddedListener batchAddedListener =
mock(TransactionPool.TransactionBatchAddedListener.class); mock(TransactionPool.TransactionBatchAddedListener.class);
private final TransactionPool.TransactionBatchAddedListener pendingBatchAddedListener =
mock(TransactionPool.TransactionBatchAddedListener.class);
private final MetricsSystem metricsSystem = new NoOpMetricsSystem(); private final MetricsSystem metricsSystem = new NoOpMetricsSystem();
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@ -97,6 +100,7 @@ public class TransactionPoolTest {
new PendingTransactions( new PendingTransactions(
TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS, TransactionPoolConfiguration.DEFAULT_TX_RETENTION_HOURS,
MAX_TRANSACTIONS, MAX_TRANSACTIONS,
MAX_TRANSACTION_HASHES,
TestClock.fixed(), TestClock.fixed(),
metricsSystem); metricsSystem);
private final Transaction transaction1 = createTransaction(1); private final Transaction transaction1 = createTransaction(1);
@ -108,6 +112,7 @@ public class TransactionPoolTest {
private SyncState syncState; private SyncState syncState;
private EthContext ethContext; private EthContext ethContext;
private PeerTransactionTracker peerTransactionTracker; private PeerTransactionTracker peerTransactionTracker;
private PeerPendingTransactionTracker peerPendingTransactionTracker;
@Before @Before
public void setUp() { public void setUp() {
@ -121,15 +126,18 @@ public class TransactionPoolTest {
EthPeers ethPeers = mock(EthPeers.class); EthPeers ethPeers = mock(EthPeers.class);
when(ethContext.getEthPeers()).thenReturn(ethPeers); when(ethContext.getEthPeers()).thenReturn(ethPeers);
peerTransactionTracker = mock(PeerTransactionTracker.class); peerTransactionTracker = mock(PeerTransactionTracker.class);
peerPendingTransactionTracker = mock(PeerPendingTransactionTracker.class);
transactionPool = transactionPool =
new TransactionPool( new TransactionPool(
transactions, transactions,
protocolSchedule, protocolSchedule,
protocolContext, protocolContext,
batchAddedListener, batchAddedListener,
pendingBatchAddedListener,
syncState, syncState,
ethContext, ethContext,
peerTransactionTracker, peerTransactionTracker,
peerPendingTransactionTracker,
Wei.of(2), Wei.of(2),
metricsSystem); metricsSystem);
blockchain.observeBlockAdded(transactionPool); blockchain.observeBlockAdded(transactionPool);
@ -380,9 +388,11 @@ public class TransactionPoolTest {
protocolSchedule, protocolSchedule,
protocolContext, protocolContext,
batchAddedListener, batchAddedListener,
pendingBatchAddedListener,
syncState, syncState,
ethContext, ethContext,
peerTransactionTracker, peerTransactionTracker,
peerPendingTransactionTracker,
Wei.ZERO, Wei.ZERO,
metricsSystem); metricsSystem);
@ -391,6 +401,7 @@ public class TransactionPoolTest {
transactionPool.addRemoteTransactions(singletonList(transaction1)); transactionPool.addRemoteTransactions(singletonList(transaction1));
verify(pendingTransactions).containsTransaction(transaction1.getHash()); verify(pendingTransactions).containsTransaction(transaction1.getHash());
verify(pendingTransactions).tryEvictTransactionHash(transaction1.getHash());
verifyZeroInteractions(transactionValidator); verifyZeroInteractions(transactionValidator);
verifyNoMoreInteractions(pendingTransactions); verifyNoMoreInteractions(pendingTransactions);
} }
@ -497,9 +508,11 @@ public class TransactionPoolTest {
protocolSchedule, protocolSchedule,
protocolContext, protocolContext,
batchAddedListener, batchAddedListener,
pendingBatchAddedListener,
syncState, syncState,
ethContext, ethContext,
peerTransactionTracker, peerTransactionTracker,
peerPendingTransactionTracker,
Wei.ZERO, Wei.ZERO,
metricsSystem); metricsSystem);
@ -563,9 +576,11 @@ public class TransactionPoolTest {
protocolSchedule, protocolSchedule,
protocolContext, protocolContext,
batchAddedListener, batchAddedListener,
pendingBatchAddedListener,
syncState, syncState,
ethContext, ethContext,
peerTransactionTracker, peerTransactionTracker,
peerPendingTransactionTracker,
Wei.ZERO, Wei.ZERO,
metricsSystem); metricsSystem);

@ -163,7 +163,7 @@ public class RetestethContext {
final EthPeers ethPeers = new EthPeers("reteseth", retestethClock, metricsSystem); final EthPeers ethPeers = new EthPeers("reteseth", retestethClock, metricsSystem);
final SyncState syncState = new SyncState(blockchain, ethPeers); 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 EthContext ethContext = new EthContext(ethPeers, new EthMessages(), ethScheduler);
final TransactionPoolConfiguration transactionPoolConfiguration = final TransactionPoolConfiguration transactionPoolConfiguration =

@ -67,4 +67,9 @@ public class PositiveNumber {
public int hashCode() { public int hashCode() {
return Objects.hash(value); return Objects.hash(value);
} }
@Override
public String toString() {
return "+" + value;
}
} }

Loading…
Cancel
Save